refactor: improve overworld map version handling and code organization

- Refactored `OverworldMap` to utilize `OverworldVersionHelper` for version checks, enhancing clarity and maintainability.
- Updated logic for loading area information and handling custom overworld data based on versioning.
- Cleaned up includes and namespace declarations for better organization and readability.

Benefits:
- Streamlines version handling, reducing code duplication and potential errors.
- Enhances overall code structure, making it easier to navigate and maintain.
This commit is contained in:
scawful
2025-10-18 01:55:06 -04:00
parent b4d4195e47
commit 5894809aaf
4 changed files with 145 additions and 115 deletions

View File

@@ -1,32 +1,37 @@
#include "overworld.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <future>
#include <mutex>
#include <iostream>
#include <ostream>
#include <set>
#include <thread>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "core/features.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/gfx/debug/performance/performance_profiler.h"
#include "app/gfx/util/compression.h"
#include "app/gfx/types/snes_tile.h"
#include "app/gfx/util/compression.h"
#include "app/rom.h"
#include "app/snes.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld_entrance.h"
#include "zelda3/overworld/overworld_exit.h"
#include "core/features.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/macro.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld_entrance.h"
#include "zelda3/overworld/overworld_exit.h"
#include "zelda3/overworld/overworld_item.h"
#include "zelda3/overworld/overworld_map.h"
#include "zelda3/overworld/overworld_version_helper.h"
namespace yaze {
namespace zelda3 {
namespace yaze::zelda3 {
absl::Status Overworld::Load(Rom* rom) {
gfx::ScopedTimer timer("Overworld::Load");
@@ -258,7 +263,8 @@ void Overworld::AssignMapSizes(std::vector<OverworldMap>& maps) {
}
}
absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size) {
absl::Status Overworld::ConfigureMultiAreaMap(int parent_index,
AreaSizeEnum size) {
if (parent_index < 0 || parent_index >= kNumOverworldMaps) {
return absl::InvalidArgumentError(
absl::StrFormat("Invalid parent index: %d", parent_index));
@@ -277,9 +283,12 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
"Wide and Tall areas require ZSCustomOverworld v3+");
}
LOG_DEBUG("Overworld", "ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, version=%d",
parent_index, static_cast<int>(overworld_maps_[parent_index].area_size()),
static_cast<int>(size), asm_version);
LOG_DEBUG("Overworld",
"ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, "
"version=%d",
parent_index,
static_cast<int>(overworld_maps_[parent_index].area_size()),
static_cast<int>(size), asm_version);
// CRITICAL: First, get OLD siblings (before changing) so we can reset them
std::vector<int> old_siblings;
@@ -288,7 +297,8 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
switch (old_size) {
case AreaSizeEnum::LargeArea:
old_siblings = {old_parent, old_parent + 1, old_parent + 8, old_parent + 9};
old_siblings = {old_parent, old_parent + 1, old_parent + 8,
old_parent + 9};
break;
case AreaSizeEnum::WideArea:
old_siblings = {old_parent, old_parent + 1};
@@ -320,10 +330,12 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
break;
case AreaSizeEnum::LargeArea:
new_siblings = {parent_index, parent_index + 1, parent_index + 8, parent_index + 9};
new_siblings = {parent_index, parent_index + 1, parent_index + 8,
parent_index + 9};
for (size_t i = 0; i < new_siblings.size(); ++i) {
int sibling = new_siblings[i];
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
overworld_maps_[sibling].SetAsLargeMap(parent_index, i);
}
break;
@@ -331,7 +343,8 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
case AreaSizeEnum::WideArea:
new_siblings = {parent_index, parent_index + 1};
for (int sibling : new_siblings) {
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
overworld_maps_[sibling].SetParent(parent_index);
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::WideArea);
}
@@ -340,7 +353,8 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
case AreaSizeEnum::TallArea:
new_siblings = {parent_index, parent_index + 8};
for (int sibling : new_siblings) {
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
overworld_maps_[sibling].SetParent(parent_index);
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::TallArea);
}
@@ -349,13 +363,18 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
// Update ROM data for ALL affected siblings (old + new)
std::set<int> all_affected;
for (int s : old_siblings) all_affected.insert(s);
for (int s : new_siblings) all_affected.insert(s);
for (int sibling : old_siblings) {
all_affected.insert(sibling);
}
for (int sibling : new_siblings) {
all_affected.insert(sibling);
}
if (asm_version >= 3 && asm_version != 0xFF) {
// v3+: Update expanded tables
for (int sibling : all_affected) {
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentIdExpanded + sibling,
overworld_maps_[sibling].parent()));
@@ -366,7 +385,8 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
} else if (asm_version < 3 && asm_version != 0xFF) {
// v1/v2: Update basic parent table
for (int sibling : all_affected) {
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
overworld_maps_[sibling].parent()));
@@ -377,21 +397,26 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
} else {
// Vanilla: Update parent and screen size tables
for (int sibling : all_affected) {
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
if (sibling < 0 || sibling >= kNumOverworldMaps)
continue;
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
overworld_maps_[sibling].parent()));
RETURN_IF_ERROR(rom()->WriteByte(
kOverworldScreenSize + (sibling & 0x3F),
(overworld_maps_[sibling].area_size() == AreaSizeEnum::LargeArea) ? 0x00 : 0x01));
(overworld_maps_[sibling].area_size() == AreaSizeEnum::LargeArea)
? 0x00
: 0x01));
}
}
LOG_DEBUG("Overworld", "Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
(size == AreaSizeEnum::LargeArea) ? "Large" :
(size == AreaSizeEnum::WideArea) ? "Wide" :
(size == AreaSizeEnum::TallArea) ? "Tall" : "Small",
parent_index, old_siblings.size(), new_siblings.size());
LOG_DEBUG("Overworld",
"Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
(size == AreaSizeEnum::LargeArea) ? "Large"
: (size == AreaSizeEnum::WideArea) ? "Wide"
: (size == AreaSizeEnum::TallArea) ? "Tall"
: "Small",
parent_index, old_siblings.size(), new_siblings.size());
return absl::OkStatus();
}
@@ -534,16 +559,7 @@ void Overworld::OrganizeMapTiles(std::vector<uint8_t>& bytes,
}
}
void Overworld::DecompressAllMapTiles() {
// Keep original method for fallback/compatibility
// Note: This method is void, so we can't return status
// The parallel version will be called from Load()
}
absl::Status Overworld::DecompressAllMapTilesParallel() {
// For now, fall back to the original sequential implementation
// The parallel version has synchronization issues that cause data corruption
util::logf("Using sequential decompression (parallel version disabled due to data integrity issues)");
const auto get_ow_map_gfx_ptr = [this](int index, uint32_t map_ptr) {
int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
@@ -606,12 +622,17 @@ absl::Status Overworld::LoadOverworldMaps() {
// Performance optimization: Only build essential maps initially
// Essential maps are the first few maps of each world that are commonly accessed
constexpr int kEssentialMapsPerWorld = 8; // Build first 8 maps of each world
constexpr int kEssentialMapsPerWorld = 16;
constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
constexpr int kDarkWorldEssential = kDarkWorldMapIdStart + kEssentialMapsPerWorld;
constexpr int kSpecialWorldEssential = kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
constexpr int kDarkWorldEssential =
kDarkWorldMapIdStart + kEssentialMapsPerWorld;
constexpr int kSpecialWorldEssential =
kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
util::logf("Building essential maps only (first %d maps per world) for faster loading", kEssentialMapsPerWorld);
util::logf(
"Building essential maps only (first %d maps per world) for faster "
"loading",
kEssentialMapsPerWorld);
std::vector<std::future<absl::Status>> futures;
@@ -670,7 +691,8 @@ absl::Status Overworld::EnsureMapBuilt(int map_index) {
// Build the map on-demand
auto size = tiles16_.size();
int world_type = 0;
if (map_index >= kDarkWorldMapIdStart && map_index < kSpecialWorldMapIdStart) {
if (map_index >= kDarkWorldMapIdStart &&
map_index < kSpecialWorldMapIdStart) {
world_type = 1;
} else if (map_index >= kSpecialWorldMapIdStart) {
world_type = 2;
@@ -2402,7 +2424,8 @@ absl::Status Overworld::SaveMap16Tiles() {
}
absl::Status Overworld::SaveEntrances() {
RETURN_IF_ERROR(::yaze::zelda3::SaveEntrances(rom_, all_entrances_, expanded_entrances_));
RETURN_IF_ERROR(
::yaze::zelda3::SaveEntrances(rom_, all_entrances_, expanded_entrances_));
RETURN_IF_ERROR(SaveHoles(rom_, all_holes_));
return absl::OkStatus();
}
@@ -2687,5 +2710,4 @@ absl::Status Overworld::SaveAreaSizes() {
return absl::OkStatus();
}
} // namespace zelda3
} // namespace yaze
} // namespace yaze::zelda3

View File

@@ -324,7 +324,6 @@ class Overworld {
void OrganizeMapTiles(std::vector<uint8_t> &bytes,
std::vector<uint8_t> &bytes2, int i, int sx, int sy,
int &ttpos);
void DecompressAllMapTiles();
absl::Status DecompressAllMapTilesParallel();
Rom *rom_;

View File

@@ -1,34 +1,41 @@
#include "overworld_map.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/gfx/types/snes_palette.h"
#include "core/features.h"
#include "app/gfx/types/snes_color.h"
#include "app/gfx/types/snes_tile.h"
#include "app/rom.h"
#include "util/macro.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld_version_helper.h"
namespace yaze {
namespace zelda3 {
namespace yaze::zelda3 {
OverworldMap::OverworldMap(int index, Rom* rom)
: index_(index), parent_(index), rom_(rom) {
LoadAreaInfo();
// Load parent ID from ROM data if available (for custom ASM versions)
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
if (asm_version != 0xFF && asm_version >= 0x03) {
auto version = OverworldVersionHelper::GetVersion(*rom_);
if (version != OverworldVersion::kVanilla && version >= OverworldVersion::kZSCustomV3) {
// For v3+, parent ID is stored in expanded table
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
}
if (asm_version != 0xFF) {
if (asm_version == 0x03) {
if (version != OverworldVersion::kVanilla) {
if (version == OverworldVersion::kZSCustomV3) {
LoadCustomOverworldData();
} else {
SetupCustomTileset(asm_version);
SetupCustomTileset(OverworldVersionHelper::GetAsmVersion(*rom_));
}
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
// Pure vanilla ROM but flag enabled - set up custom data manually
@@ -42,11 +49,11 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
OverworldBlockset& world_blockset) {
game_state_ = game_state;
world_ = world;
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
auto version = OverworldVersionHelper::GetVersion(*rom_);
// 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 || asm_version == 0x00)) {
if (large_map_ && (version == OverworldVersion::kVanilla)) {
if (parent_ != index_ && !initialized_) {
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
index_ != 0x88) {
@@ -79,7 +86,7 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
}
void OverworldMap::LoadAreaInfo() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
auto version = OverworldVersionHelper::GetVersion(*rom_);
// ZSCustomOverworld ASM Version System:
// 0x00-0x02: Legacy versions with limited features
@@ -87,7 +94,7 @@ void OverworldMap::LoadAreaInfo() {
// 0xFF: Pure vanilla ROM (no ASM applied)
// Load message ID and area size based on ASM version
if (asm_version < 3 || asm_version == 0xFF) {
if (version < OverworldVersion::kZSCustomV2 || version == OverworldVersion::kVanilla) {
// v2 and vanilla: use original message table
message_id_ = (*rom_)[kOverworldMessageIds + (parent_ * 2)] |
((*rom_)[kOverworldMessageIds + (parent_ * 2) + 1] << 8);
@@ -157,7 +164,7 @@ void OverworldMap::LoadAreaInfo() {
area_music_[3] = (*rom_)[kOverworldMusicAgahnim + parent_];
// For v2/vanilla, use original palette table
if (asm_version < 3 || asm_version == 0xFF) {
if (version < OverworldVersion::kZSCustomV2 || version == OverworldVersion::kVanilla) {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
}
} else if (index_ < kSpecialWorldMapIdStart) {
@@ -183,7 +190,7 @@ void OverworldMap::LoadAreaInfo() {
(*rom_)[kOverworldMusicDarkWorld + (parent_ - kDarkWorldMapIdStart)];
// For v2/vanilla, use original palette table
if (asm_version < 3 || asm_version == 0xFF) {
if (version < OverworldVersion::kZSCustomV2 || version == OverworldVersion::kVanilla) {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
}
} else {
@@ -191,7 +198,7 @@ void OverworldMap::LoadAreaInfo() {
// Message ID already loaded above based on ASM version
// For v3, use expanded sprite tables with full customization support
if (asm_version >= 3 && asm_version != 0xFF) {
if (version == OverworldVersion::kZSCustomV3) {
sprite_graphics_[0] =
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
kSpecialWorldMapIdStart];
@@ -230,7 +237,7 @@ void OverworldMap::LoadAreaInfo() {
// 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 (version < OverworldVersion::kZSCustomV2 || version == OverworldVersion::kVanilla) {
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
// Handle special world area cases based on ZScream documentation
@@ -862,19 +869,17 @@ absl::Status OverworldMap::LoadPalette() {
}
absl::Status OverworldMap::LoadOverlay() {
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
// Load overlays based on ROM version
if (asm_version == 0xFF) {
if (OverworldVersionHelper::GetVersion(*rom_) == OverworldVersion::kVanilla) {
// Vanilla ROM - load overlay from overlay pointers
return LoadVanillaOverlayData();
} else {
// Custom overworld ROM - use overlay from custom data
overlay_id_ = subscreen_overlay_;
has_overlay_ = (overlay_id_ != 0x00FF);
overlay_data_.clear();
return absl::OkStatus();
}
// Custom overworld ROM - use overlay from custom data
overlay_id_ = subscreen_overlay_;
has_overlay_ = (overlay_id_ != 0x00FF);
overlay_data_.clear();
return absl::OkStatus();
}
absl::Status OverworldMap::LoadVanillaOverlayData() {
@@ -1125,5 +1130,4 @@ absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
return absl::OkStatus();
}
} // namespace zelda3
} // namespace yaze
} // namespace yaze::zelda3

View File

@@ -55,12 +55,13 @@ class OverworldVersionHelper {
return OverworldVersion::kVanilla;
}
// Otherwise return version number directly
if (asm_version == 1) {
return OverworldVersion::kZSCustomV1;
} else if (asm_version == 2) {
}
if (asm_version == 2) {
return OverworldVersion::kZSCustomV2;
} else if (asm_version >= 3) {
}
if (asm_version >= 3) {
return OverworldVersion::kZSCustomV3;
}
@@ -68,6 +69,10 @@ class OverworldVersionHelper {
return OverworldVersion::kVanilla;
}
static uint8_t GetAsmVersion(const Rom& rom) {
return rom.data()[OverworldCustomASMHasBeenApplied];
}
/**
* @brief Check if ROM supports area enum system (v3+ only)
*