13#include <unordered_map>
16#include "absl/status/status.h"
17#include "absl/status/statusor.h"
18#include "absl/strings/str_format.h"
41 std::array<int, 4> destinations{};
42 int storage_bytes_per_quadrant = 0;
43 int definition_capacity = 0;
44 bool expanded =
false;
52 layout.
expanded = profile.has_expanded_tile32;
66 return absl::OutOfRangeError(absl::StrFormat(
67 "Tile32 destination 0x%06X is outside the ROM",
destination));
70 usable_bytes = std::min(usable_bytes,
71 static_cast<int>(std::min<size_t>(
72 available, std::numeric_limits<int>::max())));
79 std::sort(ordered_destinations.begin(), ordered_destinations.end());
80 for (
size_t i = 1; i < ordered_destinations.size(); ++i) {
81 const int span = ordered_destinations[i] - ordered_destinations[i - 1];
83 return absl::FailedPreconditionError(
84 "Tile32 destinations overlap or share the same address");
86 usable_bytes = std::min(usable_bytes, span);
91 if (usable_bytes <= 0) {
92 return absl::ResourceExhaustedError(
93 "Tile32 destinations have no usable packed storage");
105 return absl::InvalidArgumentError(absl::StrFormat(
106 "Tile32 definition count %zu is not aligned to a packed group of %d",
110 return absl::ResourceExhaustedError(absl::StrFormat(
111 "Number of unique Tiles32: %zu Out of: %d\n"
112 "Unique Tile32 count exceeds the %s destination capacity; the ROM has "
115 layout.
expanded ?
"expanded" :
"vanilla"));
118 const size_t required_bytes =
122 return absl::ResourceExhaustedError(absl::StrFormat(
123 "Tile32 definitions require %zu bytes per quadrant; destination "
124 "capacity is %d bytes",
127 return absl::OkStatus();
139 if (
rom->size() == 0) {
140 return absl::InvalidArgumentError(
"ROM file not loaded");
142 const auto layout =
rom->InspectLyraLayout();
144 return absl::FailedPreconditionError(
145 "Invalid Lyra descriptor; overworld loading refused");
148 return absl::FailedPreconditionError(
149 "Lyra descriptor canvas loading is not yet supported");
239 return absl::OkStatus();
243 if (
rom ==
nullptr ||
rom->size() == 0) {
244 return absl::InvalidArgumentError(
"ROM file not loaded");
247 return absl::FailedPreconditionError(
248 "Lyra terrain preview requires a fresh overworld model");
250 auto layout =
rom->InspectLyraLayout();
252 return absl::FailedPreconditionError(
253 "Valid Lyra descriptor required for terrain preview");
279 return absl::FailedPreconditionError(
280 absl::StrFormat(
"Lyra map %02X stream is invalid", map_index));
282 const int local = map_index & 0x3F;
283 const int sx = local % 8;
284 const int sy = local / 8;
286 for (
int y = 0; y < 16; ++y) {
287 for (
int x = 0; x < 16; ++x) {
288 const int tile = (*tiles)[y * 16 + x];
290 return absl::FailedPreconditionError(
291 "Lyra Tile32 definition unavailable");
307 return absl::OkStatus();
327 }
else if (i == parent + 1) {
329 }
else if (i == parent + 8) {
331 }
else if (i == parent + 9) {
342 std::array<bool, kNumOverworldMaps> map_checked{};
343 std::ranges::fill(map_checked,
false);
346 for (
int world_offset = 0; world_offset < 128; world_offset += 64) {
347 for (
int local = 0; local < 64; local++) {
348 int i = world_offset + local;
363 }
else if (i == parent + 1) {
365 }
else if (i == parent + 8) {
367 }
else if (i == parent + 9) {
372 map_checked[i] =
true;
377 std::array<int, 4> siblings = {parent, parent + 1, parent + 8,
379 int world_start = world_offset;
380 int world_end = world_offset + 64;
381 for (
int q = 0; q < 4; q++) {
382 int sibling = siblings[q];
384 if (sibling >= world_start && sibling < world_end &&
385 !map_checked[sibling]) {
387 map_checked[sibling] =
true;
393 map_checked[i] =
true;
411 int i = world + xx + (yy * 8);
413 if (i >=
static_cast<int>(map_checked.size())) {
417 if (!map_checked[i]) {
418 switch (maps[i].area_size()) {
420 map_checked[i] =
true;
425 map_checked[i] =
true;
426 maps[i].SetAsLargeMap(i, 0);
428 if (i + 1 <
static_cast<int>(maps.size())) {
429 map_checked[i + 1] =
true;
430 maps[i + 1].SetAsLargeMap(i, 1);
433 if (i + 8 <
static_cast<int>(maps.size())) {
434 map_checked[i + 8] =
true;
435 maps[i + 8].SetAsLargeMap(i, 2);
438 if (i + 9 <
static_cast<int>(maps.size())) {
439 map_checked[i + 9] =
true;
440 maps[i + 9].SetAsLargeMap(i, 3);
447 map_checked[i] =
true;
450 maps[i].SetParent(i);
453 if (i + 1 <
static_cast<int>(maps.size())) {
454 map_checked[i + 1] =
true;
455 maps[i + 1].SetParent(i);
463 map_checked[i] =
true;
466 maps[i].SetParent(i);
469 if (i + 8 <
static_cast<int>(maps.size())) {
470 map_checked[i + 8] =
true;
471 maps[i + 8].SetParent(i);
494 return absl::InvalidArgumentError(
495 absl::StrFormat(
"Invalid parent index: %d", parent_index));
504 return absl::FailedPreconditionError(
505 "Wide and Tall areas require ZSCustomOverworld v3+");
509 return absl::FailedPreconditionError(
510 "Special World multi-area changes require ZSCustomOverworld v3+");
514 "ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, "
518 static_cast<int>(size),
522 std::vector<int> old_siblings;
528 old_siblings = {old_parent, old_parent + 1, old_parent + 8,
532 old_siblings = {old_parent, old_parent + 1};
535 old_siblings = {old_parent, old_parent + 8};
538 old_siblings = {parent_index};
544 std::vector<int> new_siblings;
547 new_siblings = {parent_index};
550 new_siblings = {parent_index, parent_index + 1, parent_index + 8,
554 new_siblings = {parent_index, parent_index + 1};
557 new_siblings = {parent_index, parent_index + 8};
560 for (
int sibling : new_siblings) {
563 return absl::FailedPreconditionError(
564 absl::StrFormat(
"Area size crosses an overworld boundary: parent=%d "
566 parent_index, sibling));
571 for (
int old_sibling : old_siblings) {
585 for (
size_t i = 0; i < new_siblings.size(); ++i) {
591 for (
int sibling : new_siblings) {
598 for (
int sibling : new_siblings) {
606 std::set<int> all_affected;
607 for (
int sibling : old_siblings) {
608 all_affected.insert(sibling);
610 for (
int sibling : new_siblings) {
611 all_affected.insert(sibling);
616 for (
int sibling : all_affected) {
632 for (
int sibling : all_affected) {
653 "Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
658 parent_index, old_siblings.size(), new_siblings.size());
660 return absl::OkStatus();
664 int index,
int quadrant,
int dimension,
const uint32_t* map32address) {
666 auto arg1,
rom()->ReadByte(map32address[dimension] + quadrant + (index)));
668 rom()->ReadWord(map32address[dimension] + (index) +
669 (quadrant <= 1 ? 4 : 5)));
670 return (uint16_t)(arg1 +
671 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
675 constexpr int kMap32TilesLength = 0x33F0;
676 int num_tile32 = kMap32TilesLength;
677 uint32_t map32address[4] = {
686 util::logf(
"Expanded tile32 flag: %d", expanded_flag);
687 if (profile.has_expanded_tile32) {
699 for (
int i = 0; i < num_tile32; i += 6) {
701 for (
int k = 0; k < 4; k++) {
725 for (
int i = 0; i < 0x200; i++) {
731 return absl::OkStatus();
744 util::logf(
"Expanded tile16 flag: %d", expanded_flag);
745 if (expanded !=
nullptr) {
746 *expanded = profile.has_expanded_tile16;
748 if (profile.has_expanded_tile16) {
755 std::vector<gfx::Tile16> tiles16;
756 tiles16.reserve(num_tile16);
757 for (
int i = 0; i < num_tile16; i += 1) {
770 tiles16.emplace_back(t0, t1, t2, t3);
776 bool expanded =
false;
784 return absl::OkStatus();
789 int position_x1 = (x * 2) + (sx * 32);
790 int position_y1 = (y * 2) + (sy * 32);
791 int position_x2 = (x * 2) + 1 + (sx * 32);
792 int position_y2 = (y * 2) + 1 + (sy * 32);
800 switch (world_type) {
819 int local_index = map_index % 64;
820 int sx = local_index % 8;
821 int sy = local_index / 8;
825 for (
int y = 0; y < 32; ++y) {
826 for (
int x = 0; x < 32; ++x) {
827 world[(sx * 32) + x][(sy * 32) + y] = 0;
833 std::vector<uint8_t>& bytes2,
int i,
int sx,
834 int sy,
int& ttpos) {
835 for (
int y = 0; y < 16; y++) {
836 for (
int x = 0; x < 16; x++) {
837 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
853 const auto get_ow_map_gfx_ptr = [
this](
int index, uint32_t map_ptr) {
854 int p = (
rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
855 (
rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
856 (
rom()->data()[map_ptr + (3 * index)]);
860 constexpr uint32_t kBaseLowest = 0x0FFFFF;
861 constexpr uint32_t kBaseHighest = 0x0F8000;
863 uint32_t lowest = kBaseLowest;
864 uint32_t highest = kBaseHighest;
871 const bool allow_special_tail =
877 if (!allow_special_tail &&
894 auto p1 = get_ow_map_gfx_ptr(
896 auto p2 = get_ow_map_gfx_ptr(
901 bool pointers_valid =
902 (p1 > 0 && p2 > 0 && p1 <
rom()->size() && p2 <
rom()->size());
903 if (!pointers_valid) {
925 if (p1 <= lowest && p1 > kBaseHighest)
927 if (p2 <= lowest && p2 > kBaseHighest)
931 size_t max_size_p2 =
rom()->size() - p2;
934 size_t max_size_p1 =
rom()->size() - p1;
939 if (bytes.empty() || bytes2.empty()) {
959 return absl::OkStatus();
963 [[maybe_unused]]
auto size =
tiles16_.size();
969 constexpr int kDarkWorldEssential =
971 constexpr int kSpecialWorldEssential =
973 std::vector<int> essential_map_ids;
977 "Building essential maps only (first %d maps per world) for faster "
987 bool is_essential =
false;
989 if (i < kLightWorldEssential) {
998 essential_map_ids.push_back(i);
1013 if (map->is_large_map() &&
1015 if (map->parent() != i && !map->is_initialized()) {
1018 map->set_sprite_graphics(0, 0x0E);
1019 map->set_sprite_graphics(1, 0x0E);
1020 map->set_sprite_graphics(2, 0x0E);
1021 map->set_area_graphics(
1025 }
else if (i == 0x88) {
1026 map->set_area_graphics(0x51);
1027 map->set_area_palette(0x00);
1031 map->set_area_palette(
1048 std::vector<std::future<absl::Status>> futures;
1052 bool is_essential =
false;
1055 if (i < kLightWorldEssential) {
1056 is_essential =
true;
1058 is_essential =
true;
1060 is_essential =
true;
1072 auto task_function = [
this, i, size, world_type]() {
1076 futures.emplace_back(std::async(std::launch::async, task_function));
1084 for (
auto& future : futures) {
1090 for (
int map_index : essential_map_ids) {
1095 util::logf(
"Essential maps built. Remaining maps will be built on-demand.");
1096 return absl::OkStatus();
1101 const int map_count =
1103 if (map_index < 0 || map_index >= map_count) {
1104 return absl::InvalidArgumentError(
"Invalid map index");
1110 const bool allow_special_tail =
1113 if (!descriptor_preview && !allow_special_tail &&
1119 return absl::OkStatus();
1131 return absl::OkStatus();
1161 if (map->parent() != map_index && !map->is_initialized()) {
1165 map_index != 0x88) {
1167 map->set_sprite_graphics(0, 0x0E);
1168 map->set_sprite_graphics(1, 0x0E);
1169 map->set_sprite_graphics(2, 0x0E);
1170 map->set_area_graphics(
1174 }
else if (map_index == 0x88) {
1175 map->set_area_graphics(0x51);
1176 map->set_area_palette(0x00);
1200 uint64_t hash = 0xcbf29ce484222325ULL;
1201 auto mix = [&hash](uint64_t value) {
1203 hash *= 0x100000001b3ULL;
1206 for (
const auto& tile16 :
tiles16_) {
1207 for (
const auto& info : tile16.tiles_info) {
1208 mix(
static_cast<uint64_t
>(info.id_) | (uint64_t{info.palette_} << 16) |
1209 (uint64_t{info.vertical_mirror_} << 24) |
1210 (uint64_t{info.horizontal_mirror_} << 25) |
1211 (uint64_t{info.over_} << 26));
1223 map.LoadAreaGraphics();
1225 if (map.HasGraphicsPreview()) {
1235 const std::vector<uint8_t>* cached_gfx =
nullptr;
1236 const std::vector<uint8_t>* cached_blockset =
nullptr;
1238 if (it !=
tileset_cache_.end() && it->second.sheet_revisions != revisions) {
1245 cached_gfx = &it->second.current_gfx;
1246 if (it->second.tiles16_fingerprint == fingerprint &&
1247 !it->second.tile16_blockset.empty()) {
1248 cached_blockset = &it->second.tile16_blockset;
1254 GetMapTiles(world_type), cached_gfx, cached_blockset));
1260 if (e->second.last_use < oldest->second.last_use) {
1267 it->second.current_gfx = map.current_graphics();
1268 it->second.sheet_revisions = revisions;
1270 if (cached_blockset ==
nullptr) {
1271 it->second.tile16_blockset = map.current_tile16_blockset();
1272 it->second.tiles16_fingerprint = fingerprint;
1275 return absl::OkStatus();
1284 for (
size_t i = 0; i < key.size(); ++i) {
1294 const auto& key = it->first;
1295 if (std::find(key.begin(), key.end(),
static_cast<uint8_t
>(sheet)) !=
1312 if (map_index < 0 ||
1317 const int parent = map.parent();
1322 if (parent == map_index || parent < 0 ||
1324 map.ClearInheritedAreaProperties();
1330absl::StatusOr<Overworld::SubscreenOverlayLayer>
1333 return absl::InvalidArgumentError(
"Invalid map index");
1338 layer.
overlay_id = map.render_subscreen_overlay();
1385 int parent_id = map->parent();
1386 std::vector<int> siblings;
1392 switch (map->area_size()) {
1394 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
1397 siblings = {parent_id, parent_id + 1};
1400 siblings = {parent_id, parent_id + 8};
1403 siblings = {map_index};
1408 if (map->is_large_map()) {
1409 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
1411 siblings = {map_index};
1416 for (
int sibling : siblings) {
1429#ifdef __EMSCRIPTEN__
1431 for (
int state = 0; state < 3; ++state) {
1437 std::vector<std::future<absl::Status>> futures;
1438 for (
int state = 0; state < 3; ++state) {
1439 futures.emplace_back(
1440 std::async(std::launch::async, [
this, layout, state]() {
1446 for (
auto& future : futures) {
1451 return absl::OkStatus();
1455 int num_maps_per_gamestate,
1457 if (!
rom_ || game_state < 0 || game_state >= 3 ||
1458 num_maps_per_gamestate < 0 || num_maps_per_gamestate > 160 ||
1460 return absl::FailedPreconditionError(
1461 "Sprite loading requires initialized maps");
1463 std::vector<Sprite> loaded;
1464 std::array<bool, 160> loaded_maps{};
1465 for (
int i = 0; i < num_maps_per_gamestate; ++i) {
1469 *
rom_, sprites_per_gamestate_ptr + i * 2));
1470 loaded_maps[i] =
true;
1471 const int screen = i % 64;
1472 for (
size_t pos = 0; pos + 1 < bytes.size(); pos += 3) {
1473 const auto y = bytes[pos];
1474 const auto x = bytes[pos + 1];
1476 static_cast<uint8_t
>(i), bytes[pos + 2], x & 0x3F,
1477 y & 0x3F, (x & 0x3F) * 16 + (screen % 8) * 512,
1478 (y & 0x3F) * 16 + (screen / 8) * 512);
1479 loaded.back().set_overworld_coordinate_flags(x, y);
1481 loaded.back().Draw();
1486 return absl::OkStatus();
1491 return absl::FailedPreconditionError(
"No ROM for overworld sprites");
1493 bool loaded =
false;
1494 for (
int state = 0; state < 3; ++state) {
1495 for (
int map = 0; map < 160; ++map) {
1502 if (sprite.deleted())
1504 const int map = sprite.map_id();
1505 if (map >= 160 || !edits[state][map]) {
1506 return absl::FailedPreconditionError(
1507 "Sprite belongs to an unloaded or unsupported map/game state");
1511 const int x = sprite.x() - (map % 8) * 512;
1512 const int y = sprite.y() - ((map % 64) / 8) * 512;
1513 if (x < 0 || x >= 1024 || y < 0 || y >= 1024 || x % 16 || y % 16) {
1514 return absl::InvalidArgumentError(
1515 "Overworld sprite must be on a 16-pixel grid within its parent "
1518 auto& bytes = *edits[state][map];
1519 bytes.push_back(
static_cast<uint8_t
>(y / 16) |
1520 sprite.overworld_y_flags());
1521 bytes.push_back(
static_cast<uint8_t
>(x / 16) |
1522 sprite.overworld_x_flags());
1523 bytes.push_back(sprite.id());
1525 for (
auto& bytes : edits[state])
1527 bytes->push_back(0xFF);
1540 if (
rom ==
nullptr) {
1541 return absl::FailedPreconditionError(
"No ROM for overworld sprites");
1569 return absl::OkStatus();
1603 util::logf(
"Overworld tiles unchanged; preserving compressed map pointers");
1606 return absl::OkStatus();
1620 std::vector<uint8_t> single_map_1(512);
1621 std::vector<uint8_t> single_map_2(512);
1625 for (
int y = 0; y < 16; y++) {
1626 for (
int x = 0; x < 16; x++) {
1628 single_map_1[npos] = packed & 0xFF;
1629 single_map_2[npos] = (packed >> 8) & 0xFF;
1638 if (a.empty() || b.empty()) {
1639 return absl::AbortedError(
"Error compressing map gfx.");
1646 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
1650 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
1651 util::logf(
"Pos set to overflow region for map %s at %s",
1656 const auto compare_array = [](
const std::vector<uint8_t>& array1,
1657 const std::vector<uint8_t>& array2) ->
bool {
1658 if (array1.size() != array2.size()) {
1662 for (
size_t i = 0; i < array1.size(); i++) {
1663 if (array1[i] != array2[i]) {
1671 for (
int j = 0; j < i; j++) {
1685 std::copy(a.begin(), a.end(),
map_data_p1[i].begin());
1688 util::logf(
"Saving map pointers1 and compressed data for map %s at %s",
1705 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
1709 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
1710 util::logf(
"Pos set to overflow region for map %s at %s",
1717 std::copy(b.begin(), b.end(),
map_data_p2[i].begin());
1720 util::logf(
"Saving map pointers2 and compressed data for map %s at %s",
1741 return absl::AbortedError(
"Too many maps data " + std::to_string(pos));
1746 return absl::OkStatus();
1751 std::vector<std::pair<uint32_t, uint32_t>> ranges;
1754 uint32_t map32address[4] = {
1757 int map32_len = 0x33F0;
1768 for (
int i = 0; i < 4; ++i) {
1769 if (map32address[i] > 0) {
1770 ranges.emplace_back(map32address[i], map32address[i] + map32_len);
1781 ranges.emplace_back(map16_addr, map16_addr + map16_len);
1787 constexpr uint32_t kCompressedBank0bEnd = 0x5FE70;
1788 constexpr uint32_t kCompressedBank0cStart = 0x60000;
1789 constexpr uint32_t kCompressedBank0cEnd = 0x6411F;
1797 kCompressedBank0bEnd);
1798 ranges.emplace_back(kCompressedBank0cStart, kCompressedBank0cEnd);
1809 bool use_expanded_transitions =
1812 if (use_expanded_transitions) {
1818 std::vector<uint8_t> checked_map;
1830 if (std::find(checked_map.begin(), checked_map.end(), i) !=
1831 checked_map.end()) {
1838 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
1839 for (
const auto& offset : large_map_offsets) {
1864 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1867 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1871 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1874 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1878 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1881 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1885 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1888 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1892 (parent_x_pos * 0x200)));
1894 (parent_y_pos * 0x200)));
1898 (parent_x_pos * 0x200)));
1901 (parent_y_pos * 0x200)));
1905 (parent_x_pos * 0x200)));
1908 (parent_y_pos * 0x200)));
1912 (parent_x_pos * 0x200)));
1915 (parent_y_pos * 0x200)));
1924 if (parent_x_pos == 0) {
2027 checked_map.emplace_back(i);
2028 checked_map.emplace_back((i + 1));
2029 checked_map.emplace_back((i + 8));
2030 checked_map.emplace_back((i + 9));
2052 if (i - 1 >= 0 && parent_x_pos != 0) {
2064 if (i + 1 < 64 && parent_x_pos != 7) {
2108 (uint16_t)((y_pos * 0x200) - 0xE0)));
2110 (uint16_t)((x_pos * 0x200) - 0x100)));
2117 checked_map.emplace_back(i);
2126 const auto legacy_size_byte = [
this](
int map_index) -> uint8_t {
2129 const auto legacy_loading_size_byte = [
this](
int map_index) -> uint8_t {
2135 legacy_size_byte(map_index)));
2137 legacy_loading_size_byte(map_index)));
2143 legacy_loading_size_byte(map_index)));
2146 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
2149 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
2151 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
2153 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
2155 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
2157 return absl::OkStatus();
2161 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
2162 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
2163 int screen_change_1,
int screen_change_2,
int screen_change_3,
2164 int screen_change_4) {
2167 rom()->WriteShort(transition_target_north + (i * 2),
2168 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
2170 rom()->WriteShort(transition_target_west + (i * 2),
2171 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
2174 rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
2176 rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
2179 uint16_t by_screen1_small = 0x0060;
2182 if ((i % 0x40) - 1 >= 0) {
2187 west_neighbor.large_index() == 3) {
2188 by_screen1_small = 0xF060;
2192 west_neighbor.large_index() == 2) {
2193 by_screen1_small = 0xF060;
2198 rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
2201 uint16_t by_screen2_small = 0x0040;
2209 east_neighbor.large_index() == 2) {
2210 by_screen2_small = 0xF040;
2214 east_neighbor.large_index() == 2) {
2215 by_screen2_small = 0xF040;
2220 rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
2223 uint16_t by_screen3_small = 0x1800;
2226 if ((i % 0x40) - 8 >= 0) {
2231 north_neighbor.large_index() == 3) {
2232 by_screen3_small = 0x17C0;
2236 north_neighbor.large_index() == 1) {
2237 by_screen3_small = 0x17C0;
2242 rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
2245 uint16_t by_screen4_small = 0x1000;
2253 south_neighbor.large_index() == 1) {
2254 by_screen4_small = 0x0FC0;
2258 south_neighbor.large_index() == 1) {
2259 by_screen4_small = 0x0FC0;
2264 rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
2266 return absl::OkStatus();
2270 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
2271 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
2272 int screen_change_1,
int screen_change_2,
int screen_change_3,
2273 int screen_change_4) {
2275 const uint16_t offsets[] = {0, 2, 16, 18};
2276 for (
auto offset : offsets) {
2278 rom()->WriteShort(transition_target_north + (i * 2) + offset,
2279 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
2281 rom()->WriteShort(transition_target_west + (i * 2) + offset,
2282 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
2284 parent_x_pos * 0x0200));
2286 parent_y_pos * 0x0200));
2291 std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
2294 if ((i % 0x40) - 1 >= 0) {
2298 switch (west_neighbor.large_index()) {
2300 by_screen1_large[2] = 0x0060;
2303 by_screen1_large[0] = 0xF060;
2307 switch (west_neighbor.large_index()) {
2309 by_screen1_large[2] = 0x0060;
2312 by_screen1_large[0] = 0xF060;
2318 for (
int j = 0; j < 4; j++) {
2320 by_screen1_large[j]));
2324 std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
2331 switch (east_neighbor.large_index()) {
2333 by_screen2_large[3] = 0x0080;
2336 by_screen2_large[1] = 0xF080;
2340 switch (east_neighbor.large_index()) {
2342 by_screen2_large[3] = 0x0080;
2345 by_screen2_large[1] = 0xF080;
2351 for (
int j = 0; j < 4; j++) {
2353 by_screen2_large[j]));
2357 std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
2360 if ((i % 0x40) - 8 >= 0) {
2364 switch (north_neighbor.large_index()) {
2366 by_screen3_large[1] = 0x1800;
2369 by_screen3_large[0] = 0x17C0;
2373 switch (north_neighbor.large_index()) {
2375 by_screen3_large[1] = 0x1800;
2378 by_screen3_large[0] = 0x17C0;
2384 for (
int j = 0; j < 4; j++) {
2386 by_screen3_large[j]));
2390 std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
2397 switch (south_neighbor.large_index()) {
2399 by_screen4_large[3] = 0x2000;
2402 by_screen4_large[2] = 0x1FC0;
2406 switch (south_neighbor.large_index()) {
2408 by_screen4_large[3] = 0x2000;
2411 by_screen4_large[2] = 0x1FC0;
2417 for (
int j = 0; j < 4; j++) {
2419 by_screen4_large[j]));
2422 return absl::OkStatus();
2426 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
2427 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
2428 int screen_change_1,
int screen_change_2,
int screen_change_3,
2429 int screen_change_4) {
2431 const uint16_t offsets[] = {0, 2};
2432 for (
auto offset : offsets) {
2434 rom()->WriteShort(transition_target_north + (i * 2) + offset,
2435 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
2437 rom()->WriteShort(transition_target_west + (i * 2) + offset,
2438 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
2440 parent_x_pos * 0x0200));
2442 parent_y_pos * 0x0200));
2446 std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
2449 if ((i % 0x40) - 1 >= 0) {
2454 west_neighbor.large_index() == 3) {
2455 by_screen1_wide[0] = 0xF060;
2459 west_neighbor.large_index() == 2) {
2460 by_screen1_wide[0] = 0xF060;
2464 for (
int j = 0; j < 2; j++) {
2466 by_screen1_wide[j]));
2470 std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
2478 east_neighbor.large_index() == 2) {
2479 by_screen2_wide[1] = 0xF080;
2483 east_neighbor.large_index() == 2) {
2484 by_screen2_wide[1] = 0xF080;
2488 for (
int j = 0; j < 2; j++) {
2490 by_screen2_wide[j]));
2494 std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
2497 if ((i % 0x40) - 8 >= 0) {
2501 switch (north_neighbor.large_index()) {
2503 by_screen3_wide[1] = 0x1800;
2506 by_screen3_wide[0] = 0x17C0;
2510 switch (north_neighbor.large_index()) {
2512 by_screen3_wide[1] = 0x1800;
2515 by_screen3_wide[0] = 0x07C0;
2521 for (
int j = 0; j < 2; j++) {
2523 by_screen3_wide[j]));
2527 std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
2534 switch (south_neighbor.large_index()) {
2536 by_screen4_wide[1] = 0x1000;
2539 by_screen4_wide[0] = 0x0FC0;
2543 if (south_neighbor.large_index() == 1) {
2544 by_screen4_wide[0] = 0x0FC0;
2546 switch (south_neighbor.large_index()) {
2548 by_screen4_wide[1] = 0x1000;
2551 by_screen4_wide[0] = 0x0FC0;
2557 for (
int j = 0; j < 2; j++) {
2559 by_screen4_wide[j]));
2562 return absl::OkStatus();
2566 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
2567 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
2568 int screen_change_1,
int screen_change_2,
int screen_change_3,
2569 int screen_change_4) {
2571 const uint16_t offsets[] = {0, 16};
2572 for (
auto offset : offsets) {
2574 rom()->WriteShort(transition_target_north + (i * 2) + offset,
2575 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
2577 rom()->WriteShort(transition_target_west + (i * 2) + offset,
2578 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
2580 parent_x_pos * 0x0200));
2582 parent_y_pos * 0x0200));
2586 std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
2589 if ((i % 0x40) - 1 >= 0) {
2593 switch (west_neighbor.large_index()) {
2595 by_screen1_tall[1] = 0x0060;
2598 by_screen1_tall[0] = 0xF060;
2602 switch (west_neighbor.large_index()) {
2604 by_screen1_tall[1] = 0x0060;
2607 by_screen1_tall[0] = 0xF060;
2613 for (
int j = 0; j < 2; j++) {
2615 by_screen1_tall[j]));
2619 std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
2626 switch (east_neighbor.large_index()) {
2628 by_screen2_tall[1] = 0x0040;
2631 by_screen2_tall[0] = 0xF040;
2635 switch (east_neighbor.large_index()) {
2637 by_screen2_tall[1] = 0x0040;
2640 by_screen2_tall[0] = 0xF040;
2646 for (
int j = 0; j < 2; j++) {
2648 by_screen2_tall[j]));
2652 std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
2655 if ((i % 0x40) - 8 >= 0) {
2660 north_neighbor.large_index() == 3) {
2661 by_screen3_tall[0] = 0x17C0;
2665 north_neighbor.large_index() == 1) {
2666 by_screen3_tall[0] = 0x17C0;
2670 for (
int j = 0; j < 2; j++) {
2672 by_screen3_tall[j]));
2676 std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
2684 south_neighbor.large_index() == 1) {
2685 by_screen4_tall[1] = 0x1FC0;
2689 south_neighbor.large_index() == 1) {
2690 by_screen4_tall[1] = 0x1FC0;
2694 for (
int j = 0; j < 2; j++) {
2696 by_screen4_tall[j]));
2699 return absl::OkStatus();
2703 util::logf(
"Saving Large Maps (v3+ Expanded)");
2715 std::vector<uint8_t> checked_map;
2720 if (std::find(checked_map.begin(), checked_map.end(), i) !=
2721 checked_map.end()) {
2737 i, parent_x_pos, parent_y_pos, transition_target_north,
2738 transition_target_west, transition_pos_x, transition_pos_y,
2739 screen_change_1, screen_change_2, screen_change_3,
2741 checked_map.emplace_back(i);
2746 i, parent_x_pos, parent_y_pos, transition_target_north,
2747 transition_target_west, transition_pos_x, transition_pos_y,
2748 screen_change_1, screen_change_2, screen_change_3,
2751 checked_map.emplace_back(i);
2752 checked_map.emplace_back(i + 1);
2753 checked_map.emplace_back(i + 8);
2754 checked_map.emplace_back(i + 9);
2759 i, parent_x_pos, parent_y_pos, transition_target_north,
2760 transition_target_west, transition_pos_x, transition_pos_y,
2761 screen_change_1, screen_change_2, screen_change_3,
2764 checked_map.emplace_back(i);
2765 checked_map.emplace_back(i + 1);
2770 i, parent_x_pos, parent_y_pos, transition_target_north,
2771 transition_target_west, transition_pos_x, transition_pos_y,
2772 screen_change_1, screen_change_2, screen_change_3,
2775 checked_map.emplace_back(i);
2776 checked_map.emplace_back(i + 8);
2781 return absl::OkStatus();
2786 std::vector<uint64_t> all_tile_16;
2801 for (
int y = 0; y < 32; y += 2) {
2802 for (
int x = 0; x < 32; x += 2) {
2804 tiles_used[x + (sx * 32)][y + (sy * 32)],
2805 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
2806 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
2807 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
2839 std::vector<uint64_t> all_tile_16 = GetAllTile16(
map_tiles_);
2842 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
2844 std::vector<uint64_t> unique_tiles(all_tile_16);
2845 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
2848 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
2849 for (
size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
2850 all_tiles_indexed.insert(
2851 {unique_tiles[tile32_id],
static_cast<uint16_t
>(tile32_id)});
2857 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
2861 for (
size_t i = 0; i < unique_tiles.size(); ++i) {
2871 ValidateMap32DefinitionCount(
tiles32_unique_.size(), storage_layout));
2876 <<
" Out of: " << storage_layout.definition_capacity << std::endl;
2879 return absl::OkStatus();
2884 util::logf(
"Overworld tiles unchanged; preserving expanded tile32");
2885 return absl::OkStatus();
2889 if (!storage_layout.expanded) {
2890 return absl::FailedPreconditionError(
2891 "Expanded Tile32 save requested for a vanilla Tile32 layout");
2896 ValidateMap32DefinitionCount(
tiles32_unique_.size(), storage_layout));
2906 const auto relocate = [
this](
int operand_pc,
int target_pc) -> absl::Status {
2908 if (
SnesToPc(current) ==
static_cast<uint32_t
>(target_pc)) {
2909 return absl::OkStatus();
2911 return rom()->WriteLong(operand_pc,
PcToSnes(target_pc));
2913 constexpr int kTopRightOperands[] = {0x0176EC, 0x0176F3, 0x0176FA,
2914 0x017701, 0x017708, 0x01771A};
2915 constexpr int kBottomLeftOperands[] = {0x01772C, 0x017733, 0x01773A,
2916 0x017741, 0x017748, 0x01775A};
2917 constexpr int kBottomRightOperands[] = {0x01776C, 0x017773, 0x01777A,
2918 0x017781, 0x017788, 0x01779A};
2919 for (
int i = 0; i < 6; ++i) {
2925 int unique_tile_index = 0;
2959 auto top_right = topRight;
2964 top_right + (i + 1),
2967 top_right + (i + 2),
2970 top_right + (i + 3),
2974 top_right + (i + 4),
2979 top_right + (i + 5),
2986 auto bottom_left = bottomLeft;
2991 bottom_left + (i + 1),
2994 bottom_left + (i + 2),
2997 bottom_left + (i + 3),
3001 bottom_left + (i + 4),
3006 bottom_left + (i + 5),
3013 auto bottom_right = bottomRight;
3018 bottom_right + (i + 1),
3021 bottom_right + (i + 2),
3024 bottom_right + (i + 3),
3028 bottom_right + (i + 4),
3033 bottom_right + (i + 5),
3039 unique_tile_index += 4;
3042 return absl::OkStatus();
3047 util::logf(
"Overworld tiles unchanged; preserving tile32 definitions");
3048 return absl::OkStatus();
3053 if (storage_layout.expanded) {
3054 return absl::FailedPreconditionError(
3055 "Vanilla Tile32 save requested for an expanded Tile32 layout");
3058 ValidateMap32DefinitionCount(
tiles32_unique_.size(), storage_layout));
3060 int unique_tile_index = 0;
3100 top_right + (i + 1),
3103 top_right + (i + 2),
3106 top_right + (i + 3),
3110 top_right + (i + 4),
3115 top_right + (i + 5),
3175 unique_tile_index += 4;
3178 return absl::OkStatus();
3183 const auto write_pointer = [
this](
int address,
3184 uint32_t target) -> absl::Status {
3189 return absl::OkStatus();
3191 return rom()->WriteLong(address, target);
3193 const auto write_bank = [
this](
int address, uint8_t bank) -> absl::Status {
3195 if ((current & 0x7F) == (bank & 0x7F)) {
3196 return absl::OkStatus();
3198 return rom()->WriteByte(address, bank);
3258 static_cast<uint8_t
>(
PcToSnes(map16_expanded) >> 16)));
3261 static_cast<uint8_t
>(
PcToSnes(map16_expanded) >> 16)));
3263 int tpos = map16_expanded;
3267 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)));
3270 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)));
3273 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)));
3276 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)));
3280 return absl::OkStatus();
3289 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)))
3292 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)))
3295 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)))
3298 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)))
3301 return absl::OkStatus();
3308 return absl::OkStatus();
3313 return absl::OkStatus();
3324 const auto serialized_items = [](
const std::vector<OverworldItem>& items) {
3325 std::vector<std::array<int, 4>> records;
3326 for (
const auto& item : items) {
3327 if (!item.deleted) {
3329 {item.room_map_id_, item.game_x_, item.game_y_, item.id_});
3332 std::stable_sort(records.begin(), records.end(),
3333 [](
const auto& a,
const auto& b) { return a[0] < b[0]; });
3336 if (serialized_items(stored_items) == serialized_items(
all_items_)) {
3337 return absl::OkStatus();
3348 return absl::OkStatus();
3355 std::vector<uint8_t> new_overlay_code = {
3361 0xBF, 0x00, 0x00, 0x00,
3363 0xBF, 0x00, 0x00, 0x00,
3379 int snes_ptr_start =
PcToSnes(ptr_start);
3389 constexpr int kExpandedOverlaySpace = 0x120000;
3390 int pos = kExpandedOverlaySpace;
3401 for (
size_t t = 0; t < overlay_data.size(); t += 3) {
3402 if (t + 2 < overlay_data.size()) {
3406 pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
3420 return absl::OkStatus();
3431 return absl::OkStatus();
3442 return absl::OkStatus();
3452 return absl::OkStatus();
3461 for (uint16_t tile_id = 0; tile_id < static_cast<uint16_t>(
tiles16_.size()) &&
3469 util::logf(
"Auto-detected %d diggable tiles",
3471 return absl::OkStatus();
3475 bool enable_main_palette,
3477 bool enable_gfx_groups,
3478 bool enable_subscreen_overlay,
3479 bool enable_animated) {
3483 return absl::OkStatus();
3488 const auto write_enable_flag = [
this](
int address,
3489 bool enabled) -> absl::Status {
3491 if ((current != 0x00) == enabled) {
3492 return absl::OkStatus();
3494 return rom()->WriteByte(address, enabled ? 0xFF : 0x00);
3502 enable_main_palette));
3505 if (enable_main_palette) {
3518 enable_gfx_groups));
3522 enable_subscreen_overlay));
3525 if (enable_mosaic) {
3529 uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) |
3530 (mosaic[1] ? 0x04 : 0x00) |
3531 (mosaic[2] ? 0x02 : 0x00) |
3532 (mosaic[3] ? 0x01 : 0x00);
3539 if (enable_gfx_groups) {
3541 for (
int j = 0; j < 8; j++) {
3548 if (enable_animated) {
3556 if (enable_subscreen_overlay) {
3565 return absl::OkStatus();
3571 return absl::OkStatus();
3574 util::logf(
"Saving Area Specific Background Colors");
3583 return absl::OkStatus();
3588 return absl::FailedPreconditionError(
"ROM not loaded");
3594 return absl::OkStatus();
3597 const auto enabled = [
this](
int address) {
3604 const bool subscreen_overlay_enabled =
3608 bg_color_enabled, main_palette_enabled, mosaic_enabled,
3609 gfx_groups_enabled, subscreen_overlay_enabled, animated_enabled));
3610 if (bg_color_enabled) {
3614 return absl::OkStatus();
3620 const bool use_zscustom_palette_table =
3622 const bool use_zscustom_special_tables =
3624 const int palette_table = use_zscustom_palette_table
3668 if (use_zscustom_special_tables) {
3672 if (use_zscustom_palette_table) {
3678 const int sprite_gfx_table =
3679 use_zscustom_special_tables
3682 const int sprite_palette_table =
3686 rom()->WriteByte(sprite_gfx_table + local_index,
3689 rom()->WriteByte(sprite_palette_table + local_index,
3693 return absl::OkStatus();
3703 const auto owns_slot = [
this](
int map_index) {
3707 return parent == map_index;
3711 if (!owns_slot(i)) {
3725 if (!owns_slot(i)) {
3733 return absl::OkStatus();
3741 if (asm_version < 3 || asm_version == 0xFF) {
3742 return absl::OkStatus();
3747 uint8_t area_size_byte =
3759 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::StatusOr< uint16_t > ReadWord(int offset) const
RAII timer for automatic timing management.
Tile composition of four 16x16 tiles.
uint64_t GetPackedValue() const
SNES 16-bit tile metadata container.
int GetDiggableCount() const
Get the count of tiles marked as diggable.
void SetVanillaDefaults()
Reset to vanilla diggable tiles.
void SetDiggable(uint16_t tile_id, bool diggable)
Set or clear the diggable bit for a Map16 tile ID.
static bool IsTile16Diggable(const gfx::Tile16 &tile16, const std::array< uint8_t, 0x200 > &all_tiles_types)
Check if a Tile16 should be diggable based on its component tiles.
void Clear()
Clear all diggable bits.
uint64_t Revision(uint16_t sheet) const
static absl::StatusOr< OverworldMap > CreateLyraPreview(int index, Rom *rom, GameData *game_data, const rom::LyraLayoutInspection *layout)
static bool SupportsCustomBGColors(OverworldVersion version)
Check if ROM supports custom background colors per area (v2+)
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
static const char * GetVersionName(OverworldVersion version)
Get human-readable version name for display/logging.
absl::Status SaveMap32Expanded()
Save expanded tile32 definitions (v1+ ROMs)
absl::Status BuildMapWithTilesetCache(int map_index, int world_type)
absl::Status DecompressAllMapTilesParallel()
std::vector< uint16_t > tiles32_list_
void InvalidateTilesetCacheForSheet(int sheet)
Drop cached tilesets that load sheet (graphics sheet edits). Built maps keep their pixels; mark them ...
bool staged_map_tiles_valid_
rom::LyraLayoutInspection descriptor_layout_
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
std::vector< OverworldItem > all_items_
void OrganizeMapTiles(std::vector< uint8_t > &bytes, std::vector< uint8_t > &bytes2, int i, int sx, int sy, int &ttpos)
zelda3_version_pointers version_constants() const
Get version-specific ROM addresses.
std::array< int, kNumOverworldMaps > map_pointers1
void SyncAreaProperties(int map_index)
Copy the area parent's render settings (main palette, animated GFX, tile GFX groups,...
std::vector< gfx::Tile32 > tiles32_unique_
const std::vector< uint8_t > & current_graphics() const
absl::Status SaveMapProperties()
Save per-area graphics, palettes, and messages.
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.
std::map< OverworldTilesetKey, TilesetCacheEntry > tileset_cache_
std::vector< OverworldEntrance > all_entrances_
absl::Status SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
Save screen transition data for tall (1x2) areas (v3+ only)
OverworldMapTiles map_tiles_
absl::Status SaveMap16Tiles()
Save tile16 definitions to ROM.
absl::Status SaveAreaSizes()
Save area size enum data (v3+ only)
absl::Status SaveSprites()
void InvalidateSiblingMapCaches(int map_index)
Invalidate cached tilesets for a map and all its siblings.
absl::Status SaveLargeMaps()
Save large map parent/sibling relationships.
void StageSavedMapTiles()
const std::vector< gfx::Tile16 > & tiles16() const
std::array< uint8_t, kNumOverworldMaps > map_parent_
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OverworldBlockset &world)
absl::Status SaveDiggableTiles()
void InvalidateMapCache(int map_index)
Invalidate cached tileset for a specific map.
bool is_lyra_preview() const
std::array< uint8_t, kNumTileTypes > all_tiles_types_
absl::StatusOr< SubscreenOverlayLayer > BuildSubscreenOverlayLayer(int map_index)
Build the subscreen overlay layer for a screen (builds the screen if needed). The overlay comes from ...
void LoadTileTypes()
Load tile type collision data.
absl::Status CreateTile32Tilemap()
Build tile32 tilemap from current tile16 data.
std::array< int, kNumOverworldMaps > map_pointers1_id
uint64_t Tiles16Fingerprint() const
void SetGraphicsPreview(OverworldGraphicsPreview preview)
absl::Status SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
Save screen transition data for large (2x2) areas.
OverworldBlockset & SelectWorldBlockset(int world_type)
bool HasExpandedPointerTables() const
Check if the ROM has expanded pointer tables for tail maps.
uint64_t tileset_cache_clock_
absl::Status SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette, bool enable_mosaic, bool enable_gfx_groups, bool enable_subscreen_overlay, bool enable_animated)
Save custom ASM feature enable flags.
absl::Status SaveCustomOverworldData()
Save all v2/v3 property tables using the ROM's current feature flags.
absl::StatusOr< OverworldSpriteSavePlan > PrepareSpriteSave() const
void FillBlankMapTiles(int map_index)
auto mutable_overworld_map(int i)
bool MapTilesUnchanged() const
absl::Status SaveEntrances()
Save entrance warp points to ROM.
absl::Status SaveExits()
Save exit return points to ROM.
absl::Status LoadSprites()
Load sprite data for all game states.
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
void RollbackSavedMapTiles()
std::vector< OverworldMap > overworld_maps_
OverworldMapTiles loaded_map_tiles_
absl::Status SaveItems()
Save hidden overworld items to ROM.
absl::Status SaveAreaSpecificBGColors()
Save per-area background colors (v2+)
absl::Status LoadDiggableTiles()
absl::Status Save(Rom *rom)
Master save method (calls sub-methods in correct order)
void CommitSavedMapTiles()
absl::Status SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
Save screen transition data for wide (2x1) areas (v3+ only)
absl::Status LoadLyraTerrainPreview(Rom *rom)
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
std::array< std::vector< Sprite >, 3 > all_sprites_
OverworldVersion cached_version_
std::array< int, kNumOverworldMaps > map_pointers2_id
absl::Status LoadOverworldMaps()
Load overworld map tile data.
std::vector< gfx::Tile16 > tiles16_
absl::Status AutoDetectDiggableTiles()
TilesetRevisions SheetRevisionsForKey(const OverworldTilesetKey &key) const
OverworldGraphicsPreview graphics_preview_
absl::Status AssembleMap16Tiles()
absl::Status LoadSpritesFromMap(int sprite_start, int sprite_count, int sprite_index)
Load sprites from a specific map range.
bool loaded_map_tiles_valid_
void set_game_state(int state)
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p1
absl::Status SaveLargeMapsExpanded()
Save expanded large map data (v1+ ROMs)
void AssignMapSizes(std::vector< OverworldMap > &maps)
Assign map sizes based on area size enum (v3+)
absl::Status SaveMap16Expanded()
Save expanded tile16 definitions (v1+ ROMs)
absl::Status BuildLyraTerrainPreview(Rom *rom, rom::LyraLayoutInspection layout)
std::vector< OverworldExit > all_exits_
std::array< int, kNumOverworldMaps > map_pointers2
absl::StatusOr< uint16_t > GetTile16ForTile32(int index, int quadrant, int dimension, const uint32_t *map32address)
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p2
absl::Status SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
Save screen transition data for small (1x1) areas.
std::deque< int > built_map_lru_
absl::Status SaveMapOverlays()
Save interactive overlay data to ROM.
absl::Status AssembleMap32Tiles()
static constexpr size_t kMaxCachedTilesets
DiggableTiles diggable_tiles_
static constexpr int kMaxBuiltMaps
std::array< std::array< bool, 160 >, 3 > sprite_maps_loaded_
OverworldBlockset & GetMapTiles(int world_type)
OverworldMapTiles staged_map_tiles_
absl::Status SaveOverworldTilesType()
Save tile type collision data to ROM.
absl::Status SaveMusic()
Save per-area music IDs.
absl::Status ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size)
Configure a multi-area map structure (Large/Wide/Tall)
std::vector< OverworldEntrance > all_holes_
std::array< uint64_t, std::tuple_size_v< OverworldTilesetKey > > TilesetRevisions
struct destination destination
Room transition destination.
#define LOG_DEBUG(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
std::vector< uint8_t > HyruleMagicDecompress(uint8_t const *src, int *const size, int const p_big_endian, size_t max_src_size)
TileInfo GetTilesInfo(uint16_t tile)
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
std::optional< std::array< uint16_t, 256 > > LyraMapTile32(const LyraLayoutInspection &layout, const uint8_t *image, size_t size, size_t map_index)
std::string HexByte(uint8_t byte, HexStringParams params)
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
std::string HexLong(uint32_t dword, HexStringParams params)
std::vector< uint64_t > GetAllTile16(OverworldMapTiles &map_tiles_)
absl::StatusOr< Map32StorageLayout > ResolveMap32StorageLayout(const Rom &rom, const zelda3_version_pointers &version_constants)
absl::Status ValidateMap32DefinitionCount(size_t definition_count, const Map32StorageLayout &layout)
Zelda 3 specific classes and functions.
constexpr int kAreaGfxIdPtr
int GetOverworldMapParentIdExpanded()
absl::Status SaveEntrances(Rom *rom, const std::vector< OverworldEntrance > &entrances, bool expanded_entrances)
constexpr int OverworldCustomTileGFXGroupEnabled
std::vector< uint8_t > OverworldSpriteBytes
constexpr int OverworldCustomAreaSpecificBGEnabled
std::map< uint16_t, PreviewTile8 > AreaGraphicsPreview
constexpr int kOverworldTransitionPositionY
constexpr int kNumMapsPerWorld
constexpr int kOverworldSpriteset
int GetMap32TileBRExpanded()
int GetOverworldScreenChange3Expanded()
constexpr int kMap16ExpandedFlagPos
constexpr int NumberOfMap16Ex
int GetOverworldScreenChange4Expanded()
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
absl::Status SaveItems(Rom *rom, const std::vector< OverworldItem > &items)
constexpr int kOverworldScreenTileMapChangeByScreen1
constexpr int kOverworldMapDataOverflow
int LegacyParentTableIndexForMap(int map_index)
constexpr int kMap32BytesPerPackedGroup
constexpr int kOverworldMapSizeHighByte
absl::StatusOr< std::vector< OverworldItem > > LoadItems(Rom *rom, std::vector< OverworldMap > &overworld_maps)
constexpr int kNumTileTypes
uint8_t ReadRomByteOr(const Rom &rom, int address, uint8_t fallback)
constexpr int NumberOfMap32
constexpr int kOverworldScreenSize
constexpr int kOverworldScreenTileMapChangeByScreen4
constexpr int kNumTile16Individual
int GetMap32TileTRExpanded()
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
constexpr int kMap16Tiles
int GetMap32TileBLExpanded()
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int Map32DefinitionCapacityForStorageBytes(int storage_bytes)
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
absl::StatusOr< OverworldSpriteBytes > ReadOverworldSpriteList(const Rom &rom, int pointer_address)
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldSpecialSpritePaletteExpandedTemp
absl::Status ApplyOverworldSpriteSave(Rom &rom, const OverworldSpriteSavePlan &plan)
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp
constexpr int kOverworldMusicBeginning
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int kOverworldTransitionPositionX
std::array< std::array< std::optional< OverworldSpriteBytes >, 160 >, 3 > OverworldSpriteEdits
constexpr int kOverworldMusicDarkWorld
std::array< uint8_t, 17 > OverworldTilesetKey
constexpr int kOverworldSpecialPalGroup
constexpr int kOverworldScreenSizeForLoading
constexpr int kOverworldSpritePaletteIds
std::map< int, AreaGraphicsPreview > OverworldGraphicsPreview
constexpr int overworldTilesType
constexpr int transition_target_westExpanded
int GetOverworldScreenChange1Expanded()
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
constexpr int kMap32TileCountExpanded
constexpr int kTransitionTargetWest
constexpr int OverworldCustomASMHasBeenApplied
constexpr int kOverworldMusicAgahnim
constexpr int kOverworldMapParentId
absl::StatusOr< std::vector< gfx::Tile16 > > ReadMap16Tiles(const Rom &rom, bool *expanded)
Represents the full Overworld data, light and dark world.
constexpr int kMap32ExpandedFlagPos
constexpr int kMap32DefinitionsPerPackedGroup
int GetOverworldTransitionPositionXExpanded()
@ kZSCustomV1
Basic features, expanded pointers.
@ kVanilla
0xFF in ROM, no ZScream ASM applied
bool IsSameOverworldWorld(int a, int b)
constexpr int kOverworldPalettesScreenToSetNew
constexpr int kOverworldMusicMasterSword
constexpr int kOverworldMusicZelda
constexpr int transition_target_northExpanded
constexpr int NumberOfMap16
constexpr int kOverworldMapSize
constexpr int kOverworldScreenTileMapChangeByScreen2
constexpr int kOverworldSpecialSpritePalette
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
OverworldRomProfile DetectOverworldRomProfile(const Rom &rom)
absl::Status SaveHoles(Rom *rom, const std::vector< OverworldEntrance > &holes)
bool CanPersistLegacyScreenSize(int map_index)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
constexpr int OverworldCustomMosaicEnabled
constexpr int kEssentialMapsPerWorld
constexpr int kOverworldCompressedMapPos
constexpr int kMaxDiggableTileId
int WorldForOverworldMap(int map_index)
bool IsBackgroundSubscreenOverlay(uint16_t overlay_id)
absl::StatusOr< OverworldSpriteSavePlan > PlanOverworldSpriteSave(const Rom &rom, const OverworldSpriteEdits &edits)
constexpr int kOverworldSpecialSpriteGFXGroup
constexpr int kOverworldScreenTileMapChangeByScreen3
constexpr int OverworldCustomTileGFXGroupArray
bool CanPersistLegacyMultiAreaMap(int map_index)
uint8_t LegacyParentTableValueForMap(int parent_index)
int SubscreenOverlayScreen(uint16_t overlay_id)
int Map32StorageBytesForProfile(const OverworldRomProfile &profile)
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
int GetOverworldMessagesExpanded()
int LegacyScreenSizeTableIndexForMap(int map_index)
OverworldSpriteLayout GetOverworldSpriteLayout(const Rom &rom)
constexpr int kTransitionTargetNorth
constexpr int kOverworldCompressedOverflowPos
int GetOverworldScreenChange2Expanded()
constexpr int kOverlayCodeStart
absl::Status SaveExits(Rom *rom, const std::vector< OverworldExit > &exits)
int GetMap16TilesExpanded()
constexpr int kOverworldMapPaletteIds
int GetOverworldTransitionPositionYExpanded()
constexpr int kOverworldSpecialGfxGroup
std::vector< OverworldSpriteWrite > OverworldSpriteSavePlan
constexpr int OverworldCustomSubscreenOverlayArray
uint32_t PcToSnes(uint32_t addr)
uint32_t SnesToPc(uint32_t addr) noexcept
#define RETURN_IF_ERROR(expr)
Room transition destination.
bool kEnableSpecialWorldExpansion
struct yaze::core::FeatureFlags::Flags::Overworld overworld
GraphicsSheetStore sheet_store
Overworld map tile32 data.
OverworldBlockset dark_world
OverworldBlockset special_world
OverworldBlockset light_world
Subscreen overlay layer for one screen, in that screen's palette.
std::vector< uint8_t > pixels
std::array< int, 4 > destinations
int storage_bytes_per_quadrant
ROM data pointers for different game versions.
uint32_t kOverworldTilesType
uint32_t kCompressedAllMap32PointersHigh
uint32_t kCompressedAllMap32PointersLow