yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_entrance.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstddef>
5#include <vector>
6
7#include "absl/status/status.h"
8#include "absl/status/statusor.h"
9#include "rom/rom.h"
10#include "util/macro.h"
12
13namespace yaze::zelda3 {
14namespace {
15
17 const core::RomAddressOverrides& addresses) {
18 const auto version = OverworldVersionHelper::GetVersion(rom);
19 if (version == OverworldVersion::kVanilla) {
20 return false;
21 }
22 const auto flag_addr =
25 if (flag_addr >= rom.size()) {
26 return false;
27 }
28 return rom.data()[flag_addr] != 0xB8;
29}
30
31} // namespace
32
33absl::StatusOr<std::vector<OverworldEntrance>> LoadEntrances(Rom* rom) {
34 return LoadEntrances(rom, core::RomSettings::Get().address_overrides());
35}
36
37absl::StatusOr<std::vector<OverworldEntrance>> LoadEntrances(
38 Rom* rom, const core::RomAddressOverrides& addresses) {
39 std::vector<OverworldEntrance> entrances;
40 int ow_entrance_map_ptr = kOverworldEntranceMap;
41 int ow_entrance_pos_ptr = kOverworldEntrancePos;
42 int ow_entrance_id_ptr = kOverworldEntranceEntranceId;
43 int num_entrances = 129;
44
45 // Check if expanded entrance data is actually present in ROM
46 // The flag position should contain 0xB8 for vanilla, something else for
47 // expanded
48 if (HasExpandedEntranceTables(*rom, addresses)) {
49 // ROM has expanded entrance data - use expanded addresses
50 ow_entrance_map_ptr =
53 ow_entrance_pos_ptr =
56 ow_entrance_id_ptr =
59 num_entrances = 256; // Expanded entrance count
60 }
61 // Otherwise use vanilla addresses (already set above)
62
63 for (int i = 0; i < num_entrances; i++) {
64 ASSIGN_OR_RETURN(auto map_id, rom->ReadWord(ow_entrance_map_ptr + (i * 2)));
65 ASSIGN_OR_RETURN(auto map_pos,
66 rom->ReadWord(ow_entrance_pos_ptr + (i * 2)));
67 ASSIGN_OR_RETURN(auto entrance_id, rom->ReadByte(ow_entrance_id_ptr + i));
68 int p = map_pos >> 1;
69 int x = (p % 64);
70 int y = (p >> 6);
71 bool deleted = false;
72 if (map_pos == 0xFFFF) {
73 deleted = true;
74 }
75 entrances.emplace_back(
76 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
77 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
78 deleted);
79 }
80
81 return entrances;
82}
83
84absl::StatusOr<std::vector<OverworldEntrance>> LoadHoles(Rom* rom) {
85 constexpr int kNumHoles = 0x13;
86 std::vector<OverworldEntrance> holes;
87 for (int i = 0; i < kNumHoles; i++) {
88 ASSIGN_OR_RETURN(auto map_id, rom->ReadWord(kOverworldHoleArea + (i * 2)));
89 ASSIGN_OR_RETURN(auto map_pos, rom->ReadWord(kOverworldHolePos + (i * 2)));
90 ASSIGN_OR_RETURN(auto entrance_id,
92 int p = (map_pos + 0x400) >> 1;
93 int x = (p % 64);
94 int y = (p >> 6);
95 holes.emplace_back(
96 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
97 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
98 (uint16_t)(map_pos + 0x400), true);
99 }
100 return holes;
101}
102
103absl::Status SaveEntrances(Rom* rom,
104 const std::vector<OverworldEntrance>& entrances,
105 bool expanded_entrances) {
106 auto write_entrance = [&](int index, uint32_t map_addr, uint32_t pos_addr,
107 uint32_t id_addr) -> absl::Status {
108 // Mirrors ZeldaFullEditor/Save.cs::SaveOWEntrances (see lines ~1081-1085)
109 // where MapID and MapPos are written as 16-bit words and EntranceID as a
110 // byte.
111 RETURN_IF_ERROR(rom->WriteShort(map_addr, entrances[index].map_id_));
112 RETURN_IF_ERROR(rom->WriteShort(pos_addr, entrances[index].map_pos_));
113 RETURN_IF_ERROR(rom->WriteByte(id_addr, entrances[index].entrance_id_));
114 return absl::OkStatus();
115 };
116
117 // Always keep the legacy tables in sync for pure vanilla ROMs so e.g. Hyrule
118 // Magic expects them. ZScream does the same in SaveOWEntrances.
119 const int legacy_count =
120 std::min<int>(kNumOverworldEntrances, entrances.size());
121 for (int i = 0; i < legacy_count; ++i) {
122 RETURN_IF_ERROR(write_entrance(i, kOverworldEntranceMap + (i * 2),
123 kOverworldEntrancePos + (i * 2),
125 }
126
127 if (expanded_entrances) {
128 // For ZS v3+ ROMs, mirror writes into the expanded tables the way
129 // ZeldaFullEditor does when the ASM patch is active.
130 constexpr int kExpandedOverworldEntrances = 256;
131 const int expanded_count =
132 std::min<int>(kExpandedOverworldEntrances, entrances.size());
133 for (int i = 0; i < expanded_count; ++i) {
135 write_entrance(i, GetOverworldEntranceMapExpanded() + (i * 2),
138 }
139 }
140
141 return absl::OkStatus();
142}
143
144absl::Status SaveHoles(Rom* rom, const std::vector<OverworldEntrance>& holes) {
145 for (int i = 0; i < kNumOverworldHoles; ++i) {
147 rom->WriteShort(kOverworldHoleArea + (i * 2), holes[i].map_id_));
148
149 // ZeldaFullEditor/Data/Overworld.cs::LoadHoles() adds 0x400 when loading
150 // (see lines ~1006-1014). SaveOWEntrances subtracts it before writing
151 // (Save.cs lines ~1088-1092). We replicate that here so vanilla ROMs
152 // receive the expected values.
153 uint16_t rom_map_pos = static_cast<uint16_t>(holes[i].map_pos_ >= 0x400
154 ? holes[i].map_pos_ - 0x400
155 : holes[i].map_pos_);
156 RETURN_IF_ERROR(rom->WriteShort(kOverworldHolePos + (i * 2), rom_map_pos));
158 rom->WriteByte(kOverworldHoleEntrance + i, holes[i].entrance_id_));
159 }
160
161 return absl::OkStatus();
162}
163
164} // namespace yaze::zelda3
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:49
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:853
absl::StatusOr< uint8_t > ReadByte(int offset) const
Definition rom.cc:785
absl::StatusOr< uint16_t > ReadWord(int offset) const
Definition rom.cc:793
auto data() const
Definition rom.h:201
auto size() const
Definition rom.h:200
absl::Status WriteShort(int addr, uint16_t value)
Definition rom.cc:897
static RomSettings & Get()
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
constexpr char kOverworldEntrancePosExpanded[]
constexpr char kOverworldEntranceMapExpanded[]
constexpr char kOverworldEntranceFlagExpanded[]
constexpr char kOverworldEntranceIdExpanded[]
bool HasExpandedEntranceTables(const Rom &rom, const core::RomAddressOverrides &addresses)
Zelda 3 specific classes and functions.
absl::Status SaveEntrances(Rom *rom, const std::vector< OverworldEntrance > &entrances, bool expanded_entrances)
constexpr int kOverworldHoleArea
constexpr int kOverworldEntrancePos
constexpr int kOverworldHoleEntrance
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
constexpr int kOverworldEntranceEntranceIdExpanded
constexpr int kNumOverworldHoles
constexpr int kOverworldEntranceEntranceId
constexpr int kOverworldHolePos
constexpr int kOverworldEntranceMap
@ kVanilla
0xFF in ROM, no ZScream ASM applied
constexpr int kOverworldEntrancePosExpanded
int GetOverworldEntrancePosExpanded()
int GetOverworldEntranceIdExpanded()
constexpr int kOverworldEntranceMapExpanded
absl::Status SaveHoles(Rom *rom, const std::vector< OverworldEntrance > &holes)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
constexpr int kOverworldEntranceExpandedFlagPos
int GetOverworldEntranceMapExpanded()
constexpr int kNumOverworldEntrances
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::optional< uint32_t > GetAddress(const std::string &key) const