8#include <unordered_map>
11#include "absl/status/status.h"
30 if (
rom->size() == 0) {
31 return absl::InvalidArgumentError(
"ROM file not loaded");
62 if (asm_version >= 3) {
109 return absl::OkStatus();
113 for (
int i = 128; i < 145; i++) {
123 std::array<bool, kNumMapsPerWorld> map_checked;
124 std::ranges::fill(map_checked,
false);
129 if (
int i = xx + (yy * 8); map_checked[i] ==
false) {
131 map_checked[i] =
true;
135 map_checked[i + 1] =
true;
139 map_checked[i + 8] =
true;
143 map_checked[i + 9] =
true;
150 map_checked[i] =
true;
177 int i = world + xx + (yy * 8);
179 if (i >=
static_cast<int>(map_checked.size())) {
183 if (!map_checked[i]) {
184 switch (maps[i].area_size()) {
186 map_checked[i] =
true;
191 map_checked[i] =
true;
192 maps[i].SetAsLargeMap(i, 0);
194 if (i + 1 <
static_cast<int>(maps.size())) {
195 map_checked[i + 1] =
true;
196 maps[i + 1].SetAsLargeMap(i, 1);
199 if (i + 8 <
static_cast<int>(maps.size())) {
200 map_checked[i + 8] =
true;
201 maps[i + 8].SetAsLargeMap(i, 2);
204 if (i + 9 <
static_cast<int>(maps.size())) {
205 map_checked[i + 9] =
true;
206 maps[i + 9].SetAsLargeMap(i, 3);
213 map_checked[i] =
true;
216 maps[i].SetParent(i);
219 if (i + 1 <
static_cast<int>(maps.size())) {
220 map_checked[i + 1] =
true;
221 maps[i + 1].SetParent(i);
229 map_checked[i] =
true;
232 maps[i].SetParent(i);
235 if (i + 8 <
static_cast<int>(maps.size())) {
236 map_checked[i + 8] =
true;
237 maps[i + 8].SetParent(i);
259 return absl::InvalidArgumentError(
260 absl::StrFormat(
"Invalid parent index: %d", parent_index));
271 (asm_version < 3 || asm_version == 0xFF)) {
272 return absl::FailedPreconditionError(
273 "Wide and Tall areas require ZSCustomOverworld v3+");
276 LOG_DEBUG(
"Overworld",
"ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, version=%d",
277 parent_index,
static_cast<int>(
overworld_maps_[parent_index].area_size()),
278 static_cast<int>(size), asm_version);
281 std::vector<int> old_siblings;
287 old_siblings = {old_parent, old_parent + 1, old_parent + 8, old_parent + 9};
290 old_siblings = {old_parent, old_parent + 1};
293 old_siblings = {old_parent, old_parent + 8};
296 old_siblings = {parent_index};
301 for (
int old_sibling : old_siblings) {
308 std::vector<int> new_siblings;
315 new_siblings = {parent_index};
319 new_siblings = {parent_index, parent_index + 1, parent_index + 8, parent_index + 9};
320 for (
size_t i = 0; i < new_siblings.size(); ++i) {
321 int sibling = new_siblings[i];
328 new_siblings = {parent_index, parent_index + 1};
329 for (
int sibling : new_siblings) {
337 new_siblings = {parent_index, parent_index + 8};
338 for (
int sibling : new_siblings) {
347 std::set<int> all_affected;
348 for (
int s : old_siblings) all_affected.insert(s);
349 for (
int s : new_siblings) all_affected.insert(s);
351 if (asm_version >= 3 && asm_version != 0xFF) {
353 for (
int sibling : all_affected) {
362 }
else if (asm_version < 3 && asm_version != 0xFF) {
364 for (
int sibling : all_affected) {
375 for (
int sibling : all_affected) {
386 LOG_DEBUG(
"Overworld",
"Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
390 parent_index, old_siblings.size(), new_siblings.size());
392 return absl::OkStatus();
396 int index,
int quadrant,
int dimension,
const uint32_t* map32address) {
398 auto arg1,
rom()->ReadByte(map32address[dimension] + quadrant + (index)));
400 rom()->ReadWord(map32address[dimension] + (index) +
401 (quadrant <= 1 ? 4 : 5)));
402 return (uint16_t)(arg1 +
403 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
407 constexpr int kMap32TilesLength = 0x33F0;
408 int num_tile32 = kMap32TilesLength;
409 uint32_t map32address[4] = {
rom()->version_constants().kMap32TileTL,
410 rom()->version_constants().kMap32TileTR,
411 rom()->version_constants().kMap32TileBL,
412 rom()->version_constants().kMap32TileBR};
418 util::logf(
"Expanded tile32 flag: %d", expanded_flag);
419 if (expanded_flag != 0x04 || asm_version >= 3) {
421 map32address[0] =
rom()->version_constants().kMap32TileTL;
431 for (
int i = 0; i < num_tile32; i += 6) {
433 for (
int k = 0; k < 4; k++) {
457 for (
int i = 0; i < 0x200; i++) {
463 return absl::OkStatus();
474 util::logf(
"Expanded tile16 flag: %d", expanded_flag);
483 for (
int i = 0; i < num_tile16; i += 1) {
496 tiles16_.emplace_back(t0, t1, t2, t3);
498 return absl::OkStatus();
503 int position_x1 = (x * 2) + (sx * 32);
504 int position_y1 = (y * 2) + (sy * 32);
505 int position_x2 = (x * 2) + 1 + (sx * 32);
506 int position_y2 = (y * 2) + 1 + (sy * 32);
514 std::vector<uint8_t>& bytes2,
int i,
int sx,
515 int sy,
int& ttpos) {
516 for (
int y = 0; y < 16; y++) {
517 for (
int x = 0; x < 16; x++) {
518 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
542 util::logf(
"Using sequential decompression (parallel version disabled due to data integrity issues)");
544 const auto get_ow_map_gfx_ptr = [
this](
int index, uint32_t map_ptr) {
545 int p = (
rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
546 (
rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
547 (
rom()->data()[map_ptr + (3 * index)]);
551 constexpr uint32_t kBaseLowest = 0x0FFFFF;
552 constexpr uint32_t kBaseHighest = 0x0F8000;
554 uint32_t lowest = kBaseLowest;
555 uint32_t highest = kBaseHighest;
561 auto p1 = get_ow_map_gfx_ptr(
562 i,
rom()->version_constants().kCompressedAllMap32PointersHigh);
563 auto p2 = get_ow_map_gfx_ptr(
564 i,
rom()->version_constants().kCompressedAllMap32PointersLow);
573 if (p1 <= lowest && p1 > kBaseHighest)
575 if (p2 <= lowest && p2 > kBaseHighest)
597 return absl::OkStatus();
605 constexpr int kEssentialMapsPerWorld = 8;
606 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
610 util::logf(
"Building essential maps only (first %d maps per world) for faster loading", kEssentialMapsPerWorld);
612 std::vector<std::future<absl::Status>> futures;
616 bool is_essential =
false;
619 if (i < kLightWorldEssential) {
635 auto task_function = [
this, i, size, world_type]() {
639 futures.emplace_back(std::async(std::launch::async, task_function));
647 for (
auto& future : futures) {
652 util::logf(
"Essential maps built. Remaining maps will be built on-demand.");
653 return absl::OkStatus();
658 return absl::InvalidArgumentError(
"Invalid map index");
663 return absl::OkStatus();
675 util::logf(
"Building map %d on-demand", map_index);
683 rom()->data()[
rom()->version_constants().kOverworldTilesType + i];
691 int num_entrances = 129;
705 for (
int i = 0; i < num_entrances; i++) {
707 rom()->ReadWord(ow_entrance_map_ptr + (i * 2)));
709 rom()->ReadWord(ow_entrance_pos_ptr + (i * 2)));
711 int p = map_pos >> 1;
714 bool deleted =
false;
715 if (map_pos == 0xFFFF) {
719 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
720 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
724 return absl::OkStatus();
728 constexpr int kNumHoles = 0x13;
729 for (
int i = 0; i < kNumHoles; i++) {
736 int p = (map_pos + 0x400) >> 1;
740 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
741 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
742 (uint16_t)(map_pos + 0x400),
true);
744 return absl::OkStatus();
748 const int NumberOfOverworldExits = 0x4F;
749 std::vector<OverworldExit>
exits;
750 for (
int i = 0; i < NumberOfOverworldExits; i++) {
751 auto rom_data =
rom()->data();
753 uint16_t exit_room_id;
754 uint16_t exit_map_id;
756 uint16_t exit_y_scroll;
757 uint16_t exit_x_scroll;
758 uint16_t exit_y_player;
759 uint16_t exit_x_player;
760 uint16_t exit_y_camera;
761 uint16_t exit_x_camera;
762 uint16_t exit_scroll_mod_y;
763 uint16_t exit_scroll_mod_x;
764 uint16_t exit_door_type_1;
765 uint16_t exit_door_type_2;
773 exit_scroll_mod_x,
OWExitUnk2 + i, exit_door_type_1,
777 uint16_t py = (uint16_t)((rom_data[
OWExitYPlayer + (i * 2) + 1] << 8) +
779 uint16_t px = (uint16_t)((rom_data[
OWExitXPlayer + (i * 2) + 1] << 8) +
782 exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll,
783 exit_x_scroll, py, px, exit_y_camera, exit_x_camera,
784 exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1,
785 exit_door_type_2, (px & py) == 0xFFFF);
788 return absl::OkStatus();
802 uint32_t item_pointer_address =
805 for (
int i = 0; i < max_ow; i++) {
808 int bank = bank_byte & 0x7F;
811 rom()->ReadByte(item_pointer_address + (i * 2)));
813 rom()->ReadByte(item_pointer_address + (i * 2) + 1));
815 uint32_t addr = (bank << 16) +
832 if (b1 == 0xFF && b2 == 0xFF) {
836 int p = (((b2 & 0x1F) << 8) + b1) >> 1;
841 int fakeID = i % 0x40;
844 int sx = fakeID - (sy * 8);
846 all_items_.emplace_back(b3, (uint16_t)i, (x * 16) + (sx * 512),
847 (y * 16) + (sy * 512),
false);
855 return absl::OkStatus();
859 std::vector<std::future<absl::Status>> futures;
864 if (asm_version >= 3 && asm_version != 0xFF) {
866 futures.emplace_back(std::async(std::launch::async, [
this]() {
869 futures.emplace_back(std::async(std::launch::async, [
this]() {
872 futures.emplace_back(std::async(std::launch::async, [
this]() {
877 futures.emplace_back(std::async(std::launch::async, [
this]() {
880 futures.emplace_back(std::async(std::launch::async, [
this]() {
883 futures.emplace_back(std::async(std::launch::async, [
this]() {
888 for (
auto& future : futures) {
892 return absl::OkStatus();
896 int num_maps_per_gamestate,
898 for (
int i = 0; i < num_maps_per_gamestate; i++) {
902 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
904 int sprite_address =
SnesToPc((0x09 << 0x10) | word_addr);
912 int editor_map_index = i;
913 if (game_state != 0) {
914 if (editor_map_index >= 128)
915 editor_map_index -= 128;
916 else if (editor_map_index >= 64)
917 editor_map_index -= 64;
919 int mapY = (editor_map_index / 8);
920 int mapX = (editor_map_index % 8);
922 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
923 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
926 (uint8_t)(b2 & 0x3F), (uint8_t)(b1 & 0x3F), realX, realY);
933 return absl::OkStatus();
957 return absl::OkStatus();
970 std::vector<uint8_t> single_map_1(512);
971 std::vector<uint8_t> single_map_2(512);
975 for (
int y = 0; y < 16; y++) {
976 for (
int x = 0; x < 16; x++) {
978 single_map_1[npos] = packed & 0xFF;
979 single_map_2[npos] = (packed >> 8) & 0xFF;
988 if (a.empty() || b.empty()) {
989 return absl::AbortedError(
"Error compressing map gfx.");
996 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
1000 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
1001 util::logf(
"Pos set to overflow region for map %s at %s",
1006 const auto compare_array = [](
const std::vector<uint8_t>& array1,
1007 const std::vector<uint8_t>& array2) ->
bool {
1008 if (array1.size() != array2.size()) {
1012 for (
size_t i = 0; i < array1.size(); i++) {
1013 if (array1[i] != array2[i]) {
1021 for (
int j = 0; j < i; j++) {
1035 std::copy(a.begin(), a.end(),
map_data_p1[i].begin());
1038 util::logf(
"Saving map pointers1 and compressed data for map %s at %s",
1041 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
1051 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
1055 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
1059 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
1060 util::logf(
"Pos set to overflow region for map %s at %s",
1067 std::copy(b.begin(), b.end(),
map_data_p2[i].begin());
1070 util::logf(
"Saving map pointers2 and compressed data for map %s at %s",
1073 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
1083 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
1091 return absl::AbortedError(
"Too many maps data " + std::to_string(pos));
1095 return absl::OkStatus();
1103 bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF);
1105 if (use_expanded_transitions) {
1111 std::vector<uint8_t> checked_map;
1123 if (std::find(checked_map.begin(), checked_map.end(), i) !=
1124 checked_map.end()) {
1131 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
1132 for (
const auto& offset : large_map_offsets) {
1157 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1160 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1164 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1167 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1171 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1174 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1178 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1181 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1185 (parent_x_pos * 0x200)));
1187 (parent_y_pos * 0x200)));
1191 (parent_x_pos * 0x200)));
1194 (parent_y_pos * 0x200)));
1198 (parent_x_pos * 0x200)));
1201 (parent_y_pos * 0x200)));
1205 (parent_x_pos * 0x200)));
1208 (parent_y_pos * 0x200)));
1217 if (parent_x_pos == 0) {
1320 checked_map.emplace_back(i);
1321 checked_map.emplace_back((i + 1));
1322 checked_map.emplace_back((i + 8));
1323 checked_map.emplace_back((i + 9));
1345 if (i - 1 >= 0 && parent_x_pos != 0) {
1357 if (i + 1 < 64 && parent_x_pos != 7) {
1401 (uint16_t)((y_pos * 0x200) - 0xE0)));
1403 (uint16_t)((x_pos * 0x200) - 0x100)));
1410 checked_map.emplace_back(i);
1414 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
1417 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
1419 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
1421 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
1423 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
1425 return absl::OkStatus();
1429 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1430 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1431 int screen_change_1,
int screen_change_2,
int screen_change_3,
1432 int screen_change_4) {
1435 rom()->WriteShort(transition_target_north + (i * 2),
1436 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1438 rom()->WriteShort(transition_target_west + (i * 2),
1439 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1442 rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
1444 rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
1447 uint16_t by_screen1_small = 0x0060;
1450 if ((i % 0x40) - 1 >= 0) {
1455 west_neighbor.large_index() == 3) {
1456 by_screen1_small = 0xF060;
1460 west_neighbor.large_index() == 2) {
1461 by_screen1_small = 0xF060;
1466 rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
1469 uint16_t by_screen2_small = 0x0040;
1477 east_neighbor.large_index() == 2) {
1478 by_screen2_small = 0xF040;
1482 east_neighbor.large_index() == 2) {
1483 by_screen2_small = 0xF040;
1488 rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
1491 uint16_t by_screen3_small = 0x1800;
1494 if ((i % 0x40) - 8 >= 0) {
1499 north_neighbor.large_index() == 3) {
1500 by_screen3_small = 0x17C0;
1504 north_neighbor.large_index() == 1) {
1505 by_screen3_small = 0x17C0;
1510 rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
1513 uint16_t by_screen4_small = 0x1000;
1521 south_neighbor.large_index() == 1) {
1522 by_screen4_small = 0x0FC0;
1526 south_neighbor.large_index() == 1) {
1527 by_screen4_small = 0x0FC0;
1532 rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
1534 return absl::OkStatus();
1538 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1539 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1540 int screen_change_1,
int screen_change_2,
int screen_change_3,
1541 int screen_change_4) {
1543 const uint16_t offsets[] = {0, 2, 16, 18};
1544 for (
auto offset : offsets) {
1546 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1547 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1549 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1550 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1552 parent_x_pos * 0x0200));
1554 parent_y_pos * 0x0200));
1559 std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
1562 if ((i % 0x40) - 1 >= 0) {
1566 switch (west_neighbor.large_index()) {
1568 by_screen1_large[2] = 0x0060;
1571 by_screen1_large[0] = 0xF060;
1575 switch (west_neighbor.large_index()) {
1577 by_screen1_large[2] = 0x0060;
1580 by_screen1_large[0] = 0xF060;
1586 for (
int j = 0; j < 4; j++) {
1588 by_screen1_large[j]));
1592 std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
1599 switch (east_neighbor.large_index()) {
1601 by_screen2_large[3] = 0x0080;
1604 by_screen2_large[1] = 0xF080;
1608 switch (east_neighbor.large_index()) {
1610 by_screen2_large[3] = 0x0080;
1613 by_screen2_large[1] = 0xF080;
1619 for (
int j = 0; j < 4; j++) {
1621 by_screen2_large[j]));
1625 std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
1628 if ((i % 0x40) - 8 >= 0) {
1632 switch (north_neighbor.large_index()) {
1634 by_screen3_large[1] = 0x1800;
1637 by_screen3_large[0] = 0x17C0;
1641 switch (north_neighbor.large_index()) {
1643 by_screen3_large[1] = 0x1800;
1646 by_screen3_large[0] = 0x17C0;
1652 for (
int j = 0; j < 4; j++) {
1654 by_screen3_large[j]));
1658 std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
1665 switch (south_neighbor.large_index()) {
1667 by_screen4_large[3] = 0x2000;
1670 by_screen4_large[2] = 0x1FC0;
1674 switch (south_neighbor.large_index()) {
1676 by_screen4_large[3] = 0x2000;
1679 by_screen4_large[2] = 0x1FC0;
1685 for (
int j = 0; j < 4; j++) {
1687 by_screen4_large[j]));
1690 return absl::OkStatus();
1694 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1695 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1696 int screen_change_1,
int screen_change_2,
int screen_change_3,
1697 int screen_change_4) {
1699 const uint16_t offsets[] = {0, 2};
1700 for (
auto offset : offsets) {
1702 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1703 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1705 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1706 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1708 parent_x_pos * 0x0200));
1710 parent_y_pos * 0x0200));
1714 std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
1717 if ((i % 0x40) - 1 >= 0) {
1722 west_neighbor.large_index() == 3) {
1723 by_screen1_wide[0] = 0xF060;
1727 west_neighbor.large_index() == 2) {
1728 by_screen1_wide[0] = 0xF060;
1732 for (
int j = 0; j < 2; j++) {
1734 by_screen1_wide[j]));
1738 std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
1746 east_neighbor.large_index() == 2) {
1747 by_screen2_wide[1] = 0xF080;
1751 east_neighbor.large_index() == 2) {
1752 by_screen2_wide[1] = 0xF080;
1756 for (
int j = 0; j < 2; j++) {
1758 by_screen2_wide[j]));
1762 std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
1765 if ((i % 0x40) - 8 >= 0) {
1769 switch (north_neighbor.large_index()) {
1771 by_screen3_wide[1] = 0x1800;
1774 by_screen3_wide[0] = 0x17C0;
1778 switch (north_neighbor.large_index()) {
1780 by_screen3_wide[1] = 0x1800;
1783 by_screen3_wide[0] = 0x07C0;
1789 for (
int j = 0; j < 2; j++) {
1791 by_screen3_wide[j]));
1795 std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
1802 switch (south_neighbor.large_index()) {
1804 by_screen4_wide[1] = 0x1000;
1807 by_screen4_wide[0] = 0x0FC0;
1811 if (south_neighbor.large_index() == 1) {
1812 by_screen4_wide[0] = 0x0FC0;
1814 switch (south_neighbor.large_index()) {
1816 by_screen4_wide[1] = 0x1000;
1819 by_screen4_wide[0] = 0x0FC0;
1825 for (
int j = 0; j < 2; j++) {
1827 by_screen4_wide[j]));
1830 return absl::OkStatus();
1834 int i,
int parent_x_pos,
int parent_y_pos,
int transition_target_north,
1835 int transition_target_west,
int transition_pos_x,
int transition_pos_y,
1836 int screen_change_1,
int screen_change_2,
int screen_change_3,
1837 int screen_change_4) {
1839 const uint16_t offsets[] = {0, 16};
1840 for (
auto offset : offsets) {
1842 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1843 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1845 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1846 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1848 parent_x_pos * 0x0200));
1850 parent_y_pos * 0x0200));
1854 std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
1857 if ((i % 0x40) - 1 >= 0) {
1861 switch (west_neighbor.large_index()) {
1863 by_screen1_tall[1] = 0x0060;
1866 by_screen1_tall[0] = 0xF060;
1870 switch (west_neighbor.large_index()) {
1872 by_screen1_tall[1] = 0x0060;
1875 by_screen1_tall[0] = 0xF060;
1881 for (
int j = 0; j < 2; j++) {
1883 by_screen1_tall[j]));
1887 std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
1894 switch (east_neighbor.large_index()) {
1896 by_screen2_tall[1] = 0x0040;
1899 by_screen2_tall[0] = 0xF040;
1903 switch (east_neighbor.large_index()) {
1905 by_screen2_tall[1] = 0x0040;
1908 by_screen2_tall[0] = 0xF040;
1914 for (
int j = 0; j < 2; j++) {
1916 by_screen2_tall[j]));
1920 std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
1923 if ((i % 0x40) - 8 >= 0) {
1928 north_neighbor.large_index() == 3) {
1929 by_screen3_tall[0] = 0x17C0;
1933 north_neighbor.large_index() == 1) {
1934 by_screen3_tall[0] = 0x17C0;
1938 for (
int j = 0; j < 2; j++) {
1940 by_screen3_tall[j]));
1944 std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
1952 south_neighbor.large_index() == 1) {
1953 by_screen4_tall[1] = 0x1FC0;
1957 south_neighbor.large_index() == 1) {
1958 by_screen4_tall[1] = 0x1FC0;
1962 for (
int j = 0; j < 2; j++) {
1964 by_screen4_tall[j]));
1967 return absl::OkStatus();
1971 util::logf(
"Saving Large Maps (v3+ Expanded)");
1983 std::vector<uint8_t> checked_map;
1988 if (std::find(checked_map.begin(), checked_map.end(), i) !=
1989 checked_map.end()) {
2004 i, parent_x_pos, parent_y_pos, transition_target_north,
2005 transition_target_west, transition_pos_x, transition_pos_y,
2006 screen_change_1, screen_change_2, screen_change_3,
2008 checked_map.emplace_back(i);
2013 i, parent_x_pos, parent_y_pos, transition_target_north,
2014 transition_target_west, transition_pos_x, transition_pos_y,
2015 screen_change_1, screen_change_2, screen_change_3,
2018 checked_map.emplace_back(i);
2019 checked_map.emplace_back(i + 1);
2020 checked_map.emplace_back(i + 8);
2021 checked_map.emplace_back(i + 9);
2026 i, parent_x_pos, parent_y_pos, transition_target_north,
2027 transition_target_west, transition_pos_x, transition_pos_y,
2028 screen_change_1, screen_change_2, screen_change_3,
2031 checked_map.emplace_back(i);
2032 checked_map.emplace_back(i + 1);
2037 i, parent_x_pos, parent_y_pos, transition_target_north,
2038 transition_target_west, transition_pos_x, transition_pos_y,
2039 screen_change_1, screen_change_2, screen_change_3,
2042 checked_map.emplace_back(i);
2043 checked_map.emplace_back(i + 8);
2048 return absl::OkStatus();
2053 std::vector<uint64_t> all_tile_16;
2068 for (
int y = 0; y < 32; y += 2) {
2069 for (
int x = 0; x < 32; x += 2) {
2071 tiles_used[x + (sx * 32)][y + (sy * 32)],
2072 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
2073 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
2074 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
2103 std::vector<uint64_t> all_tile_16 = GetAllTile16(
map_tiles_);
2106 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
2108 std::vector<uint64_t> unique_tiles(all_tile_16);
2109 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
2112 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
2113 for (
size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
2114 all_tiles_indexed.insert(
2115 {unique_tiles[tile32_id],
static_cast<uint16_t
>(tile32_id)});
2121 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
2125 for (
size_t i = 0; i < unique_tiles.size(); ++i) {
2135 return absl::InternalError(absl::StrFormat(
2136 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
2137 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
2138 "tiles to free some space\nOr use the option Clear DW Tiles in the "
2155 return absl::OkStatus();
2204 constexpr int kTilesPer32x32Tile = 6;
2205 int unique_tile_index = 0;
2208 for (
int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2209 if (unique_tile_index >= limit) {
2210 return absl::AbortedError(
"Too many unique tile32 definitions.");
2214 auto top_left =
rom()->version_constants().kMap32TileTL;
2241 auto top_right = topRight;
2246 top_right + (i + 1),
2249 top_right + (i + 2),
2252 top_right + (i + 3),
2256 top_right + (i + 4),
2261 top_right + (i + 5),
2268 auto bottom_left = bottomLeft;
2273 bottom_left + (i + 1),
2276 bottom_left + (i + 2),
2279 bottom_left + (i + 3),
2283 bottom_left + (i + 4),
2288 bottom_left + (i + 5),
2295 auto bottom_right = bottomRight;
2300 bottom_right + (i + 1),
2303 bottom_right + (i + 2),
2306 bottom_right + (i + 3),
2310 bottom_right + (i + 4),
2315 bottom_right + (i + 5),
2321 unique_tile_index += 4;
2324 return absl::OkStatus();
2329 constexpr int kMaxUniqueTiles = 0x4540;
2330 constexpr int kTilesPer32x32Tile = 6;
2332 int unique_tile_index = 0;
2335 for (
int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2336 if (unique_tile_index >= kMaxUniqueTiles) {
2337 return absl::AbortedError(
"Too many unique tile32 definitions.");
2341 auto top_left =
rom()->version_constants().kMap32TileTL;
2369 auto top_right =
rom()->version_constants().kMap32TileTR;
2374 top_right + (i + 1),
2377 top_right + (i + 2),
2380 top_right + (i + 3),
2384 top_right + (i + 4),
2389 top_right + (i + 5),
2449 unique_tile_index += 4;
2450 num_unique_tiles += 2;
2453 return absl::OkStatus();
2535 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)));
2538 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)));
2541 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)));
2544 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)));
2548 return absl::OkStatus();
2557 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile0_)))
2560 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile1_)))
2563 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile2_)))
2566 rom()->WriteShort(tpos, TileInfoToShort(
tiles16_[i].tile3_)))
2569 return absl::OkStatus();
2605 return absl::OkStatus();
2616 if (asm_version == 0x00) {
2652 return absl::OkStatus();
2657 std::vector<OverworldItem> item_array2) {
2658 if (item_array1.size() != item_array2.size()) {
2663 for (
size_t i = 0; i < item_array1.size(); i++) {
2665 for (
size_t j = 0; j < item_array2.size(); j++) {
2667 if (item_array1[i].x_ == item_array2[j].x_ &&
2668 item_array1[i].y_ == item_array2[j].y_ &&
2669 item_array1[i].id_ == item_array2[j].id_) {
2685 std::vector<std::vector<OverworldItem>> room_items(
2689 room_items[i] = std::vector<OverworldItem>();
2691 if (item.room_map_id_ == i) {
2692 room_items[i].emplace_back(item);
2693 if (item.id_ == 0x86) {
2695 0x16DC5 + (i * 2), (item.game_x_ + (item.game_y_ * 64)) * 2));
2704 int empty_pointer = 0;
2706 item_pointers_reuse[i] = -1;
2707 for (
int ci = 0; ci < i; ci++) {
2708 if (room_items[i].empty()) {
2709 item_pointers_reuse[i] = -2;
2714 if (CompareItemsArrays(
2715 std::vector<OverworldItem>(room_items[i].begin(),
2716 room_items[i].end()),
2717 std::vector<OverworldItem>(room_items[ci].begin(),
2718 room_items[ci].end()))) {
2719 item_pointers_reuse[i] = ci;
2726 if (item_pointers_reuse[i] == -1) {
2727 item_pointers[i] = data_pos;
2730 static_cast<short>(((item.game_y_ << 6) + item.game_x_) << 1);
2732 uint32_t data =
static_cast<uint8_t
>(map_pos & 0xFF) |
2733 static_cast<uint8_t
>(map_pos >> 8) |
2734 static_cast<uint8_t
>(item.id_);
2739 empty_pointer = data_pos;
2742 }
else if (item_pointers_reuse[i] == -2) {
2743 item_pointers[i] = empty_pointer;
2745 item_pointers[i] = item_pointers[item_pointers_reuse[i]];
2748 int snesaddr =
PcToSnes(item_pointers[i]);
2754 return absl::AbortedError(
"Too many items");
2759 return absl::OkStatus();
2766 std::vector<uint8_t> new_overlay_code = {
2772 0xBF, 0x00, 0x00, 0x00,
2774 0xBF, 0x00, 0x00, 0x00,
2790 int snes_ptr_start =
PcToSnes(ptr_start);
2800 constexpr int kExpandedOverlaySpace = 0x120000;
2801 int pos = kExpandedOverlaySpace;
2812 for (
size_t t = 0; t < overlay_data.size(); t += 3) {
2813 if (t + 2 < overlay_data.size()) {
2817 pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
2831 return absl::OkStatus();
2842 return absl::OkStatus();
2846 bool enable_main_palette,
2848 bool enable_gfx_groups,
2849 bool enable_subscreen_overlay,
2850 bool enable_animated) {
2854 uint8_t enable_value = enable_bg_color ? 0xFF : 0x00;
2858 enable_value = enable_main_palette ? 0xFF : 0x00;
2862 enable_value = enable_mosaic ? 0xFF : 0x00;
2865 enable_value = enable_gfx_groups ? 0xFF : 0x00;
2869 enable_value = enable_animated ? 0xFF : 0x00;
2873 enable_value = enable_subscreen_overlay ? 0xFF : 0x00;
2887 uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) |
2888 (mosaic[1] ? 0x04 : 0x00) |
2889 (mosaic[2] ? 0x02 : 0x00) |
2890 (mosaic[3] ? 0x01 : 0x00);
2898 for (
int j = 0; j < 8; j++) {
2914 return absl::OkStatus();
2918 util::logf(
"Saving Area Specific Background Colors");
2927 return absl::OkStatus();
2978 return absl::OkStatus();
3003 return absl::OkStatus();
3011 if (asm_version < 3 || asm_version == 0xFF) {
3012 return absl::OkStatus();
3017 uint8_t area_size_byte =
3029 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 LoadEntrances()
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)
bool CompareItemsArrays(std::vector< OverworldItem > item_array1, std::vector< OverworldItem > item_array2)
std::vector< uint64_t > GetAllTile16(OverworldMapTiles &map_tiles_)
constexpr int OWExitYScroll
constexpr int kAreaGfxIdPtr
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int kOverworldHoleArea
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldTransitionPositionY
constexpr int kOverworldEntrancePos
constexpr int kOverworldHoleEntrance
constexpr int kNumMapsPerWorld
constexpr int kOverworldSpriteset
constexpr int kMap16ExpandedFlagPos
constexpr int LimitOfMap32
constexpr int NumberOfMap16Ex
constexpr int kOverworldScreenTileMapChangeByScreen1
constexpr int kOverworldScreenTileMapChangeByScreen2Expanded
constexpr int kOverworldMapDataOverflow
constexpr int kOverworldMapSizeHighByte
constexpr int kOverworldEntranceEntranceIdExpanded
constexpr int overworldItemsAddressBank
constexpr int overworldSpritesBeginingExpanded
constexpr int kNumTileTypes
constexpr int NumberOfMap32
constexpr int kNumOverworldMapItemPointers
constexpr int kOverworldScreenSize
constexpr int kOverworldScreenTileMapChangeByScreen4
constexpr int kNumTile16Individual
constexpr int kNumOverworldHoles
constexpr int kSpecialWorldMapIdStart
constexpr int OWExitDoorType2
constexpr int kOverworldEntranceEntranceId
constexpr int OverworldCustomMosaicArray
constexpr int kOverworldTransitionPositionXExpanded
constexpr int kMap16Tiles
constexpr int overworldSpritesAgahnimExpanded
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kOverworldHolePos
constexpr int kNumOverworldMaps
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldTransitionPositionYExpanded
constexpr int kOverworldEntranceMap
constexpr int kOverworldItemsEndData
constexpr int OWExitYCamera
constexpr int kOverworldMapParentIdExpanded
constexpr int kOverworldMusicBeginning
constexpr int kMap32TileBLExpanded
constexpr int kOverworldTransitionPositionX
constexpr int OWExitXScroll
constexpr int kOverworldMusicDarkWorld
constexpr int OWExitRoomId
constexpr int OWExitXCamera
constexpr int OWExitYPlayer
constexpr int kOverworldScreenSizeForLoading
constexpr int kOverworldSpritePaletteIds
constexpr int overworldTilesType
constexpr int transition_target_westExpanded
constexpr int overworldItemsAddress
constexpr int kMap32TileBRExpanded
constexpr int kMap32TileCountExpanded
constexpr int OWExitMapId
constexpr int kTransitionTargetWest
constexpr int OverworldCustomASMHasBeenApplied
constexpr int kOverworldMusicAgahnim
constexpr int kOverworldSpritesZelda
constexpr int kOverworldMapParentId
constexpr int kMap32ExpandedFlagPos
constexpr int kOverworldEntrancePosExpanded
constexpr int OWExitDoorType1
constexpr int kOverworldItemsPointers
constexpr int kOverworldMessagesExpanded
constexpr int kOverworldMusicMasterSword
constexpr int kOverworldScreenTileMapChangeByScreen3Expanded
constexpr int kOverworldMusicZelda
constexpr int kNumOverworldExits
constexpr int transition_target_northExpanded
constexpr int NumberOfMap16
constexpr int kOverworldMapSize
constexpr int kOverworldScreenTileMapChangeByScreen2
constexpr int kOverworldEntranceMapExpanded
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
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 kOverworldEntranceExpandedFlagPos
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int kNumOverworldEntrances
constexpr int OverworldCustomAreaSpecificBGPalette
constexpr int kOverworldSpritesAgahnim
constexpr int kTransitionTargetNorth
constexpr int overworldSpritesZeldaExpanded
constexpr int kOverworldCompressedOverflowPos
constexpr int kOverlayCodeStart
constexpr int kMap32TileTRExpanded
constexpr int OWExitXPlayer
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