6#include "absl/strings/str_format.h"
20static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
21 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5,
22 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
23 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6,
24 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 4, 8, 4, 3,
25 3, 3, 3, 3, 3, 3, 3, 0, 0, 8, 8, 4, 9, 16, 16, 16,
26 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3,
27 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1,
28 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8,
29 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6,
30 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
31 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
32 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1,
33 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 28, 28, 1,
34 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8,
35 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1,
36 1, 1, 1, 1, 1, 1, 1, 1
41static inline int GetSubtype1TileCount(
int object_id) {
42 int index = object_id & 0xFF;
44 int count = kSubtype1TileLengths[index];
45 return (count > 0) ? count : 8;
55 if (
rom_ ==
nullptr) {
56 return absl::InvalidArgumentError(
"ROM is null");
61 return absl::InvalidArgumentError(
62 absl::StrFormat(
"Invalid object ID: %d", object_id));
75 return absl::InvalidArgumentError(
76 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
82 if (
rom_ ==
nullptr) {
83 return absl::InvalidArgumentError(
"ROM is null");
87 if (!subtype_info.ok()) {
88 return subtype_info.status();
92 routine_info.
routine_ptr = subtype_info->routine_ptr;
93 routine_info.
tile_ptr = subtype_info->subtype_ptr;
94 routine_info.
tile_count = subtype_info->max_tile_count;
108 int index = object_id & 0xFF;
116 int index = (object_id - 0x100) & 0x3F;
125 int index = (object_id - 0xF80) & 0x7F;
131 return absl::InvalidArgumentError(
132 absl::StrFormat(
"Invalid object subtype for ID: %#04x", object_id));
141 int16_t object_id, uint8_t size_byte) {
145 int size_x = size_byte & 0x03;
146 int size_y = (size_byte >> 2) & 0x03;
153 if (object_id >= 0x80 && object_id <= 0xFF) {
170 int index = object_id & 0xFF;
173 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
174 return absl::OutOfRangeError(
175 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
179 uint8_t low =
rom_->
data()[tile_ptr];
180 uint8_t high =
rom_->
data()[tile_ptr + 1];
181 int16_t offset = (int16_t)((high << 8) | low);
185 bool is_debug_object = (object_id == 0x61 || object_id == 0x62 ||
186 object_id == 0xC0 || object_id == 0xC2);
187 static int debug_count = 0;
188 if (debug_count < 10 || is_debug_object) {
190 "ParseSubtype1: obj=0x%02X%s tile_ptr=0x%04X (SNES $01:%04X)",
191 object_id, is_debug_object ?
" (DEBUG)" :
"", tile_ptr, tile_ptr);
193 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=%d (0x%04X)",
194 tile_ptr, tile_ptr + 1, low, high, offset, (uint16_t)offset);
196 " tile_data_ptr=0x%04X+0x%04X=0x%04X (SNES $00:%04X)",
198 tile_data_ptr + 0x8000);
201 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (
int)
rom_->
size()) {
204 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
205 (
rom_->
data()[tile_data_ptr + 3] << 8);
206 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
207 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
209 LOG_DEBUG(
"ObjectParser",
" Tile data at 0x%04X: <OUT OF BOUNDS>",
212 if (!is_debug_object)
217 int tile_count = GetSubtype1TileCount(object_id);
224 int index = (object_id - 0x100) & 0x3F;
227 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
228 return absl::OutOfRangeError(
229 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
233 uint8_t low =
rom_->
data()[tile_ptr];
234 uint8_t high =
rom_->
data()[tile_ptr + 1];
241 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
244 "ParseSubtype2: CORNER obj=0x%03X index=%d tile_ptr=0x%04X",
245 object_id, index, tile_ptr);
247 " ROM[0x%04X..0x%04X]=0x%02X 0x%02X offset=0x%04X", tile_ptr,
248 tile_ptr + 1, low, high, (high << 8) | low);
250 "ObjectParser",
" tile_data_ptr=0x%04X+0x%04X=0x%04X (tile_count=%d)",
254 if (tile_data_ptr >= 0 && tile_data_ptr + 4 < (
int)
rom_->
size()) {
257 uint16_t tw1 =
rom_->
data()[tile_data_ptr + 2] |
258 (
rom_->
data()[tile_data_ptr + 3] << 8);
259 LOG_DEBUG(
"ObjectParser",
" First 2 tiles: $%04X(id=%d) $%04X(id=%d)",
260 tw0, tw0 & 0x3FF, tw1, tw1 & 0x3FF);
271 int index = (object_id - 0xF80) & 0x7F;
274 if (tile_ptr + 1 >= (
int)
rom_->
size()) {
275 return absl::OutOfRangeError(
276 absl::StrFormat(
"Tile pointer out of range: %#06x", tile_ptr));
280 uint8_t low =
rom_->
data()[tile_ptr];
281 uint8_t high =
rom_->
data()[tile_ptr + 1];
290 int address,
int tile_count) {
294 if (address < 0 || address + (tile_count * 2) >= (
int)
rom_->
size()) {
295 return absl::OutOfRangeError(
296 absl::StrFormat(
"Tile data address out of range: %#06x", address));
299 std::vector<gfx::TileInfo> tiles;
300 tiles.reserve(tile_count);
303 static int debug_read_count = 0;
304 bool should_log = (debug_read_count < 3);
306 for (
int i = 0; i < tile_count; i++) {
307 int tile_offset = address + (i * 2);
314 tiles.push_back(tile_info);
317 if (should_log && i < 4) {
319 "ReadTile[%d]: addr=0x%06X word=0x%04X id=0x%03X pal=%d "
320 "mirror=(h:%d,v:%d)",
321 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
322 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
328 "ReadTileData: addr=0x%06X count=%d loaded %zu tiles", address,
329 tile_count, tiles.size());
339 if (object_id >= 0x100 && object_id <= 0x10F) {
344 if (object_id >= 0x110 && object_id <= 0x117) {
348 if (object_id == 0x11C || object_id == 0x124 || object_id == 0x125 ||
349 object_id == 0x129 || (object_id >= 0x12D && object_id <= 0x133) ||
350 (object_id >= 0x135 && object_id <= 0x137) || object_id == 0x13C ||
351 object_id == 0x13F) {
355 if (object_id == 0x122 || object_id == 0x128) {
359 if (object_id == 0x123 || object_id == 0x13D) {
363 if (object_id == 0x12C || object_id == 0x13E) {
367 if (object_id >= 0x138 && object_id <= 0x13B) {
380 if (object_id == 0xF80) {
383 if (object_id == 0xF81) {
386 if (object_id == 0xF82) {
392 if (object_id == 0xFB1 || object_id == 0xFB2) {
396 if (object_id == 0xF94 || object_id == 0xFCE ||
397 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
398 (object_id >= 0xFEC && object_id <= 0xFED)) {
403 if (object_id == 0xFC8 || object_id == 0xFE6 || object_id == 0xFEB ||
404 object_id == 0xFFA) {
408 if (object_id == 0xF95 || object_id == 0xFF2 || object_id == 0xFFB) {
412 if ((object_id >= 0xF9B && object_id <= 0xF9D) || object_id == 0xFB3) {
416 if ((object_id >= 0xF9E && object_id <= 0xFA1) ||
417 (object_id >= 0xFA6 && object_id <= 0xFA9)) {
421 if (object_id == 0xFAA || object_id == 0xFAD || object_id == 0xFAE ||
422 (object_id >= 0xFB4 && object_id <= 0xFB9) || object_id == 0xFCB ||
423 object_id == 0xFCC || object_id == 0xFD4 || object_id == 0xFE2 ||
424 object_id == 0xFF4 || object_id == 0xFF6 || object_id == 0xFF7) {
428 if (object_id == 0xFCD || object_id == 0xFDD) {
432 if (object_id == 0xFD5 || object_id == 0xFDB) {
436 if (object_id >= 0xFE0 && object_id <= 0xFE1) {
440 if (object_id == 0xFE9 || object_id == 0xFEA || object_id == 0xFEE ||
441 object_id == 0xFEF) {
445 if (object_id == 0xFF0) {
448 if (object_id == 0xFF1) {
452 if (object_id == 0xFF8) {
456 if (object_id == 0xFF9) {
465 if (object_id >= 0xF80) {
467 }
else if (object_id >= 0x100) {
481 return GetSubtype1TileCount(object_id);
497 if (object_id == 0x00) {
502 }
else if (object_id >= 0x01 && object_id <= 0x02) {
507 }
else if (object_id >= 0x03 && object_id <= 0x04) {
512 }
else if (object_id >= 0x05 && object_id <= 0x06) {
514 info.
routine_name =
"Rightwards2x4spaced4_1to16_BothBG";
518 }
else if (object_id >= 0x07 && object_id <= 0x08) {
523 }
else if (object_id == 0x09) {
528 }
else if (object_id >= 0x0A && object_id <= 0x0B) {
533 }
else if (object_id >= 0x0C && object_id <= 0x0D) {
539 }
else if (object_id >= 0x0E && object_id <= 0x0F) {
545 }
else if (object_id >= 0x10 && object_id <= 0x11) {
551 }
else if (object_id >= 0x12 && object_id <= 0x13) {
557 }
else if (object_id >= 0x14 && object_id <= 0x15) {
563 }
else if (object_id >= 0x16 && object_id <= 0x17) {
569 }
else if (object_id >= 0x18 && object_id <= 0x19) {
575 }
else if (object_id >= 0x1A && object_id <= 0x1B) {
581 }
else if (object_id >= 0x1C && object_id <= 0x1D) {
587 }
else if (object_id >= 0x1E && object_id <= 0x1F) {
593 }
else if (object_id == 0x20) {
599 }
else if (object_id == 0x21) {
604 }
else if (object_id == 0x22) {
609 }
else if (object_id >= 0x23 && object_id <= 0x24) {
614 }
else if (object_id >= 0x25 && object_id <= 0x26) {
619 }
else if (object_id == 0x27) {
621 info.
routine_name =
"RightwardsTopCorners1x2_1to16_plus13";
624 }
else if (object_id == 0x28) {
627 info.
routine_name =
"RightwardsBottomCorners1x2_1to16_plus13";
630 }
else if (object_id >= 0x29 && object_id <= 0x2E) {
635 }
else if (object_id == 0x2F) {
637 info.
routine_name =
"RightwardsTopCorners1x2_1to16_plus13";
640 }
else if (object_id == 0x30) {
643 info.
routine_name =
"RightwardsBottomCorners1x2_1to16_plus13";
646 }
else if (object_id >= 0x31 && object_id <= 0x32) {
650 }
else if (object_id == 0x33) {
655 }
else if (object_id == 0x34) {
660 }
else if (object_id == 0x35) {
664 }
else if (object_id >= 0x36 && object_id <= 0x37) {
669 }
else if (object_id == 0x38) {
674 }
else if (object_id == 0x39 || object_id == 0x3D) {
679 }
else if (object_id >= 0x3A && object_id <= 0x3B) {
684 }
else if (object_id == 0x3C) {
686 info.
routine_name =
"RightwardsDoubled2x2spaced2_1to16";
689 }
else if (object_id == 0x3E) {
694 }
else if (object_id >= 0x3F && object_id <= 0x40) {
700 }
else if (object_id >= 0x40 && object_id <= 0x4F) {
705 }
else if (object_id >= 0x60 && object_id <= 0x6F) {
707 if (object_id == 0x60) {
712 }
else if (object_id >= 0x61 && object_id <= 0x62) {
717 }
else if (object_id >= 0x63 && object_id <= 0x64) {
723 }
else if (object_id >= 0x65 && object_id <= 0x66) {
728 }
else if (object_id >= 0x67 && object_id <= 0x68) {
733 }
else if (object_id == 0x69) {
738 }
else if (object_id >= 0x6A && object_id <= 0x6B) {
743 }
else if (object_id == 0x6C) {
746 info.
routine_name =
"DownwardsLeftCorners2x1_1to16_plus12";
749 }
else if (object_id == 0x6D) {
752 info.
routine_name =
"DownwardsRightCorners2x1_1to16_plus12";
760 }
else if (object_id >= 0x100 && object_id <= 0x10F) {
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 kRoomObjectSubtype3
constexpr int kRoomObjectSubtype1
constexpr int kRoomObjectSubtype2
constexpr int kRoomObjectTileAddress
Draw routine information for object rendering.
Object routine information.
bool is_orientation_dependent
Object size and orientation information.
Object subtype information.