9#include "absl/strings/numbers.h"
10#include "absl/strings/str_format.h"
32 return absl::InvalidArgumentError(
33 absl::StrFormat(
"Map ID out of range: 0x%02X", map_id));
35 return absl::OkStatus();
42 const std::string&
name) {
45 if (!value.has_value() || !ParseHexString(*value, &map_id)) {
46 return absl::InvalidArgumentError(
47 absl::StrFormat(
"--%s must be a hex map id (e.g. 0x40 or 40)",
name));
54 const auto value = parser.
GetString(
"tile");
56 if (!value.has_value() || !ParseHexString(*value, &tile_id)) {
57 return absl::InvalidArgumentError(
58 "--tile must be a hex tile16 id (e.g. 0x0255)");
60 if (tile_id < 0 || tile_id > 0xFFFF) {
61 return absl::InvalidArgumentError(
62 absl::StrFormat(
"Tile ID out of range: 0x%X", tile_id));
68 const std::string&
name) {
71 return absl::InvalidArgumentError(absl::StrFormat(
72 "--%s must be a decimal tile coordinate (or 0x-prefixed hex): %s",
name,
73 value.status().message()));
100 if (profile.has_expanded_tile32) {
121 &want.light_world, &want.dark_world, &want.special_world};
123 &got.light_world, &got.dark_world, &got.special_world};
124 for (
int world = 0; world < 3; ++world) {
125 const auto& w = *want_worlds[world];
126 const auto& g = *got_worlds[world];
127 if (w.size() != g.size()) {
128 return absl::DataLossError(
"Reloaded tile grid has a different size");
130 for (
size_t x = 0; x < w.size(); ++x) {
134 for (
size_t y = 0; y < w[x].size() && y < g[x].size(); ++y) {
135 if (w[x][y] != g[x][y]) {
136 return absl::DataLossError(absl::StrFormat(
137 "Saved overworld does not read back: %s World tile (%d,%d) is "
138 "0x%04X, expected 0x%04X",
140 static_cast<int>(y), g[x][y], w[x][y]));
143 return absl::DataLossError(
"Reloaded tile grid column size differs");
146 return absl::OkStatus();
153 uint16_t tile_id,
bool do_write,
bool mock_rom,
157 if (before == tile_id) {
158 formatter.
AddField(
"write_status",
"not-needed");
159 return absl::OkStatus();
162 const std::vector<uint8_t> before_rom = rom.
vector();
172 const auto& after_rom = rom.
vector();
173 int changed_bytes = 0;
174 int changed_ranges = 0;
175 int outside_bytes = 0;
176 size_t first_outside = 0;
177 bool in_range =
false;
178 for (
size_t i = 0; i < after_rom.size(); ++i) {
180 i >= before_rom.size() || after_rom[i] != before_rom[i];
181 changed_bytes += differs ? 1 : 0;
182 changed_ranges += (differs && !in_range) ? 1 : 0;
188 for (
const auto& [start, end] : save_ranges) {
189 owned |= i >= start && i < end;
191 if (!owned && outside_bytes++ == 0) {
195 formatter.
AddField(
"changed_bytes", changed_bytes);
196 formatter.
AddField(
"changed_ranges", changed_ranges);
197 formatter.
AddField(
"bytes_outside_save_ranges", outside_bytes);
198 if (outside_bytes > 0) {
199 return absl::FailedPreconditionError(absl::StrFormat(
200 "Overworld save changed %d byte(s) outside its documented ranges, "
201 "first at PC $%06X; nothing was written",
202 outside_bytes, first_outside));
204 formatter.
AddField(
"verified_grid",
true);
207 formatter.
AddField(
"write_status",
"dry-run");
208 return absl::OkStatus();
211 formatter.
AddField(
"save_status",
"mock-rom-skipped");
212 return absl::OkStatus();
219 formatter.
AddField(
"save_status",
"saved");
225 return absl::DataLossError(
226 "Saved ROM file differs from the verified image");
228 formatter.
AddField(
"verified_reload",
true);
229 return absl::OkStatus();
240 const auto inspection =
243 formatter.
AddField(
"available", inspection.available);
244 formatter.
AddField(
"provenance", inspection.provenance);
245 formatter.
AddField(
"limitation", inspection.limitation);
246 formatter.
AddField(
"overlay_may_replace_tiles",
247 inspection.overlay_may_replace_tiles);
249 formatter.
AddHexField(
"parent_area", inspection.parent_area, 2);
251 static_cast<int>(inspection.matches.size()));
253 for (
const auto& match : inspection.matches) {
254 const auto& row = inspection.rows[match.row_index];
256 formatter.
AddHexField(
"screen_id", match.screen_id, 2);
257 formatter.
AddHexField(
"source_area", match.parent_area, 2);
258 formatter.
AddField(
"tile_x", match.tile_x);
259 formatter.
AddField(
"tile_y", match.tile_y);
260 formatter.
AddField(
"world_x", match.world_x);
261 formatter.
AddField(
"world_y", match.world_y);
262 formatter.
AddHexField(
"map16_id", match.tile16_id, 3);
263 formatter.
AddHexField(
"first_word", match.first_word, 4);
264 formatter.
AddHexField(
"masked_trigger", match.masked_trigger, 3);
265 formatter.
AddHexField(
"destination_marker", row.destination_marker, 4);
266 formatter.
AddHexField(
"arrival_direction", row.direction, 4);
267 if (row.destination_area >= 0) {
268 formatter.
AddHexField(
"destination_area", row.destination_area, 2);
270 formatter.
AddField(
"destination_area",
"unresolved");
276 return absl::OkStatus();
294 AddTileLocationFields(formatter, location);
297 return absl::OkStatus();
304 if (rom !=
nullptr && !rom->
filename().empty()) {
319 const bool do_write = parser.
HasFlag(
"write");
320 const bool mock_rom = parser.
HasFlag(
"mock-rom");
321 if (rom ==
nullptr || !rom->
is_loaded()) {
322 return absl::FailedPreconditionError(
"ROM not loaded");
328 std::filesystem::path(rom->
filename()));
329 if (do_write && !mock_rom && !parser.
HasFlag(
"allow-project-rom") &&
331 return absl::PermissionDeniedError(absl::StrFormat(
332 "Refusing --write to %s: it is inside an Oracle of Secrets checkout "
333 "(Oracle_main.asm next to its Roms/ folder). Copy the ROM (with "
334 "Data/dungeons/custom_collision.json) and write the copy, or pass "
335 "--allow-project-rom deliberately.",
336 active_path.string()));
343 const int tile16_count =
static_cast<int>(overworld.
tiles16().size());
344 if (tile_value >= tile16_count) {
345 return absl::OutOfRangeError(absl::StrFormat(
346 "Tile ID 0x%04X is past the last tile16 definition (0x%04X)",
347 tile_value, tile16_count - 1));
353 formatter.
AddField(
"mode", do_write ?
"write" :
"dry-run");
354 AddTileLocationFields(formatter, location);
356 formatter.
AddHexField(
"tile_after", tile_value, 4);
357 const absl::Status status = ApplyTileEdit(*rom, overworld, location,
358 static_cast<uint16_t
>(tile_value),
359 do_write, mock_rom, formatter);
361 formatter.
AddField(
"error", std::string(status.message()));
373 if (parser.
GetString(
"map").has_value()) {
377 if (
auto world = parser.
GetString(
"world"); world.has_value()) {
380 options.
world = world_id;
385 auto ow_status = overworld.
Load(rom);
386 if (!ow_status.ok()) {
392 overworld,
static_cast<uint16_t
>(tile_id), options);
393 if (!matches_or.ok()) {
394 return matches_or.status();
396 const auto& matches = matches_or.value();
400 formatter.
AddField(
"tile_id", absl::StrFormat(
"0x%04X", tile_id));
401 formatter.
AddField(
"matches_found",
static_cast<int>(matches.size()));
403 "x/y are area-relative (overworld-get-tile/set-tile "
404 "input); local_x/local_y are within map_id; "
405 "global_x/global_y index the world grid");
408 for (
const auto& match : matches) {
410 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", match.map_id));
413 match.local_x, match.local_y);
416 absl::StrFormat(
"0x%02X", area->parent_map));
417 formatter.
AddField(
"x", area->area_x);
418 formatter.
AddField(
"y", area->area_y);
420 formatter.
AddField(
"local_x", match.local_x);
421 formatter.
AddField(
"local_y", match.local_y);
422 formatter.
AddField(
"global_x", match.global_x);
423 formatter.
AddField(
"global_y", match.global_y);
429 return absl::OkStatus();
439 auto ow_status = overworld.
Load(rom);
440 if (!ow_status.ok()) {
446 if (!summary_or.ok()) {
447 return summary_or.status();
449 const auto& summary = summary_or.value();
452 formatter.
AddField(
"screen_id", absl::StrFormat(
"0x%02X", summary.map_id));
456 formatter.
AddField(
"x", summary.map_x);
457 formatter.
AddField(
"y", summary.map_y);
458 formatter.
AddField(
"local_index", summary.local_index);
462 formatter.
AddField(
"label", summary.area_size);
463 formatter.
AddField(
"is_large", summary.is_large_map);
464 formatter.
AddField(
"parent", absl::StrFormat(
"0x%02X", summary.parent_map));
465 formatter.
AddField(
"quadrant", summary.large_quadrant);
467 if (
const auto* parent = overworld.
overworld_map(summary.parent_map);
471 absl::StrFormat(
"%dx%d", columns * 32, rows * 32));
476 absl::StrFormat(
"0x%04X", summary.message_id));
478 absl::StrFormat(
"0x%02X", summary.area_graphics));
480 absl::StrFormat(
"0x%02X", summary.area_palette));
482 absl::StrFormat(
"0x%02X", summary.main_palette));
484 absl::StrFormat(
"0x%02X", summary.animated_gfx));
485 formatter.
AddField(
"subscreen_overlay",
486 absl::StrFormat(
"0x%04X", summary.subscreen_overlay));
487 formatter.
AddField(
"area_specific_bg_color",
488 absl::StrFormat(
"0x%04X", summary.area_specific_bg_color));
493 if (entrances_or.ok()) {
494 for (
const auto& entrance : entrances_or.value()) {
495 if (entrance.map_id_ == screen_id) {
497 formatter.
AddField(
"entrance_id", entrance.entrance_id_);
498 formatter.
AddField(
"x", entrance.x_);
499 formatter.
AddField(
"y", entrance.y_);
500 formatter.
AddField(
"is_hole",
false);
508 for (
const auto& hole : holes_or.value()) {
509 if (hole.map_id_ == screen_id) {
511 formatter.
AddField(
"entrance_id", hole.entrance_id_);
514 formatter.
AddField(
"is_hole",
true);
525 for (
const auto& exit : exits_or.value()) {
526 if (exit.map_id_ == screen_id) {
528 formatter.
AddField(
"room_id", exit.room_id_);
538 for (uint8_t gfx : summary.sprite_graphics) {
544 for (uint8_t pal : summary.sprite_palettes) {
550 for (uint8_t music : summary.area_music) {
551 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", music));
556 for (uint8_t sgfx : summary.static_graphics) {
557 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X", sgfx));
562 formatter.
AddField(
"enabled", summary.has_overlay);
563 formatter.
AddField(
"id", absl::StrFormat(
"0x%04X", summary.overlay_id));
568 return absl::OkStatus();
574 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
578 auto ow_status = overworld.
Load(rom);
579 if (!ow_status.ok()) {
585 if (screen_id_str !=
"all") {
587 if (!ParseHexString(screen_id_str, &map_id)) {
588 return absl::InvalidArgumentError(
589 "Invalid screen ID format. Must be hex.");
596 if (!warps_or.ok()) {
597 return warps_or.status();
599 const auto& warps = warps_or.value();
603 formatter.
AddField(
"screen_filter", screen_id_str);
604 formatter.
AddField(
"total_warps",
static_cast<int>(warps.size()));
607 for (
const auto& warp : warps) {
610 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", warp.map_id));
613 absl::StrFormat(
"(%d,%d)", warp.pixel_x, warp.pixel_y));
614 formatter.
AddField(
"map_pos", absl::StrFormat(
"0x%04X", warp.map_pos));
616 if (warp.entrance_id.has_value()) {
618 absl::StrFormat(
"0x%02X", warp.entrance_id.value()));
620 if (warp.entrance_name.has_value()) {
621 formatter.
AddField(
"entrance_name", warp.entrance_name.value());
623 if (warp.room_id.has_value()) {
625 absl::StrFormat(
"0x%04X", warp.room_id.value()));
628 formatter.
AddField(
"deleted", warp.deleted);
629 formatter.
AddField(
"is_hole", warp.is_hole);
635 return absl::OkStatus();
641 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
644 std::string phase_filter =
"all";
645 if (
auto phase_str = parser.
GetString(
"phase"); phase_str.has_value()) {
647 if (!absl::SimpleAtoi(*phase_str, &phase) || phase < 0 || phase > 2) {
648 return absl::InvalidArgumentError(
"--phase must be 0, 1, or 2");
651 phase_filter = *phase_str;
656 auto ow_status = overworld.
Load(rom);
657 if (!ow_status.ok()) {
662 std::optional<int> requested_screen;
663 if (screen_id_str !=
"all") {
665 if (!ParseHexString(screen_id_str, &map_id)) {
666 return absl::InvalidArgumentError(
667 "Invalid screen ID format. Must be hex.");
670 if (map ==
nullptr) {
671 return absl::InvalidArgumentError(
672 absl::StrFormat(
"Screen 0x%02X is out of range", map_id));
674 requested_screen = map_id;
675 query.
map_id = map->parent();
679 if (!sprites_or.ok()) {
680 return sprites_or.status();
682 const auto& sprites = sprites_or.value();
685 formatter.
AddField(
"screen_filter", screen_id_str);
686 if (requested_screen.has_value()) {
687 formatter.
AddField(
"parent_area", absl::StrFormat(
"0x%02X", *query.
map_id));
689 formatter.
AddField(
"phase_filter", phase_filter);
692 "0=beginning, 1=first_part, 2=second_part (vanilla: game state "
693 "<2 / 2 / >=3; Oracle ZSOW: GameState 0-1 / GameState 2 day / night or "
695 formatter.
AddField(
"total_sprites",
static_cast<int>(sprites.size()));
698 for (
const auto& sprite : sprites) {
700 formatter.
AddField(
"phase", sprite.phase);
702 formatter.
AddField(
"list_index", sprite.list_index);
704 absl::StrFormat(
"0x%02X", sprite.sprite_id));
705 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", sprite.map_id));
708 absl::StrFormat(
"(%d,%d)", sprite.x, sprite.y));
710 "tile", absl::StrFormat(
"(%d,%d)", sprite.local_x, sprite.local_y));
712 if (sprite.sprite_name.has_value()) {
713 formatter.
AddField(
"name", sprite.sprite_name.value());
721 return absl::OkStatus();
727 auto screen_id_str = parser.
GetString(
"screen").value_or(
"all");
731 auto ow_status = overworld.
Load(rom);
732 if (!ow_status.ok()) {
737 std::optional<int> map_filter;
738 if (screen_id_str !=
"all") {
740 if (!ParseHexString(screen_id_str, &map_id)) {
741 return absl::InvalidArgumentError(
742 "Invalid screen ID format. Must be hex.");
753 formatter.
AddField(
"screen_filter", screen_id_str);
757 for (
const auto& item : items) {
758 if (map_filter.has_value() &&
759 static_cast<int>(item.room_map_id_) != map_filter.value()) {
763 std::string world_name =
"Unknown";
770 formatter.
AddField(
"item_id", absl::StrFormat(
"0x%02X", item.id_));
772 if (item.id_ < item_names.size()) {
773 formatter.
AddField(
"item_name", item_names[item.id_]);
776 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", item.room_map_id_));
777 formatter.
AddField(
"world", world_name);
779 absl::StrFormat(
"(%d,%d)", item.game_x_, item.game_y_));
781 absl::StrFormat(
"(%d,%d)", item.x_, item.y_));
787 formatter.
AddField(
"total_items", total_items);
790 return absl::OkStatus();
796 auto entrance_id_str = parser.
GetString(
"entrance").value();
799 if (!ParseHexString(entrance_id_str, &entrance_id)) {
800 return absl::InvalidArgumentError(
801 "Invalid entrance ID format. Must be hex.");
806 auto ow_status = overworld.
Load(rom);
807 if (!ow_status.ok()) {
813 if (!details_or.ok()) {
814 return details_or.status();
816 const auto& details = details_or.value();
821 absl::StrFormat(
"0x%02X", details.entrance_id));
822 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", details.map_id));
825 absl::StrFormat(
"(%d,%d)", details.x, details.y));
826 formatter.
AddField(
"area_position", absl::StrFormat(
"(%d,%d)", details.area_x,
828 formatter.
AddField(
"map_pos", absl::StrFormat(
"0x%04X", details.map_pos));
829 formatter.
AddField(
"is_hole", details.is_hole);
831 if (details.entrance_name.has_value()) {
832 formatter.
AddField(
"name", details.entrance_name.value());
837 return absl::OkStatus();
844 const bool has_screen = parser.
GetString(
"screen").has_value();
845 const bool has_map = parser.
GetString(
"map").has_value();
846 if (has_screen || has_map) {
848 ParseMapIdArg(parser, has_screen ?
"screen" :
"map"));
849 if (has_screen && has_map) {
851 if (other != map_id) {
852 return absl::InvalidArgumentError(
853 "--screen and --map name different screens; pass one");
858 if (
auto world = parser.
GetString(
"world"); world.has_value()) {
861 query.
world = world_id;
863 if (parser.
GetString(
"tile").has_value()) {
865 query.
tile_id =
static_cast<uint16_t
>(tile_id);
868 if (parser.
GetString(
"top").has_value()) {
869 auto top_or = parser.
GetInt(
"top");
870 if (!top_or.ok() || *top_or < 0) {
871 return absl::InvalidArgumentError(
872 "--top must be a non-negative count (0 lists every tile)");
882 formatter.
BeginObject(
"Overworld Tile Statistics");
885 ? absl::StrFormat(
"0x%02X", *query.
map_id)
886 : std::string(
"all"));
889 : std::string(
"all"));
890 formatter.
AddField(
"screens_scanned", summary.screens_scanned);
891 formatter.
AddField(
"total_tiles", summary.total_tiles);
892 formatter.
AddField(
"unique_tiles",
static_cast<int>(summary.tiles.size()));
894 if (query.
tile_id.has_value()) {
897 for (
const auto& usage : summary.tiles) {
898 if (usage.tile_id == *query.
tile_id) {
900 screens = usage.screens;
905 formatter.
AddField(
"tile_id", absl::StrFormat(
"0x%04X", *query.
tile_id));
907 formatter.
AddField(
"screens", screens);
912 for (
const auto& world : summary.worlds) {
915 formatter.
AddField(
"screens", world.screens);
916 formatter.
AddField(
"total_tiles", world.total_tiles);
917 formatter.
AddField(
"unique_tiles", world.unique_tiles);
925 for (
const auto& screen : summary.screens) {
927 formatter.
AddField(
"map_id", absl::StrFormat(
"0x%02X", screen.map_id));
930 absl::StrFormat(
"0x%02X", screen.parent_map));
931 formatter.
AddField(
"total_tiles", screen.total_tiles);
932 formatter.
AddField(
"unique_tiles", screen.unique_tiles);
933 formatter.
AddField(
"top_tile", absl::StrFormat(
"0x%04X", screen.top_tile));
934 formatter.
AddField(
"top_tile_count", screen.top_tile_count);
935 if (query.
tile_id.has_value()) {
936 formatter.
AddField(
"tile_count", screen.tile_count);
942 const size_t shown = top == 0 ? summary.tiles.size()
943 : std::min<size_t>(summary.tiles.size(), top);
944 formatter.
AddField(
"tile_counts_shown",
static_cast<int>(shown));
946 for (
size_t i = 0; i < shown; ++i) {
947 const auto& usage = summary.tiles[i];
949 formatter.
AddField(
"tile_id", absl::StrFormat(
"0x%04X", usage.tile_id));
950 formatter.
AddField(
"count", usage.count);
951 formatter.
AddField(
"screens", usage.screens);
957 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 LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
const auto & vector() const
absl::Status SaveToFile(const SaveSettings &settings)
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
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 ExecuteWithContext(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter, const resources::CommandInvocationContext &invocation_context) override
Execute with immutable, invocation-scoped ROM path identity.
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.
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
absl::Status SaveMap32Expanded()
Save expanded tile32 definitions (v1+ ROMs)
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
std::vector< std::pair< uint32_t, uint32_t > > GetProjectedWriteRanges() const
Get the projected write ranges (PC offsets) for overworld map saves.
absl::Status SaveMap32Tiles()
Save tile32 definitions to ROM.
const std::vector< gfx::Tile16 > & tiles16() const
absl::Status CreateTile32Tilemap()
Build tile32 tilemap from current tile16 data.
const std::vector< OverworldMap > & overworld_maps() const
auto overworld_map(int i) const
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
absl::Status VerifyTileGridRoundTrip(const Rom &rom, const zelda3::Overworld &expected)
void AddTileLocationFields(resources::OutputFormatter &formatter, const overworld::AreaTileLocation &location)
absl::Status ApplyTileEdit(Rom &rom, zelda3::Overworld &overworld, const overworld::AreaTileLocation &location, uint16_t tile_id, bool do_write, bool mock_rom, resources::OutputFormatter &formatter)
absl::StatusOr< int > ParseCoordinateArg(const resources::ArgumentParser &parser, const std::string &name)
absl::StatusOr< int > ParseMapIdArg(const resources::ArgumentParser &parser, const std::string &name)
absl::Status SaveOverworldTileGrid(Rom &rom, zelda3::Overworld &overworld)
absl::StatusOr< int > ParseTileIdArg(const resources::ArgumentParser &parser)
absl::Status ValidateMapId(int map_id)
bool IsOracleProjectRomPath(const std::filesystem::path &rom_path)
absl::StatusOr< TileUsageSummary > CollectTileUsage(zelda3::Overworld &overworld, const TileUsageQuery &query)
absl::StatusOr< int > InferWorldFromMapId(int map_id)
absl::StatusOr< MapSummary > BuildMapSummary(zelda3::Overworld &overworld, int map_id)
absl::Status WriteAreaTile(zelda3::Overworld &overworld, const AreaTileLocation &location, uint16_t tile_id)
absl::StatusOr< std::vector< WarpEntry > > CollectWarpEntries(const zelda3::Overworld &overworld, const WarpQuery &query)
absl::StatusOr< int > ParseWorldSpecifier(std::string_view value)
absl::StatusOr< AreaTileLocation > ResolveAreaTileForMap(const zelda3::Overworld &overworld, int map_id, int x, int y)
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< uint16_t > ReadAreaTile(zelda3::Overworld &overworld, const AreaTileLocation &location)
absl::StatusOr< AreaTileLocation > LocateScreenTile(const zelda3::Overworld &overworld, int screen_id, int screen_x, int screen_y)
absl::StatusOr< std::vector< TileMatch > > FindTileMatches(zelda3::Overworld &overworld, uint16_t tile_id, const TileSearchOptions &options)
std::string WarpTypeName(WarpType type)
const char * SpritePhaseName(int phase)
std::string WorldName(int world)
std::pair< int, int > AreaScreenSpan(const zelda3::OverworldMap &parent_map)
bool ParseHexString(absl::string_view str, uint64_t *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
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
SpecialEntranceInspection InspectSpecialEntrances(const Rom &rom, Overworld &overworld, int map_id)
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
OverworldRomProfile DetectOverworldRomProfile(const Rom &rom)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
#define RETURN_IF_ERROR(expr)
bool load_resource_labels
std::optional< int > phase
std::optional< int > map_id
std::optional< int > world
std::optional< int > map_id
std::optional< uint16_t > tile_id
std::optional< int > map_id
std::optional< int > world
std::optional< int > map_id
std::optional< std::filesystem::path > active_rom_path
std::optional< std::filesystem::path > source_rom_path
static const std::vector< std::string > & GetItemNames()