6#include "absl/strings/str_format.h"
21static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
22 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5,
23 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
24 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6,
25 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 4, 8, 4, 3,
26 3, 3, 3, 3, 3, 3, 3, 0, 0, 8, 8, 4, 9, 16, 16, 16,
27 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3,
28 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1,
29 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8,
30 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6,
31 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
32 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
33 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1,
34 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 28, 28, 1,
35 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8,
36 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1,
37 1, 1, 1, 1, 1, 1, 1, 1
42static inline int GetSubtype1TileCount(
int object_id) {
43 int index = object_id & 0xFF;
45 int count = kSubtype1TileLengths[index];
46 return (count > 0) ? count : 8;
56 if (
rom_ ==
nullptr) {
57 return absl::InvalidArgumentError(
"ROM is null");
62 return absl::InvalidArgumentError(
63 absl::StrFormat(
"Invalid object ID: %d", object_id));
76 return absl::InvalidArgumentError(
77 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
83 if (
rom_ ==
nullptr) {
84 return absl::InvalidArgumentError(
"ROM is null");
88 if (!subtype_info.ok()) {
89 return subtype_info.status();
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;
109 int index = object_id & 0xFF;
117 int index = (object_id - 0x100) & 0x3F;
126 int index = (object_id - 0xF80) & 0x7F;
132 return absl::InvalidArgumentError(
133 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
142 int16_t object_id, uint8_t size_byte) {
146 int size_x = size_byte & 0x03;
147 int size_y = (size_byte >> 2) & 0x03;
154 if (object_id >= 0x80 && object_id <= 0xFF) {
171 int index = object_id & 0xFF;
174 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
175 return absl::OutOfRangeError(
176 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
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);
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) {
191 "ParseSubtype1: obj=0x%02X%s tile_ptr=0x%04X (SNES $01:%04X)",
192 object_id, is_debug_object ?
" (DEBUG)" :
"", tile_ptr, tile_ptr);
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);
197 " tile_data_ptr=0x%04X+0x%04X=0x%04X (SNES $00:%04X)",
199 tile_data_ptr + 0x8000);
202 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (
int)
rom_->
size()) {
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);
210 LOG_DEBUG(
"ObjectParser",
" Tile data at 0x%04X: <OUT OF BOUNDS>",
213 if (!is_debug_object)
218 int tile_count = GetSubtype1TileCount(object_id);
225 int index = (object_id - 0x100) & 0x3F;
228 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
229 return absl::OutOfRangeError(
230 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
234 uint8_t low =
rom_->
data()[tile_ptr];
235 uint8_t high =
rom_->
data()[tile_ptr + 1];
242 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
245 "ParseSubtype2: CORNER obj=0x%03X index=%d tile_ptr=0x%04X",
246 object_id, index, tile_ptr);
248 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=0x%04X", tile_ptr,
249 tile_ptr + 1, low, high, (high << 8) | low);
251 "ObjectParser",
" tile_data_ptr=0x%04X+0x%04X=0x%04X (tile_count=%d)",
255 if (tile_data_ptr >= 0 && tile_data_ptr + 4 < (
int)
rom_->
size()) {
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);
272 int index = (object_id - 0xF80) & 0x7F;
275 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
276 return absl::OutOfRangeError(
277 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
281 uint8_t low =
rom_->
data()[tile_ptr];
282 uint8_t high =
rom_->
data()[tile_ptr + 1];
291 int address,
int tile_count) {
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));
300 std::vector<gfx::TileInfo> tiles;
301 tiles.reserve(tile_count);
304 static int debug_read_count = 0;
305 bool should_log = (debug_read_count < 3);
307 for (
int i = 0; i < tile_count; i++) {
308 int tile_offset = address + (i * 2);
315 tiles.push_back(tile_info);
318 if (should_log && i < 4) {
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_);
329 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
330 tile_count, tiles.size());
340 if (object_id >= 0x100 && object_id <= 0x10F) {
345 if (object_id >= 0x110 && object_id <= 0x117) {
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) {
356 if (object_id == 0x122 || object_id == 0x128) {
360 if (object_id == 0x123 || object_id == 0x13D) {
364 if (object_id == 0x12C || object_id == 0x13E) {
368 if (object_id >= 0x138 && object_id <= 0x13B) {
381 if (object_id == 0xF80) {
384 if (object_id == 0xF81) {
387 if (object_id == 0xF82) {
393 if (object_id == 0xFB1 || object_id == 0xFB2) {
397 if (object_id == 0xF94 || object_id == 0xFCE ||
398 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
399 (object_id >= 0xFEC && object_id <= 0xFED)) {
404 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
405 object_id == 0xFFA) {
409 if (object_id == 0xF95 || object_id == 0xFF2 || object_id == 0xFFB) {
413 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
417 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
418 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
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) {
429 if (object_id == 0xFCD || object_id == 0xFDD) {
433 if (object_id == 0xFD5 || object_id == 0xFDB) {
437 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
441 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
442 object_id == 0xFEF) {
446 if (object_id == 0xFF0) {
449 if (object_id == 0xFF1) {
453 if (object_id == 0xFF8) {
457 if (object_id == 0xFF9) {
466 if (object_id >= 0xF80) {
468 }
else if (object_id >= 0x100) {
482 return GetSubtype1TileCount(object_id);
499 const bool is_vertical_band = (object_id >= 0x60 && object_id <= 0x6F);
510 registry.RefreshFeatureFlagMappings();
512 int registry_routine = registry.GetRoutineIdForObject(object_id);
513 if (registry_routine < 0) {
519 registry.GetRoutineInfo(registry_routine);
520 routine_info !=
nullptr) {
522 info.
both_layers = routine_info->draws_to_both_bgs;
524 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.