5#include "absl/strings/str_format.h"
52static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
53 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5,
54 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
55 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6,
56 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 8, 8, 4, 3,
57 3, 3, 3, 3, 3, 3, 3, 15, 9, 8, 8, 4, 12, 16, 16, 16,
58 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3,
59 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1,
60 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8,
61 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6,
62 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
63 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
64 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1,
65 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 24, 24, 1,
66 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8,
67 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1,
68 1, 1, 1, 1, 1, 1, 1, 1
72static inline bool IsSubtype1LogicOnlyZeroTileObject(
int object_id) {
73 int index = object_id & 0xFF;
74 return index >= 0xD3 && index <= 0xD6;
78static inline int GetSubtype1TileCount(
int object_id) {
79 int index = object_id & 0xFF;
81 int count = kSubtype1TileLengths[index];
85 if (IsSubtype1LogicOnlyZeroTileObject(index)) {
98 if (
rom_ ==
nullptr) {
99 return absl::InvalidArgumentError(
"ROM is null");
104 return absl::InvalidArgumentError(
105 absl::StrFormat(
"Invalid object ID: %d", object_id));
118 return absl::InvalidArgumentError(
119 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
125 if (
rom_ ==
nullptr) {
126 return absl::InvalidArgumentError(
"ROM is null");
130 if (!subtype_info.ok()) {
131 return subtype_info.status();
135 routine_info.
routine_ptr = subtype_info->routine_ptr;
136 routine_info.
tile_ptr = subtype_info->subtype_ptr;
137 routine_info.
tile_count = subtype_info->max_tile_count;
151 int index = object_id & 0xFF;
159 int index = (object_id - 0x100) & 0x3F;
168 int index = (object_id - 0xF80) & 0x7F;
174 return absl::InvalidArgumentError(
175 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
184 int16_t object_id, uint8_t size_byte) {
188 int size_x = size_byte & 0x03;
189 int size_y = (size_byte >> 2) & 0x03;
196 if (object_id >= 0x80 && object_id <= 0xFF) {
213 int index = object_id & 0xFF;
216 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
217 return absl::OutOfRangeError(
218 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
222 uint8_t low =
rom_->
data()[tile_ptr];
223 uint8_t high =
rom_->
data()[tile_ptr + 1];
224 int16_t offset = (int16_t)((high << 8) | low);
228 bool is_debug_object = (object_id == 0x61 || object_id == 0x62 ||
229 object_id == 0xC0 || object_id == 0xC2);
230 static int debug_count = 0;
231 if (debug_count < 10 || is_debug_object) {
233 "ParseSubtype1: obj=0x%02X%s tile_ptr=0x%04X (SNES $01:%04X)",
234 object_id, is_debug_object ?
" (DEBUG)" :
"", tile_ptr, tile_ptr);
236 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=%d (0x%04X)",
237 tile_ptr, tile_ptr + 1, low, high, offset, (uint16_t)offset);
239 " tile_data_ptr=0x%04X+0x%04X=0x%04X (SNES $00:%04X)",
241 tile_data_ptr + 0x8000);
244 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (
int)
rom_->
size()) {
247 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
248 (
rom_->
data()[tile_data_ptr + 3] << 8);
249 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
250 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
252 LOG_DEBUG(
"ObjectParser",
" Tile data at 0x%04X: <OUT OF BOUNDS>",
255 if (!is_debug_object)
260 int tile_count = GetSubtype1TileCount(object_id);
266 constexpr int kDamFloodGateOpenTileOffset = 0x13E8;
267 constexpr int kDamFloodGateTileCount = 40;
270 int index = (object_id - 0x100) & 0x3F;
273 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
274 return absl::OutOfRangeError(
275 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
279 uint8_t low =
rom_->
data()[tile_ptr];
280 uint8_t high =
rom_->
data()[tile_ptr + 1];
287 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
290 "ParseSubtype2: CORNER obj=0x%03X index=%d tile_ptr=0x%04X",
291 object_id, index, tile_ptr);
293 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=0x%04X", tile_ptr,
294 tile_ptr + 1, low, high, (high << 8) | low);
296 "ObjectParser",
" tile_data_ptr=0x%04X+0x%04X=0x%04X (tile_count=%d)",
300 if (tile_data_ptr >= 0 && tile_data_ptr + 4 < (
int)
rom_->
size()) {
303 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
304 (
rom_->
data()[tile_data_ptr + 3] << 8);
305 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
306 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
310 if (object_id == 0x137) {
311 auto closed_tiles =
ReadTileData(tile_data_ptr, kDamFloodGateTileCount);
312 if (!closed_tiles.ok()) {
313 return closed_tiles.status();
317 kDamFloodGateTileCount);
318 if (!open_tiles.ok()) {
319 return open_tiles.status();
321 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
331 constexpr int kBombableFloorOpenTileOffset = 0x05BA;
332 constexpr int kBombableFloorStateTileCount = 16;
333 constexpr int kPrisonCellTileOffset = 0x1488;
334 constexpr int kPrisonCellTileCount = 6;
335 constexpr int kBigKeyLockTileOffset = 0x1494;
336 constexpr int kClosedChestTileOffset = 0x149C;
337 constexpr int kOpenChestTileOffset = 0x14A4;
338 constexpr int kChestStateTileCount = 4;
339 constexpr int kClosedBigChestTileOffset = 0x14AC;
340 constexpr int kOpenBigChestTileOffset = 0x14C4;
341 constexpr int kBigChestStateTileCount = 12;
345 int index = (object_id - 0xF80) & 0x7F;
348 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
349 return absl::OutOfRangeError(
350 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
354 uint8_t low =
rom_->
data()[tile_ptr];
355 uint8_t high =
rom_->
data()[tile_ptr + 1];
364 if (object_id == 0xFC7) {
366 ReadTileData(tile_data_ptr, kBombableFloorStateTileCount);
367 if (!intact_tiles.ok()) {
368 return intact_tiles.status();
372 kBombableFloorStateTileCount);
373 if (!open_tiles.ok()) {
374 return open_tiles.status();
376 intact_tiles->insert(intact_tiles->end(), open_tiles->begin(),
385 if (object_id == 0xF8D || object_id == 0xF97) {
387 kPrisonCellTileCount);
393 if (object_id == 0xF98) {
395 kChestStateTileCount);
399 if (object_id == 0xF99) {
402 if (!closed_tiles.ok()) {
403 return closed_tiles.status();
407 if (!open_tiles.ok()) {
408 return open_tiles.status();
410 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
417 if (object_id == 0xFB1) {
420 kBigChestStateTileCount);
421 if (!closed_tiles.ok()) {
422 return closed_tiles.status();
426 kBigChestStateTileCount);
427 if (!open_tiles.ok()) {
428 return open_tiles.status();
430 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
439 int address,
int tile_count) {
443 if (tile_count < 0) {
444 return absl::InvalidArgumentError(
445 absl::StrFormat(
"Invalid tile count: %d", tile_count));
447 if (tile_count == 0) {
448 return std::vector<gfx::TileInfo>{};
450 if (address < 0 || address + (tile_count * 2) >= (
int)
rom_->
size()) {
451 return absl::OutOfRangeError(
452 absl::StrFormat(
"Tile data address out of range: %#06x", address));
455 std::vector<gfx::TileInfo> tiles;
456 tiles.reserve(tile_count);
459 static int debug_read_count = 0;
460 bool should_log = (debug_read_count < 3);
462 for (
int i = 0; i < tile_count; i++) {
463 int tile_offset = address + (i * 2);
470 tiles.push_back(tile_info);
473 if (should_log && i < 4) {
475 "ReadTile[%d]: addr=0x%06X word=0x%04X id=0x%03X pal=%d "
476 "mirror=(h:%d,v:%d)",
477 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
478 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
484 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
485 tile_count, tiles.size());
495 if (object_id >= 0x100 && object_id <= 0x10F) {
500 if (object_id >= 0x110 && object_id <= 0x117) {
504 if (object_id == 0x11C || object_id == 0x124 || object_id == 0x125 ||
505 object_id == 0x129 || (object_id >= 0x12D && object_id <= 0x133) ||
506 object_id == 0x13C || object_id == 0x13F) {
510 if (object_id == 0x135 || object_id == 0x136) {
514 if (object_id == 0x137) {
518 if (object_id == 0x122 || object_id == 0x128) {
522 if (object_id == 0x123 || object_id == 0x13D) {
526 if (object_id == 0x12C || object_id == 0x13E) {
530 if (object_id >= 0x138 && object_id <= 0x13B) {
543 if (object_id == 0xF80) {
546 if (object_id == 0xF81) {
549 if (object_id == 0xF82) {
555 if (object_id == 0xF8D || object_id == 0xF97) {
561 if (object_id == 0xF92) {
567 if (object_id == 0xF96) {
574 if (object_id == 0xF98) {
580 if (object_id == 0xF99) {
583 if (object_id == 0xF9A) {
590 if (object_id == 0xFC7) {
596 if ((object_id >= 0xF83 && object_id <= 0xF8C) || object_id == 0xF8E ||
597 object_id == 0xF8F) {
603 if (object_id == 0xFB1) {
606 if (object_id == 0xFB2) {
610 if (object_id == 0xF94 || object_id == 0xFCE ||
611 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
612 (object_id >= 0xFEC && object_id <= 0xFED)) {
617 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
618 object_id == 0xFFA) {
622 if (object_id == 0xF95 || object_id == 0xFF2 || object_id == 0xFFB) {
626 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
630 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
631 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
635 if (object_id == 0xFAA || object_id == 0xFAD || object_id == 0xFAE ||
636 (object_id >= 0xFB4 && object_id <= 0xFB9) || object_id == 0xFCB ||
637 object_id == 0xFCC || object_id == 0xFD4 || object_id == 0xFE2 ||
638 object_id == 0xFF6 || object_id == 0xFF7) {
650 if (object_id >= 0xFBA && object_id <= 0xFBD) {
654 if (object_id == 0xFCD || object_id == 0xFDD) {
658 if (object_id == 0xFD5 || object_id == 0xFDB) {
662 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
666 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
667 object_id == 0xFEF) {
673 if (object_id == 0xFF0) {
677 if (object_id == 0xFF1) {
682 if (object_id == 0xFF4) {
686 if (object_id == 0xFF8) {
690 if (object_id == 0xFF9) {
699 if (object_id >= 0xF80) {
701 }
else if (object_id >= 0x100) {
715 return GetSubtype1TileCount(object_id);
732 const bool is_vertical_band = (object_id >= 0x60 && object_id <= 0x6F);
743 registry.RefreshFeatureFlagMappings();
745 int registry_routine = registry.GetRoutineIdForObject(object_id);
746 if (registry_routine < 0) {
752 registry.GetRoutineInfo(registry_routine);
753 routine_info !=
nullptr) {
755 info.
both_layers = routine_info->draws_to_both_bgs;
757 switch (routine_info->category) {
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,...)
TileInfo WordToTileInfo(uint16_t word)
constexpr int kRightwards1x1Solid_1to16_plus3
constexpr int kRoomObjectSubtype3
constexpr int kRoomObjectSubtype1
constexpr int kRoomObjectSubtype2
constexpr int kRoomObjectTileAddress
Metadata about a draw routine.
Draw routine information for object rendering.
Object routine information.
bool is_orientation_dependent
Object size and orientation information.
Object subtype information.