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:
@@ -1,36 +1,41 @@
|
|||||||
#include "overworld.h"
|
#include "overworld.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <mutex>
|
#include <iostream>
|
||||||
|
#include <ostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <thread>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#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/debug/performance/performance_profiler.h"
|
||||||
#include "app/gfx/util/compression.h"
|
|
||||||
#include "app/gfx/types/snes_tile.h"
|
#include "app/gfx/types/snes_tile.h"
|
||||||
|
#include "app/gfx/util/compression.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/snes.h"
|
#include "app/snes.h"
|
||||||
#include "zelda3/common.h"
|
#include "core/features.h"
|
||||||
#include "zelda3/overworld/overworld_entrance.h"
|
|
||||||
#include "zelda3/overworld/overworld_exit.h"
|
|
||||||
#include "util/hex.h"
|
#include "util/hex.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/macro.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_item.h"
|
||||||
#include "zelda3/overworld/overworld_map.h"
|
#include "zelda3/overworld/overworld_map.h"
|
||||||
|
#include "zelda3/overworld/overworld_version_helper.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze::zelda3 {
|
||||||
namespace zelda3 {
|
|
||||||
|
|
||||||
absl::Status Overworld::Load(Rom* rom) {
|
absl::Status Overworld::Load(Rom* rom) {
|
||||||
gfx::ScopedTimer timer("Overworld::Load");
|
gfx::ScopedTimer timer("Overworld::Load");
|
||||||
|
|
||||||
if (rom->size() == 0) {
|
if (rom->size() == 0) {
|
||||||
return absl::InvalidArgumentError("ROM file not loaded");
|
return absl::InvalidArgumentError("ROM file not loaded");
|
||||||
}
|
}
|
||||||
@@ -72,37 +77,37 @@ absl::Status Overworld::Load(Rom* rom) {
|
|||||||
// Phase 5: Data Loading (with individual timing)
|
// Phase 5: Data Loading (with individual timing)
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer data_loading_timer("LoadOverworldData");
|
gfx::ScopedTimer data_loading_timer("LoadOverworldData");
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer tile_types_timer("LoadTileTypes");
|
gfx::ScopedTimer tile_types_timer("LoadTileTypes");
|
||||||
LoadTileTypes();
|
LoadTileTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer entrances_timer("LoadEntrances");
|
gfx::ScopedTimer entrances_timer("LoadEntrances");
|
||||||
ASSIGN_OR_RETURN(all_entrances_, LoadEntrances(rom_));
|
ASSIGN_OR_RETURN(all_entrances_, LoadEntrances(rom_));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer holes_timer("LoadHoles");
|
gfx::ScopedTimer holes_timer("LoadHoles");
|
||||||
ASSIGN_OR_RETURN(all_holes_, LoadHoles(rom_));
|
ASSIGN_OR_RETURN(all_holes_, LoadHoles(rom_));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer exits_timer("LoadExits");
|
gfx::ScopedTimer exits_timer("LoadExits");
|
||||||
ASSIGN_OR_RETURN(all_exits_, LoadExits(rom_));
|
ASSIGN_OR_RETURN(all_exits_, LoadExits(rom_));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer items_timer("LoadItems");
|
gfx::ScopedTimer items_timer("LoadItems");
|
||||||
ASSIGN_OR_RETURN(all_items_, LoadItems(rom_, overworld_maps_));
|
ASSIGN_OR_RETURN(all_items_, LoadItems(rom_, overworld_maps_));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer overworld_maps_timer("LoadOverworldMaps");
|
gfx::ScopedTimer overworld_maps_timer("LoadOverworldMaps");
|
||||||
RETURN_IF_ERROR(LoadOverworldMaps());
|
RETURN_IF_ERROR(LoadOverworldMaps());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
gfx::ScopedTimer sprites_timer("LoadSprites");
|
gfx::ScopedTimer sprites_timer("LoadSprites");
|
||||||
RETURN_IF_ERROR(LoadSprites());
|
RETURN_IF_ERROR(LoadSprites());
|
||||||
@@ -258,15 +263,16 @@ 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) {
|
if (parent_index < 0 || parent_index >= kNumOverworldMaps) {
|
||||||
return absl::InvalidArgumentError(
|
return absl::InvalidArgumentError(
|
||||||
absl::StrFormat("Invalid parent index: %d", parent_index));
|
absl::StrFormat("Invalid parent index: %d", parent_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ROM version
|
// Check ROM version
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
|
||||||
// Version requirements:
|
// Version requirements:
|
||||||
// - Vanilla (0xFF): Supports Small and Large only
|
// - Vanilla (0xFF): Supports Small and Large only
|
||||||
// - v1-v2: Supports Small and Large only
|
// - v1-v2: Supports Small and Large only
|
||||||
@@ -276,19 +282,23 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
|
|||||||
return absl::FailedPreconditionError(
|
return absl::FailedPreconditionError(
|
||||||
"Wide and Tall areas require ZSCustomOverworld v3+");
|
"Wide and Tall areas require ZSCustomOverworld v3+");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("Overworld", "ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, version=%d",
|
LOG_DEBUG("Overworld",
|
||||||
parent_index, static_cast<int>(overworld_maps_[parent_index].area_size()),
|
"ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, "
|
||||||
static_cast<int>(size), asm_version);
|
"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
|
// CRITICAL: First, get OLD siblings (before changing) so we can reset them
|
||||||
std::vector<int> old_siblings;
|
std::vector<int> old_siblings;
|
||||||
auto old_size = overworld_maps_[parent_index].area_size();
|
auto old_size = overworld_maps_[parent_index].area_size();
|
||||||
int old_parent = overworld_maps_[parent_index].parent();
|
int old_parent = overworld_maps_[parent_index].parent();
|
||||||
|
|
||||||
switch (old_size) {
|
switch (old_size) {
|
||||||
case AreaSizeEnum::LargeArea:
|
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;
|
break;
|
||||||
case AreaSizeEnum::WideArea:
|
case AreaSizeEnum::WideArea:
|
||||||
old_siblings = {old_parent, old_parent + 1};
|
old_siblings = {old_parent, old_parent + 1};
|
||||||
@@ -300,17 +310,17 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
|
|||||||
old_siblings = {parent_index}; // Was small, just this map
|
old_siblings = {parent_index}; // Was small, just this map
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all old siblings to SmallArea first (clean slate)
|
// Reset all old siblings to SmallArea first (clean slate)
|
||||||
for (int old_sibling : old_siblings) {
|
for (int old_sibling : old_siblings) {
|
||||||
if (old_sibling >= 0 && old_sibling < kNumOverworldMaps) {
|
if (old_sibling >= 0 && old_sibling < kNumOverworldMaps) {
|
||||||
overworld_maps_[old_sibling].SetAsSmallMap(old_sibling);
|
overworld_maps_[old_sibling].SetAsSmallMap(old_sibling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now configure NEW siblings based on requested size
|
// Now configure NEW siblings based on requested size
|
||||||
std::vector<int> new_siblings;
|
std::vector<int> new_siblings;
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case AreaSizeEnum::SmallArea:
|
case AreaSizeEnum::SmallArea:
|
||||||
// Just configure this single map as small
|
// Just configure this single map as small
|
||||||
@@ -318,45 +328,54 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
|
|||||||
overworld_maps_[parent_index].SetAreaSize(AreaSizeEnum::SmallArea);
|
overworld_maps_[parent_index].SetAreaSize(AreaSizeEnum::SmallArea);
|
||||||
new_siblings = {parent_index};
|
new_siblings = {parent_index};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AreaSizeEnum::LargeArea:
|
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) {
|
for (size_t i = 0; i < new_siblings.size(); ++i) {
|
||||||
int sibling = new_siblings[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);
|
overworld_maps_[sibling].SetAsLargeMap(parent_index, i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AreaSizeEnum::WideArea:
|
case AreaSizeEnum::WideArea:
|
||||||
new_siblings = {parent_index, parent_index + 1};
|
new_siblings = {parent_index, parent_index + 1};
|
||||||
for (int sibling : new_siblings) {
|
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].SetParent(parent_index);
|
||||||
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::WideArea);
|
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::WideArea);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AreaSizeEnum::TallArea:
|
case AreaSizeEnum::TallArea:
|
||||||
new_siblings = {parent_index, parent_index + 8};
|
new_siblings = {parent_index, parent_index + 8};
|
||||||
for (int sibling : new_siblings) {
|
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].SetParent(parent_index);
|
||||||
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::TallArea);
|
overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::TallArea);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ROM data for ALL affected siblings (old + new)
|
// Update ROM data for ALL affected siblings (old + new)
|
||||||
std::set<int> all_affected;
|
std::set<int> all_affected;
|
||||||
for (int s : old_siblings) all_affected.insert(s);
|
for (int sibling : old_siblings) {
|
||||||
for (int s : new_siblings) all_affected.insert(s);
|
all_affected.insert(sibling);
|
||||||
|
}
|
||||||
|
for (int sibling : new_siblings) {
|
||||||
|
all_affected.insert(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
// v3+: Update expanded tables
|
// v3+: Update expanded tables
|
||||||
for (int sibling : all_affected) {
|
for (int sibling : all_affected) {
|
||||||
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
|
if (sibling < 0 || sibling >= kNumOverworldMaps)
|
||||||
|
continue;
|
||||||
|
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentIdExpanded + sibling,
|
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentIdExpanded + sibling,
|
||||||
overworld_maps_[sibling].parent()));
|
overworld_maps_[sibling].parent()));
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(
|
RETURN_IF_ERROR(rom()->WriteByte(
|
||||||
@@ -366,8 +385,9 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
|
|||||||
} else if (asm_version < 3 && asm_version != 0xFF) {
|
} else if (asm_version < 3 && asm_version != 0xFF) {
|
||||||
// v1/v2: Update basic parent table
|
// v1/v2: Update basic parent table
|
||||||
for (int sibling : all_affected) {
|
for (int sibling : all_affected) {
|
||||||
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
|
if (sibling < 0 || sibling >= kNumOverworldMaps)
|
||||||
|
continue;
|
||||||
|
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
|
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
|
||||||
overworld_maps_[sibling].parent()));
|
overworld_maps_[sibling].parent()));
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(
|
RETURN_IF_ERROR(rom()->WriteByte(
|
||||||
@@ -377,22 +397,27 @@ absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum siz
|
|||||||
} else {
|
} else {
|
||||||
// Vanilla: Update parent and screen size tables
|
// Vanilla: Update parent and screen size tables
|
||||||
for (int sibling : all_affected) {
|
for (int sibling : all_affected) {
|
||||||
if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
|
if (sibling < 0 || sibling >= kNumOverworldMaps)
|
||||||
|
continue;
|
||||||
|
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
|
RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
|
||||||
overworld_maps_[sibling].parent()));
|
overworld_maps_[sibling].parent()));
|
||||||
RETURN_IF_ERROR(rom()->WriteByte(
|
RETURN_IF_ERROR(rom()->WriteByte(
|
||||||
kOverworldScreenSize + (sibling & 0x3F),
|
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",
|
LOG_DEBUG("Overworld",
|
||||||
(size == AreaSizeEnum::LargeArea) ? "Large" :
|
"Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
|
||||||
(size == AreaSizeEnum::WideArea) ? "Wide" :
|
(size == AreaSizeEnum::LargeArea) ? "Large"
|
||||||
(size == AreaSizeEnum::TallArea) ? "Tall" : "Small",
|
: (size == AreaSizeEnum::WideArea) ? "Wide"
|
||||||
parent_index, old_siblings.size(), new_siblings.size());
|
: (size == AreaSizeEnum::TallArea) ? "Tall"
|
||||||
|
: "Small",
|
||||||
|
parent_index, old_siblings.size(), new_siblings.size());
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,17 +559,8 @@ 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() {
|
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) {
|
const auto get_ow_map_gfx_ptr = [this](int index, uint32_t map_ptr) {
|
||||||
int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
|
int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
|
||||||
(rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
|
(rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
|
||||||
@@ -560,7 +576,7 @@ absl::Status Overworld::DecompressAllMapTilesParallel() {
|
|||||||
int sx = 0;
|
int sx = 0;
|
||||||
int sy = 0;
|
int sy = 0;
|
||||||
int c = 0;
|
int c = 0;
|
||||||
|
|
||||||
for (int i = 0; i < kNumOverworldMaps; i++) {
|
for (int i = 0; i < kNumOverworldMaps; i++) {
|
||||||
auto p1 = get_ow_map_gfx_ptr(
|
auto p1 = get_ow_map_gfx_ptr(
|
||||||
i, rom()->version_constants().kCompressedAllMap32PointersHigh);
|
i, rom()->version_constants().kCompressedAllMap32PointersHigh);
|
||||||
@@ -603,22 +619,27 @@ absl::Status Overworld::DecompressAllMapTilesParallel() {
|
|||||||
|
|
||||||
absl::Status Overworld::LoadOverworldMaps() {
|
absl::Status Overworld::LoadOverworldMaps() {
|
||||||
auto size = tiles16_.size();
|
auto size = tiles16_.size();
|
||||||
|
|
||||||
// Performance optimization: Only build essential maps initially
|
// Performance optimization: Only build essential maps initially
|
||||||
// Essential maps are the first few maps of each world that are commonly accessed
|
// 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 kLightWorldEssential = kEssentialMapsPerWorld;
|
||||||
constexpr int kDarkWorldEssential = kDarkWorldMapIdStart + kEssentialMapsPerWorld;
|
constexpr int kDarkWorldEssential =
|
||||||
constexpr int kSpecialWorldEssential = kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
|
kDarkWorldMapIdStart + kEssentialMapsPerWorld;
|
||||||
|
constexpr int kSpecialWorldEssential =
|
||||||
util::logf("Building essential maps only (first %d maps per world) for faster loading", kEssentialMapsPerWorld);
|
kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
|
||||||
|
|
||||||
|
util::logf(
|
||||||
|
"Building essential maps only (first %d maps per world) for faster "
|
||||||
|
"loading",
|
||||||
|
kEssentialMapsPerWorld);
|
||||||
|
|
||||||
std::vector<std::future<absl::Status>> futures;
|
std::vector<std::future<absl::Status>> futures;
|
||||||
|
|
||||||
// Build essential maps only
|
// Build essential maps only
|
||||||
for (int i = 0; i < kNumOverworldMaps; ++i) {
|
for (int i = 0; i < kNumOverworldMaps; ++i) {
|
||||||
bool is_essential = false;
|
bool is_essential = false;
|
||||||
|
|
||||||
// Check if this is an essential map
|
// Check if this is an essential map
|
||||||
if (i < kLightWorldEssential) {
|
if (i < kLightWorldEssential) {
|
||||||
is_essential = true;
|
is_essential = true;
|
||||||
@@ -627,7 +648,7 @@ absl::Status Overworld::LoadOverworldMaps() {
|
|||||||
} else if (i >= kSpecialWorldMapIdStart && i < kSpecialWorldEssential) {
|
} else if (i >= kSpecialWorldMapIdStart && i < kSpecialWorldEssential) {
|
||||||
is_essential = true;
|
is_essential = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_essential) {
|
if (is_essential) {
|
||||||
int world_type = 0;
|
int world_type = 0;
|
||||||
if (i >= kDarkWorldMapIdStart && i < kSpecialWorldMapIdStart) {
|
if (i >= kDarkWorldMapIdStart && i < kSpecialWorldMapIdStart) {
|
||||||
@@ -635,7 +656,7 @@ absl::Status Overworld::LoadOverworldMaps() {
|
|||||||
} else if (i >= kSpecialWorldMapIdStart) {
|
} else if (i >= kSpecialWorldMapIdStart) {
|
||||||
world_type = 2;
|
world_type = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto task_function = [this, i, size, world_type]() {
|
auto task_function = [this, i, size, world_type]() {
|
||||||
return overworld_maps_[i].BuildMap(size, game_state_, world_type,
|
return overworld_maps_[i].BuildMap(size, game_state_, world_type,
|
||||||
tiles16_, GetMapTiles(world_type));
|
tiles16_, GetMapTiles(world_type));
|
||||||
@@ -652,7 +673,7 @@ absl::Status Overworld::LoadOverworldMaps() {
|
|||||||
future.wait();
|
future.wait();
|
||||||
RETURN_IF_ERROR(future.get());
|
RETURN_IF_ERROR(future.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
util::logf("Essential maps built. Remaining maps will be built on-demand.");
|
util::logf("Essential maps built. Remaining maps will be built on-demand.");
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
@@ -661,21 +682,22 @@ absl::Status Overworld::EnsureMapBuilt(int map_index) {
|
|||||||
if (map_index < 0 || map_index >= kNumOverworldMaps) {
|
if (map_index < 0 || map_index >= kNumOverworldMaps) {
|
||||||
return absl::InvalidArgumentError("Invalid map index");
|
return absl::InvalidArgumentError("Invalid map index");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if map is already built
|
// Check if map is already built
|
||||||
if (overworld_maps_[map_index].is_built()) {
|
if (overworld_maps_[map_index].is_built()) {
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the map on-demand
|
// Build the map on-demand
|
||||||
auto size = tiles16_.size();
|
auto size = tiles16_.size();
|
||||||
int world_type = 0;
|
int world_type = 0;
|
||||||
if (map_index >= kDarkWorldMapIdStart && map_index < kSpecialWorldMapIdStart) {
|
if (map_index >= kDarkWorldMapIdStart &&
|
||||||
|
map_index < kSpecialWorldMapIdStart) {
|
||||||
world_type = 1;
|
world_type = 1;
|
||||||
} else if (map_index >= kSpecialWorldMapIdStart) {
|
} else if (map_index >= kSpecialWorldMapIdStart) {
|
||||||
world_type = 2;
|
world_type = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return overworld_maps_[map_index].BuildMap(size, game_state_, world_type,
|
return overworld_maps_[map_index].BuildMap(size, game_state_, world_type,
|
||||||
tiles16_, GetMapTiles(world_type));
|
tiles16_, GetMapTiles(world_type));
|
||||||
}
|
}
|
||||||
@@ -2402,7 +2424,8 @@ absl::Status Overworld::SaveMap16Tiles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Overworld::SaveEntrances() {
|
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_IF_ERROR(SaveHoles(rom_, all_holes_));
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
@@ -2687,5 +2710,4 @@ absl::Status Overworld::SaveAreaSizes() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zelda3
|
} // namespace yaze::zelda3
|
||||||
} // namespace yaze
|
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ class Overworld {
|
|||||||
void OrganizeMapTiles(std::vector<uint8_t> &bytes,
|
void OrganizeMapTiles(std::vector<uint8_t> &bytes,
|
||||||
std::vector<uint8_t> &bytes2, int i, int sx, int sy,
|
std::vector<uint8_t> &bytes2, int i, int sx, int sy,
|
||||||
int &ttpos);
|
int &ttpos);
|
||||||
void DecompressAllMapTiles();
|
|
||||||
absl::Status DecompressAllMapTilesParallel();
|
absl::Status DecompressAllMapTilesParallel();
|
||||||
|
|
||||||
Rom *rom_;
|
Rom *rom_;
|
||||||
|
|||||||
@@ -1,34 +1,41 @@
|
|||||||
#include "overworld_map.h"
|
#include "overworld_map.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "app/gfx/types/snes_palette.h"
|
||||||
#include "core/features.h"
|
#include "core/features.h"
|
||||||
#include "app/gfx/types/snes_color.h"
|
#include "app/gfx/types/snes_color.h"
|
||||||
#include "app/gfx/types/snes_tile.h"
|
#include "app/gfx/types/snes_tile.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
#include "util/macro.h"
|
||||||
|
#include "zelda3/common.h"
|
||||||
#include "zelda3/overworld/overworld.h"
|
#include "zelda3/overworld/overworld.h"
|
||||||
|
#include "zelda3/overworld/overworld_version_helper.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze::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)
|
// Load parent ID from ROM data if available (for custom ASM versions)
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
if (asm_version != 0xFF && asm_version >= 0x03) {
|
if (version != OverworldVersion::kVanilla && version >= OverworldVersion::kZSCustomV3) {
|
||||||
// For v3+, parent ID is stored in expanded table
|
// For v3+, parent ID is stored in expanded table
|
||||||
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
|
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asm_version != 0xFF) {
|
if (version != OverworldVersion::kVanilla) {
|
||||||
if (asm_version == 0x03) {
|
if (version == OverworldVersion::kZSCustomV3) {
|
||||||
LoadCustomOverworldData();
|
LoadCustomOverworldData();
|
||||||
} else {
|
} else {
|
||||||
SetupCustomTileset(asm_version);
|
SetupCustomTileset(OverworldVersionHelper::GetAsmVersion(*rom_));
|
||||||
}
|
}
|
||||||
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
|
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
|
||||||
// Pure vanilla ROM but flag enabled - set up custom data manually
|
// 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) {
|
OverworldBlockset& world_blockset) {
|
||||||
game_state_ = game_state;
|
game_state_ = game_state;
|
||||||
world_ = world;
|
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
|
// 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
|
// 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 (parent_ != index_ && !initialized_) {
|
||||||
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
|
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
|
||||||
index_ != 0x88) {
|
index_ != 0x88) {
|
||||||
@@ -79,7 +86,7 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OverworldMap::LoadAreaInfo() {
|
void OverworldMap::LoadAreaInfo() {
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
|
|
||||||
// ZSCustomOverworld ASM Version System:
|
// ZSCustomOverworld ASM Version System:
|
||||||
// 0x00-0x02: Legacy versions with limited features
|
// 0x00-0x02: Legacy versions with limited features
|
||||||
@@ -87,7 +94,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
// 0xFF: Pure vanilla ROM (no ASM applied)
|
// 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 (version < OverworldVersion::kZSCustomV2 || version == OverworldVersion::kVanilla) {
|
||||||
// v2 and vanilla: use original message table
|
// v2 and vanilla: use original message table
|
||||||
message_id_ = (*rom_)[kOverworldMessageIds + (parent_ * 2)] |
|
message_id_ = (*rom_)[kOverworldMessageIds + (parent_ * 2)] |
|
||||||
((*rom_)[kOverworldMessageIds + (parent_ * 2) + 1] << 8);
|
((*rom_)[kOverworldMessageIds + (parent_ * 2) + 1] << 8);
|
||||||
@@ -157,7 +164,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
area_music_[3] = (*rom_)[kOverworldMusicAgahnim + parent_];
|
area_music_[3] = (*rom_)[kOverworldMusicAgahnim + parent_];
|
||||||
|
|
||||||
// For v2/vanilla, use original palette table
|
// 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_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
}
|
}
|
||||||
} else if (index_ < kSpecialWorldMapIdStart) {
|
} else if (index_ < kSpecialWorldMapIdStart) {
|
||||||
@@ -183,7 +190,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
(*rom_)[kOverworldMusicDarkWorld + (parent_ - kDarkWorldMapIdStart)];
|
(*rom_)[kOverworldMusicDarkWorld + (parent_ - kDarkWorldMapIdStart)];
|
||||||
|
|
||||||
// For v2/vanilla, use original palette table
|
// 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_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -191,7 +198,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
// Message ID already loaded above based on ASM version
|
// Message ID already loaded above based on ASM version
|
||||||
|
|
||||||
// For v3, use expanded sprite tables with full customization support
|
// For v3, use expanded sprite tables with full customization support
|
||||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
if (version == OverworldVersion::kZSCustomV3) {
|
||||||
sprite_graphics_[0] =
|
sprite_graphics_[0] =
|
||||||
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
|
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
|
||||||
kSpecialWorldMapIdStart];
|
kSpecialWorldMapIdStart];
|
||||||
@@ -230,7 +237,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
|
|
||||||
// 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
|
// 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_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
|
|
||||||
// Handle special world area cases based on ZScream documentation
|
// Handle special world area cases based on ZScream documentation
|
||||||
@@ -862,19 +869,17 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::LoadOverlay() {
|
absl::Status OverworldMap::LoadOverlay() {
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
|
||||||
|
|
||||||
// Load overlays based on ROM version
|
// Load overlays based on ROM version
|
||||||
if (asm_version == 0xFF) {
|
if (OverworldVersionHelper::GetVersion(*rom_) == OverworldVersion::kVanilla) {
|
||||||
// Vanilla ROM - load overlay from overlay pointers
|
// Vanilla ROM - load overlay from overlay pointers
|
||||||
return LoadVanillaOverlayData();
|
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() {
|
absl::Status OverworldMap::LoadVanillaOverlayData() {
|
||||||
@@ -1125,5 +1130,4 @@ absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zelda3
|
} // namespace yaze::zelda3
|
||||||
} // namespace yaze
|
|
||||||
|
|||||||
@@ -55,12 +55,13 @@ class OverworldVersionHelper {
|
|||||||
return OverworldVersion::kVanilla;
|
return OverworldVersion::kVanilla;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise return version number directly
|
|
||||||
if (asm_version == 1) {
|
if (asm_version == 1) {
|
||||||
return OverworldVersion::kZSCustomV1;
|
return OverworldVersion::kZSCustomV1;
|
||||||
} else if (asm_version == 2) {
|
}
|
||||||
|
if (asm_version == 2) {
|
||||||
return OverworldVersion::kZSCustomV2;
|
return OverworldVersion::kZSCustomV2;
|
||||||
} else if (asm_version >= 3) {
|
}
|
||||||
|
if (asm_version >= 3) {
|
||||||
return OverworldVersion::kZSCustomV3;
|
return OverworldVersion::kZSCustomV3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,10 @@ class OverworldVersionHelper {
|
|||||||
return OverworldVersion::kVanilla;
|
return OverworldVersion::kVanilla;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t GetAsmVersion(const Rom& rom) {
|
||||||
|
return rom.data()[OverworldCustomASMHasBeenApplied];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if ROM supports area enum system (v3+ only)
|
* @brief Check if ROM supports area enum system (v3+ only)
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user