yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_dimensions.cc
Go to the documentation of this file.
1#include "object_dimensions.h"
2
4
5namespace yaze {
6namespace zelda3 {
7
9 static ObjectDimensionTable instance;
10 return instance;
11}
12
14 if (!rom || !rom->is_loaded()) {
15 return absl::FailedPreconditionError("ROM not loaded");
16 }
17
18 dimensions_.clear();
20
21 // Parse ROM tables for refinement
25
26 loaded_ = true;
27 return absl::OkStatus();
28}
29
30std::pair<int, int> ObjectDimensionTable::GetBaseDimensions(int object_id) const {
31 auto it = dimensions_.find(object_id);
32 if (it != dimensions_.end()) {
33 return {it->second.base_width, it->second.base_height};
34 }
35 return {2, 2}; // Default 16x16 pixels (2x2 tiles)
36}
37
38std::pair<int, int> ObjectDimensionTable::GetDimensions(int object_id, int size) const {
39 auto it = dimensions_.find(object_id);
40 if (it == dimensions_.end()) {
41 // Unknown object - estimate from size
42 // ASM: When size is 0, default to 32 (not 1)
43 int s = (size == 0) ? 32 : size + 1;
44 return {2 * s, 2};
45 }
46
47 const auto& entry = it->second;
48 int w = entry.base_width;
49 int h = entry.base_height;
50
51 // ASM: GetSize_1to15or32 uses 32 when combined size is 0
52 // This affects variable-size objects like floors
53 int effective_size = size;
54 if (entry.use_32_when_zero && size == 0) {
55 effective_size = 32;
56 }
57
58 switch (entry.extend_dir) {
60 w += effective_size * entry.extend_multiplier;
61 break;
63 h += effective_size * entry.extend_multiplier;
64 break;
66 w += effective_size * entry.extend_multiplier;
67 h += effective_size * entry.extend_multiplier;
68 break;
70 // Diagonals extend both dimensions based on count
71 // Width = base_width + size, Height = base_height + size
72 // ASM: Each iteration draws 5 tiles vertically and advances X by 1
73 // Bounding box height = 5 + (count - 1) = count + 4
74 w += effective_size * entry.extend_multiplier;
75 h += effective_size * entry.extend_multiplier;
76 break;
78 default:
79 break;
80 }
81
82 return {w, h};
83}
84
85std::pair<int, int> ObjectDimensionTable::GetSelectionDimensions(int object_id, int size) const {
86 auto it = dimensions_.find(object_id);
87 if (it == dimensions_.end()) {
88 // Unknown object - use reasonable default based on size
89 int s = std::max(1, size + 1);
90 return {std::min(s * 2, 16), 2}; // Cap at 16 tiles wide for selection
91 }
92
93 const auto& entry = it->second;
94 int w = entry.base_width;
95 int h = entry.base_height;
96
97 // For selection bounds, use actual size (not 32-when-zero)
98 // This gives a more accurate visual selection box
99 int effective_size = size;
100
101 switch (entry.extend_dir) {
103 w += effective_size * entry.extend_multiplier;
104 break;
106 h += effective_size * entry.extend_multiplier;
107 break;
109 w += effective_size * entry.extend_multiplier;
110 h += effective_size * entry.extend_multiplier;
111 break;
113 // Diagonals: both dimensions scale with size
114 w += effective_size * entry.extend_multiplier;
115 h += effective_size * entry.extend_multiplier;
116 break;
118 default:
119 break;
120 }
121
122 // Ensure minimum visible size
123 w = std::max(w, 2);
124 h = std::max(h, 2);
125
126 return {w, h};
127}
128
129std::tuple<int, int, int, int> ObjectDimensionTable::GetHitTestBounds(
130 const RoomObject& obj) const {
131 auto [w, h] = GetDimensions(obj.id_, obj.size_);
132 return {obj.x_, obj.y_, w, h};
133}
134
136 using Dir = DimensionEntry::ExtendDir;
137
138 // ============================================================================
139 // Subtype 1 objects (0x00-0xF7)
140 // ============================================================================
141
142 // 0x00: Ceiling 2x2 - uses GetSize_1to15or32
143 dimensions_[0x00] = {2, 2, Dir::Horizontal, 2, true};
144
145 // 0x01-0x02: Wall 2x4 - uses GetSize_1to15or26
146 for (int id = 0x01; id <= 0x02; id++) {
147 dimensions_[id] = {2, 4, Dir::Horizontal, 2, false}; // Use 26 when zero
148 }
149
150 // 0x03-0x04: Wall 2x4 spaced 4 - GetSize_1to16
151 for (int id = 0x03; id <= 0x04; id++) {
152 dimensions_[id] = {2, 4, Dir::Horizontal, 2, false};
153 }
154
155 // 0x05-0x06: Wall 2x4 spaced 4 BothBG - GetSize_1to16
156 for (int id = 0x05; id <= 0x06; id++) {
157 dimensions_[id] = {2, 4, Dir::Horizontal, 2, false};
158 }
159
160 // 0x07-0x08: Floor 2x2 - GetSize_1to16
161 for (int id = 0x07; id <= 0x08; id++) {
162 dimensions_[id] = {2, 2, Dir::Horizontal, 2, false};
163 }
164
165 // 0x09-0x14: Diagonal walls - non-BothBG (count = size + 7)
166 // Height = count + 4 tiles (5 tiles per column + diagonal extent)
167 for (int id = 0x09; id <= 0x14; id++) {
168 // Diagonal pattern: width = count tiles, height = count + 4 tiles
169 dimensions_[id] = {7, 11, Dir::Diagonal, 1, false}; // base 7 + size*1, height 11 + size*1
170 }
171
172 // 0x15-0x20: Diagonal walls - BothBG (count = size + 6)
173 for (int id = 0x15; id <= 0x20; id++) {
174 dimensions_[id] = {6, 10, Dir::Diagonal, 1, false}; // base 6 + size*1, height 10 + size*1
175 }
176
177 // 0x21: Edge 1x2 +2 (count = size*2 + 1)
178 dimensions_[0x21] = {1, 2, Dir::Horizontal, 2, false};
179
180 // 0x22: Edge 1x1 +3 (count = size + 2)
181 dimensions_[0x22] = {2, 1, Dir::Horizontal, 1, false};
182
183 // 0x23-0x2E: Edge 1x1 +2 (count = size + 1)
184 for (int id = 0x23; id <= 0x2E; id++) {
185 dimensions_[id] = {1, 1, Dir::Horizontal, 1, false};
186 }
187
188 // 0x2F: Top corners 1x2 +13
189 dimensions_[0x2F] = {10, 2, Dir::Horizontal, 1, false};
190
191 // 0x30: Bottom corners 1x2 +13
192 dimensions_[0x30] = {10, 2, Dir::Horizontal, 1, false};
193
194 // 0x31-0x32: Nothing
195 dimensions_[0x31] = {1, 1, Dir::None, 0, false};
196 dimensions_[0x32] = {1, 1, Dir::None, 0, false};
197
198 // 0x33: 4x4 block - GetSize_1to16
199 dimensions_[0x33] = {4, 4, Dir::Horizontal, 4, false};
200
201 // 0x34: Solid 1x1 +3 (count = size + 4)
202 dimensions_[0x34] = {4, 1, Dir::Horizontal, 1, false};
203
204 // 0x35: Door switcherer - fixed 4x4
205 dimensions_[0x35] = {4, 4, Dir::None, 0, false};
206
207 // 0x36-0x37: Decor 4x4 spaced 2 - spacing 6 tiles
208 for (int id = 0x36; id <= 0x37; id++) {
209 dimensions_[id] = {4, 4, Dir::Horizontal, 6, false};
210 }
211
212 // 0x38: Statue 2x3 spaced 2 - spacing 4 tiles
213 dimensions_[0x38] = {2, 3, Dir::Horizontal, 4, false};
214
215 // 0x39: Pillar 2x4 spaced 4 - spacing 6 tiles
216 dimensions_[0x39] = {2, 4, Dir::Horizontal, 6, false};
217
218 // 0x3A-0x3B: Decor 4x3 spaced 4 - spacing 6 tiles
219 for (int id = 0x3A; id <= 0x3B; id++) {
220 dimensions_[id] = {4, 3, Dir::Horizontal, 6, false};
221 }
222
223 // 0x3C: Doubled 2x2 spaced 2 - spacing 6 tiles
224 dimensions_[0x3C] = {4, 2, Dir::Horizontal, 6, false};
225
226 // 0x3D: Pillar 2x4 spaced 4 - spacing 6 tiles
227 dimensions_[0x3D] = {2, 4, Dir::Horizontal, 6, false};
228
229 // 0x3E: Decor 2x2 spaced 12 - spacing 14 tiles
230 dimensions_[0x3E] = {2, 2, Dir::Horizontal, 14, false};
231
232 // 0x3F-0x46: Edge 1x1 +2 (count = size + 1)
233 for (int id = 0x3F; id <= 0x46; id++) {
234 dimensions_[id] = {1, 1, Dir::Horizontal, 1, false};
235 }
236
237 // 0x47: Waterfall47 - 1x5 columns, width = 2 + (size+1)*2
238 dimensions_[0x47] = {4, 5, Dir::Horizontal, 2, false};
239
240 // 0x48: Waterfall48 - 1x3 columns, width = 2 + (size+1)*2
241 dimensions_[0x48] = {4, 3, Dir::Horizontal, 2, false};
242
243 // 0x49-0x4A: Floor Tile 4x2 - GetSize_1to16
244 for (int id = 0x49; id <= 0x4A; id++) {
245 dimensions_[id] = {4, 2, Dir::Horizontal, 4, false};
246 }
247
248 // 0x4B: Decor 2x2 spaced 12 - spacing 14 tiles
249 dimensions_[0x4B] = {2, 2, Dir::Horizontal, 14, false};
250
251 // 0x4C: Bar 4x3 - spacing 6 tiles
252 dimensions_[0x4C] = {4, 3, Dir::Horizontal, 6, false};
253
254 // 0x4D-0x4F: Shelf 4x4 - spacing 6 tiles
255 for (int id = 0x4D; id <= 0x4F; id++) {
256 dimensions_[id] = {4, 4, Dir::Horizontal, 6, false};
257 }
258
259 // 0x50: Line 1x1 +1 (count = size + 2)
260 dimensions_[0x50] = {2, 1, Dir::Horizontal, 1, false};
261
262 // 0x51-0x52: Cannon Hole 4x3 - GetSize_1to16
263 for (int id = 0x51; id <= 0x52; id++) {
264 dimensions_[id] = {4, 3, Dir::Horizontal, 4, false};
265 }
266
267 // 0x53: Floor 2x2 - GetSize_1to16
268 dimensions_[0x53] = {2, 2, Dir::Horizontal, 2, false};
269
270 // 0x54-0x5A: Nothing
271 for (int id = 0x54; id <= 0x5A; id++) {
272 dimensions_[id] = {1, 1, Dir::None, 0, false};
273 }
274
275 // 0x5B-0x5C: Cannon Hole 4x3
276 for (int id = 0x5B; id <= 0x5C; id++) {
277 dimensions_[id] = {4, 3, Dir::Horizontal, 4, false};
278 }
279
280 // 0x5D: Big Rail 1x3 +5 (count = size + 6)
281 dimensions_[0x5D] = {6, 3, Dir::Horizontal, 1, false};
282
283 // 0x5E: Block 2x2 spaced 2
284 dimensions_[0x5E] = {2, 2, Dir::Horizontal, 4, false};
285
286 // 0x5F: Edge 1x1 +23 (count = size + 1 but offset +23)
287 dimensions_[0x5F] = {1, 1, Dir::Horizontal, 1, false};
288
289 // 0x60: Downwards 2x2 - GetSize_1to15or32
290 dimensions_[0x60] = {2, 2, Dir::Vertical, 2, true};
291
292 // 0x61-0x62: Downwards 4x2 - GetSize_1to15or26
293 for (int id = 0x61; id <= 0x62; id++) {
294 dimensions_[id] = {4, 2, Dir::Vertical, 2, false};
295 }
296
297 // 0x63-0x68: Downwards variants - GetSize_1to16
298 for (int id = 0x63; id <= 0x68; id++) {
299 dimensions_[id] = {4, 2, Dir::Vertical, 2, false};
300 }
301
302 // 0x69: Downwards edge +3
303 dimensions_[0x69] = {1, 2, Dir::Vertical, 1, false};
304
305 // 0x6A-0x6B: Downwards edge
306 for (int id = 0x6A; id <= 0x6B; id++) {
307 dimensions_[id] = {1, 1, Dir::Vertical, 1, false};
308 }
309
310 // 0x6C-0x6D: Downwards corners
311 for (int id = 0x6C; id <= 0x6D; id++) {
312 dimensions_[id] = {2, 1, Dir::Vertical, 1, false};
313 }
314
315 // 0x6E-0x6F: Nothing
316 dimensions_[0x6E] = {1, 1, Dir::None, 0, false};
317 dimensions_[0x6F] = {1, 1, Dir::None, 0, false};
318
319 // 0x70: Downwards Floor 4x4
320 dimensions_[0x70] = {4, 4, Dir::Vertical, 4, false};
321
322 // 0x71: Downwards Solid 1x1 +3
323 dimensions_[0x71] = {1, 4, Dir::Vertical, 1, false};
324
325 // 0x72: Nothing
326 dimensions_[0x72] = {1, 1, Dir::None, 0, false};
327
328 // 0x73-0x74: Downwards Decor 4x4 spaced 2
329 for (int id = 0x73; id <= 0x74; id++) {
330 dimensions_[id] = {4, 4, Dir::Vertical, 6, false};
331 }
332
333 // 0x75: Downwards Pillar 2x4 spaced 2
334 dimensions_[0x75] = {2, 4, Dir::Vertical, 6, false};
335
336 // 0x76-0x77: Downwards Decor 3x4 spaced 4
337 for (int id = 0x76; id <= 0x77; id++) {
338 dimensions_[id] = {3, 4, Dir::Vertical, 6, false};
339 }
340
341 // 0x78, 0x7B: Downwards Decor 2x2 spaced 12
342 dimensions_[0x78] = {2, 2, Dir::Vertical, 14, false};
343 dimensions_[0x7B] = {2, 2, Dir::Vertical, 14, false};
344
345 // 0x79-0x7A: Downwards Edge 1x1
346 for (int id = 0x79; id <= 0x7A; id++) {
347 dimensions_[id] = {1, 1, Dir::Vertical, 1, false};
348 }
349
350 // 0x7C: Downwards Line 1x1 +1
351 dimensions_[0x7C] = {1, 2, Dir::Vertical, 1, false};
352
353 // 0x7D: Downwards 2x2
354 dimensions_[0x7D] = {2, 2, Dir::Vertical, 2, false};
355
356 // 0x7E: Nothing
357 dimensions_[0x7E] = {1, 1, Dir::None, 0, false};
358
359 // 0x7F-0x80: Downwards Decor 2x4 spaced 8
360 for (int id = 0x7F; id <= 0x80; id++) {
361 dimensions_[id] = {2, 4, Dir::Vertical, 10, false};
362 }
363
364 // 0x81-0x84: Downwards Decor 3x4 spaced 2
365 for (int id = 0x81; id <= 0x84; id++) {
366 dimensions_[id] = {3, 4, Dir::Vertical, 5, false};
367 }
368
369 // 0x85-0x86: Downwards Cannon Hole 3x6
370 for (int id = 0x85; id <= 0x86; id++) {
371 dimensions_[id] = {3, 6, Dir::Vertical, 6, false};
372 }
373
374 // 0x87: Downwards Pillar 2x4 spaced 2
375 dimensions_[0x87] = {2, 4, Dir::Vertical, 6, false};
376
377 // 0x88: Downwards Big Rail 3x1 +5
378 dimensions_[0x88] = {3, 6, Dir::Vertical, 1, false};
379
380 // 0x89: Downwards Block 2x2 spaced 2
381 dimensions_[0x89] = {2, 2, Dir::Vertical, 4, false};
382
383 // 0x8A-0x8C: Edge variants
384 for (int id = 0x8A; id <= 0x8C; id++) {
385 dimensions_[id] = {1, 1, Dir::Vertical, 1, false};
386 }
387
388 // 0x8D-0x8E: Downwards Edge 1x1
389 for (int id = 0x8D; id <= 0x8E; id++) {
390 dimensions_[id] = {1, 1, Dir::Vertical, 1, false};
391 }
392
393 // 0x8F: Downwards Bar 2x3
394 dimensions_[0x8F] = {2, 3, Dir::Vertical, 3, false};
395
396 // 0x90-0x94: Staircase objects - 4x4 or 4x2
397 for (int id = 0x90; id <= 0x94; id++) {
398 dimensions_[id] = {4, 4, Dir::None, 0, false};
399 }
400
401 // 0x95: Downwards Pots 2x2
402 dimensions_[0x95] = {2, 2, Dir::Vertical, 2, false};
403
404 // 0x96: Downwards Hammer Pegs 2x2
405 dimensions_[0x96] = {2, 2, Dir::Vertical, 2, false};
406
407 // 0x97-0x9F: Nothing
408 for (int id = 0x97; id <= 0x9F; id++) {
409 dimensions_[id] = {1, 1, Dir::None, 0, false};
410 }
411
412 // ============================================================================
413 // Diagonal ceilings (0xA0-0xAC)
414 // ============================================================================
415 // ASM: These have fixed patterns, 4x4 base size
416 // TopLeft: 0xA0, 0xA5, 0xA9
417 for (int id : {0xA0, 0xA5, 0xA9}) {
418 dimensions_[id] = {4, 4, Dir::Diagonal, 4, false};
419 }
420 // BottomLeft: 0xA1, 0xA6, 0xAA
421 for (int id : {0xA1, 0xA6, 0xAA}) {
422 dimensions_[id] = {4, 4, Dir::Diagonal, 4, false};
423 }
424 // TopRight: 0xA2, 0xA7, 0xAB
425 for (int id : {0xA2, 0xA7, 0xAB}) {
426 dimensions_[id] = {4, 4, Dir::Diagonal, 4, false};
427 }
428 // BottomRight: 0xA3, 0xA8, 0xAC
429 for (int id : {0xA3, 0xA8, 0xAC}) {
430 dimensions_[id] = {4, 4, Dir::Diagonal, 4, false};
431 }
432 // BigHole4x4: 0xA4 - extends both directions
433 dimensions_[0xA4] = {4, 4, Dir::Both, 4, true};
434
435 // 0xAD-0xAF, 0xBE-0xBF: Nothing
436 for (int id : {0xAD, 0xAE, 0xAF, 0xBE, 0xBF}) {
437 dimensions_[id] = {1, 1, Dir::None, 0, false};
438 }
439
440 // 0xB0-0xB1: Rightwards Edge 1x1 +7
441 for (int id = 0xB0; id <= 0xB1; id++) {
442 dimensions_[id] = {7, 1, Dir::Horizontal, 1, false};
443 }
444
445 // 0xB2: 4x4 block
446 dimensions_[0xB2] = {4, 4, Dir::Horizontal, 4, false};
447
448 // 0xB3-0xB4: Edge 1x1
449 for (int id = 0xB3; id <= 0xB4; id++) {
450 dimensions_[id] = {1, 1, Dir::Horizontal, 1, false};
451 }
452
453 // 0xB5: Weird 2x4
454 dimensions_[0xB5] = {2, 4, Dir::Vertical, 2, false};
455
456 // 0xB6-0xB7: Rightwards 2x4
457 for (int id = 0xB6; id <= 0xB7; id++) {
458 dimensions_[id] = {2, 4, Dir::Horizontal, 2, false};
459 }
460
461 // 0xB8-0xB9: Rightwards 2x2
462 for (int id = 0xB8; id <= 0xB9; id++) {
463 dimensions_[id] = {2, 2, Dir::Horizontal, 2, true};
464 }
465
466 // 0xBA: 4x4 block
467 dimensions_[0xBA] = {4, 4, Dir::Horizontal, 4, false};
468
469 // 0xBB: Rightwards Block 2x2 spaced 2
470 dimensions_[0xBB] = {2, 2, Dir::Horizontal, 4, false};
471
472 // 0xBC: Rightwards Pots 2x2
473 dimensions_[0xBC] = {2, 2, Dir::Horizontal, 2, false};
474
475 // 0xBD: Rightwards Hammer Pegs 2x2
476 dimensions_[0xBD] = {2, 2, Dir::Horizontal, 2, false};
477
478 // ============================================================================
479 // Chests - fixed 2x2
480 // ============================================================================
481 for (int id : {0xF9, 0xFA, 0xFB, 0xFC, 0xFD}) {
482 dimensions_[id] = {2, 2, Dir::None, 0, false};
483 }
484
485 // ============================================================================
486 // Subtype 2 objects (0x100-0x13F)
487 // ============================================================================
488 // Layout corners - 4x4
489 for (int id = 0x100; id <= 0x103; id++) {
490 dimensions_[id] = {4, 4, Dir::None, 0, false};
491 }
492
493 // Other 4x4 patterns
494 for (int id = 0x104; id <= 0x10F; id++) {
495 dimensions_[id] = {4, 4, Dir::None, 0, false};
496 }
497
498 // Corners - 4x4
499 for (int id = 0x110; id <= 0x11F; id++) {
500 dimensions_[id] = {4, 4, Dir::None, 0, false};
501 }
502
503 // Tables, beds, etc
504 dimensions_[0x122] = {4, 5, Dir::None, 0, false}; // Bed
505 dimensions_[0x123] = {4, 3, Dir::None, 0, false}; // Table
506 dimensions_[0x128] = {4, 5, Dir::None, 0, false}; // Bed variant
507 dimensions_[0x12C] = {3, 6, Dir::None, 0, false}; // 3x6 pattern
508 dimensions_[0x13E] = {6, 3, Dir::None, 0, false}; // Utility 6x3
509
510 // ============================================================================
511 // Subtype 3 objects (0xF80-0xFFF)
512 // ============================================================================
513 // Default for Type 3: most are 2x2 fixed objects
514 for (int id = 0xF80; id <= 0xFFF; id++) {
515 dimensions_[id] = {2, 2, Dir::None, 0, false};
516 }
517
518 // Override specific Type 3 objects with known sizes
519 // 0xFB1-0xFB2: Big Chest 4x3
520 dimensions_[0xFB1] = {4, 3, Dir::None, 0, false};
521 dimensions_[0xFB2] = {4, 3, Dir::None, 0, false};
522
523 // Light beams and boss shells
524 dimensions_[0xFF0] = {2, 4, Dir::None, 0, false}; // Light beam
525 dimensions_[0xFF1] = {4, 8, Dir::None, 0, false}; // Big light beam
526 dimensions_[0xFF8] = {4, 8, Dir::None, 0, false}; // Ganon Triforce floor
527}
528
530 // ROM addresses from ZScream:
531 // Tile data offset table: $018000
532 // Routine pointer table: $018200
533 // These tables help determine object sizes based on tile counts
534
535 constexpr int kSubtype1TileOffsets = 0x8000;
536 constexpr int kSubtype1Routines = 0x8200;
537
538 // Read tile count for each object to refine dimensions
539 for (int id = 0; id < 0xF8; id++) {
540 auto offset_result = rom->ReadWord(kSubtype1TileOffsets + id * 2);
541 if (!offset_result.ok()) continue;
542
543 // Tile count can inform base size
544 // This is a simplified heuristic - full accuracy requires parsing
545 // the actual tile data
546 }
547}
548
550 // Subtype 2 data offset: $0183F0
551 // Subtype 2 routine ptr: $018470
552 constexpr int kSubtype2TileOffsets = 0x83F0;
553 (void)kSubtype2TileOffsets;
554 (void)rom;
555 // Similar parsing for subtype 2 objects
556}
557
559 // Subtype 3 data offset: $0184F0
560 // Subtype 3 routine ptr: $0185F0
561 constexpr int kSubtype3TileOffsets = 0x84F0;
562 (void)kSubtype3TileOffsets;
563 (void)rom;
564 // Similar parsing for subtype 3 objects
565}
566
567} // namespace zelda3
568} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.cc:228
bool is_loaded() const
Definition rom.h:128
ROM-based object dimension lookup table.
std::pair< int, int > GetBaseDimensions(int object_id) const
std::tuple< int, int, int, int > GetHitTestBounds(const RoomObject &obj) const
std::pair< int, int > GetDimensions(int object_id, int size) const
std::unordered_map< int, DimensionEntry > dimensions_
static ObjectDimensionTable & Get()
std::pair< int, int > GetSelectionDimensions(int object_id, int size) const