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;
342 constexpr int kBigGrayRockTileOffset = 0x0E62;
343 constexpr int kBigGrayRockTileCount = 16;
344 constexpr int kAgahnimsAltarTileOffset = 0x1B4A;
345 constexpr int kAgahnimsAltarTileCount = 84;
346 constexpr int kFortuneTellerRoomTileOffset = 0x202E;
347 constexpr int kFortuneTellerRoomTileCount = 26;
348 constexpr int kSmithyFurnaceTileOffset = 0x1F92;
349 constexpr int kSmithyFurnaceTileCount = 48;
353 int index = (object_id - 0xF80) & 0x7F;
356 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
357 return absl::OutOfRangeError(
358 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
362 uint8_t low =
rom_->
data()[tile_ptr];
363 uint8_t high =
rom_->
data()[tile_ptr + 1];
372 if (object_id == 0xFC7) {
374 ReadTileData(tile_data_ptr, kBombableFloorStateTileCount);
375 if (!intact_tiles.ok()) {
376 return intact_tiles.status();
380 kBombableFloorStateTileCount);
381 if (!open_tiles.ok()) {
382 return open_tiles.status();
384 intact_tiles->insert(intact_tiles->end(), open_tiles->begin(),
393 if (object_id == 0xF8D || object_id == 0xF97) {
395 kPrisonCellTileCount);
401 if (object_id == 0xF98) {
403 kChestStateTileCount);
407 if (object_id == 0xF99) {
410 if (!closed_tiles.ok()) {
411 return closed_tiles.status();
415 if (!open_tiles.ok()) {
416 return open_tiles.status();
418 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
425 if (object_id == 0xFB1) {
428 kBigChestStateTileCount);
429 if (!closed_tiles.ok()) {
430 return closed_tiles.status();
434 kBigChestStateTileCount);
435 if (!open_tiles.ok()) {
436 return open_tiles.status();
438 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
446 if (object_id == 0xFAC) {
448 kBigGrayRockTileCount);
453 if (object_id == 0xFAD) {
455 kAgahnimsAltarTileCount);
460 if (object_id == 0xFD4) {
462 kFortuneTellerRoomTileCount);
468 if (object_id == 0xFCC) {
470 kSmithyFurnaceTileCount);
477 int address,
int tile_count) {
481 if (tile_count < 0) {
482 return absl::InvalidArgumentError(
483 absl::StrFormat(
"Invalid tile count: %d", tile_count));
485 if (tile_count == 0) {
486 return std::vector<gfx::TileInfo>{};
488 if (address < 0 || address + (tile_count * 2) >= (
int)
rom_->
size()) {
489 return absl::OutOfRangeError(
490 absl::StrFormat(
"Tile data address out of range: %#06x", address));
493 std::vector<gfx::TileInfo> tiles;
494 tiles.reserve(tile_count);
497 static int debug_read_count = 0;
498 bool should_log = (debug_read_count < 3);
500 for (
int i = 0; i < tile_count; i++) {
501 int tile_offset = address + (i * 2);
508 tiles.push_back(tile_info);
511 if (should_log && i < 4) {
513 "ReadTile[%d]: addr=0x%06X word=0x%04X id=0x%03X pal=%d "
514 "mirror=(h:%d,v:%d)",
515 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
516 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
522 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
523 tile_count, tiles.size());
533 if (object_id >= 0x100 && object_id <= 0x10F) {
538 if (object_id >= 0x110 && object_id <= 0x117) {
542 if (object_id == 0x11F || object_id == 0x120) {
546 if (object_id == 0x11C || object_id == 0x124 || object_id == 0x125 ||
547 object_id == 0x129 || (object_id >= 0x12D && object_id <= 0x133) ||
548 object_id == 0x13C || object_id == 0x13F) {
552 if (object_id == 0x135 || object_id == 0x136) {
556 if (object_id == 0x137) {
560 if (object_id == 0x122 || object_id == 0x128) {
564 if (object_id == 0x123 || object_id == 0x13D) {
568 if (object_id == 0x12C || object_id == 0x13E) {
572 if (object_id >= 0x138 && object_id <= 0x13B) {
585 if (object_id == 0xF80) {
588 if (object_id == 0xF81) {
591 if (object_id == 0xF82) {
597 if (object_id == 0xF8D || object_id == 0xF97) {
603 if (object_id == 0xF92) {
609 if (object_id == 0xF96) {
616 if (object_id == 0xF98) {
622 if (object_id == 0xF99) {
625 if (object_id == 0xF9A) {
632 if (object_id == 0xFC7) {
638 if ((object_id >= 0xF83 && object_id <= 0xF8C) || object_id == 0xF8E ||
639 object_id == 0xF8F) {
645 if (object_id == 0xFB1) {
648 if (object_id == 0xFB2) {
652 if (object_id == 0xF94 || object_id == 0xFCE ||
653 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
654 (object_id >= 0xFEC && object_id <= 0xFED)) {
659 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
660 object_id == 0xFFA) {
664 if (object_id == 0xF95 || object_id == 0xFF2 || object_id == 0xFFB) {
668 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
672 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
673 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
678 if (object_id == 0xFAC) {
683 if (object_id == 0xFAD) {
688 if (object_id == 0xFD4) {
693 if (object_id == 0xFCC) {
697 if (object_id == 0xFAA || object_id == 0xFAE ||
698 (object_id >= 0xFB4 && object_id <= 0xFB9) || object_id == 0xFE2) {
703 if (object_id == 0xFCB || object_id == 0xFF6 || object_id == 0xFF7) {
715 if (object_id >= 0xFBA && object_id <= 0xFBD) {
719 if (object_id == 0xFCD || object_id == 0xFDD) {
723 if (object_id == 0xFD5 || object_id == 0xFDB) {
727 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
731 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
732 object_id == 0xFEF) {
738 if (object_id == 0xFF0) {
742 if (object_id == 0xFF1) {
747 if (object_id == 0xFF4) {
751 if (object_id == 0xFF8) {
755 if (object_id == 0xFF9) {
764 if (object_id >= 0xF80) {
766 }
else if (object_id >= 0x100) {
780 return GetSubtype1TileCount(object_id);
797 const bool is_vertical_band = (object_id >= 0x60 && object_id <= 0x6F);
808 registry.RefreshFeatureFlagMappings();
810 int registry_routine = registry.GetRoutineIdForObject(object_id);
811 if (registry_routine < 0) {
817 registry.GetRoutineInfo(registry_routine);
818 routine_info !=
nullptr) {
820 info.
both_layers = routine_info->draws_to_both_bgs;
822 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.