yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_parser.cc
Go to the documentation of this file.
1#include "object_parser.h"
2
3#include <algorithm>
4#include <cstring>
5
6#include "absl/strings/str_format.h"
7#include "util/log.h"
10
11// ROM addresses for object data are defined in room_object.h:
12// - kRoomObjectSubtype1 = 0x8000 (SNES $01:8000)
13// - kRoomObjectSubtype2 = 0x83F0 (SNES $01:83F0)
14// - kRoomObjectSubtype3 = 0x84F0 (SNES $01:84F0)
15// - kRoomObjectTileAddress = 0x1B52 (SNES $00:9B52)
16
17// Subtype 1 tile count lookup table (from ZScream's DungeonObjectData.cs)
18// Each entry specifies how many tiles to read for that object ID (0x00-0xF7)
19// Index directly by (object_id & 0xFF) for subtype 1 objects
20// clang-format off
21static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
22 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5, // 0x00-0x0F
23 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0x10-0x1F
24 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, // 0x20-0x2F
25 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 4, 8, 4, 3, // 0x30-0x3F
26 3, 3, 3, 3, 3, 3, 3, 0, 0, 8, 8, 4, 9, 16, 16, 16, // 0x40-0x4F
27 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3, // 0x50-0x5F
28 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1, // 0x60-0x6F
29 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8, // 0x70-0x7F
30 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6, // 0x80-0x8F
31 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90-0x9F
32 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xA0-0xAF
33 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1, // 0xB0-0xBF
34 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 28, 28, 1, // 0xC0-0xCF
35 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8, // 0xD0-0xDF
36 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, // 0xE0-0xEF
37 1, 1, 1, 1, 1, 1, 1, 1 // 0xF0-0xF7
38};
39// clang-format on
40
41// Helper function to get tile count for Subtype 1 objects
42static inline int GetSubtype1TileCount(int object_id) {
43 int index = object_id & 0xFF;
44 if (index < 0xF8) {
45 int count = kSubtype1TileLengths[index];
46 return (count > 0) ? count : 8; // Default to 8 if table has 0
47 }
48 return 8; // Default for IDs >= 0xF8
49}
50
51namespace yaze {
52namespace zelda3 {
53
54absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseObject(
55 int16_t object_id) {
56 if (rom_ == nullptr) {
57 return absl::InvalidArgumentError("ROM is null");
58 }
59
60 // Validate object ID is non-negative
61 if (object_id < 0) {
62 return absl::InvalidArgumentError(
63 absl::StrFormat("Invalid object ID: %d", object_id));
64 }
65
66 int subtype = DetermineSubtype(object_id);
67
68 switch (subtype) {
69 case 1:
70 return ParseSubtype1(object_id);
71 case 2:
72 return ParseSubtype2(object_id);
73 case 3:
74 return ParseSubtype3(object_id);
75 default:
76 return absl::InvalidArgumentError(
77 absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
78 }
79}
80
81absl::StatusOr<ObjectRoutineInfo> ObjectParser::ParseObjectRoutine(
82 int16_t object_id) {
83 if (rom_ == nullptr) {
84 return absl::InvalidArgumentError("ROM is null");
85 }
86
87 auto subtype_info = GetObjectSubtype(object_id);
88 if (!subtype_info.ok()) {
89 return subtype_info.status();
90 }
91
92 ObjectRoutineInfo routine_info;
93 routine_info.routine_ptr = subtype_info->routine_ptr;
94 routine_info.tile_ptr = subtype_info->subtype_ptr;
95 routine_info.tile_count = subtype_info->max_tile_count;
96 routine_info.is_repeatable = true;
97 routine_info.is_orientation_dependent = true;
98
99 return routine_info;
100}
101
102absl::StatusOr<ObjectSubtypeInfo> ObjectParser::GetObjectSubtype(
103 int16_t object_id) {
105 info.subtype = DetermineSubtype(object_id);
106
107 switch (info.subtype) {
108 case 1: {
109 int index = object_id & 0xFF;
110 info.subtype_ptr = kRoomObjectSubtype1 + (index * 2);
111 info.routine_ptr = kRoomObjectSubtype1 + 0x200 + (index * 2);
112 break;
113 }
114 case 2: {
115 // Type 2 objects: 0x100-0x13F (64 objects only)
116 // Index mask 0x3F ensures we stay within 64-entry table bounds
117 int index = (object_id - 0x100) & 0x3F;
118 info.subtype_ptr = kRoomObjectSubtype2 + (index * 2);
119 // Routine table starts 128 bytes (64 entries * 2 bytes) after data table
120 info.routine_ptr = kRoomObjectSubtype2 + 0x80 + (index * 2);
121 break;
122 }
123 case 3: {
124 // Type 3 object IDs are 0xF80-0xFFF (128 objects)
125 // Table index should be 0-127
126 int index = (object_id - 0xF80) & 0x7F;
127 info.subtype_ptr = kRoomObjectSubtype3 + (index * 2);
128 info.routine_ptr = kRoomObjectSubtype3 + 0x100 + (index * 2);
129 break;
130 }
131 default:
132 return absl::InvalidArgumentError(
133 absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
134 }
135
137
138 return info;
139}
140
141absl::StatusOr<ObjectSizeInfo> ObjectParser::ParseObjectSize(
142 int16_t object_id, uint8_t size_byte) {
143 ObjectSizeInfo info;
144
145 // Extract size bits (0-3 for X, 4-7 for Y)
146 int size_x = size_byte & 0x03;
147 int size_y = (size_byte >> 2) & 0x03;
148
149 info.width_tiles = (size_x + 1) * 2; // Convert to tile count
150 info.height_tiles = (size_y + 1) * 2;
151
152 // Determine orientation based on object ID and size
153 // This is a heuristic based on the object naming patterns
154 if (object_id >= 0x80 && object_id <= 0xFF) {
155 // Objects 0x80-0xFF are typically vertical
156 info.is_horizontal = false;
157 } else {
158 // Objects 0x00-0x7F are typically horizontal
159 info.is_horizontal = true;
160 }
161
162 // Determine if object is repeatable
163 info.is_repeatable = (size_byte != 0);
164 info.repeat_count = size_byte == 0 ? 32 : size_byte;
165
166 return info;
167}
168
169absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype1(
170 int16_t object_id) {
171 int index = object_id & 0xFF;
172 int tile_ptr = kRoomObjectSubtype1 + (index * 2);
173
174 if (tile_ptr + 1 >= (int)rom_->size()) {
175 return absl::OutOfRangeError(
176 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
177 }
178
179 // Read tile data pointer (16-bit little-endian offset)
180 uint8_t low = rom_->data()[tile_ptr];
181 uint8_t high = rom_->data()[tile_ptr + 1];
182 int16_t offset = (int16_t)((high << 8) | low); // Signed offset!
183 int tile_data_ptr = kRoomObjectTileAddress + offset;
184
185 // DEBUG: Log wall objects 0x61/0x62 and ceiling 0xC0 to verify tile data
186 bool is_debug_object = (object_id == 0x61 || object_id == 0x62 ||
187 object_id == 0xC0 || object_id == 0xC2);
188 static int debug_count = 0;
189 if (debug_count < 10 || is_debug_object) {
190 LOG_DEBUG("ObjectParser",
191 "ParseSubtype1: obj=0x%02X%s tile_ptr=0x%04X (SNES $01:%04X)",
192 object_id, is_debug_object ? " (DEBUG)" : "", tile_ptr, tile_ptr);
193 LOG_DEBUG("ObjectParser",
194 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=%d (0x%04X)",
195 tile_ptr, tile_ptr + 1, low, high, offset, (uint16_t)offset);
196 LOG_DEBUG("ObjectParser",
197 " tile_data_ptr=0x%04X+0x%04X=0x%04X (SNES $00:%04X)",
198 kRoomObjectTileAddress, (uint16_t)offset, tile_data_ptr,
199 tile_data_ptr + 0x8000);
200
201 // Fix: Check for negative tile_data_ptr to prevent SIGSEGV on corrupted ROMs
202 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (int)rom_->size()) {
203 uint16_t tw0 =
204 rom_->data()[tile_data_ptr] | (rom_->data()[tile_data_ptr + 1] << 8);
205 uint16_t tw1 = rom_->data()[tile_data_ptr + 2] |
206 (rom_->data()[tile_data_ptr + 3] << 8);
207 LOG_DEBUG("ObjectParser", " First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
208 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
209 } else {
210 LOG_DEBUG("ObjectParser", " Tile data at 0x%04X: <OUT OF BOUNDS>",
211 tile_data_ptr);
212 }
213 if (!is_debug_object)
214 debug_count++;
215 }
216
217 // Use lookup table for correct tile count per object ID
218 int tile_count = GetSubtype1TileCount(object_id);
219 return ReadTileData(tile_data_ptr, tile_count);
220}
221
222absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype2(
223 int16_t object_id) {
224 // Type 2 objects: 0x100-0x13F (64 objects only)
225 int index = (object_id - 0x100) & 0x3F;
226 int tile_ptr = kRoomObjectSubtype2 + (index * 2);
227
228 if (tile_ptr + 1 >= (int)rom_->size()) {
229 return absl::OutOfRangeError(
230 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
231 }
232
233 // Read tile data pointer
234 uint8_t low = rom_->data()[tile_ptr];
235 uint8_t high = rom_->data()[tile_ptr + 1];
236 int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
237
238 // Determine tile count based on object ID
239 int tile_count = GetSubtype2TileCount(object_id);
240
241 // DEBUG: Log corner tile loading (0x100-0x103)
242 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
243 if (is_corner) {
244 LOG_DEBUG("ObjectParser",
245 "ParseSubtype2: CORNER obj=0x%03X index=%d tile_ptr=0x%04X",
246 object_id, index, tile_ptr);
247 LOG_DEBUG("ObjectParser",
248 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=0x%04X", tile_ptr,
249 tile_ptr + 1, low, high, (high << 8) | low);
250 LOG_DEBUG(
251 "ObjectParser", " tile_data_ptr=0x%04X+0x%04X=0x%04X (tile_count=%d)",
252 kRoomObjectTileAddress, (high << 8) | low, tile_data_ptr, tile_count);
253
254 // Show first 2 tile words
255 if (tile_data_ptr >= 0 && tile_data_ptr + 4 < (int)rom_->size()) {
256 uint16_t tw0 =
257 rom_->data()[tile_data_ptr] | (rom_->data()[tile_data_ptr + 1] << 8);
258 uint16_t tw1 = rom_->data()[tile_data_ptr + 2] |
259 (rom_->data()[tile_data_ptr + 3] << 8);
260 LOG_DEBUG("ObjectParser", " First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
261 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
262 }
263 }
264
265 return ReadTileData(tile_data_ptr, tile_count);
266}
267
268absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype3(
269 int16_t object_id) {
270 // Type 3 object IDs are 0xF80-0xFFF (128 objects)
271 // Table index should be 0-127, calculated by subtracting base offset 0xF80
272 int index = (object_id - 0xF80) & 0x7F;
273 int tile_ptr = kRoomObjectSubtype3 + (index * 2);
274
275 if (tile_ptr + 1 >= (int)rom_->size()) {
276 return absl::OutOfRangeError(
277 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
278 }
279
280 // Read tile data pointer
281 uint8_t low = rom_->data()[tile_ptr];
282 uint8_t high = rom_->data()[tile_ptr + 1];
283 int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
284
285 // Determine tile count based on object ID
286 int tile_count = GetSubtype3TileCount(object_id);
287 return ReadTileData(tile_data_ptr, tile_count);
288}
289
290absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ReadTileData(
291 int address, int tile_count) {
292 // Each tile is stored as a 16-bit word (2 bytes), not 8 bytes!
293 // ZScream: tiles.Add(new Tile(ROM.DATA[pos + ((i * 2))], ROM.DATA[pos + ((i *
294 // 2)) + 1]));
295 if (address < 0 || address + (tile_count * 2) >= (int)rom_->size()) {
296 return absl::OutOfRangeError(
297 absl::StrFormat("Tile data address out of range: %#06x", address));
298 }
299
300 std::vector<gfx::TileInfo> tiles;
301 tiles.reserve(tile_count);
302
303 // DEBUG: Log first tile read
304 static int debug_read_count = 0;
305 bool should_log = (debug_read_count < 3);
306
307 for (int i = 0; i < tile_count; i++) {
308 int tile_offset = address + (i * 2); // 2 bytes per tile word
309
310 // Read 1 word (2 bytes) per tile - this is the SNES tile format
311 uint16_t tile_word =
312 rom_->data()[tile_offset] | (rom_->data()[tile_offset + 1] << 8);
313
314 auto tile_info = gfx::WordToTileInfo(tile_word);
315 tiles.push_back(tile_info);
316
317 // DEBUG: Log first few tiles
318 if (should_log && i < 4) {
319 LOG_DEBUG("ObjectParser",
320 "ReadTile[%d]: addr=0x%06X word=0x%04X id=0x%03X pal=%d "
321 "mirror=(h:%d,v:%d)",
322 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
323 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
324 }
325 }
326
327 if (should_log) {
328 LOG_DEBUG("ObjectParser",
329 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
330 tile_count, tiles.size());
331 debug_read_count++;
332 }
333
334 return tiles;
335}
336
337int ObjectParser::GetSubtype2TileCount(int16_t object_id) const {
338 // 4x4 corners (0x100-0x10F): 16 tiles (32 bytes)
339 // These are RoomDraw_4x4 and RoomDraw_4x4Corner_BothBG routines
340 if (object_id >= 0x100 && object_id <= 0x10F) {
341 return 16;
342 }
343 // Weird corners (0x110-0x117): 12 tiles (24 bytes)
344 // These are RoomDraw_WeirdCornerBottom_BothBG and RoomDraw_WeirdCornerTop_BothBG
345 if (object_id >= 0x110 && object_id <= 0x117) {
346 return 12;
347 }
348 // 4x4 fixed patterns (stairs/altars/walls)
349 if (object_id == 0x11C || object_id == 0x124 || object_id == 0x125 ||
350 object_id == 0x129 || (object_id >= 0x12D && object_id <= 0x133) ||
351 (object_id >= 0x135 && object_id <= 0x137) || object_id == 0x13C ||
352 object_id == 0x13F) {
353 return 16;
354 }
355 // Beds (4x5)
356 if (object_id == 0x122 || object_id == 0x128) {
357 return 20;
358 }
359 // Tables (4x3)
360 if (object_id == 0x123 || object_id == 0x13D) {
361 return 12;
362 }
363 // 3x6 and 6x3 utility patterns
364 if (object_id == 0x12C || object_id == 0x13E) {
365 return 18;
366 }
367 // Spiral stairs (4x3)
368 if (object_id >= 0x138 && object_id <= 0x13B) {
369 return 12;
370 }
371 // Default: 8 tiles for other subtype 2 objects
372 return 8;
373}
374
375int ObjectParser::GetSubtype3TileCount(int16_t object_id) const {
376 // Water face variants:
377 // - 0xF80 (Empty) may branch to the 0xF81 tile block at runtime, so we load
378 // both spans to preserve parity for state-dependent rendering.
379 // - 0xF81 (Spitting): 4x5
380 // - 0xF82 (Drenching): 4x7
381 if (object_id == 0xF80) {
382 return 32;
383 }
384 if (object_id == 0xF81) {
385 return 20;
386 }
387 if (object_id == 0xF82) {
388 return 28;
389 }
390
391 // BigChest (0xFB1 = ASM 0x231) and OpenBigChest (0xFB2 = ASM 0x232): 12 tiles
392 // These use RoomDraw_1x3N_rightwards with N=4 (4 columns × 3 rows)
393 if (object_id == 0xFB1 || object_id == 0xFB2) {
394 return 12;
395 }
396 // TableRock4x3 variants (0xF94, 0xFCE, 0xFE7-0xFE8, 0xFEC-0xFED): 12 tiles
397 if (object_id == 0xF94 || object_id == 0xFCE ||
398 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
399 (object_id >= 0xFEC && object_id <= 0xFED)) {
400 return 12;
401 }
402 // 4x4 pattern objects: 16 tiles
403 // (0xFC8 = 0x248, 0xFE6 = 0x266, 0xFEB = 0x26B, 0xFFA = 0x27A)
404 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
405 object_id == 0xFFA) {
406 return 16;
407 }
408 // Boss shells (4x4)
409 if (object_id == 0xF95 || object_id == 0xFF2 || object_id == 0xFFB) {
410 return 16;
411 }
412 // Auto stairs (4x4)
413 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
414 return 16;
415 }
416 // Straight inter-room stairs (4x4)
417 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
418 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
419 return 16;
420 }
421 // 4x4 single-pattern objects
422 if (object_id == 0xFAA || object_id == 0xFAD || object_id == 0xFAE ||
423 (object_id >= 0xFB4 && object_id <= 0xFB9) || object_id == 0xFCB ||
424 object_id == 0xFCC || object_id == 0xFD4 || object_id == 0xFE2 ||
425 object_id == 0xFF4 || object_id == 0xFF6 || object_id == 0xFF7) {
426 return 16;
427 }
428 // Utility 6x3 (18 tiles)
429 if (object_id == 0xFCD || object_id == 0xFDD) {
430 return 18;
431 }
432 // Utility 3x5 (15 tiles)
433 if (object_id == 0xFD5 || object_id == 0xFDB) {
434 return 15;
435 }
436 // Archery game target door (3x6, 18 tiles)
437 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
438 return 18;
439 }
440 // Solid wall decor 3x4 (12 tiles)
441 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
442 object_id == 0xFEF) {
443 return 12;
444 }
445 // Light beams
446 if (object_id == 0xFF0) {
447 return 16;
448 }
449 if (object_id == 0xFF1) {
450 return 36;
451 }
452 // Ganon Triforce floor decor (two 4x4 blocks -> 32 tiles)
453 if (object_id == 0xFF8) {
454 return 32;
455 }
456 // Table rock 4x3
457 if (object_id == 0xFF9) {
458 return 12;
459 }
460 // Default: 8 tiles for most subtype 3 objects
461 return 8;
462}
463
464int ObjectParser::DetermineSubtype(int16_t object_id) const {
465 // Type 3 IDs from decoding are 0xF80-0xFFF (b3 0xF8-0xFF shifted).
466 if (object_id >= 0xF80) {
467 return 3;
468 } else if (object_id >= 0x100) {
469 return 2;
470 } else {
471 return 1;
472 }
473}
474
475int ObjectParser::ResolveTileCountForObject(int16_t object_id) const {
476 if (object_id < 0) {
477 return 1;
478 }
479
480 switch (DetermineSubtype(object_id)) {
481 case 1:
482 return GetSubtype1TileCount(object_id);
483 case 2:
484 return GetSubtype2TileCount(object_id);
485 case 3:
486 return GetSubtype3TileCount(object_id);
487 default:
488 return 1;
489 }
490}
491
493 ObjectDrawInfo info;
494
495 // Keep draw-info tile counts sourced from subtype tables (ROM-facing data),
496 // while routine selection comes from DrawRoutineRegistry (renderer-facing
497 // canonical mapping).
498 info.tile_count = std::max(1, ResolveTileCountForObject(object_id));
499 const bool is_vertical_band = (object_id >= 0x60 && object_id <= 0x6F);
500 info.is_horizontal = !is_vertical_band;
501 info.is_vertical = is_vertical_band;
502
504 info.routine_name = "DefaultSolid";
505 info.both_layers = false;
506
507 auto& registry = DrawRoutineRegistry::Get();
508 // Feature flags (custom objects) can be toggled at runtime by project
509 // context, so keep parser metadata aligned with current registry mappings.
510 registry.RefreshFeatureFlagMappings();
511
512 int registry_routine = registry.GetRoutineIdForObject(object_id);
513 if (registry_routine < 0) {
514 return info;
515 }
516
517 info.draw_routine_id = registry_routine;
518 if (const DrawRoutineInfo* routine_info =
519 registry.GetRoutineInfo(registry_routine);
520 routine_info != nullptr) {
521 info.routine_name = routine_info->name;
522 info.both_layers = routine_info->draws_to_both_bgs;
523
524 switch (routine_info->category) {
526 info.is_horizontal = false;
527 info.is_vertical = true;
528 break;
530 info.is_horizontal = false;
531 info.is_vertical = false;
532 break;
533 default:
534 info.is_horizontal = true;
535 info.is_vertical = false;
536 break;
537 }
538 }
539
540 return info;
541}
542
543} // namespace zelda3
544} // namespace yaze
auto data() const
Definition rom.h:139
auto size() const
Definition rom.h:138
static DrawRoutineRegistry & Get()
absl::StatusOr< ObjectSubtypeInfo > GetObjectSubtype(int16_t object_id)
Get object subtype information.
absl::StatusOr< ObjectRoutineInfo > ParseObjectRoutine(int16_t object_id)
Parse object routine data.
absl::StatusOr< ObjectSizeInfo > ParseObjectSize(int16_t object_id, uint8_t size_byte)
Parse object size and orientation.
int GetSubtype3TileCount(int16_t object_id) const
Get tile count for subtype 3 objects.
absl::StatusOr< std::vector< gfx::TileInfo > > ParseSubtype2(int16_t object_id)
Parse subtype 2 objects (0x100-0x1FF)
absl::StatusOr< std::vector< gfx::TileInfo > > ParseObject(int16_t object_id)
Parse object data directly from ROM.
int GetSubtype2TileCount(int16_t object_id) const
Get tile count for subtype 2 objects.
absl::StatusOr< std::vector< gfx::TileInfo > > ParseSubtype1(int16_t object_id)
Parse subtype 1 objects (0x00-0xFF)
absl::StatusOr< std::vector< gfx::TileInfo > > ParseSubtype3(int16_t object_id)
Parse subtype 3 objects (0x200+)
absl::StatusOr< std::vector< gfx::TileInfo > > ReadTileData(int address, int tile_count)
Read tile data from ROM.
ObjectDrawInfo GetObjectDrawInfo(int16_t object_id) const
Get draw routine information for an object.
int ResolveTileCountForObject(int16_t object_id) const
int DetermineSubtype(int16_t object_id) const
Determine object subtype from ID.
#define LOG_DEBUG(category, format,...)
Definition log.h:103
TileInfo WordToTileInfo(uint16_t word)
Definition snes_tile.cc:378
constexpr int kRoomObjectSubtype3
Definition room_object.h:46
constexpr int kRoomObjectSubtype1
Definition room_object.h:44
constexpr int kRoomObjectSubtype2
Definition room_object.h:45
constexpr int kRoomObjectTileAddress
Definition room_object.h:47
Metadata about a draw routine.
Draw routine information for object rendering.
Object routine information.
Object size and orientation information.
Object subtype information.