Refactor map properties handling in Overworld Editor for improved clarity and performance

- Rearranged include statements for better organization and reduced redundancy.
- Enhanced the DrawSimplifiedMapSettings method by improving formatting and readability.
- Streamlined the logic for handling world and map changes, ensuring more efficient updates.
- Updated the DrawMapPropertiesPanel and related methods to improve layout and user interaction.
- Refactored overlay handling logic to enhance maintainability and clarity in the Overworld class.
This commit is contained in:
scawful
2025-09-27 19:42:32 -04:00
parent 0460a6f638
commit 7815f8cc85
5 changed files with 873 additions and 574 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -444,10 +444,7 @@ void OverworldEditor::DrawOverworldMapSettings() {
// World selector (always present) // World selector (always present)
TableNextColumn(); TableNextColumn();
ImGui::SetNextItemWidth(120.f); ImGui::SetNextItemWidth(120.f);
if (ImGui::Combo("##world", &current_world_, kWorldList.data(), 3)) { ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
// Update current map when world changes
RefreshOverworldMap();
}
// Area Graphics (always present) // Area Graphics (always present)
TableNextColumn(); TableNextColumn();
@@ -2170,18 +2167,7 @@ void OverworldEditor::DrawMapPropertiesPanel() {
Text("World"); Text("World");
TableNextColumn(); TableNextColumn();
ImGui::SetNextItemWidth(100.f); ImGui::SetNextItemWidth(100.f);
if (ImGui::Combo("##world", &current_world_, kWorldList.data(), 3)) { ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
// Update current map based on world change
if (current_map_ >= 0x40 && current_world_ == 0) {
current_map_ -= 0x40;
} else if (current_map_ < 0x40 && current_world_ == 1) {
current_map_ += 0x40;
} else if (current_map_ < 0x80 && current_world_ == 2) {
current_map_ += 0x80;
} else if (current_map_ >= 0x80 && current_world_ != 2) {
current_map_ -= 0x80;
}
}
TableNextColumn(); TableNextColumn();
Text("Area Graphics"); Text("Area Graphics");

View File

@@ -39,7 +39,12 @@ absl::Status Overworld::Load(Rom* rom) {
map_parent_[map_index] = overworld_maps_[map_index].parent(); map_parent_[map_index] = overworld_maps_[map_index].parent();
} }
FetchLargeMaps(); uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version >= 3) {
AssignMapSizes(overworld_maps_);
} else {
FetchLargeMaps();
}
LoadTileTypes(); LoadTileTypes();
RETURN_IF_ERROR(LoadEntrances()); RETURN_IF_ERROR(LoadEntrances());
RETURN_IF_ERROR(LoadHoles()); RETURN_IF_ERROR(LoadHoles());
@@ -105,6 +110,90 @@ void Overworld::FetchLargeMaps() {
} }
} }
/**
* @brief Loads all maps from ROM to see what size they are.
* @param maps The maps to update (passed by reference)
*/
void Overworld::AssignMapSizes(std::vector<OverworldMap>& maps) {
std::vector<bool> map_checked(kNumOverworldMaps, false);
int xx = 0;
int yy = 0;
int world = 0;
while (true) {
int i = world + xx + (yy * 8);
if (i >= static_cast<int>(map_checked.size())) {
break;
}
if (!map_checked[i]) {
switch (maps[i].area_size()) {
case AreaSizeEnum::SmallArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::SmallArea);
break;
case AreaSizeEnum::LargeArea:
map_checked[i] = true;
maps[i].SetAsLargeMap(i, 0);
if (i + 1 < static_cast<int>(maps.size())) {
map_checked[i + 1] = true;
maps[i + 1].SetAsLargeMap(i, 1);
}
if (i + 8 < static_cast<int>(maps.size())) {
map_checked[i + 8] = true;
maps[i + 8].SetAsLargeMap(i, 2);
}
if (i + 9 < static_cast<int>(maps.size())) {
map_checked[i + 9] = true;
maps[i + 9].SetAsLargeMap(i, 3);
}
xx++;
break;
case AreaSizeEnum::WideArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::WideArea);
if (i + 1 < static_cast<int>(maps.size())) {
map_checked[i + 1] = true;
maps[i + 1].SetAreaSize(AreaSizeEnum::WideArea);
}
xx++;
break;
case AreaSizeEnum::TallArea:
map_checked[i] = true;
maps[i].SetAreaSize(AreaSizeEnum::TallArea);
if (i + 8 < static_cast<int>(maps.size())) {
map_checked[i + 8] = true;
maps[i + 8].SetAreaSize(AreaSizeEnum::TallArea);
}
break;
}
}
xx++;
if (xx >= 8) {
xx = 0;
yy += 1;
if (yy >= 8) {
yy = 0;
world += 0x40;
}
}
}
}
absl::StatusOr<uint16_t> Overworld::GetTile16ForTile32( absl::StatusOr<uint16_t> Overworld::GetTile16ForTile32(
int index, int quadrant, int dimension, const uint32_t* map32address) { int index, int quadrant, int dimension, const uint32_t* map32address) {
ASSIGN_OR_RETURN( ASSIGN_OR_RETURN(
@@ -443,7 +532,8 @@ absl::Status Overworld::LoadItems() {
// Determine max number of overworld maps based on actual ASM version // Determine max number of overworld maps based on actual ASM version
// Only use expanded maps (0xA0) if v3+ ASM is actually applied // Only use expanded maps (0xA0) if v3+ ASM is actually applied
int max_ow = (asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80; int max_ow =
(asm_version >= 0x03 && asm_version != 0xFF) ? kNumOverworldMaps : 0x80;
ASSIGN_OR_RETURN(uint32_t pointer_snes, ASSIGN_OR_RETURN(uint32_t pointer_snes,
rom()->ReadLong(zelda3::overworldItemsAddress)); rom()->ReadLong(zelda3::overworldItemsAddress));
@@ -745,16 +835,16 @@ absl::Status Overworld::SaveOverworldMaps() {
absl::Status Overworld::SaveLargeMaps() { absl::Status Overworld::SaveLargeMaps() {
util::logf("Saving Large Maps"); util::logf("Saving Large Maps");
// Check if this is a v3+ ROM to use expanded transition system // Check if this is a v3+ ROM to use expanded transition system
uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF); bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF);
if (use_expanded_transitions) { if (use_expanded_transitions) {
// Use new v3+ complex transition system with neighbor awareness // Use new v3+ complex transition system with neighbor awareness
return SaveLargeMapsExpanded(); return SaveLargeMapsExpanded();
} }
// Original vanilla/v2 logic preserved // Original vanilla/v2 logic preserved
std::vector<uint8_t> checked_map; std::vector<uint8_t> checked_map;
@@ -1073,123 +1163,143 @@ absl::Status Overworld::SaveLargeMaps() {
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status Overworld::SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, absl::Status Overworld::SaveSmallAreaTransitions(
int transition_target_north, int transition_target_west, int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_pos_x, int transition_pos_y, int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_3, int screen_change_4) { int screen_change_4) {
// Set basic transition targets // Set basic transition targets
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2), RETURN_IF_ERROR(
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); rom()->WriteShort(transition_target_north + (i * 2),
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2), (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
(uint16_t)((parent_x_pos * 0x0200) - 0x0100))); RETURN_IF_ERROR(
rom()->WriteShort(transition_target_west + (i * 2),
(uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200)); RETURN_IF_ERROR(
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200)); rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
RETURN_IF_ERROR(
rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
// byScreen1 = Transitioning right // byScreen1 = Transitioning right
uint16_t by_screen1_small = 0x0060; uint16_t by_screen1_small = 0x0060;
// Check west neighbor for transition adjustments // Check west neighbor for transition adjustments
if ((i % 0x40) - 1 >= 0) { if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1]; auto& west_neighbor = overworld_maps_[i - 1];
// Transition from bottom right quadrant of large area to small area // Transition from bottom right quadrant of large area to small area
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) { if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
west_neighbor.large_index() == 3) {
by_screen1_small = 0xF060; by_screen1_small = 0xF060;
} }
// Transition from bottom quadrant of tall area to small area // Transition from bottom quadrant of tall area to small area
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) { else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
west_neighbor.large_index() == 2) {
by_screen1_small = 0xF060; by_screen1_small = 0xF060;
} }
} }
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small)); RETURN_IF_ERROR(
rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
// byScreen2 = Transitioning left // byScreen2 = Transitioning left
uint16_t by_screen2_small = 0x0040; uint16_t by_screen2_small = 0x0040;
// Check east neighbor for transition adjustments // Check east neighbor for transition adjustments
if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) { if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 1]; auto& east_neighbor = overworld_maps_[i + 1];
// Transition from bottom left quadrant of large area to small area // Transition from bottom left quadrant of large area to small area
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) { if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
east_neighbor.large_index() == 2) {
by_screen2_small = 0xF040; by_screen2_small = 0xF040;
} }
// Transition from bottom quadrant of tall area to small area // Transition from bottom quadrant of tall area to small area
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) { else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
east_neighbor.large_index() == 2) {
by_screen2_small = 0xF040; by_screen2_small = 0xF040;
} }
} }
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small)); RETURN_IF_ERROR(
rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
// byScreen3 = Transitioning down // byScreen3 = Transitioning down
uint16_t by_screen3_small = 0x1800; uint16_t by_screen3_small = 0x1800;
// Check north neighbor for transition adjustments // Check north neighbor for transition adjustments
if ((i % 0x40) - 8 >= 0) { if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8]; auto& north_neighbor = overworld_maps_[i - 8];
// Transition from bottom right quadrant of large area to small area // Transition from bottom right quadrant of large area to small area
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) { if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
north_neighbor.large_index() == 3) {
by_screen3_small = 0x17C0; by_screen3_small = 0x17C0;
} }
// Transition from right quadrant of wide area to small area // Transition from right quadrant of wide area to small area
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) { else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
north_neighbor.large_index() == 1) {
by_screen3_small = 0x17C0; by_screen3_small = 0x17C0;
} }
} }
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small)); RETURN_IF_ERROR(
rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
// byScreen4 = Transitioning up // byScreen4 = Transitioning up
uint16_t by_screen4_small = 0x1000; uint16_t by_screen4_small = 0x1000;
// Check south neighbor for transition adjustments // Check south neighbor for transition adjustments
if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) { if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 8]; auto& south_neighbor = overworld_maps_[i + 8];
// Transition from top right quadrant of large area to small area // Transition from top right quadrant of large area to small area
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) { if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
south_neighbor.large_index() == 1) {
by_screen4_small = 0x0FC0; by_screen4_small = 0x0FC0;
} }
// Transition from right quadrant of wide area to small area // Transition from right quadrant of wide area to small area
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) { else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
south_neighbor.large_index() == 1) {
by_screen4_small = 0x0FC0; by_screen4_small = 0x0FC0;
} }
} }
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small)); RETURN_IF_ERROR(
rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos, absl::Status Overworld::SaveLargeAreaTransitions(
int transition_target_north, int transition_target_west, int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_pos_x, int transition_pos_y, int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_3, int screen_change_4) { int screen_change_4) {
// Set transition targets for all 4 quadrants // Set transition targets for all 4 quadrants
const uint16_t offsets[] = {0, 2, 16, 18}; const uint16_t offsets[] = {0, 2, 16, 18};
for (auto offset : offsets) { for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, RETURN_IF_ERROR(
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); rom()->WriteShort(transition_target_north + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
(uint16_t)((parent_x_pos * 0x0200) - 0x0100))); RETURN_IF_ERROR(
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); rom()->WriteShort(transition_target_west + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
} }
// Complex neighbor-aware transition calculations for large areas // Complex neighbor-aware transition calculations for large areas
// byScreen1 = Transitioning right // byScreen1 = Transitioning right
std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060}; std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
// Check west neighbor // Check west neighbor
if ((i % 0x40) - 1 >= 0) { if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1]; auto& west_neighbor = overworld_maps_[i - 1];
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (west_neighbor.large_index()) { switch (west_neighbor.large_index()) {
case 1: // From bottom right to bottom left of large area case 1: // From bottom right to bottom left of large area
@@ -1210,18 +1320,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
} }
} }
} }
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_large[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_large[j]));
} }
// byScreen2 = Transitioning left // byScreen2 = Transitioning left
std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080}; std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
// Check east neighbor // Check east neighbor
if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) { if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 2]; auto& east_neighbor = overworld_maps_[i + 2];
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (east_neighbor.large_index()) { switch (east_neighbor.large_index()) {
case 0: // From bottom left to bottom right of large area case 0: // From bottom left to bottom right of large area
@@ -1242,18 +1353,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
} }
} }
} }
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_large[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_large[j]));
} }
// byScreen3 = Transitioning down // byScreen3 = Transitioning down
std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840}; std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
// Check north neighbor // Check north neighbor
if ((i % 0x40) - 8 >= 0) { if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8]; auto& north_neighbor = overworld_maps_[i - 8];
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (north_neighbor.large_index()) { switch (north_neighbor.large_index()) {
case 2: // From bottom right to top right of large area case 2: // From bottom right to top right of large area
@@ -1274,18 +1386,19 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
} }
} }
} }
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_large[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_large[j]));
} }
// byScreen4 = Transitioning up // byScreen4 = Transitioning up
std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040}; std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
// Check south neighbor // Check south neighbor
if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) { if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 16]; auto& south_neighbor = overworld_maps_[i + 16];
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (south_neighbor.large_index()) { switch (south_neighbor.large_index()) {
case 0: // From top right to bottom right of large area case 0: // From top right to bottom right of large area
@@ -1306,79 +1419,90 @@ absl::Status Overworld::SaveLargeAreaTransitions(int i, int parent_x_pos, int pa
} }
} }
} }
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_large[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_large[j]));
} }
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos, absl::Status Overworld::SaveWideAreaTransitions(
int transition_target_north, int transition_target_west, int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_pos_x, int transition_pos_y, int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_3, int screen_change_4) { int screen_change_4) {
// Set transition targets for both quadrants // Set transition targets for both quadrants
const uint16_t offsets[] = {0, 2}; const uint16_t offsets[] = {0, 2};
for (auto offset : offsets) { for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, RETURN_IF_ERROR(
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); rom()->WriteShort(transition_target_north + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
(uint16_t)((parent_x_pos * 0x0200) - 0x0100))); RETURN_IF_ERROR(
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); rom()->WriteShort(transition_target_west + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
} }
// byScreen1 = Transitioning right // byScreen1 = Transitioning right
std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060}; std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
// Check west neighbor // Check west neighbor
if ((i % 0x40) - 1 >= 0) { if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1]; auto& west_neighbor = overworld_maps_[i - 1];
// From bottom right of large to left of wide // From bottom right of large to left of wide
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea && west_neighbor.large_index() == 3) { if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
west_neighbor.large_index() == 3) {
by_screen1_wide[0] = 0xF060; by_screen1_wide[0] = 0xF060;
} }
// From bottom of tall to left of wide // From bottom of tall to left of wide
else if (west_neighbor.area_size() == AreaSizeEnum::TallArea && west_neighbor.large_index() == 2) { else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
west_neighbor.large_index() == 2) {
by_screen1_wide[0] = 0xF060; by_screen1_wide[0] = 0xF060;
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_wide[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_wide[j]));
} }
// byScreen2 = Transitioning left // byScreen2 = Transitioning left
std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080}; std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
// Check east neighbor // Check east neighbor
if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) { if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 2]; auto& east_neighbor = overworld_maps_[i + 2];
// From bottom left of large to right of wide // From bottom left of large to right of wide
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea && east_neighbor.large_index() == 2) { if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
east_neighbor.large_index() == 2) {
by_screen2_wide[1] = 0xF080; by_screen2_wide[1] = 0xF080;
} }
// From bottom of tall to right of wide // From bottom of tall to right of wide
else if (east_neighbor.area_size() == AreaSizeEnum::TallArea && east_neighbor.large_index() == 2) { else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
east_neighbor.large_index() == 2) {
by_screen2_wide[1] = 0xF080; by_screen2_wide[1] = 0xF080;
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_wide[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_wide[j]));
} }
// byScreen3 = Transitioning down // byScreen3 = Transitioning down
std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840}; std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
// Check north neighbor // Check north neighbor
if ((i % 0x40) - 8 >= 0) { if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8]; auto& north_neighbor = overworld_maps_[i - 8];
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (north_neighbor.large_index()) { switch (north_neighbor.large_index()) {
case 2: // From bottom right of large to right of wide case 2: // From bottom right of large to right of wide
@@ -1399,18 +1523,19 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par
} }
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_wide[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_wide[j]));
} }
// byScreen4 = Transitioning up // byScreen4 = Transitioning up
std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040}; std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
// Check south neighbor // Check south neighbor
if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) { if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 8]; auto& south_neighbor = overworld_maps_[i + 8];
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (south_neighbor.large_index()) { switch (south_neighbor.large_index()) {
case 0: // From top right of large to right of wide case 0: // From top right of large to right of wide
@@ -1434,37 +1559,42 @@ absl::Status Overworld::SaveWideAreaTransitions(int i, int parent_x_pos, int par
} }
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_wide[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_wide[j]));
} }
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, absl::Status Overworld::SaveTallAreaTransitions(
int transition_target_north, int transition_target_west, int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
int transition_pos_x, int transition_pos_y, int transition_target_west, int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2, int screen_change_1, int screen_change_2, int screen_change_3,
int screen_change_3, int screen_change_4) { int screen_change_4) {
// Set transition targets for both quadrants // Set transition targets for both quadrants
const uint16_t offsets[] = {0, 16}; const uint16_t offsets[] = {0, 16};
for (auto offset : offsets) { for (auto offset : offsets) {
RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2) + offset, RETURN_IF_ERROR(
(uint16_t)((parent_y_pos * 0x0200) - 0x00E0))); rom()->WriteShort(transition_target_north + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2) + offset, (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
(uint16_t)((parent_x_pos * 0x0200) - 0x0100))); RETURN_IF_ERROR(
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset, parent_x_pos * 0x0200)); rom()->WriteShort(transition_target_west + (i * 2) + offset,
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset, parent_y_pos * 0x0200)); (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
parent_x_pos * 0x0200));
RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
parent_y_pos * 0x0200));
} }
// byScreen1 = Transitioning right // byScreen1 = Transitioning right
std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060}; std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
// Check west neighbor // Check west neighbor
if ((i % 0x40) - 1 >= 0) { if ((i % 0x40) - 1 >= 0) {
auto& west_neighbor = overworld_maps_[i - 1]; auto& west_neighbor = overworld_maps_[i - 1];
if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (west_neighbor.large_index()) { switch (west_neighbor.large_index()) {
case 1: // From bottom right of large to bottom of tall case 1: // From bottom right of large to bottom of tall
@@ -1485,18 +1615,19 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
} }
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j], by_screen1_tall[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
by_screen1_tall[j]));
} }
// byScreen2 = Transitioning left // byScreen2 = Transitioning left
std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040}; std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
// Check east neighbor // Check east neighbor
if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) { if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
auto& east_neighbor = overworld_maps_[i + 1]; auto& east_neighbor = overworld_maps_[i + 1];
if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) { if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
switch (east_neighbor.large_index()) { switch (east_neighbor.large_index()) {
case 0: // From bottom left of large to bottom of tall case 0: // From bottom left of large to bottom of tall
@@ -1517,51 +1648,58 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
} }
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j], by_screen2_tall[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
by_screen2_tall[j]));
} }
// byScreen3 = Transitioning down // byScreen3 = Transitioning down
std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800}; std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
// Check north neighbor // Check north neighbor
if ((i % 0x40) - 8 >= 0) { if ((i % 0x40) - 8 >= 0) {
auto& north_neighbor = overworld_maps_[i - 8]; auto& north_neighbor = overworld_maps_[i - 8];
// From bottom right of large to top of tall // From bottom right of large to top of tall
if (north_neighbor.area_size() == AreaSizeEnum::LargeArea && north_neighbor.large_index() == 3) { if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
north_neighbor.large_index() == 3) {
by_screen3_tall[0] = 0x17C0; by_screen3_tall[0] = 0x17C0;
} }
// From right of wide to top of tall // From right of wide to top of tall
else if (north_neighbor.area_size() == AreaSizeEnum::WideArea && north_neighbor.large_index() == 1) { else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
north_neighbor.large_index() == 1) {
by_screen3_tall[0] = 0x17C0; by_screen3_tall[0] = 0x17C0;
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j], by_screen3_tall[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
by_screen3_tall[j]));
} }
// byScreen4 = Transitioning up // byScreen4 = Transitioning up
std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000}; std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
// Check south neighbor // Check south neighbor
if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) { if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
auto& south_neighbor = overworld_maps_[i + 16]; auto& south_neighbor = overworld_maps_[i + 16];
// From top right of large to bottom of tall // From top right of large to bottom of tall
if (south_neighbor.area_size() == AreaSizeEnum::LargeArea && south_neighbor.large_index() == 1) { if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
south_neighbor.large_index() == 1) {
by_screen4_tall[1] = 0x1FC0; by_screen4_tall[1] = 0x1FC0;
} }
// From right of wide to bottom of tall // From right of wide to bottom of tall
else if (south_neighbor.area_size() == AreaSizeEnum::WideArea && south_neighbor.large_index() == 1) { else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
south_neighbor.large_index() == 1) {
by_screen4_tall[1] = 0x1FC0; by_screen4_tall[1] = 0x1FC0;
} }
} }
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j], by_screen4_tall[j])); RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
by_screen4_tall[j]));
} }
return absl::OkStatus(); return absl::OkStatus();
@@ -1569,7 +1707,7 @@ absl::Status Overworld::SaveTallAreaTransitions(int i, int parent_x_pos, int par
absl::Status Overworld::SaveLargeMapsExpanded() { absl::Status Overworld::SaveLargeMapsExpanded() {
util::logf("Saving Large Maps (v3+ Expanded)"); util::logf("Saving Large Maps (v3+ Expanded)");
// Use expanded memory locations for v3+ // Use expanded memory locations for v3+
int transition_target_north = zelda3::transition_target_northExpanded; int transition_target_north = zelda3::transition_target_northExpanded;
int transition_target_west = zelda3::transition_target_westExpanded; int transition_target_west = zelda3::transition_target_westExpanded;
@@ -1585,7 +1723,8 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
// Process all overworld maps (0xA0 for v3) // Process all overworld maps (0xA0 for v3)
for (int i = 0; i < kNumOverworldMaps; ++i) { for (int i = 0; i < kNumOverworldMaps; ++i) {
// Skip if this map was already processed as part of a multi-area structure // Skip if this map was already processed as part of a multi-area structure
if (std::find(checked_map.begin(), checked_map.end(), i) != checked_map.end()) { if (std::find(checked_map.begin(), checked_map.end(), i) !=
checked_map.end()) {
continue; continue;
} }
@@ -1599,20 +1738,20 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
// Handle transitions based on area size // Handle transitions based on area size
switch (overworld_maps_[i].area_size()) { switch (overworld_maps_[i].area_size()) {
case AreaSizeEnum::SmallArea: case AreaSizeEnum::SmallArea:
RETURN_IF_ERROR(SaveSmallAreaTransitions(i, parent_x_pos, parent_y_pos, RETURN_IF_ERROR(SaveSmallAreaTransitions(
transition_target_north, transition_target_west, i, parent_x_pos, parent_y_pos, transition_target_north,
transition_pos_x, transition_pos_y, transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_1, screen_change_2, screen_change_3,
screen_change_3, screen_change_4)); screen_change_4));
checked_map.emplace_back(i); checked_map.emplace_back(i);
break; break;
case AreaSizeEnum::LargeArea: case AreaSizeEnum::LargeArea:
RETURN_IF_ERROR(SaveLargeAreaTransitions(i, parent_x_pos, parent_y_pos, RETURN_IF_ERROR(SaveLargeAreaTransitions(
transition_target_north, transition_target_west, i, parent_x_pos, parent_y_pos, transition_target_north,
transition_pos_x, transition_pos_y, transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_1, screen_change_2, screen_change_3,
screen_change_3, screen_change_4)); screen_change_4));
// Mark all 4 quadrants as processed // Mark all 4 quadrants as processed
checked_map.emplace_back(i); checked_map.emplace_back(i);
checked_map.emplace_back(i + 1); checked_map.emplace_back(i + 1);
@@ -1621,22 +1760,22 @@ absl::Status Overworld::SaveLargeMapsExpanded() {
break; break;
case AreaSizeEnum::WideArea: case AreaSizeEnum::WideArea:
RETURN_IF_ERROR(SaveWideAreaTransitions(i, parent_x_pos, parent_y_pos, RETURN_IF_ERROR(SaveWideAreaTransitions(
transition_target_north, transition_target_west, i, parent_x_pos, parent_y_pos, transition_target_north,
transition_pos_x, transition_pos_y, transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_1, screen_change_2, screen_change_3,
screen_change_3, screen_change_4)); screen_change_4));
// Mark both horizontal quadrants as processed // Mark both horizontal quadrants as processed
checked_map.emplace_back(i); checked_map.emplace_back(i);
checked_map.emplace_back(i + 1); checked_map.emplace_back(i + 1);
break; break;
case AreaSizeEnum::TallArea: case AreaSizeEnum::TallArea:
RETURN_IF_ERROR(SaveTallAreaTransitions(i, parent_x_pos, parent_y_pos, RETURN_IF_ERROR(SaveTallAreaTransitions(
transition_target_north, transition_target_west, i, parent_x_pos, parent_y_pos, transition_target_north,
transition_pos_x, transition_pos_y, transition_target_west, transition_pos_x, transition_pos_y,
screen_change_1, screen_change_2, screen_change_1, screen_change_2, screen_change_3,
screen_change_3, screen_change_4)); screen_change_4));
// Mark both vertical quadrants as processed // Mark both vertical quadrants as processed
checked_map.emplace_back(i); checked_map.emplace_back(i);
checked_map.emplace_back(i + 8); checked_map.emplace_back(i + 8);
@@ -2170,7 +2309,7 @@ absl::Status Overworld::SaveMap16Tiles() {
absl::Status Overworld::SaveEntrances() { absl::Status Overworld::SaveEntrances() {
util::logf("Saving Entrances"); util::logf("Saving Entrances");
// Use expanded entrance tables if available // Use expanded entrance tables if available
if (expanded_entrances_) { if (expanded_entrances_) {
for (int i = 0; i < kNumOverworldEntrances; i++) { for (int i = 0; i < kNumOverworldEntrances; i++) {
@@ -2206,10 +2345,10 @@ absl::Status Overworld::SaveEntrances() {
absl::Status Overworld::SaveExits() { absl::Status Overworld::SaveExits() {
util::logf("Saving Exits"); util::logf("Saving Exits");
// ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically // ASM version 0x03 added SW support and the exit leading to Zora's Domain specifically
// needs to be updated because its camera values are incorrect. // needs to be updated because its camera values are incorrect.
// We only update it if it was a vanilla ROM though because we don't know if the // We only update it if it was a vanilla ROM though because we don't know if the
// user has already adjusted it or not. // user has already adjusted it or not.
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version == 0x00) { if (asm_version == 0x00) {
@@ -2219,7 +2358,7 @@ absl::Status Overworld::SaveExits() {
// all_exits_[0x4D].SpecialUpdatePosition(); // all_exits_[0x4D].SpecialUpdatePosition();
// } // }
} }
for (int i = 0; i < kNumOverworldExits; i++) { for (int i = 0; i < kNumOverworldExits; i++) {
RETURN_IF_ERROR( RETURN_IF_ERROR(
rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_)); rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_));
@@ -2360,24 +2499,24 @@ absl::Status Overworld::SaveItems() {
absl::Status Overworld::SaveMapOverlays() { absl::Status Overworld::SaveMapOverlays() {
util::logf("Saving Map Overlays"); util::logf("Saving Map Overlays");
// Generate the new overlay code that handles interactive overlays // Generate the new overlay code that handles interactive overlays
std::vector<uint8_t> new_overlay_code = { std::vector<uint8_t> new_overlay_code = {
0xC2, 0x30, // REP #$30 0xC2, 0x30, // REP #$30
0xA5, 0x8A, // LDA $8A 0xA5, 0x8A, // LDA $8A
0x0A, 0x18, // ASL : CLC 0x0A, 0x18, // ASL : CLC
0x65, 0x8A, // ADC $8A 0x65, 0x8A, // ADC $8A
0xAA, // TAX 0xAA, // TAX
0xBF, 0x00, 0x00, 0x00, // LDA, X 0xBF, 0x00, 0x00, 0x00, // LDA, X
0x85, 0x00, // STA $00 0x85, 0x00, // STA $00
0xBF, 0x00, 0x00, 0x00, // LDA, X +2 0xBF, 0x00, 0x00, 0x00, // LDA, X +2
0x85, 0x02, // STA $02 0x85, 0x02, // STA $02
0x4B, // PHK 0x4B, // PHK
0xF4, 0x00, 0x00, // This position +3 ? 0xF4, 0x00, 0x00, // This position +3 ?
0xDC, 0x00, 0x00, // JML [$00 00] 0xDC, 0x00, 0x00, // JML [$00 00]
0xE2, 0x30, // SEP #$30 0xE2, 0x30, // SEP #$30
0xAB, // PLB 0xAB, // PLB
0x6B, // RTL 0x6B, // RTL
}; };
// Write overlay code to ROM // Write overlay code to ROM
@@ -2399,7 +2538,7 @@ absl::Status Overworld::SaveMapOverlays() {
constexpr int kExpandedOverlaySpace = 0x120000; constexpr int kExpandedOverlaySpace = 0x120000;
int pos = kExpandedOverlaySpace; int pos = kExpandedOverlaySpace;
int ptr_pos = kOverlayCodeStart + 32; int ptr_pos = kOverlayCodeStart + 32;
for (int i = 0; i < kNumOverworldMaps; i++) { for (int i = 0; i < kNumOverworldMaps; i++) {
int snes_addr = PcToSnes(pos); int snes_addr = PcToSnes(pos);
RETURN_IF_ERROR(rom()->WriteLong(ptr_pos, snes_addr & 0xFFFFFF)); RETURN_IF_ERROR(rom()->WriteLong(ptr_pos, snes_addr & 0xFFFFFF));
@@ -2412,7 +2551,8 @@ absl::Status Overworld::SaveMapOverlays() {
if (t + 2 < overlay_data.size()) { if (t + 2 < overlay_data.size()) {
// Generate LDA/STA sequence for each overlay tile // Generate LDA/STA sequence for each overlay tile
RETURN_IF_ERROR(rom()->WriteByte(pos, 0xA9)); // LDA #$ RETURN_IF_ERROR(rom()->WriteByte(pos, 0xA9)); // LDA #$
RETURN_IF_ERROR(rom()->WriteShort(pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8))); RETURN_IF_ERROR(rom()->WriteShort(
pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
pos += 3; pos += 3;
RETURN_IF_ERROR(rom()->WriteByte(pos, 0x8D)); // STA $xxxx RETURN_IF_ERROR(rom()->WriteByte(pos, 0x8D)); // STA $xxxx
@@ -2431,37 +2571,46 @@ absl::Status Overworld::SaveMapOverlays() {
absl::Status Overworld::SaveOverworldTilesType() { absl::Status Overworld::SaveOverworldTilesType() {
util::logf("Saving Overworld Tiles Types"); util::logf("Saving Overworld Tiles Types");
for (int i = 0; i < kNumTileTypes; i++) { for (int i = 0; i < kNumTileTypes; i++) {
RETURN_IF_ERROR(rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i])); RETURN_IF_ERROR(
rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i]));
} }
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette, absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color,
bool enable_mosaic, bool enable_gfx_groups, bool enable_main_palette,
bool enable_subscreen_overlay, bool enable_animated) { bool enable_mosaic,
bool enable_gfx_groups,
bool enable_subscreen_overlay,
bool enable_animated) {
util::logf("Applying Custom Overworld ASM"); util::logf("Applying Custom Overworld ASM");
// Set the enable/disable settings // Set the enable/disable settings
uint8_t enable_value = enable_bg_color ? 0xFF : 0x00; uint8_t enable_value = enable_bg_color ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value));
enable_value = enable_main_palette ? 0xFF : 0x00; enable_value = enable_main_palette ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value));
enable_value = enable_mosaic ? 0xFF : 0x00; enable_value = enable_mosaic ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicEnabled, enable_value)); RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicEnabled, enable_value));
enable_value = enable_gfx_groups ? 0xFF : 0x00; enable_value = enable_gfx_groups ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value));
enable_value = enable_animated ? 0xFF : 0x00; enable_value = enable_animated ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value));
enable_value = enable_subscreen_overlay ? 0xFF : 0x00; enable_value = enable_subscreen_overlay ? 0xFF : 0x00;
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value));
// Write the main palette table // Write the main palette table
for (int i = 0; i < kNumOverworldMaps; i++) { for (int i = 0; i < kNumOverworldMaps; i++) {
@@ -2474,18 +2623,20 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
const auto& mosaic = overworld_maps_[i].mosaic_expanded(); const auto& mosaic = overworld_maps_[i].mosaic_expanded();
// .... udlr bit format // .... udlr bit format
uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) | // up uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) | // up
(mosaic[1] ? 0x04 : 0x00) | // down (mosaic[1] ? 0x04 : 0x00) | // down
(mosaic[2] ? 0x02 : 0x00) | // left (mosaic[2] ? 0x02 : 0x00) | // left
(mosaic[3] ? 0x01 : 0x00); // right (mosaic[3] ? 0x01 : 0x00); // right
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte)); RETURN_IF_ERROR(
rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte));
} }
// Write the main and animated gfx tiles table // Write the main and animated gfx tiles table
for (int i = 0; i < kNumOverworldMaps; i++) { for (int i = 0; i < kNumOverworldMaps; i++) {
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j, RETURN_IF_ERROR(
overworld_maps_[i].custom_tileset(j))); rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j,
overworld_maps_[i].custom_tileset(j)));
} }
RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXArray + i, RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomAnimatedGFXArray + i,
overworld_maps_[i].animated_gfx())); overworld_maps_[i].animated_gfx()));
@@ -2493,8 +2644,9 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
// Write the subscreen overlay table // Write the subscreen overlay table
for (int i = 0; i < kNumOverworldMaps; i++) { for (int i = 0; i < kNumOverworldMaps; i++) {
RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2), RETURN_IF_ERROR(
overworld_maps_[i].subscreen_overlay())); rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2),
overworld_maps_[i].subscreen_overlay()));
} }
return absl::OkStatus(); return absl::OkStatus();
@@ -2502,11 +2654,12 @@ absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color, bool enable
absl::Status Overworld::SaveAreaSpecificBGColors() { absl::Status Overworld::SaveAreaSpecificBGColors() {
util::logf("Saving Area Specific Background Colors"); util::logf("Saving Area Specific Background Colors");
// Write area-specific background colors if enabled // Write area-specific background colors if enabled
for (int i = 0; i < kNumOverworldMaps; i++) { for (int i = 0; i < kNumOverworldMaps; i++) {
uint16_t bg_color = overworld_maps_[i].area_specific_bg_color(); uint16_t bg_color = overworld_maps_[i].area_specific_bg_color();
RETURN_IF_ERROR(rom()->WriteShort(OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color)); RETURN_IF_ERROR(rom()->WriteShort(
OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color));
} }
return absl::OkStatus(); return absl::OkStatus();

View File

@@ -190,6 +190,7 @@ class Overworld {
absl::Status SaveMapProperties(); absl::Status SaveMapProperties();
absl::Status SaveMusic(); absl::Status SaveMusic();
absl::Status SaveAreaSizes(); absl::Status SaveAreaSizes();
void AssignMapSizes(std::vector<OverworldMap>& maps);
auto rom() const { return rom_; } auto rom() const { return rom_; }
auto mutable_rom() { return rom_; } auto mutable_rom() { return rom_; }

View File

@@ -14,20 +14,20 @@
namespace yaze { namespace yaze {
namespace zelda3 { namespace zelda3 {
OverworldMap::OverworldMap(int index, Rom *rom) OverworldMap::OverworldMap(int index, Rom* rom)
: index_(index), parent_(index), rom_(rom) { : index_(index), parent_(index), rom_(rom) {
LoadAreaInfo(); LoadAreaInfo();
// Load parent ID from ROM data if available (for custom ASM versions)
// Use ASM version byte as source of truth
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version != 0xFF && asm_version >= 0x03) {
// For v3+, parent ID is stored in expanded table
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
}
if (asm_version != 0xFF) { if (asm_version != 0xFF) {
// Custom overworld ASM is applied - use custom logic if (asm_version == 0x03) {
if (asm_version == 0x00) {
// Special case: version 0 means flag-enabled vanilla mode
LoadCustomOverworldData(); LoadCustomOverworldData();
} else { } else {
// Custom overworld ASM applied - set up custom tileset
SetupCustomTileset(asm_version); SetupCustomTileset(asm_version);
} }
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) { } else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
@@ -38,21 +38,28 @@ OverworldMap::OverworldMap(int index, Rom *rom)
} }
absl::Status OverworldMap::BuildMap(int count, int game_state, int world, absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
std::vector<gfx::Tile16> &tiles16, std::vector<gfx::Tile16>& tiles16,
OverworldBlockset &world_blockset) { OverworldBlockset& world_blockset) {
game_state_ = game_state; game_state_ = game_state;
world_ = world; world_ = world;
if (large_map_) { uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// For large maps in vanilla ROMs, we need to handle special world graphics
// This ensures proper rendering of special overworld areas like Zora's Domain
if (large_map_ && asm_version == 0xFF) {
if (parent_ != index_ && !initialized_) { if (parent_ != index_ && !initialized_) {
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A && if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
index_ != 0x88) { index_ != 0x88) {
// Most special world areas use the special graphics group
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup + area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
(parent_ - kSpecialWorldMapIdStart)]; (parent_ - kSpecialWorldMapIdStart)];
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1]; area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
} else if (index_ == 0x88) { } else if (index_ == 0x88) {
// Triforce room has special hardcoded values
area_graphics_ = 0x51; area_graphics_ = 0x51;
area_palette_ = 0x00; area_palette_ = 0x00;
} else { } else {
// Fallback to standard area graphics
area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_]; area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
} }
@@ -74,6 +81,11 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
void OverworldMap::LoadAreaInfo() { void OverworldMap::LoadAreaInfo() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// ZSCustomOverworld ASM Version System:
// 0x00-0x02: Legacy versions with limited features
// 0x03: Current version with full area size expansion and custom data support
// 0xFF: Pure vanilla ROM (no ASM applied)
// Load message ID and area size based on ASM version // Load message ID and area size based on ASM version
if (asm_version < 3 || asm_version == 0xFF) { if (asm_version < 3 || asm_version == 0xFF) {
// v2 and vanilla: use original message table // v2 and vanilla: use original message table
@@ -83,6 +95,7 @@ void OverworldMap::LoadAreaInfo() {
// Load area size for v2/vanilla // Load area size for v2/vanilla
if (index_ < 0x80) { if (index_ < 0x80) {
// For LW and DW, check the screen size byte // For LW and DW, check the screen size byte
// Note: v2 had a bug where large/small values were swapped
uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)]; uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
switch (size_byte) { switch (size_byte) {
case 0: case 0:
@@ -101,6 +114,7 @@ void OverworldMap::LoadAreaInfo() {
} }
} else { } else {
// For SW, use hardcoded values for v2 compatibility // For SW, use hardcoded values for v2 compatibility
// Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas
area_size_ = area_size_ =
(index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A) (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
? AreaSizeEnum::LargeArea ? AreaSizeEnum::LargeArea
@@ -108,6 +122,7 @@ void OverworldMap::LoadAreaInfo() {
} }
} else { } else {
// v3: use expanded message table and area size table // v3: use expanded message table and area size table
// All area sizes are now stored in the expanded table, supporting all size types
message_id_ = message_id_ =
(*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] | (*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] |
((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8); ((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8);
@@ -172,10 +187,10 @@ void OverworldMap::LoadAreaInfo() {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
} }
} else { } else {
// Special World (SW) areas // Special World (SW) areas (index >= 0x80)
// Message ID already loaded above based on ASM version // Message ID already loaded above based on ASM version
// For v3, use expanded sprite tables // For v3, use expanded sprite tables with full customization support
if (asm_version >= 3 && asm_version != 0xFF) { if (asm_version >= 3 && asm_version != 0xFF) {
sprite_graphics_[0] = sprite_graphics_[0] =
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ - (*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
@@ -214,20 +229,24 @@ void OverworldMap::LoadAreaInfo() {
area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_]; area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_];
// For v2/vanilla, use original palette table and handle special cases // For v2/vanilla, use original palette table and handle special cases
// These hardcoded cases are needed for vanilla compatibility
if (asm_version < 3 || asm_version == 0xFF) { if (asm_version < 3 || asm_version == 0xFF) {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
// Handle special world area cases // Handle special world area cases based on ZScream documentation
if (index_ == 0x88 || index_ == 0x93) { if (index_ == 0x88 || index_ == 0x93) {
// Triforce room - special graphics and palette
area_graphics_ = 0x51; area_graphics_ = 0x51;
area_palette_ = 0x00; area_palette_ = 0x00;
} else if (index_ == 0x80) { } else if (index_ == 0x80) {
// Master Sword area - use special graphics group
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup + area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
(parent_ - kSpecialWorldMapIdStart)]; (parent_ - kSpecialWorldMapIdStart)];
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1]; area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
} else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || } else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
index_ == 0x8A) { index_ == 0x8A) {
// Zora's Domain areas - use special sprite graphics // Zora's Domain areas - use special sprite graphics and area graphics
// Note: These are the large area maps that were causing crashes
sprite_graphics_[0] = 0x0E; sprite_graphics_[0] = 0x0E;
sprite_graphics_[1] = 0x0E; sprite_graphics_[1] = 0x0E;
sprite_graphics_[2] = 0x0E; sprite_graphics_[2] = 0x0E;
@@ -253,7 +272,7 @@ void OverworldMap::LoadAreaInfo() {
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43]; area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
} else { } else {
// Default case // Default case - use basic graphics
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00]; area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00]; area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
} }
@@ -578,7 +597,7 @@ void OverworldMap::LoadAreaGraphics() {
namespace palette_internal { namespace palette_internal {
absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette &current, absl::Status SetColorsPalette(Rom& rom, int index, gfx::SnesPalette& current,
gfx::SnesPalette main, gfx::SnesPalette animated, gfx::SnesPalette main, gfx::SnesPalette animated,
gfx::SnesPalette aux1, gfx::SnesPalette aux2, gfx::SnesPalette aux1, gfx::SnesPalette aux2,
gfx::SnesPalette hud, gfx::SnesColor bgrcolor, gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
@@ -699,7 +718,7 @@ absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette &current,
} // namespace palette_internal } // namespace palette_internal
absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette( absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
const gfx::PaletteGroup &palette_group, int index, int previous_index, const gfx::PaletteGroup& palette_group, int index, int previous_index,
int limit) { int limit) {
if (index == 255) { if (index == 255) {
index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
@@ -713,10 +732,10 @@ absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
absl::Status OverworldMap::LoadPalette() { absl::Status OverworldMap::LoadPalette() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
int previous_pal_id = 0; int previous_pal_id = 0;
int previous_spr_pal_id = 0; int previous_spr_pal_id = 0;
if (index_ > 0) { if (index_ > 0) {
// Load previous palette ID based on ASM version // Load previous palette ID based on ASM version
if (asm_version < 3 || asm_version == 0xFF) { if (asm_version < 3 || asm_version == 0xFF) {
@@ -725,7 +744,7 @@ absl::Status OverworldMap::LoadPalette() {
// v3 uses expanded palette table // v3 uses expanded palette table
previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1]; previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
} }
previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1]; previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
} }
@@ -751,12 +770,12 @@ absl::Status OverworldMap::LoadPalette() {
pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4)]; (previous_pal_id * 4)];
} }
if (pal2 == 0xFF) { if (pal2 == 0xFF) {
pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4) + 1]; (previous_pal_id * 4) + 1];
} }
if (pal3 == 0xFF) { if (pal3 == 0xFF) {
pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup + pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
(previous_pal_id * 4) + 2]; (previous_pal_id * 4) + 2];
@@ -769,11 +788,14 @@ absl::Status OverworldMap::LoadPalette() {
GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20)); GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
// Set background color based on world type and area-specific settings // Set background color based on world type and area-specific settings
bool use_area_specific_bg = (*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00; bool use_area_specific_bg =
(*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00;
if (use_area_specific_bg) { if (use_area_specific_bg) {
// Use area-specific background color from custom array // Use area-specific background color from custom array
area_specific_bg_color_ = (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] | area_specific_bg_color_ =
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1] << 8); (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] |
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1]
<< 8);
// Convert 15-bit SNES color to palette color // Convert 15-bit SNES color to palette color
bgr = gfx::SnesColor(area_specific_bg_color_); bgr = gfx::SnesColor(area_specific_bg_color_);
} else { } else {
@@ -806,15 +828,16 @@ absl::Status OverworldMap::LoadPalette() {
if (pal4 == 0xFF) { if (pal4 == 0xFF) {
pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)]; pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
} }
if (pal4 == 0xFF) { if (pal4 == 0xFF) {
pal4 = 0; // Fallback to 0 if still 0xFF pal4 = 0; // Fallback to 0 if still 0xFF
} }
if (pal5 == 0xFF) { if (pal5 == 0xFF) {
pal5 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1]; pal5 =
(*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
} }
if (pal5 == 0xFF) { if (pal5 == 0xFF) {
pal5 = 0; // Fallback to 0 if still 0xFF pal5 = 0; // Fallback to 0 if still 0xFF
} }
@@ -840,7 +863,7 @@ absl::Status OverworldMap::LoadPalette() {
absl::Status OverworldMap::LoadOverlay() { absl::Status OverworldMap::LoadOverlay() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied]; uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// Load overlays based on ROM version // Load overlays based on ROM version
if (asm_version == 0xFF) { if (asm_version == 0xFF) {
// Vanilla ROM - load overlay from overlay pointers // Vanilla ROM - load overlay from overlay pointers
@@ -855,15 +878,15 @@ absl::Status OverworldMap::LoadOverlay() {
} }
absl::Status OverworldMap::LoadVanillaOverlayData() { absl::Status OverworldMap::LoadVanillaOverlayData() {
// Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements) // Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements)
int address = (kOverlayPointersBank << 16) + int address = (kOverlayPointersBank << 16) +
((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) + ((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
(*rom_)[kOverlayPointers + (index_ * 2)]; (*rom_)[kOverlayPointers + (index_ * 2)];
// Convert SNES address to PC address // Convert SNES address to PC address
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF); address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
// Check if custom overlay code is present // Check if custom overlay code is present
if ((*rom_)[kOverlayData1] == 0x6B) { if ((*rom_)[kOverlayData1] == 0x6B) {
// Use custom overlay data pointer // Use custom overlay data pointer
@@ -872,7 +895,7 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
(*rom_)[kOverlayData2 + (index_ * 3)]; (*rom_)[kOverlayData2 + (index_ * 3)];
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF); address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
} }
// Validate address // Validate address
if (address >= rom_->size()) { if (address >= rom_->size()) {
has_overlay_ = false; has_overlay_ = false;
@@ -880,15 +903,15 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
overlay_data_.clear(); overlay_data_.clear();
return absl::OkStatus(); return absl::OkStatus();
} }
// Parse overlay data (interactive overlays) // Parse overlay data (interactive overlays)
overlay_data_.clear(); overlay_data_.clear();
uint8_t b = (*rom_)[address]; uint8_t b = (*rom_)[address];
// Parse overlay commands until we hit END (0x60) // Parse overlay commands until we hit END (0x60)
while (b != 0x60 && address < rom_->size()) { while (b != 0x60 && address < rom_->size()) {
overlay_data_.push_back(b); overlay_data_.push_back(b);
// Handle different overlay commands // Handle different overlay commands
switch (b) { switch (b) {
case 0xA9: // LDA #$ case 0xA9: // LDA #$
@@ -957,28 +980,28 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
address++; address++;
break; break;
} }
if (address < rom_->size()) { if (address < rom_->size()) {
b = (*rom_)[address]; b = (*rom_)[address];
} else { } else {
break; break;
} }
} }
// Add the END command if we found it // Add the END command if we found it
if (b == 0x60) { if (b == 0x60) {
overlay_data_.push_back(0x60); overlay_data_.push_back(0x60);
} }
// Set overlay ID based on map index (simplified) // Set overlay ID based on map index (simplified)
overlay_id_ = index_; overlay_id_ = index_;
has_overlay_ = !overlay_data_.empty(); has_overlay_ = !overlay_data_.empty();
return absl::OkStatus(); return absl::OkStatus();
} }
void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset, void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
int size, uint8_t *all_gfx) { int size, uint8_t* all_gfx) {
// Ensure we don't go out of bounds // Ensure we don't go out of bounds
int max_offset = static_graphics_offset * size + size; int max_offset = static_graphics_offset * size + size;
if (max_offset > rom_->graphics_buffer().size()) { if (max_offset > rom_->graphics_buffer().size()) {
@@ -988,7 +1011,7 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
} }
return; return;
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
auto byte = all_gfx[i + (static_graphics_offset * size)]; auto byte = all_gfx[i + (static_graphics_offset * size)];
switch (index) { switch (index) {
@@ -1004,8 +1027,9 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
} }
absl::Status OverworldMap::BuildTileset() { absl::Status OverworldMap::BuildTileset() {
if (current_gfx_.size() == 0) current_gfx_.resize(0x10000, 0x00); if (current_gfx_.size() == 0)
current_gfx_.resize(0x10000, 0x00);
// Process the 8 main graphics sheets (slots 0-7) // Process the 8 main graphics sheets (slots 0-7)
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if (static_graphics_[i] != 0) { if (static_graphics_[i] != 0) {
@@ -1013,7 +1037,7 @@ absl::Status OverworldMap::BuildTileset() {
rom_->graphics_buffer().data()); rom_->graphics_buffer().data());
} }
} }
// Process sprite graphics (slots 8-15) // Process sprite graphics (slots 8-15)
for (int i = 8; i < 16; i++) { for (int i = 8; i < 16; i++) {
if (static_graphics_[i] != 0) { if (static_graphics_[i] != 0) {
@@ -1021,19 +1045,20 @@ absl::Status OverworldMap::BuildTileset() {
rom_->graphics_buffer().data()); rom_->graphics_buffer().data());
} }
} }
// Process animated graphics if available (slot 16) // Process animated graphics if available (slot 16)
if (static_graphics_[16] != 0) { if (static_graphics_[16] != 0) {
ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000, ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000,
rom_->graphics_buffer().data()); rom_->graphics_buffer().data());
} }
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16, absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16,
int count) { int count) {
if (current_blockset_.size() == 0) current_blockset_.resize(0x100000, 0x00); if (current_blockset_.size() == 0)
current_blockset_.resize(0x100000, 0x00);
const int offsets[] = {0x00, 0x08, 0x400, 0x408}; const int offsets[] = {0x00, 0x08, 0x400, 0x408};
auto yy = 0; auto yy = 0;
@@ -1077,7 +1102,7 @@ absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16,
return absl::OkStatus(); return absl::OkStatus();
} }
absl::Status OverworldMap::BuildBitmap(OverworldBlockset &world_blockset) { absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
if (bitmap_data_.size() != 0) { if (bitmap_data_.size() != 0) {
bitmap_data_.clear(); bitmap_data_.clear();
} }