yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_object.cc
Go to the documentation of this file.
1#include "room_object.h"
2
3#include "absl/status/status.h"
5#include "util/log.h"
6
7namespace yaze {
8namespace zelda3 {
9
10namespace {
12 int base_ptr; // base address of subtype table in ROM (PC)
13 int index_mask; // mask to apply to object id for index
14
15 SubtypeTableInfo(int base, int mask) : base_ptr(base), index_mask(mask) {}
16};
17
19 // Heuristic: 0x00-0xFF => subtype1, 0x100-0x1FF => subtype2, >=0x200 => subtype3
20 if (object_id >= 0x200) {
22 } else if (object_id >= 0x100) {
24 } else {
26 }
27}
28} // namespace
29
31 return static_cast<ObjectOption>(static_cast<int>(lhs) |
32 static_cast<int>(rhs));
33}
34
36 return static_cast<ObjectOption>(static_cast<int>(lhs) &
37 static_cast<int>(rhs));
38}
39
41 return static_cast<ObjectOption>(static_cast<int>(lhs) ^
42 static_cast<int>(rhs));
43}
44
46 return static_cast<ObjectOption>(~static_cast<int>(option));
47}
48
49// NOTE: DrawTile was legacy ZScream code that is no longer used.
50// Modern rendering uses ObjectDrawer which draws directly to BackgroundBuffer bitmaps.
51
53 if (tiles_loaded_) {
54 return;
55 }
56
57 if (rom_ == nullptr) {
58 return;
59 }
60
61 // Try the new parser first - this is more efficient and accurate
62 auto parser_status = LoadTilesWithParser();
63 if (parser_status.ok()) {
64 tiles_loaded_ = true;
65 return;
66 }
67
68 // Fallback to legacy method for compatibility with enhanced validation
69 auto rom_data = rom_->data();
70
71 // Determine which subtype table to use and compute the tile data offset.
72 SubtypeTableInfo sti = GetSubtypeTable(id_);
73 int index = (id_ & sti.index_mask);
74 int tile_ptr = sti.base_ptr + (index * 2);
75
76 // Enhanced bounds checking
77 if (tile_ptr < 0 || tile_ptr + 1 >= (int)rom_->size()) {
78 // Log error but don't crash
79 return;
80 }
81
82 int tile_rel = (int16_t)((rom_data[tile_ptr + 1] << 8) + rom_data[tile_ptr]);
83 int pos = kRoomObjectTileAddress + tile_rel;
84 tile_data_ptr_ = pos;
85
86 // Enhanced bounds checking for tile data
87 if (pos < 0 || pos + 7 >= (int)rom_->size()) {
88 // Log error but don't crash
89 return;
90 }
91
92 // Read tile data with validation
93 uint16_t w0 = (uint16_t)(rom_data[pos] | (rom_data[pos + 1] << 8));
94 uint16_t w1 = (uint16_t)(rom_data[pos + 2] | (rom_data[pos + 3] << 8));
95 uint16_t w2 = (uint16_t)(rom_data[pos + 4] | (rom_data[pos + 5] << 8));
96 uint16_t w3 = (uint16_t)(rom_data[pos + 6] | (rom_data[pos + 7] << 8));
97
98 tiles_.clear();
99 tiles_.push_back(gfx::WordToTileInfo(w0));
100 tiles_.push_back(gfx::WordToTileInfo(w1));
101 tiles_.push_back(gfx::WordToTileInfo(w2));
102 tiles_.push_back(gfx::WordToTileInfo(w3));
103 tile_count_ = 1;
104 tiles_loaded_ = true;
105}
106
108 if (rom_ == nullptr) {
109 return absl::InvalidArgumentError("ROM is null");
110 }
111
112 ObjectParser parser(rom_);
113 auto result = parser.ParseObject(id_);
114 if (!result.ok()) {
115 return result.status();
116 }
117
118 tiles_ = std::move(result.value());
119 tile_count_ = tiles_.size();
120 return absl::OkStatus();
121}
122
123absl::StatusOr<std::span<const gfx::TileInfo>> RoomObject::GetTiles() const {
124 if (!tiles_loaded_) {
125 const_cast<RoomObject*>(this)->EnsureTilesLoaded();
126 }
127
128 if (tiles_.empty()) {
129 return absl::FailedPreconditionError("No tiles loaded for object");
130 }
131
132 return std::span<const gfx::TileInfo>(tiles_.data(), tiles_.size());
133}
134
135absl::StatusOr<const gfx::TileInfo*> RoomObject::GetTile(int index) const {
136 if (!tiles_loaded_) {
137 const_cast<RoomObject*>(this)->EnsureTilesLoaded();
138 }
139
140 if (index < 0 || index >= static_cast<int>(tiles_.size())) {
141 return absl::OutOfRangeError(
142 absl::StrFormat("Tile index %d out of range (0-%d)", index, tiles_.size() - 1));
143 }
144
145 return &tiles_[index];
146}
147
149 if (!tiles_loaded_) {
150 const_cast<RoomObject*>(this)->EnsureTilesLoaded();
151 }
152
153 return tile_count_;
154}
155
156// ============================================================================
157// Object Encoding/Decoding Implementation (Phase 1, Task 1.1)
158// ============================================================================
159
160int RoomObject::DetermineObjectType(uint8_t /* b1 */, uint8_t b3) {
161 // Type 3: Objects with ID >= 0xF00
162 // These have b3 >= 0xF8 (top nibble is 0xF)
163 if (b3 >= 0xF8) {
164 return 3;
165 }
166
167 // Type 1: Standard objects (ID 0x00-0xFF) - check this first
168 // Type 2: Objects with ID >= 0x100 (these have b1 >= 0xFC)
169 // We'll handle Type 2 in the decoding logic after Type 1
170
171 return 1;
172}
173
174RoomObject RoomObject::DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
175 uint8_t layer) {
176 uint8_t x = 0;
177 uint8_t y = 0;
178 uint8_t size = 0;
179 uint16_t id = 0;
180
181 // Follow ZScream's parsing logic exactly
182 if (b3 >= 0xF8) {
183 // Type 3: xxxxxxii yyyyyyii 11111iii
184 // ZScream: oid = (ushort)((b3 << 4) | 0x80 + (((b2 & 0x03) << 2) + ((b1 & 0x03))));
185 id = (static_cast<uint16_t>(b3) << 4) | 0x80 |
186 ((static_cast<uint16_t>(b2 & 0x03) << 2) + (b1 & 0x03));
187 x = (b1 & 0xFC) >> 2;
188 y = (b2 & 0xFC) >> 2;
189 size = ((b1 & 0x03) << 2) | (b2 & 0x03);
190 LOG_DEBUG("ObjectParser", "Type3: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
191 b1, b2, b3, id, x, y, size);
192 } else {
193 // Type 1: xxxxxxss yyyyyyss iiiiiiii
194 id = b3;
195 x = (b1 & 0xFC) >> 2;
196 y = (b2 & 0xFC) >> 2;
197 size = ((b1 & 0x03) << 2) | (b2 & 0x03);
198
199 // Check for Type 2 override: 111111xx xxxxyyyy yyiiiiii
200 if (b1 >= 0xFC) {
201 id = (b3 & 0x3F) | 0x100;
202 x = ((b2 & 0xF0) >> 4) | ((b1 & 0x03) << 4);
203 y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6);
204 size = 0;
205 LOG_DEBUG("ObjectParser", "Type2: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
206 b1, b2, b3, id, x, y, size);
207 } else {
208 LOG_DEBUG("ObjectParser", "Type1: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
209 b1, b2, b3, id, x, y, size);
210 }
211 }
212
213 return RoomObject(static_cast<int16_t>(id), x, y, size, layer);
214}
215
217 ObjectBytes bytes;
218
219 // Determine type based on object ID
220 if (id_ >= 0x100 && id_ < 0x200) {
221 // Type 2: 111111xx xxxxyyyy yyiiiiii
222 bytes.b1 = 0xFC | ((x_ & 0x30) >> 4);
223 bytes.b2 = ((x_ & 0x0F) << 4) | ((y_ & 0x3C) >> 2);
224 bytes.b3 = ((y_ & 0x03) << 6) | (id_ & 0x3F);
225 } else if (id_ >= 0xF00) {
226 // Type 3: xxxxxxii yyyyyyii 11111iii
227 bytes.b1 = (x_ << 2) | (id_ & 0x03);
228 bytes.b2 = (y_ << 2) | ((id_ >> 2) & 0x03);
229 bytes.b3 = (id_ >> 4) & 0xFF;
230 } else {
231 // Type 1: xxxxxxss yyyyyyss iiiiiiii
232 uint8_t clamped_size = size_ > 15 ? 15 : size_;
233 bytes.b1 = (x_ << 2) | ((clamped_size >> 2) & 0x03);
234 bytes.b2 = (y_ << 2) | (clamped_size & 0x03);
235 bytes.b3 = static_cast<uint8_t>(id_);
236 }
237
238 return bytes;
239}
240
241} // namespace zelda3
242} // namespace yaze
auto data() const
Definition rom.h:203
auto size() const
Definition rom.h:202
Direct ROM parser for dungeon objects.
absl::StatusOr< std::vector< gfx::TileInfo > > ParseObject(int16_t object_id)
Parse object data directly from ROM.
absl::StatusOr< const gfx::TileInfo * > GetTile(int index) const
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t layer)
std::vector< gfx::TileInfo > tiles_
ObjectBytes EncodeObjectToBytes() const
static int DetermineObjectType(uint8_t b1, uint8_t b3)
absl::Status LoadTilesWithParser()
uint8_t size() const
Definition room_object.h:77
absl::StatusOr< std::span< const gfx::TileInfo > > GetTiles() const
#define LOG_DEBUG(category, format,...)
Definition log.h:104
TileInfo WordToTileInfo(uint16_t word)
Definition snes_tile.cc:308
SubtypeTableInfo GetSubtypeTable(int object_id)
constexpr int kRoomObjectSubtype3
Definition room_object.h:45
ObjectOption operator|(ObjectOption lhs, ObjectOption rhs)
ObjectOption operator^(ObjectOption lhs, ObjectOption rhs)
constexpr int kRoomObjectSubtype1
Definition room_object.h:43
constexpr int kRoomObjectSubtype2
Definition room_object.h:44
constexpr int kRoomObjectTileAddress
Definition room_object.h:46
ObjectOption operator~(ObjectOption option)
ObjectOption operator&(ObjectOption lhs, ObjectOption rhs)
Main namespace for the application.