yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_parser.cc
Go to the documentation of this file.
1#include "object_parser.h"
2
3#include <algorithm>
4#include <cstring>
5
6#include "absl/strings/str_format.h"
7#include "util/log.h"
9
10// ROM addresses for object data are defined in room_object.h:
11// - kRoomObjectSubtype1 = 0x8000 (SNES $01:8000)
12// - kRoomObjectSubtype2 = 0x83F0 (SNES $01:83F0)
13// - kRoomObjectSubtype3 = 0x84F0 (SNES $01:84F0)
14// - kRoomObjectTileAddress = 0x1B52 (SNES $00:9B52)
15
16// Subtype 1 tile count lookup table (from ZScream's DungeonObjectData.cs)
17// Each entry specifies how many tiles to read for that object ID (0x00-0xF7)
18// Index directly by (object_id & 0xFF) for subtype 1 objects
19// clang-format off
20static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
21 4, 8, 8, 8, 8, 8, 8, 4, 4, 5, 5, 5, 5, 5, 5, 5, // 0x00-0x0F
22 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0x10-0x1F
23 5, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, // 0x20-0x2F
24 6, 1, 1, 16, 1, 1, 16, 16, 6, 8, 12, 12, 4, 8, 4, 3, // 0x30-0x3F
25 3, 3, 3, 3, 3, 3, 3, 0, 0, 8, 8, 4, 9, 16, 16, 16, // 0x40-0x4F
26 1, 18, 18, 4, 1, 8, 8, 1, 1, 1, 1, 18, 18, 15, 4, 3, // 0x50-0x5F
27 4, 8, 8, 8, 8, 8, 8, 4, 4, 3, 1, 1, 6, 6, 1, 1, // 0x60-0x6F
28 16, 1, 1, 16, 16, 8, 16, 16, 4, 1, 1, 4, 1, 4, 1, 8, // 0x70-0x7F
29 8, 12, 12, 12, 12, 18, 18, 8, 12, 4, 3, 3, 3, 1, 1, 6, // 0x80-0x8F
30 8, 8, 4, 4, 16, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90-0x9F
31 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xA0-0xAF
32 1, 1, 16, 3, 3, 8, 8, 8, 4, 4, 16, 4, 4, 4, 1, 1, // 0xB0-0xBF
33 1, 68, 1, 1, 8, 8, 8, 8, 8, 8, 8, 1, 1, 28, 28, 1, // 0xC0-0xCF
34 1, 8, 8, 0, 0, 0, 0, 1, 8, 8, 8, 8, 21, 16, 4, 8, // 0xD0-0xDF
35 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, // 0xE0-0xEF
36 1, 1, 1, 1, 1, 1, 1, 1 // 0xF0-0xF7
37};
38// clang-format on
39
40// Helper function to get tile count for Subtype 1 objects
41static inline int GetSubtype1TileCount(int object_id) {
42 int index = object_id & 0xFF;
43 if (index < 0xF8) {
44 int count = kSubtype1TileLengths[index];
45 return (count > 0) ? count : 8; // Default to 8 if table has 0
46 }
47 return 8; // Default for IDs >= 0xF8
48}
49
50namespace yaze {
51namespace zelda3 {
52
53absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseObject(
54 int16_t object_id) {
55 if (rom_ == nullptr) {
56 return absl::InvalidArgumentError("ROM is null");
57 }
58
59 // Validate object ID is non-negative
60 if (object_id < 0) {
61 return absl::InvalidArgumentError(
62 absl::StrFormat("Invalid object ID: %d", object_id));
63 }
64
65 int subtype = DetermineSubtype(object_id);
66
67 switch (subtype) {
68 case 1:
69 return ParseSubtype1(object_id);
70 case 2:
71 return ParseSubtype2(object_id);
72 case 3:
73 return ParseSubtype3(object_id);
74 default:
75 return absl::InvalidArgumentError(
76 absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
77 }
78}
79
80absl::StatusOr<ObjectRoutineInfo> ObjectParser::ParseObjectRoutine(
81 int16_t object_id) {
82 if (rom_ == nullptr) {
83 return absl::InvalidArgumentError("ROM is null");
84 }
85
86 auto subtype_info = GetObjectSubtype(object_id);
87 if (!subtype_info.ok()) {
88 return subtype_info.status();
89 }
90
91 ObjectRoutineInfo routine_info;
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;
95 routine_info.is_repeatable = true;
96 routine_info.is_orientation_dependent = true;
97
98 return routine_info;
99}
100
101absl::StatusOr<ObjectSubtypeInfo> ObjectParser::GetObjectSubtype(
102 int16_t object_id) {
104 info.subtype = DetermineSubtype(object_id);
105
106 switch (info.subtype) {
107 case 1: {
108 int index = object_id & 0xFF;
109 info.subtype_ptr = kRoomObjectSubtype1 + (index * 2);
110 info.routine_ptr = kRoomObjectSubtype1 + 0x200 + (index * 2);
111 info.max_tile_count = GetSubtype1TileCount(object_id); // Use lookup table
112 break;
113 }
114 case 2: {
115 // Type 2 objects: 0x100-0x13F (64 objects only)
116 // Index mask 0x3F ensures we stay within 64-entry table bounds
117 int index = (object_id - 0x100) & 0x3F;
118 info.subtype_ptr = kRoomObjectSubtype2 + (index * 2);
119 // Routine table starts 128 bytes (64 entries * 2 bytes) after data table
120 info.routine_ptr = kRoomObjectSubtype2 + 0x80 + (index * 2);
121 info.max_tile_count = 8;
122 break;
123 }
124 case 3: {
125 // Type 3 object IDs are 0xF80-0xFFF (128 objects)
126 // Table index should be 0-127
127 int index = (object_id - 0xF80) & 0x7F;
128 info.subtype_ptr = kRoomObjectSubtype3 + (index * 2);
129 info.routine_ptr = kRoomObjectSubtype3 + 0x100 + (index * 2);
130 info.max_tile_count = 8;
131 break;
132 }
133 default:
134 return absl::InvalidArgumentError(
135 absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
136 }
137
138 return info;
139}
140
141absl::StatusOr<ObjectSizeInfo> ObjectParser::ParseObjectSize(
142 int16_t object_id, uint8_t size_byte) {
143 ObjectSizeInfo info;
144
145 // Extract size bits (0-3 for X, 4-7 for Y)
146 int size_x = size_byte & 0x03;
147 int size_y = (size_byte >> 2) & 0x03;
148
149 info.width_tiles = (size_x + 1) * 2; // Convert to tile count
150 info.height_tiles = (size_y + 1) * 2;
151
152 // Determine orientation based on object ID and size
153 // This is a heuristic based on the object naming patterns
154 if (object_id >= 0x80 && object_id <= 0xFF) {
155 // Objects 0x80-0xFF are typically vertical
156 info.is_horizontal = false;
157 } else {
158 // Objects 0x00-0x7F are typically horizontal
159 info.is_horizontal = true;
160 }
161
162 // Determine if object is repeatable
163 info.is_repeatable = (size_byte != 0);
164 info.repeat_count = size_byte == 0 ? 32 : size_byte;
165
166 return info;
167}
168
169absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype1(
170 int16_t object_id) {
171 int index = object_id & 0xFF;
172 int tile_ptr = kRoomObjectSubtype1 + (index * 2);
173
174 if (tile_ptr + 1 >= (int)rom_->size()) {
175 return absl::OutOfRangeError(
176 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
177 }
178
179 // Read tile data pointer (16-bit little-endian offset)
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); // Signed offset!
183 int tile_data_ptr = kRoomObjectTileAddress + offset;
184
185 // DEBUG: Always log wall objects 0x61 and 0x62 to verify tile data differs
186 bool is_wall_object = (object_id == 0x61 || object_id == 0x62);
187 static int debug_count = 0;
188 if (debug_count < 10 || is_wall_object) {
189 printf("[ParseSubtype1] obj=0x%02X%s: tile_ptr=0x%04X (SNES $01:%04X)\n",
190 object_id, is_wall_object ? " (WALL)" : "", tile_ptr, tile_ptr);
191 printf(" ROM[0x%04X..0x%04X] = 0x%02X 0x%02X -> offset=%d (0x%04X)\n",
192 tile_ptr, tile_ptr+1, low, high, offset, (uint16_t)offset);
193 printf(" tile_data_ptr = 0x%04X + 0x%04X = 0x%04X (SNES $00:%04X)\n",
194 kRoomObjectTileAddress, (uint16_t)offset, tile_data_ptr,
195 tile_data_ptr + 0x8000);
196
197 // Show all 8 tile words for wall objects, first 4 for others
198 int num_tiles = is_wall_object ? 8 : 4;
199 // Fix: Check for negative tile_data_ptr to prevent SIGSEGV on corrupted ROMs
200 if (tile_data_ptr >= 0 && tile_data_ptr + (num_tiles * 2) < (int)rom_->size()) {
201 printf(" Tile data at 0x%04X: ", tile_data_ptr);
202 for (int i = 0; i < num_tiles; i++) {
203 uint16_t tw = rom_->data()[tile_data_ptr + i*2] |
204 (rom_->data()[tile_data_ptr + i*2 + 1] << 8);
205 uint16_t tid = tw & 0x3FF;
206 uint8_t pal = (tw >> 10) & 0x07;
207 bool hflip = (tw >> 14) & 0x01;
208 bool vflip = (tw >> 15) & 0x01;
209 printf("$%04X(id=%d,p=%d,h:%d,v:%d) ", tw, tid, pal, hflip, vflip);
210 if (i == 3 && is_wall_object) printf("\n ");
211 }
212 printf("\n");
213 } else {
214 printf(" Tile data at 0x%04X: <OUT OF BOUNDS>\n", tile_data_ptr);
215 }
216 if (!is_wall_object) debug_count++;
217 }
218
219 // Use lookup table for correct tile count per object ID
220 int tile_count = GetSubtype1TileCount(object_id);
221 return ReadTileData(tile_data_ptr, tile_count);
222}
223
224absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype2(
225 int16_t object_id) {
226 // Type 2 objects: 0x100-0x13F (64 objects only)
227 int index = (object_id - 0x100) & 0x3F;
228 int tile_ptr = kRoomObjectSubtype2 + (index * 2);
229
230 if (tile_ptr + 1 >= (int)rom_->size()) {
231 return absl::OutOfRangeError(
232 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
233 }
234
235 // Read tile data pointer
236 uint8_t low = rom_->data()[tile_ptr];
237 uint8_t high = rom_->data()[tile_ptr + 1];
238 int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
239
240 // Determine tile count based on object ID
241 int tile_count = GetSubtype2TileCount(object_id);
242
243 // DEBUG: Log corner tile loading (0x100-0x103)
244 bool is_corner = (object_id >= 0x100 && object_id <= 0x103);
245 if (is_corner) {
246 printf("[ParseSubtype2] CORNER obj=0x%03X: index=%d tile_ptr=0x%04X\n",
247 object_id, index, tile_ptr);
248 printf(" ROM[0x%04X..0x%04X] = 0x%02X 0x%02X -> offset=0x%04X\n",
249 tile_ptr, tile_ptr+1, low, high, (high << 8) | low);
250 printf(" tile_data_ptr = 0x%04X + 0x%04X = 0x%04X (tile_count=%d)\n",
251 kRoomObjectTileAddress, (high << 8) | low, tile_data_ptr, tile_count);
252
253 // Show first 4 tile words
254 if (tile_data_ptr >= 0 && tile_data_ptr + 8 < (int)rom_->size()) {
255 printf(" First 4 tiles: ");
256 for (int i = 0; i < 4; i++) {
257 uint16_t tw = rom_->data()[tile_data_ptr + i*2] |
258 (rom_->data()[tile_data_ptr + i*2 + 1] << 8);
259 uint16_t tid = tw & 0x3FF;
260 printf("$%04X(id=%d) ", tw, tid);
261 }
262 printf("\n");
263 }
264 fflush(stdout);
265 }
266
267 return ReadTileData(tile_data_ptr, tile_count);
268}
269
270absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype3(
271 int16_t object_id) {
272 // Type 3 object IDs are 0xF80-0xFFF (128 objects)
273 // Table index should be 0-127, calculated by subtracting base offset 0xF80
274 int index = (object_id - 0xF80) & 0x7F;
275 int tile_ptr = kRoomObjectSubtype3 + (index * 2);
276
277 if (tile_ptr + 1 >= (int)rom_->size()) {
278 return absl::OutOfRangeError(
279 absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
280 }
281
282 // Read tile data pointer
283 uint8_t low = rom_->data()[tile_ptr];
284 uint8_t high = rom_->data()[tile_ptr + 1];
285 int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
286
287 // Determine tile count based on object ID
288 int tile_count = GetSubtype3TileCount(object_id);
289 return ReadTileData(tile_data_ptr, tile_count);
290}
291
292absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ReadTileData(
293 int address, int tile_count) {
294 // Each tile is stored as a 16-bit word (2 bytes), not 8 bytes!
295 // ZScream: tiles.Add(new Tile(ROM.DATA[pos + ((i * 2))], ROM.DATA[pos + ((i *
296 // 2)) + 1]));
297 if (address < 0 || address + (tile_count * 2) >= (int)rom_->size()) {
298 return absl::OutOfRangeError(
299 absl::StrFormat("Tile data address out of range: %#06x", address));
300 }
301
302 std::vector<gfx::TileInfo> tiles;
303 tiles.reserve(tile_count);
304
305 // DEBUG: Log first tile read
306 static int debug_read_count = 0;
307 bool should_log = (debug_read_count < 3);
308
309 for (int i = 0; i < tile_count; i++) {
310 int tile_offset = address + (i * 2); // 2 bytes per tile word
311
312 // Read 1 word (2 bytes) per tile - this is the SNES tile format
313 uint16_t tile_word =
314 rom_->data()[tile_offset] | (rom_->data()[tile_offset + 1] << 8);
315
316 auto tile_info = gfx::WordToTileInfo(tile_word);
317 tiles.push_back(tile_info);
318
319 // DEBUG: Log first few tiles
320 if (should_log && i < 4) {
321 printf(
322 "[ObjectParser] ReadTile[%d]: addr=0x%06X word=0x%04X → id=0x%03X "
323 "pal=%d mirror=(h:%d,v:%d)\n",
324 i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
325 tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
326 }
327 }
328
329 if (should_log) {
330 printf(
331 "[ObjectParser] ReadTileData: addr=0x%06X count=%d → loaded %zu "
332 "tiles\n",
333 address, tile_count, tiles.size());
334 debug_read_count++;
335 }
336
337 return tiles;
338}
339
340int ObjectParser::GetSubtype2TileCount(int16_t object_id) const {
341 // 4x4 corners (0x100-0x10F): 16 tiles (32 bytes)
342 // These are RoomDraw_4x4 and RoomDraw_4x4Corner_BothBG routines
343 if (object_id >= 0x100 && object_id <= 0x10F) {
344 return 16;
345 }
346 // Weird corners (0x110-0x117): 12 tiles (24 bytes)
347 // These are RoomDraw_WeirdCornerBottom_BothBG and RoomDraw_WeirdCornerTop_BothBG
348 if (object_id >= 0x110 && object_id <= 0x117) {
349 return 12;
350 }
351 // Default: 8 tiles for other subtype 2 objects
352 return 8;
353}
354
355int ObjectParser::GetSubtype3TileCount(int16_t object_id) const {
356 // BigChest (0xFB1 = ASM 0x231) and OpenBigChest (0xFB2 = ASM 0x232): 12 tiles
357 // These use RoomDraw_1x3N_rightwards with N=4 (4 columns × 3 rows)
358 if (object_id == 0xFB1 || object_id == 0xFB2) {
359 return 12;
360 }
361 // TableRock4x3 variants (0xF94, 0xFCE, 0xFE7-0xFE8, 0xFEC-0xFED): 12 tiles
362 if (object_id == 0xF94 || object_id == 0xFCE ||
363 (object_id >= 0xFE7 && object_id <= 0xFE8) ||
364 (object_id >= 0xFEC && object_id <= 0xFED)) {
365 return 12;
366 }
367 // 4x4 pattern objects: 16 tiles
368 // (0xFC8 = 0x248, 0xFE6 = 0x266, 0xFEB = 0x26B, 0xFFA = 0x27A)
369 if (object_id == 0xFC8 || object_id == 0xFE6 ||
370 object_id == 0xFEB || object_id == 0xFFA) {
371 return 16;
372 }
373 // Default: 8 tiles for most subtype 3 objects
374 return 8;
375}
376
377int ObjectParser::DetermineSubtype(int16_t object_id) const {
378 // Type 3 IDs from decoding are 0xF80-0xFFF (b3 0xF8-0xFF shifted).
379 if (object_id >= 0xF80) {
380 return 3;
381 } else if (object_id >= 0x100) {
382 return 2;
383 } else {
384 return 1;
385 }
386}
387
389 ObjectDrawInfo info;
390
391 // Map object ID to draw routine based on ZScream's subtype1_routines table
392 // This is based on the DungeonObjectData.cs mapping from ZScream
393
394 if (object_id == 0x00) {
395 info.draw_routine_id = 0; // RoomDraw_Rightwards2x2_1to15or32
396 info.routine_name = "Rightwards2x2_1to15or32";
397 info.tile_count = 4;
398 info.is_horizontal = true;
399 } else if (object_id >= 0x01 && object_id <= 0x02) {
400 info.draw_routine_id = 1; // RoomDraw_Rightwards2x4_1to15or26
401 info.routine_name = "Rightwards2x4_1to15or26";
402 info.tile_count = 8;
403 info.is_horizontal = true;
404 } else if (object_id >= 0x03 && object_id <= 0x04) {
405 info.draw_routine_id = 2; // RoomDraw_Rightwards2x4spaced4_1to16
406 info.routine_name = "Rightwards2x4spaced4_1to16";
407 info.tile_count = 8;
408 info.is_horizontal = true;
409 } else if (object_id >= 0x05 && object_id <= 0x06) {
410 info.draw_routine_id = 3; // RoomDraw_Rightwards2x4spaced4_1to16_BothBG
411 info.routine_name = "Rightwards2x4spaced4_1to16_BothBG";
412 info.tile_count = 8;
413 info.is_horizontal = true;
414 info.both_layers = true;
415 } else if (object_id >= 0x07 && object_id <= 0x08) {
416 info.draw_routine_id = 4; // RoomDraw_Rightwards2x2_1to16
417 info.routine_name = "Rightwards2x2_1to16";
418 info.tile_count = 4;
419 info.is_horizontal = true;
420 } else if (object_id == 0x09) {
421 info.draw_routine_id = 5; // RoomDraw_DiagonalAcute_1to16
422 info.routine_name = "DiagonalAcute_1to16";
423 info.tile_count = 5;
424 info.is_horizontal = false;
425 } else if (object_id >= 0x0A && object_id <= 0x0B) {
426 info.draw_routine_id = 6; // RoomDraw_DiagonalGrave_1to16
427 info.routine_name = "DiagonalGrave_1to16";
428 info.tile_count = 5;
429 info.is_horizontal = false;
430 } else if (object_id >= 0x0C && object_id <= 0x0D) {
431 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
432 info.routine_name = "DiagonalAcute_1to16_BothBG";
433 info.tile_count = 5;
434 info.is_horizontal = false;
435 info.both_layers = true;
436 } else if (object_id >= 0x0E && object_id <= 0x0F) {
437 info.draw_routine_id = 18; // RoomDraw_DiagonalGrave_1to16_BothBG
438 info.routine_name = "DiagonalGrave_1to16_BothBG";
439 info.tile_count = 5;
440 info.is_horizontal = false;
441 info.both_layers = true;
442 } else if (object_id >= 0x10 && object_id <= 0x11) {
443 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
444 info.routine_name = "DiagonalAcute_1to16_BothBG";
445 info.tile_count = 5;
446 info.is_horizontal = false;
447 info.both_layers = true;
448 } else if (object_id >= 0x12 && object_id <= 0x13) {
449 info.draw_routine_id = 18; // RoomDraw_DiagonalGrave_1to16_BothBG
450 info.routine_name = "DiagonalGrave_1to16_BothBG";
451 info.tile_count = 5;
452 info.is_horizontal = false;
453 info.both_layers = true;
454 } else if (object_id >= 0x14 && object_id <= 0x15) {
455 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
456 info.routine_name = "DiagonalAcute_1to16_BothBG";
457 info.tile_count = 5;
458 info.is_horizontal = false;
459 info.both_layers = true;
460 } else if (object_id >= 0x16 && object_id <= 0x17) {
461 info.draw_routine_id = 18; // RoomDraw_DiagonalGrave_1to16_BothBG
462 info.routine_name = "DiagonalGrave_1to16_BothBG";
463 info.tile_count = 5;
464 info.is_horizontal = false;
465 info.both_layers = true;
466 } else if (object_id >= 0x18 && object_id <= 0x19) {
467 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
468 info.routine_name = "DiagonalAcute_1to16_BothBG";
469 info.tile_count = 5;
470 info.is_horizontal = false;
471 info.both_layers = true;
472 } else if (object_id >= 0x1A && object_id <= 0x1B) {
473 info.draw_routine_id = 18; // RoomDraw_DiagonalGrave_1to16_BothBG
474 info.routine_name = "DiagonalGrave_1to16_BothBG";
475 info.tile_count = 5;
476 info.is_horizontal = false;
477 info.both_layers = true;
478 } else if (object_id >= 0x1C && object_id <= 0x1D) {
479 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
480 info.routine_name = "DiagonalAcute_1to16_BothBG";
481 info.tile_count = 5;
482 info.is_horizontal = false;
483 info.both_layers = true;
484 } else if (object_id >= 0x1E && object_id <= 0x1F) {
485 info.draw_routine_id = 18; // RoomDraw_DiagonalGrave_1to16_BothBG
486 info.routine_name = "DiagonalGrave_1to16_BothBG";
487 info.tile_count = 5;
488 info.is_horizontal = false;
489 info.both_layers = true;
490 } else if (object_id == 0x20) {
491 info.draw_routine_id = 17; // RoomDraw_DiagonalAcute_1to16_BothBG
492 info.routine_name = "DiagonalAcute_1to16_BothBG";
493 info.tile_count = 5;
494 info.is_horizontal = false;
495 info.both_layers = true;
496 } else if (object_id == 0x21) {
497 info.draw_routine_id = 20; // RoomDraw_Rightwards1x2_1to16_plus2
498 info.routine_name = "Rightwards1x2_1to16_plus2";
499 info.tile_count = 2;
500 info.is_horizontal = true;
501 } else if (object_id == 0x22) {
502 info.draw_routine_id = 20; // RoomDraw_Rightwards1x2_1to16_plus2
503 info.routine_name = "Rightwards1x2_1to16_plus2";
504 info.tile_count = 2;
505 info.is_horizontal = true;
506 } else if (object_id >= 0x23 && object_id <= 0x24) {
507 info.draw_routine_id = 21; // RoomDraw_RightwardsHasEdge1x1_1to16_plus3
508 info.routine_name = "RightwardsHasEdge1x1_1to16_plus3";
509 info.tile_count = 1;
510 info.is_horizontal = true;
511 } else if (object_id >= 0x25 && object_id <= 0x26) {
512 info.draw_routine_id = 22; // RoomDraw_RightwardsHasEdge1x1_1to16_plus2
513 info.routine_name = "RightwardsHasEdge1x1_1to16_plus2";
514 info.tile_count = 1;
515 info.is_horizontal = true;
516 } else if (object_id == 0x27) {
517 info.draw_routine_id = 23; // RoomDraw_RightwardsTopCorners1x2_1to16_plus13
518 info.routine_name = "RightwardsTopCorners1x2_1to16_plus13";
519 info.tile_count = 2;
520 info.is_horizontal = true;
521 } else if (object_id == 0x28) {
522 info.draw_routine_id = 24; // RoomDraw_RightwardsBottomCorners1x2_1to16_plus13
523 info.routine_name = "RightwardsBottomCorners1x2_1to16_plus13";
524 info.tile_count = 2;
525 info.is_horizontal = true;
526 } else if (object_id >= 0x29 && object_id <= 0x2E) {
527 info.draw_routine_id = 22; // RoomDraw_RightwardsHasEdge1x1_1to16_plus2
528 info.routine_name = "RightwardsHasEdge1x1_1to16_plus2";
529 info.tile_count = 1;
530 info.is_horizontal = true;
531 } else if (object_id == 0x2F) {
532 info.draw_routine_id = 23; // RoomDraw_RightwardsTopCorners1x2_1to16_plus13
533 info.routine_name = "RightwardsTopCorners1x2_1to16_plus13";
534 info.tile_count = 2;
535 info.is_horizontal = true;
536 } else if (object_id == 0x30) {
537 info.draw_routine_id = 24; // RoomDraw_RightwardsBottomCorners1x2_1to16_plus13
538 info.routine_name = "RightwardsBottomCorners1x2_1to16_plus13";
539 info.tile_count = 2;
540 info.is_horizontal = true;
541 } else if (object_id >= 0x31 && object_id <= 0x32) {
542 info.draw_routine_id = 14; // CustomDraw
543 info.routine_name = "CustomDraw";
544 info.tile_count = 1;
545 } else if (object_id == 0x33) {
546 info.draw_routine_id = 16; // RoomDraw_Rightwards4x4_1to16
547 info.routine_name = "Rightwards4x4_1to16";
548 info.tile_count = 16;
549 info.is_horizontal = true;
550 } else if (object_id == 0x34) {
551 info.draw_routine_id = 25; // RoomDraw_Rightwards1x1Solid_1to16_plus3
552 info.routine_name = "Rightwards1x1Solid_1to16_plus3";
553 info.tile_count = 1;
554 info.is_horizontal = true;
555 } else if (object_id == 0x35) {
556 info.draw_routine_id = 26; // RoomDraw_DoorSwitcherer
557 info.routine_name = "DoorSwitcherer";
558 info.tile_count = 1;
559 } else if (object_id >= 0x36 && object_id <= 0x37) {
560 info.draw_routine_id = 27; // RoomDraw_RightwardsDecor4x4spaced2_1to16
561 info.routine_name = "RightwardsDecor4x4spaced2_1to16";
562 info.tile_count = 16;
563 info.is_horizontal = true;
564 } else if (object_id == 0x38) {
565 info.draw_routine_id = 28; // RoomDraw_RightwardsStatue2x3spaced2_1to16
566 info.routine_name = "RightwardsStatue2x3spaced2_1to16";
567 info.tile_count = 6;
568 info.is_horizontal = true;
569 } else if (object_id == 0x39 || object_id == 0x3D) {
570 info.draw_routine_id = 29; // RoomDraw_RightwardsPillar2x4spaced4_1to16
571 info.routine_name = "RightwardsPillar2x4spaced4_1to16";
572 info.tile_count = 8;
573 info.is_horizontal = true;
574 } else if (object_id >= 0x3A && object_id <= 0x3B) {
575 info.draw_routine_id = 30; // RoomDraw_RightwardsDecor4x3spaced4_1to16
576 info.routine_name = "RightwardsDecor4x3spaced4_1to16";
577 info.tile_count = 12;
578 info.is_horizontal = true;
579 } else if (object_id == 0x3C) {
580 info.draw_routine_id = 31; // RoomDraw_RightwardsDoubled2x2spaced2_1to16
581 info.routine_name = "RightwardsDoubled2x2spaced2_1to16";
582 info.tile_count = 8;
583 info.is_horizontal = true;
584 } else if (object_id == 0x3E) {
585 info.draw_routine_id = 32; // RoomDraw_RightwardsDecor2x2spaced12_1to16
586 info.routine_name = "RightwardsDecor2x2spaced12_1to16";
587 info.tile_count = 4;
588 info.is_horizontal = true;
589 } else if (object_id >= 0x3F && object_id <= 0x40) {
590 info.draw_routine_id = 22; // RoomDraw_RightwardsHasEdge1x1_1to16_plus2 (variant)
591 info.routine_name = "RightwardsHasEdge1x1_1to16_plus2";
592 info.tile_count = 1;
593 info.is_horizontal = true;
594 } else if (object_id >= 0x40 && object_id <= 0x4F) {
595 info.draw_routine_id = 19; // RoomDraw_Corner4x4 (Type 2 corners)
596 info.routine_name = "Corner4x4";
597 info.tile_count = 16;
598 info.is_horizontal = true;
599 } else if (object_id >= 0x60 && object_id <= 0x6F) {
600 // Vertical objects (Subtype 1)
601 if (object_id == 0x60) {
602 info.draw_routine_id = 7; // RoomDraw_Downwards2x2_1to15or32
603 info.routine_name = "Downwards2x2_1to15or32";
604 info.tile_count = 4;
605 info.is_horizontal = false;
606 } else if (object_id >= 0x61 && object_id <= 0x62) {
607 info.draw_routine_id = 8; // RoomDraw_Downwards4x2_1to15or26
608 info.routine_name = "Downwards4x2_1to15or26";
609 info.tile_count = 8;
610 info.is_horizontal = false;
611 } else if (object_id >= 0x63 && object_id <= 0x64) {
612 info.draw_routine_id = 9; // RoomDraw_Downwards4x2_1to16_BothBG
613 info.routine_name = "Downwards4x2_1to16_BothBG";
614 info.tile_count = 8;
615 info.is_horizontal = false;
616 info.both_layers = true;
617 } else if (object_id >= 0x65 && object_id <= 0x66) {
618 info.draw_routine_id = 10; // RoomDraw_DownwardsDecor4x2spaced4_1to16
619 info.routine_name = "DownwardsDecor4x2spaced4_1to16";
620 info.tile_count = 8;
621 info.is_horizontal = false;
622 } else if (object_id >= 0x67 && object_id <= 0x68) {
623 info.draw_routine_id = 11; // RoomDraw_Downwards2x2_1to16
624 info.routine_name = "Downwards2x2_1to16";
625 info.tile_count = 4;
626 info.is_horizontal = false;
627 } else if (object_id == 0x69) {
628 info.draw_routine_id = 12; // RoomDraw_DownwardsHasEdge1x1_1to16_plus3
629 info.routine_name = "DownwardsHasEdge1x1_1to16_plus3";
630 info.tile_count = 1;
631 info.is_horizontal = false;
632 } else if (object_id >= 0x6A && object_id <= 0x6B) {
633 info.draw_routine_id = 13; // RoomDraw_DownwardsEdge1x1_1to16
634 info.routine_name = "DownwardsEdge1x1_1to16";
635 info.tile_count = 1;
636 info.is_horizontal = false;
637 } else if (object_id == 0x6C) {
638 info.draw_routine_id = 14; // RoomDraw_DownwardsLeftCorners2x1_1to16_plus12
639 info.routine_name = "DownwardsLeftCorners2x1_1to16_plus12";
640 info.tile_count = 2;
641 info.is_horizontal = false;
642 } else if (object_id == 0x6D) {
643 info.draw_routine_id = 15; // RoomDraw_DownwardsRightCorners2x1_1to16_plus12
644 info.routine_name = "DownwardsRightCorners2x1_1to16_plus12";
645 info.tile_count = 2;
646 info.is_horizontal = false;
647 } else {
648 info.draw_routine_id = 16; // Default
649 info.routine_name = "DefaultSolid";
650 info.tile_count = 1;
651 }
652 } else if (object_id >= 0x100 && object_id <= 0x10F) {
653 info.draw_routine_id = 16; // RoomDraw_Rightwards4x4_1to16 (Type 2)
654 info.routine_name = "Rightwards4x4_1to16";
655 info.tile_count = 16;
656 info.is_horizontal = true;
657 } else {
658 // Default to simple 1x1 solid for unmapped objects
659 info.draw_routine_id = 25; // Use solid block routine (0x34)
660 info.routine_name = "DefaultSolid";
661 info.tile_count = 1;
662 info.is_horizontal = true;
663 }
664
665 return info;
666}
667
668} // namespace zelda3
669} // namespace yaze
auto data() const
Definition rom.h:135
auto size() const
Definition rom.h:134
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 DetermineSubtype(int16_t object_id) const
Determine object subtype from ID.
TileInfo WordToTileInfo(uint16_t word)
Definition snes_tile.cc:321
constexpr int kRoomObjectSubtype3
Definition room_object.h:46
constexpr int kRoomObjectSubtype1
Definition room_object.h:44
constexpr int kRoomObjectSubtype2
Definition room_object.h:45
constexpr int kRoomObjectTileAddress
Definition room_object.h:47
Draw routine information for object rendering.
Object routine information.
Object size and orientation information.
Object subtype information.