9#include <unordered_map>
12#include "absl/status/status.h"
34 if (
rom->size() == 0) {
35 return absl::InvalidArgumentError(
"ROM file not loaded");
66 if (asm_version >= 3) {
113 return absl::OkStatus();
117 for (
int i = 128; i < 145; i++) {
127 std::array<bool, kNumMapsPerWorld> map_checked;
128 std::ranges::fill(map_checked,
false);
133 if (
int i = xx + (yy * 8); map_checked[i] ==
false) {
135 map_checked[i] =
true;
139 map_checked[i + 1] =
true;
143 map_checked[i + 8] =
true;
147 map_checked[i + 9] =
true;
154 map_checked[i] =
true;
181 int i = world + xx + (yy * 8);
183 if (i >=
static_cast<int>(map_checked.size())) {
187 if (!map_checked[i]) {
188 switch (maps[i].area_size()) {
190 map_checked[i] =
true;
195 map_checked[i] =
true;
196 maps[i].SetAsLargeMap(i, 0);
198 if (i + 1 <
static_cast<int>(maps.size())) {
199 map_checked[i + 1] =
true;
200 maps[i + 1].SetAsLargeMap(i, 1);
203 if (i + 8 <
static_cast<int>(maps.size())) {
204 map_checked[i + 8] =
true;
205 maps[i + 8].SetAsLargeMap(i, 2);
208 if (i + 9 <
static_cast<int>(maps.size())) {
209 map_checked[i + 9] =
true;
210 maps[i + 9].SetAsLargeMap(i, 3);
217 map_checked[i] =
true;
220 maps[i].SetParent(i);
223 if (i + 1 <
static_cast<int>(maps.size())) {
224 map_checked[i + 1] =
true;
225 maps[i + 1].SetParent(i);
233 map_checked[i] =
true;
236 maps[i].SetParent(i);
239 if (i + 8 <
static_cast<int>(maps.size())) {
240 map_checked[i + 8] =
true;
241 maps[i + 8].SetParent(i);
263 return absl::InvalidArgumentError(
264 absl::StrFormat(
"Invalid parent index: %d", parent_index));
275 (asm_version < 3 || asm_version == 0xFF)) {
276 return absl::FailedPreconditionError(
277 "Wide and Tall areas require ZSCustomOverworld v3+");
280 LOG_DEBUG(
"Overworld",
"ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, version=%d",
281 parent_index,
static_cast<int>(
overworld_maps_[parent_index].area_size()),
282 static_cast<int>(size), asm_version);
285 std::vector<int> old_siblings;
291 old_siblings = {old_parent, old_parent + 1, old_parent + 8, old_parent + 9};
294 old_siblings = {old_parent, old_parent + 1};
297 old_siblings = {old_parent, old_parent + 8};
300 old_siblings = {parent_index};
305 for (
int old_sibling : old_siblings) {
312 std::vector<int> new_siblings;
319 new_siblings = {parent_index};
323 new_siblings = {parent_index, parent_index + 1, parent_index + 8, parent_index + 9};
324 for (
size_t i = 0; i < new_siblings.size(); ++i) {
325 int sibling = new_siblings[i];
332 new_siblings = {parent_index, parent_index + 1};
333 for (
int sibling : new_siblings) {
341 new_siblings = {parent_index, parent_index + 8};
342 for (
int sibling : new_siblings) {
351 std::set<int> all_affected;
352 for (
int s : old_siblings) all_affected.insert(s);
353 for (
int s : new_siblings) all_affected.insert(s);
355 if (asm_version >= 3 && asm_version != 0xFF) {
357 for (
int sibling : all_affected) {
366 }
else if (asm_version < 3 && asm_version != 0xFF) {
368 for (
int sibling : all_affected) {
379 for (
int sibling : all_affected) {
390 LOG_DEBUG(
"Overworld",
"Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
394 parent_index, old_siblings.size(), new_siblings.size());
396 return absl::OkStatus();
400 int index,
int quadrant,
int dimension,
const uint32_t* map32address) {
402 auto arg1,
rom()->ReadByte(map32address[dimension] + quadrant + (index)));
404 rom()->ReadWord(map32address[dimension] + (index) +
405 (quadrant <= 1 ? 4 : 5)));
406 return (uint16_t)(arg1 +
407 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
411 constexpr int kMap32TilesLength = 0x33F0;
412 int num_tile32 = kMap32TilesLength;
413 uint32_t map32address[4] = {
rom()->version_constants().kMap32TileTL,
414 rom()->version_constants().kMap32TileTR,
415 rom()->version_constants().kMap32TileBL,
416 rom()->version_constants().kMap32TileBR};
422 util::logf(
"Expanded tile32 flag: %d", expanded_flag);
423 if (expanded_flag != 0x04 || asm_version >= 3) {
425 map32address[0] =
rom()->version_constants().kMap32TileTL;
435 for (
int i = 0; i < num_tile32; i += 6) {
437 for (
int k = 0; k < 4; k++) {
461 for (
int i = 0; i < 0x200; i++) {
467 return absl::OkStatus();
478 util::logf(
"Expanded tile16 flag: %d", expanded_flag);
487 for (
int i = 0; i < num_tile16; i += 1) {
500 tiles16_.emplace_back(t0, t1, t2, t3);
502 return absl::OkStatus();
507 int position_x1 = (x * 2) + (sx * 32);
508 int position_y1 = (y * 2) + (sy * 32);
509 int position_x2 = (x * 2) + 1 + (sx * 32);
510 int position_y2 = (y * 2) + 1 + (sy * 32);
518 std::vector<uint8_t>& bytes2,
int i,
int sx,
519 int sy,
int& ttpos) {
520 for (
int y = 0; y < 16; y++) {
521 for (
int x = 0; x < 16; x++) {
522 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
546 util::logf(
"Using sequential decompression (parallel version disabled due to data integrity issues)");
548 const auto get_ow_map_gfx_ptr = [
this](
int index, uint32_t map_ptr) {
549 int p = (
rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
550 (
rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
551 (
rom()->data()[map_ptr + (3 * index)]);
555 constexpr uint32_t kBaseLowest = 0x0FFFFF;
556 constexpr uint32_t kBaseHighest = 0x0F8000;
558 uint32_t lowest = kBaseLowest;
559 uint32_t highest = kBaseHighest;
565 auto p1 = get_ow_map_gfx_ptr(
566 i,
rom()->version_constants().kCompressedAllMap32PointersHigh);
567 auto p2 = get_ow_map_gfx_ptr(
568 i,
rom()->version_constants().kCompressedAllMap32PointersLow);
577 if (p1 <= lowest && p1 > kBaseHighest)
579 if (p2 <= lowest && p2 > kBaseHighest)
601 return absl::OkStatus();
609 constexpr int kEssentialMapsPerWorld = 8;
610 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
614 util::logf(
"Building essential maps only (first %d maps per world) for faster loading", kEssentialMapsPerWorld);
616 std::vector<std::future<absl::Status>> futures;
620 bool is_essential =
false;
623 if (i < kLightWorldEssential) {
639 auto task_function = [
this, i, size, world_type]() {
643 futures.emplace_back(std::async(std::launch::async, task_function));
651 for (
auto& future : futures) {
656 util::logf(
"Essential maps built. Remaining maps will be built on-demand.");
657 return absl::OkStatus();
662 return absl::InvalidArgumentError(
"Invalid map index");
667 return absl::OkStatus();
686 rom()->data()[
rom()->version_constants().kOverworldTilesType + i];
691 std::vector<std::future<absl::Status>> futures;
696 if (asm_version >= 3 && asm_version != 0xFF) {
698 futures.emplace_back(std::async(std::launch::async, [
this]() {
701 futures.emplace_back(std::async(std::launch::async, [
this]() {
704 futures.emplace_back(std::async(std::launch::async, [
this]() {
709 futures.emplace_back(std::async(std::launch::async, [
this]() {
712 futures.emplace_back(std::async(std::launch::async, [
this]() {
715 futures.emplace_back(std::async(std::launch::async, [
this]() {
720 for (
auto& future : futures) {
724 return absl::OkStatus();
728 int num_maps_per_gamestate,
730 for (
int i = 0; i < num_maps_per_gamestate; i++) {
734 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
736 int sprite_address =
SnesToPc((0x09 << 0x10) | word_addr);
744 int editor_map_index = i;
745 if (game_state != 0) {
746 if (editor_map_index >= 128)
747 editor_map_index -= 128;
748 else if (editor_map_index >= 64)
749 editor_map_index -= 64;
751 int mapY = (editor_map_index / 8);
752 int mapX = (editor_map_index % 8);
754 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
755 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
758 (uint8_t)(b2 & 0x3F), (uint8_t)(b1 & 0x3F), realX, realY);
765 return absl::OkStatus();
789 return absl::OkStatus();
802 std::vector<uint8_t> single_map_1(512);
803 std::vector<uint8_t> single_map_2(512);
807 for (
int y = 0; y < 16; y++) {
808 for (
int x = 0; x < 16; x++) {
810 single_map_1[npos] = packed & 0xFF;
811 single_map_2[npos] = (packed >> 8) & 0xFF;
820 if (a.empty() || b.empty()) {
821 return absl::AbortedError(
"Error compressing map gfx.");
828 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
832 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
833 util::logf(
"Pos set to overflow region for map %s at %s",
838 const auto compare_array = [](
const std::vector<uint8_t>& array1,
839 const std::vector<uint8_t>& array2) ->
bool {
840 if (array1.size() != array2.size()) {
844 for (
size_t i = 0; i < array1.size(); i++) {
845 if (array1[i] != array2[i]) {
853 for (
int j = 0; j < i; j++) {
867 std::copy(a.begin(), a.end(),
map_data_p1[i].begin());
870 util::logf(
"Saving map pointers1 and compressed data for map %s at %s",
873 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
883 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
887 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
891 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
892 util::logf(
"Pos set to overflow region for map %s at %s",
899 std::copy(b.begin(), b.end(),
map_data_p2[i].begin());
902 util::logf(
"Saving map pointers2 and compressed data for map %s at %s",
905 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
915 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
923 return absl::AbortedError(
"Too many maps data " + std::to_string(pos));
927 return absl::OkStatus();
935 bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF);
937 if (use_expanded_transitions) {
943 std::vector<uint8_t> checked_map;
955 if (std::find(checked_map.begin(), checked_map.end(), i) !=
963 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
964 for (
const auto& offset : large_map_offsets) {
989 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
992 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
996 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
999 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1003 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1006 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1010 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1013 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1017 (parent_x_pos * 0x200)));
1019 (parent_y_pos * 0x200)));
1023 (parent_x_pos * 0x200)));
1026 (parent_y_pos * 0x200)));
1030 (parent_x_pos * 0x200)));
1033 (parent_y_pos * 0x200)));
1037 (parent_x_pos * 0x200)));
1040 (parent_y_pos * 0x200)));
1049 if (parent_x_pos == 0) {
1152 checked_map.emplace_back(i);
1153 checked_map.emplace_back((i + 1));
1154 checked_map.emplace_back((i + 8));
1155 checked_map.emplace_back((i + 9));
1177 if (i - 1 >= 0 && parent_x_pos != 0) {
1189 if (i + 1 < 64 && parent_x_pos != 7) {
1233 (uint16_t)((y_pos * 0x200) - 0xE0)));
1235 (uint16_t)((x_pos * 0x200) - 0x100)));
1242 checked_map.emplace_back(i);
1246 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
1249 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
1251 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
1253 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
1255 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
1257 return absl::OkStatus();
1261 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1262 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1263 int screen_change_1,
int screen_change_2,
int screen_change_3,
1264 int screen_change_4) {
1267 rom()->WriteShort(transition_target_north + (i * 2),
1268 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1270 rom()->WriteShort(transition_target_west + (i * 2),
1271 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1274 rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
1276 rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
1279 uint16_t by_screen1_small = 0x0060;
1282 if ((i % 0x40) - 1 >= 0) {
1287 west_neighbor.large_index() == 3) {
1288 by_screen1_small = 0xF060;
1292 west_neighbor.large_index() == 2) {
1293 by_screen1_small = 0xF060;
1298 rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
1301 uint16_t by_screen2_small = 0x0040;
1309 east_neighbor.large_index() == 2) {
1310 by_screen2_small = 0xF040;
1314 east_neighbor.large_index() == 2) {
1315 by_screen2_small = 0xF040;
1320 rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
1323 uint16_t by_screen3_small = 0x1800;
1326 if ((i % 0x40) - 8 >= 0) {
1331 north_neighbor.large_index() == 3) {
1332 by_screen3_small = 0x17C0;
1336 north_neighbor.large_index() == 1) {
1337 by_screen3_small = 0x17C0;
1342 rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
1345 uint16_t by_screen4_small = 0x1000;
1353 south_neighbor.large_index() == 1) {
1354 by_screen4_small = 0x0FC0;
1358 south_neighbor.large_index() == 1) {
1359 by_screen4_small = 0x0FC0;
1364 rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
1366 return absl::OkStatus();
1370 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1371 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1372 int screen_change_1,
int screen_change_2,
int screen_change_3,
1373 int screen_change_4) {
1375 const uint16_t offsets[] = {0, 2, 16, 18};
1376 for (
auto offset : offsets) {
1378 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1379 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1381 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1382 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1384 parent_x_pos * 0x0200));
1386 parent_y_pos * 0x0200));
1391 std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
1394 if ((i % 0x40) - 1 >= 0) {
1398 switch (west_neighbor.large_index()) {
1400 by_screen1_large[2] = 0x0060;
1403 by_screen1_large[0] = 0xF060;
1407 switch (west_neighbor.large_index()) {
1409 by_screen1_large[2] = 0x0060;
1412 by_screen1_large[0] = 0xF060;
1418 for (
int j = 0; j < 4; j++) {
1420 by_screen1_large[j]));
1424 std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
1431 switch (east_neighbor.large_index()) {
1433 by_screen2_large[3] = 0x0080;
1436 by_screen2_large[1] = 0xF080;
1440 switch (east_neighbor.large_index()) {
1442 by_screen2_large[3] = 0x0080;
1445 by_screen2_large[1] = 0xF080;
1451 for (
int j = 0; j < 4; j++) {
1453 by_screen2_large[j]));
1457 std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
1460 if ((i % 0x40) - 8 >= 0) {
1464 switch (north_neighbor.large_index()) {
1466 by_screen3_large[1] = 0x1800;
1469 by_screen3_large[0] = 0x17C0;
1473 switch (north_neighbor.large_index()) {
1475 by_screen3_large[1] = 0x1800;
1478 by_screen3_large[0] = 0x17C0;
1484 for (
int j = 0; j < 4; j++) {
1486 by_screen3_large[j]));
1490 std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
1497 switch (south_neighbor.large_index()) {
1499 by_screen4_large[3] = 0x2000;
1502 by_screen4_large[2] = 0x1FC0;
1506 switch (south_neighbor.large_index()) {
1508 by_screen4_large[3] = 0x2000;
1511 by_screen4_large[2] = 0x1FC0;
1517 for (
int j = 0; j < 4; j++) {
1519 by_screen4_large[j]));
1522 return absl::OkStatus();
1526 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1527 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1528 int screen_change_1,
int screen_change_2,
int screen_change_3,
1529 int screen_change_4) {
1531 const uint16_t offsets[] = {0, 2};
1532 for (
auto offset : offsets) {
1534 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1535 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1537 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1538 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1540 parent_x_pos * 0x0200));
1542 parent_y_pos * 0x0200));
1546 std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
1549 if ((i % 0x40) - 1 >= 0) {
1554 west_neighbor.large_index() == 3) {
1555 by_screen1_wide[0] = 0xF060;
1559 west_neighbor.large_index() == 2) {
1560 by_screen1_wide[0] = 0xF060;
1564 for (
int j = 0; j < 2; j++) {
1566 by_screen1_wide[j]));
1570 std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
1578 east_neighbor.large_index() == 2) {
1579 by_screen2_wide[1] = 0xF080;
1583 east_neighbor.large_index() == 2) {
1584 by_screen2_wide[1] = 0xF080;
1588 for (
int j = 0; j < 2; j++) {
1590 by_screen2_wide[j]));
1594 std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
1597 if ((i % 0x40) - 8 >= 0) {
1601 switch (north_neighbor.large_index()) {
1603 by_screen3_wide[1] = 0x1800;
1606 by_screen3_wide[0] = 0x17C0;
1610 switch (north_neighbor.large_index()) {
1612 by_screen3_wide[1] = 0x1800;
1615 by_screen3_wide[0] = 0x07C0;
1621 for (
int j = 0; j < 2; j++) {
1623 by_screen3_wide[j]));
1627 std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
1634 switch (south_neighbor.large_index()) {
1636 by_screen4_wide[1] = 0x1000;
1639 by_screen4_wide[0] = 0x0FC0;
1643 if (south_neighbor.large_index() == 1) {
1644 by_screen4_wide[0] = 0x0FC0;
1646 switch (south_neighbor.large_index()) {
1648 by_screen4_wide[1] = 0x1000;
1651 by_screen4_wide[0] = 0x0FC0;
1657 for (
int j = 0; j < 2; j++) {
1659 by_screen4_wide[j]));
1662 return absl::OkStatus();
1666 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1667 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1668 int screen_change_1,
int screen_change_2,
int screen_change_3,
1669 int screen_change_4) {
1671 const uint16_t offsets[] = {0, 16};
1672 for (
auto offset : offsets) {
1674 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1675 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1677 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1678 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1680 parent_x_pos * 0x0200));
1682 parent_y_pos * 0x0200));
1686 std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
1689 if ((i % 0x40) - 1 >= 0) {
1693 switch (west_neighbor.large_index()) {
1695 by_screen1_tall[1] = 0x0060;
1698 by_screen1_tall[0] = 0xF060;
1702 switch (west_neighbor.large_index()) {
1704 by_screen1_tall[1] = 0x0060;
1707 by_screen1_tall[0] = 0xF060;
1713 for (
int j = 0; j < 2; j++) {
1715 by_screen1_tall[j]));
1719 std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
1726 switch (east_neighbor.large_index()) {
1728 by_screen2_tall[1] = 0x0040;
1731 by_screen2_tall[0] = 0xF040;
1735 switch (east_neighbor.large_index()) {
1737 by_screen2_tall[1] = 0x0040;
1740 by_screen2_tall[0] = 0xF040;
1746 for (
int j = 0; j < 2; j++) {
1748 by_screen2_tall[j]));
1752 std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
1755 if ((i % 0x40) - 8 >= 0) {
1760 north_neighbor.large_index() == 3) {
1761 by_screen3_tall[0] = 0x17C0;
1765 north_neighbor.large_index() == 1) {
1766 by_screen3_tall[0] = 0x17C0;
1770 for (
int j = 0; j < 2; j++) {
1772 by_screen3_tall[j]));
1776 std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
1784 south_neighbor.large_index() == 1) {
1785 by_screen4_tall[1] = 0x1FC0;
1789 south_neighbor.large_index() == 1) {
1790 by_screen4_tall[1] = 0x1FC0;
1794 for (
int j = 0; j < 2; j++) {
1796 by_screen4_tall[j]));
1799 return absl::OkStatus();
1803 util::logf(
"Saving Large Maps (v3+ Expanded)");
1815 std::vector<uint8_t> checked_map;
1820 if (std::find(checked_map.begin(), checked_map.end(), i) !=
1821 checked_map.end()) {
1836 i, parent_x_pos, parent_y_pos, transition_target_north,
1837 transition_target_west, transition_pos_x, transition_pos_y,
1838 screen_change_1, screen_change_2, screen_change_3,
1840 checked_map.emplace_back(i);
1845 i, parent_x_pos, parent_y_pos, transition_target_north,
1846 transition_target_west, transition_pos_x, transition_pos_y,
1847 screen_change_1, screen_change_2, screen_change_3,
1850 checked_map.emplace_back(i);
1851 checked_map.emplace_back(i + 1);
1852 checked_map.emplace_back(i + 8);
1853 checked_map.emplace_back(i + 9);
1858 i, parent_x_pos, parent_y_pos, transition_target_north,
1859 transition_target_west, transition_pos_x, transition_pos_y,
1860 screen_change_1, screen_change_2, screen_change_3,
1863 checked_map.emplace_back(i);
1864 checked_map.emplace_back(i + 1);
1869 i, parent_x_pos, parent_y_pos, transition_target_north,
1870 transition_target_west, transition_pos_x, transition_pos_y,
1871 screen_change_1, screen_change_2, screen_change_3,
1874 checked_map.emplace_back(i);
1875 checked_map.emplace_back(i + 8);
1880 return absl::OkStatus();
1885 std::vector<uint64_t> all_tile_16;
1900 for (
int y = 0; y < 32; y += 2) {
1901 for (
int x = 0; x < 32; x += 2) {
1903 tiles_used[x + (sx * 32)][y + (sy * 32)],
1904 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
1905 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
1906 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
1935 std::vector<uint64_t> all_tile_16 = GetAllTile16(
map_tiles_);
1938 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
1940 std::vector<uint64_t> unique_tiles(all_tile_16);
1941 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
1944 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
1945 for (
size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
1946 all_tiles_indexed.insert(
1947 {unique_tiles[tile32_id],
static_cast<uint16_t
>(tile32_id)});
1953 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
1957 for (
size_t i = 0; i < unique_tiles.size(); ++i) {
1967 return absl::InternalError(absl::StrFormat(
1968 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
1969 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
1970 "tiles to free some space\nOr use the option Clear DW Tiles in the "
1987 return absl::OkStatus();
2036 constexpr int kTilesPer32x32Tile = 6;
2037 int unique_tile_index = 0;
2040 for (
int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2041 if (unique_tile_index >= limit) {
2042 return absl::AbortedError(
"Too many unique tile32 definitions.");
2046 auto top_left =
rom()->version_constants().kMap32TileTL;
2073 auto top_right = topRight;
2078 top_right + (i + 1),
2081 top_right + (i + 2),
2084 top_right + (i + 3),
2088 top_right + (i + 4),
2093 top_right + (i + 5),
2100 auto bottom_left = bottomLeft;
2105 bottom_left + (i + 1),
2108 bottom_left + (i + 2),
2111 bottom_left + (i + 3),
2115 bottom_left + (i + 4),
2120 bottom_left + (i + 5),
2127 auto bottom_right = bottomRight;
2132 bottom_right + (i + 1),
2135 bottom_right + (i + 2),
2138 bottom_right + (i + 3),
2142 bottom_right + (i + 4),
2147 bottom_right + (i + 5),
2153 unique_tile_index += 4;
2156 return absl::OkStatus();
2161 constexpr int kMaxUniqueTiles = 0x4540;
2162 constexpr int kTilesPer32x32Tile = 6;
2164 int unique_tile_index = 0;
2167 for (
int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2168 if (unique_tile_index >= kMaxUniqueTiles) {
2169 return absl::AbortedError(
"Too many unique tile32 definitions.");
2173 auto top_left =
rom()->version_constants().kMap32TileTL;
2201 auto top_right =
rom()->version_constants().kMap32TileTR;
2206 top_right + (i + 1),
2209 top_right + (i + 2),
2212 top_right + (i + 3),
2216 top_right + (i + 4),
2221 top_right + (i + 5),
2281 unique_tile_index += 4;
2282 num_unique_tiles += 2;
2285 return absl::OkStatus();
2367 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)));
2370 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)));
2373 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)));
2376 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)));
2380 return absl::OkStatus();
2389 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)))
2392 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)))
2395 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)))
2398 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)))
2401 return absl::OkStatus();
2407 return absl::OkStatus();
2412 return absl::OkStatus();
2417 return absl::OkStatus();
2424 std::vector<uint8_t> new_overlay_code = {
2430 0xBF, 0x00, 0x00, 0x00,
2432 0xBF, 0x00, 0x00, 0x00,
2448 int snes_ptr_start =
PcToSnes(ptr_start);
2458 constexpr int kExpandedOverlaySpace = 0x120000;
2459 int pos = kExpandedOverlaySpace;
2470 for (
size_t t = 0; t < overlay_data.size(); t += 3) {
2471 if (t + 2 < overlay_data.size()) {
2475 pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
2489 return absl::OkStatus();
2500 return absl::OkStatus();
2504 bool enable_main_palette,
2506 bool enable_gfx_groups,
2507 bool enable_subscreen_overlay,
2508 bool enable_animated) {
2512 uint8_t enable_value = enable_bg_color ? 0xFF : 0x00;
2516 enable_value = enable_main_palette ? 0xFF : 0x00;
2520 enable_value = enable_mosaic ? 0xFF : 0x00;
2523 enable_value = enable_gfx_groups ? 0xFF : 0x00;
2527 enable_value = enable_animated ? 0xFF : 0x00;
2531 enable_value = enable_subscreen_overlay ? 0xFF : 0x00;
2545 uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) |
2546 (mosaic[1] ? 0x04 : 0x00) |
2547 (mosaic[2] ? 0x02 : 0x00) |
2548 (mosaic[3] ? 0x01 : 0x00);
2556 for (
int j = 0; j < 8; j++) {
2572 return absl::OkStatus();
2576 util::logf(
"Saving Area Specific Background Colors");
2585 return absl::OkStatus();
2636 return absl::OkStatus();
2661 return absl::OkStatus();
2669 if (asm_version < 3 || asm_version == 0xFF) {
2670 return absl::OkStatus();
2675 uint8_t area_size_byte =
2687 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data.
RAII timer for automatic timing management.
Tile composition of four 16x16 tiles.
uint64_t GetPackedValue() const
SNES 16-bit tile metadata container.
absl::Status SaveMap32Expanded()
absl::Status DecompressAllMapTilesParallel()
std::vector< uint16_t > tiles32_list_
absl::Status Load(Rom *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)
std::array< int, kNumOverworldMaps > map_pointers1
std::vector< gfx::Tile32 > tiles32_unique_
void DecompressAllMapTiles()
absl::Status SaveMapProperties()
absl::Status SaveMap32Tiles()
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)
OverworldMapTiles map_tiles_
absl::Status SaveMap16Tiles()
absl::Status SaveAreaSizes()
absl::Status SaveLargeMaps()
std::array< uint8_t, kNumOverworldMaps > map_parent_
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OverworldBlockset &world)
std::array< uint8_t, kNumTileTypes > all_tiles_types_
absl::Status CreateTile32Tilemap()
std::array< int, kNumOverworldMaps > map_pointers1_id
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)
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)
absl::Status SaveEntrances()
absl::Status LoadSprites()
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
std::vector< OverworldMap > overworld_maps_
absl::Status SaveAreaSpecificBGColors()
absl::Status Save(Rom *rom)
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)
absl::Status SaveOverworldMaps()
std::array< std::vector< Sprite >, 3 > all_sprites_
std::array< int, kNumOverworldMaps > map_pointers2_id
absl::Status LoadOverworldMaps()
std::vector< gfx::Tile16 > tiles16_
absl::Status AssembleMap16Tiles()
absl::Status LoadSpritesFromMap(int sprite_start, int sprite_count, int sprite_index)
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p1
absl::Status SaveLargeMapsExpanded()
void AssignMapSizes(std::vector< OverworldMap > &maps)
Loads all maps from ROM to see what size they are.
absl::Status SaveMap16Expanded()
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)
absl::Status SaveMapOverlays()
absl::Status AssembleMap32Tiles()
OverworldBlockset & GetMapTiles(int world_type)
absl::Status SaveOverworldTilesType()
absl::Status ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size)
Configure a multi-area map structure (Large/Wide/Tall)
std::vector< OverworldEntrance > all_holes_
#define LOG_DEBUG(category, format,...)
#define RETURN_IF_ERROR(expression)
#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)
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::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_)
constexpr int kAreaGfxIdPtr
absl::Status SaveEntrances(Rom *rom, const std::vector< OverworldEntrance > &entrances, bool expanded_entrances)
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldTransitionPositionY
constexpr int kNumMapsPerWorld
constexpr int kOverworldSpriteset
constexpr int kMap16ExpandedFlagPos
constexpr int LimitOfMap32
constexpr int NumberOfMap16Ex
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
absl::Status SaveItems(Rom *rom, const std::vector< OverworldItem > &items)
constexpr int kOverworldScreenTileMapChangeByScreen1
constexpr int kOverworldScreenTileMapChangeByScreen2Expanded
constexpr int kOverworldMapDataOverflow
constexpr int kOverworldMapSizeHighByte
absl::StatusOr< std::vector< OverworldItem > > LoadItems(Rom *rom, std::vector< OverworldMap > &overworld_maps)
constexpr int overworldSpritesBeginingExpanded
constexpr int kNumTileTypes
constexpr int NumberOfMap32
constexpr int kOverworldScreenSize
constexpr int kOverworldScreenTileMapChangeByScreen4
constexpr int kNumTile16Individual
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
constexpr int kOverworldTransitionPositionXExpanded
constexpr int kMap16Tiles
constexpr int overworldSpritesAgahnimExpanded
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldTransitionPositionYExpanded
constexpr int kOverworldMapParentIdExpanded
constexpr int kOverworldMusicBeginning
constexpr int kMap32TileBLExpanded
constexpr int kOverworldTransitionPositionX
constexpr int kOverworldMusicDarkWorld
constexpr int kOverworldScreenSizeForLoading
constexpr int kOverworldSpritePaletteIds
constexpr int overworldTilesType
constexpr int transition_target_westExpanded
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
constexpr int kMap32TileBRExpanded
constexpr int kMap32TileCountExpanded
constexpr int kTransitionTargetWest
constexpr int OverworldCustomASMHasBeenApplied
constexpr int kOverworldMusicAgahnim
constexpr int kOverworldSpritesZelda
constexpr int kOverworldMapParentId
constexpr int kMap32ExpandedFlagPos
constexpr int kOverworldMessagesExpanded
constexpr int kOverworldMusicMasterSword
constexpr int kOverworldScreenTileMapChangeByScreen3Expanded
constexpr int kOverworldMusicZelda
constexpr int transition_target_northExpanded
constexpr int NumberOfMap16
constexpr int kOverworldMapSize
constexpr int kOverworldScreenTileMapChangeByScreen2
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
absl::Status SaveHoles(Rom *rom, const std::vector< OverworldEntrance > &holes)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
constexpr int OverworldCustomMosaicEnabled
constexpr int kOverworldCompressedMapPos
constexpr int kOverworldScreenTileMapChangeByScreen4Expanded
constexpr int kOverworldSpritesBeginning
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
constexpr int kOverworldScreenTileMapChangeByScreen3
constexpr int kOverworldScreenTileMapChangeByScreen1Expanded
constexpr int OverworldCustomTileGFXGroupArray
constexpr int kMap16TilesExpanded
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
constexpr int kOverworldSpritesAgahnim
constexpr int kTransitionTargetNorth
constexpr int overworldSpritesZeldaExpanded
constexpr int kOverworldCompressedOverflowPos
constexpr int kOverlayCodeStart
constexpr int kMap32TileTRExpanded
absl::Status SaveExits(Rom *rom, const std::vector< OverworldExit > &exits)
constexpr int kOverworldMapPaletteIds
constexpr int OverworldCustomSubscreenOverlayArray
Main namespace for the application.
uint32_t PcToSnes(uint32_t addr)
uint32_t SnesToPc(uint32_t addr) noexcept
Overworld map tile32 data.
OverworldBlockset dark_world
OverworldBlockset special_world
OverworldBlockset light_world