7#include "absl/strings/str_format.h"
54static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
55 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5,
56 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
57 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6,
58 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 8, 8, 4, 3,
59 3, 3, 3, 3, 3, 3, 3, 15, 9, 8, 8, 4, 12, 16, 16, 16,
60 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3,
61 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1,
62 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8,
63 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6,
64 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
65 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
66 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1,
67 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 24, 24, 1,
68 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8,
69 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1,
70 1, 1, 1, 1, 1, 1, 1, 1
74static inline bool IsSubtype1LogicOnlyZeroTileObject(
int object_id) {
75 int index = object_id & 0xFF;
76 return index >= 0xD3 && index <= 0xD6;
80static inline int GetSubtype1TileCount(
int object_id) {
81 int index = object_id & 0xFF;
83 int count = kSubtype1TileLengths[index];
87 if (IsSubtype1LogicOnlyZeroTileObject(index)) {
100 if (
rom_ ==
nullptr) {
101 return absl::InvalidArgumentError(
"ROM is null");
106 return absl::InvalidArgumentError(
107 absl::StrFormat(
"Invalid object ID: %d", object_id));
120 return absl::InvalidArgumentError(
121 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
125absl::StatusOr<std::vector<ObjectTileReadRange>>
128 return absl::FailedPreconditionError(
129 "Object tile source resolution is already active");
132 std::vector<ObjectTileReadRange> ranges;
136 if (!parsed_or.ok()) {
137 return parsed_or.status();
140 std::sort(ranges.begin(), ranges.end(),
142 return std::tie(lhs.begin, lhs.end) <
143 std::tie(rhs.begin, rhs.end);
145 ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
151 if (
rom_ ==
nullptr) {
152 return absl::InvalidArgumentError(
"ROM is null");
156 if (!subtype_info.ok()) {
157 return subtype_info.status();
161 routine_info.
routine_ptr = subtype_info->routine_ptr;
162 routine_info.
tile_ptr = subtype_info->subtype_ptr;
163 routine_info.
tile_count = subtype_info->max_tile_count;
177 int index = object_id & 0xFF;
185 int index = (object_id - 0x100) & 0x3F;
194 int index = (object_id - 0xF80) & 0x7F;
200 return absl::InvalidArgumentError(
201 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
210 int16_t object_id, uint8_t size_byte) {
214 int size_x = size_byte & 0x03;
215 int size_y = (size_byte >> 2) & 0x03;
222 if (object_id >= 0x80 && object_id <= 0xFF) {
239 int index = object_id & 0xFF;
242 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
243 return absl::OutOfRangeError(
244 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
248 uint8_t low =
rom_->
data()[tile_ptr];
249 uint8_t high =
rom_->
data()[tile_ptr + 1];
250 int16_t offset = (int16_t)((high << 8) | low);
254 bool is_debug_object = (object_id == 0x61 || object_id == 0x62 ||
255 object_id == 0xC0 || object_id == 0xC2);
256 static int debug_count = 0;
257 if (debug_count < 10 || is_debug_object) {
259 "ParseSubtype1: obj=0x%02X%s tile_ptr=0x%04X (SNES $01:%04X)",
260 object_id, is_debug_object ?
" (DEBUG)" :
"", tile_ptr, tile_ptr);
262 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=%d (0x%04X)",
263 tile_ptr, tile_ptr + 1, low, high, offset, (uint16_t)offset);
265 " tile_data_ptr=0x%04X+0x%04X=0x%04X (SNES $00:%04X)",
267 tile_data_ptr + 0x8000);
270 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (
int)
rom_->
size()) {
273 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
274 (
rom_->
data()[tile_data_ptr + 3] << 8);
275 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
276 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
278 LOG_DEBUG(
"ObjectParser",
" Tile data at 0x%04X: <OUT OF BOUNDS>",
281 if (!is_debug_object)
286 int tile_count = GetSubtype1TileCount(object_id);
292 constexpr int kDamFloodGateOpenTileOffset = 0x13E8;
293 constexpr int kDamFloodGateTileCount = 40;
296 int index = (object_id - 0x100) & 0x3F;
299 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
300 return absl::OutOfRangeError(
301 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
305 uint8_t low =
rom_->
data()[tile_ptr];
306 uint8_t high =
rom_->
data()[tile_ptr + 1];
313 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
316 "ParseSubtype2: CORNER obj=0x%03X index=%d tile_ptr=0x%04X",
317 object_id, index, tile_ptr);
319 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=0x%04X", tile_ptr,
320 tile_ptr + 1, low, high, (high << 8) | low);
322 "ObjectParser",
" tile_data_ptr=0x%04X+0x%04X=0x%04X (tile_count=%d)",
326 if (tile_data_ptr >= 0 && tile_data_ptr + 4 < (
int)
rom_->
size()) {
329 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
330 (
rom_->
data()[tile_data_ptr + 3] << 8);
331 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
332 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
336 if (object_id == 0x137) {
337 auto closed_tiles =
ReadTileData(tile_data_ptr, kDamFloodGateTileCount);
338 if (!closed_tiles.ok()) {
339 return closed_tiles.status();
343 kDamFloodGateTileCount);
344 if (!open_tiles.ok()) {
345 return open_tiles.status();
347 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
357 constexpr int kBombableFloorOpenTileOffset = 0x05BA;
358 constexpr int kBombableFloorStateTileCount = 16;
359 constexpr int kPrisonCellTileOffset = 0x1488;
360 constexpr int kPrisonCellTileCount = 6;
361 constexpr int kRupeeFloorTileOffset = 0x1DD6;
362 constexpr int kRupeeFloorTileCount = 2;
363 constexpr int kBigKeyLockTileOffset = 0x1494;
364 constexpr int kClosedChestTileOffset = 0x149C;
365 constexpr int kOpenChestTileOffset = 0x14A4;
366 constexpr int kChestStateTileCount = 4;
367 constexpr int kClosedBigChestTileOffset = 0x14AC;
368 constexpr int kOpenBigChestTileOffset = 0x14C4;
369 constexpr int kBigChestStateTileCount = 12;
370 constexpr int kBigGrayRockTileOffset = 0x0E62;
371 constexpr int kBigGrayRockTileCount = 16;
372 constexpr int kAgahnimsAltarTileOffset = 0x1B4A;
373 constexpr int kAgahnimsAltarTileCount = 84;
374 constexpr int kFortuneTellerRoomTileOffset = 0x202E;
375 constexpr int kFortuneTellerRoomTileCount = 26;
376 constexpr int kSmithyFurnaceTileOffset = 0x1F92;
377 constexpr int kSmithyFurnaceTileCount = 48;
381 int index = (object_id - 0xF80) & 0x7F;
384 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
385 return absl::OutOfRangeError(
386 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
390 uint8_t low =
rom_->
data()[tile_ptr];
391 uint8_t high =
rom_->
data()[tile_ptr + 1];
400 if (object_id == 0xFC7) {
402 ReadTileData(tile_data_ptr, kBombableFloorStateTileCount);
403 if (!intact_tiles.ok()) {
404 return intact_tiles.status();
408 kBombableFloorStateTileCount);
409 if (!open_tiles.ok()) {
410 return open_tiles.status();
412 intact_tiles->insert(intact_tiles->end(), open_tiles->begin(),
421 if (object_id == 0xF8D || object_id == 0xF97) {
423 kPrisonCellTileCount);
430 if (object_id == 0xF92) {
432 kRupeeFloorTileCount);
438 if (object_id == 0xF98) {
440 kChestStateTileCount);
444 if (object_id == 0xF99) {
447 if (!closed_tiles.ok()) {
448 return closed_tiles.status();
452 if (!open_tiles.ok()) {
453 return open_tiles.status();
455 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
462 if (object_id == 0xFB1) {
465 kBigChestStateTileCount);
466 if (!closed_tiles.ok()) {
467 return closed_tiles.status();
471 kBigChestStateTileCount);
472 if (!open_tiles.ok()) {
473 return open_tiles.status();
475 closed_tiles->insert(closed_tiles->end(), open_tiles->begin(),
483 if (object_id == 0xFAC) {
485 kBigGrayRockTileCount);
490 if (object_id == 0xFAD) {
492 kAgahnimsAltarTileCount);
497 if (object_id == 0xFD4) {
499 kFortuneTellerRoomTileCount);
505 if (object_id == 0xFCC) {
507 kSmithyFurnaceTileCount);
514 int address,
int tile_count) {
518 if (tile_count < 0) {
519 return absl::InvalidArgumentError(
520 absl::StrFormat(
"Invalid tile count: %d", tile_count));
522 if (tile_count == 0) {
523 return std::vector<gfx::TileInfo>{};
525 if (address < 0 || address + (tile_count * 2) >= (
int)
rom_->
size()) {
526 return absl::OutOfRangeError(
527 absl::StrFormat(
"Tile data address out of range: %#06x", address));
532 {
static_cast<uint32_t
>(address),
533 static_cast<uint32_t
>(address + tile_count * 2)});
536 std::vector<gfx::TileInfo> tiles;
537 tiles.reserve(tile_count);
540 static int debug_read_count = 0;
541 bool should_log = (debug_read_count < 3);
543 for (
int i = 0; i < tile_count; i++) {
544 int tile_offset = address + (i * 2);
551 tiles.push_back(tile_info);
554 if (should_log && i < 4) {
556 "ReadTile[%d]: addr=0x%06X word=0x%04X id=0x%03X pal=%d "
557 "mirror=(h:%d,v:%d)",
558 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
559 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
565 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
566 tile_count, tiles.size());
576 if (object_id >= 0x100 && object_id <= 0x10F) {
581 if (object_id >= 0x110 && object_id <= 0x117) {
585 if (object_id == 0x11F || object_id == 0x120) {
589 if (object_id == 0x11C || object_id == 0x124 || object_id == 0x125 ||
590 object_id == 0x129 || (object_id >= 0x12D && object_id <= 0x133) ||
591 object_id == 0x13C) {
596 if (object_id == 0x13F) {
600 if (object_id == 0x135 || object_id == 0x136) {
604 if (object_id == 0x137) {
608 if (object_id == 0x122 || object_id == 0x128) {
612 if (object_id == 0x123 || object_id == 0x13D) {
616 if (object_id == 0x12C || object_id == 0x13E) {
620 if (object_id >= 0x138 && object_id <= 0x13B) {
633 if (object_id == 0xF80) {
636 if (object_id == 0xF81) {
639 if (object_id == 0xF82) {
645 if (object_id == 0xF8D || object_id == 0xF97) {
651 if (object_id == 0xF92) {
657 if (object_id == 0xF96) {
664 if (object_id == 0xF98) {
670 if (object_id == 0xF99) {
673 if (object_id == 0xF9A) {
680 if (object_id == 0xFC7) {
686 if ((object_id >= 0xF83 && object_id <= 0xF8C) || object_id == 0xF8E ||
687 object_id == 0xF8F) {
693 if (object_id == 0xFB1) {
696 if (object_id == 0xFB2) {
700 if (object_id == 0xF94 || object_id == 0xFCE ||
701 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
702 (object_id >= 0xFEC && object_id <= 0xFED)) {
707 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
708 object_id == 0xFFA) {
712 if (object_id == 0xF95 || object_id == 0xFF2) {
717 if (object_id == 0xFFB) {
721 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
725 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
726 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
731 if (object_id == 0xFAC) {
736 if (object_id == 0xFAD) {
741 if (object_id == 0xFD4) {
746 if (object_id == 0xFCC) {
750 if (object_id == 0xFAA || object_id == 0xFAE ||
751 (object_id >= 0xFB4 && object_id <= 0xFB9) || object_id == 0xFE2) {
756 if (object_id == 0xFCB || object_id == 0xFF6 || object_id == 0xFF7) {
768 if ((object_id >= 0xFBA && object_id <= 0xFBD) || object_id == 0xFDC) {
772 if (object_id == 0xFCD || object_id == 0xFDD) {
776 if (object_id == 0xFD5 || object_id == 0xFDB) {
780 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
784 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
785 object_id == 0xFEF) {
791 if (object_id == 0xFF0) {
795 if (object_id == 0xFF1) {
800 if (object_id == 0xFF4) {
804 if (object_id == 0xFF8) {
808 if (object_id == 0xFF9) {
817 if (object_id >= 0xF80) {
819 }
else if (object_id >= 0x100) {
833 return GetSubtype1TileCount(object_id);
850 const bool is_vertical_band = (object_id >= 0x60 && object_id <= 0x6F);
861 registry.RefreshFeatureFlagMappings();
863 int registry_routine = registry.GetRoutineIdForObject(object_id);
864 if (registry_routine < 0) {
870 registry.GetRoutineInfo(registry_routine);
871 routine_info !=
nullptr) {
873 info.
both_layers = routine_info->draws_to_both_bgs;
875 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.
absl::StatusOr< std::vector< ObjectTileReadRange > > ResolveTileReadRanges(int16_t object_id)
Resolve every tile-data range consumed by ParseObject.
int GetSubtype3TileCount(int16_t object_id) const
Get tile count for subtype 3 objects.
std::vector< ObjectTileReadRange > * tile_read_range_collector_
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.
One half-open PC range consumed while parsing an object's tile data.