yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_block_codec.h
Go to the documentation of this file.
1#ifndef YAZE_ZELDA3_DUNGEON_DUNGEON_BLOCK_CODEC_H_
2#define YAZE_ZELDA3_DUNGEON_DUNGEON_BLOCK_CODEC_H_
3
4#include <cstdint>
5
6namespace yaze {
7namespace zelda3 {
8
9// Pure encode/decode helpers for the vanilla pushable-block save table at
10// `SpecialUnderworldObjects_pushable_block` ($04:F1DE..$04:F3DD, 512 bytes,
11// 128 max entries × 4 bytes). The runtime scan in bank_01.asm:1162 walks the
12// flat table entry-by-entry and matches on `room_id`; there is no per-room
13// terminator. The renderer entry `RoomDraw_PushableBlock` at $01:B4D6 reads
14// the encoded word with `AND.w #$3FFF` before using it as a tilemap index.
15// That 14-bit index contains a 13-bit 64x64 tile position plus bit 13, which
16// selects the lower/BG2 tilemap. `PushBlock_CheckForPit` at $01:D8D4 tests bit
17// 14 independently when selecting the behavior/collision layer.
18//
19// Word layout (b4 << 8 | b3):
20// bit 0 : unused (always 0; LSL-by-1 padding)
21// bits 1..6 : px (6 bits, range 0..63)
22// bits 7..12 : py (6 bits, range 0..63)
23// bit 13 : draw target (0 = upper/BG1, 1 = lower/BG2)
24// bit 14 : behavior/pit selector (0 = upper, 1 = lower)
25// bit 15 : unused (always 0 in vanilla)
26//
27// This module replaces ad-hoc bit twiddling that previously lived in
28// `Room::LoadBlocks`. Bit 13 was previously misidentified as the high bit of
29// py, causing three vanilla lower-layer blocks (rooms 0xA8, 0x66, and 0x2C)
30// to load 64 tiles too low and render on BG1.
31
33 uint16_t room_id = 0;
34 uint8_t px = 0; // 0..63
35 uint8_t py = 0; // 0..63
36 uint8_t draw_layer = 0; // bit 13: 0=upper/BG1, 1=lower/BG2
37 uint8_t behavior_layer = 0; // bit 14: pit/collision layer selector
38};
39
41 uint8_t b1 = 0; // room_id low byte
42 uint8_t b2 = 0; // room_id high byte
43 uint8_t b3 = 0; // position+layer word, low byte
44 uint8_t b4 = 0; // position+layer word, high byte
45};
46
48 const PushableBlockBytes& bytes) {
50 out.room_id = static_cast<uint16_t>(bytes.b1 | (bytes.b2 << 8));
51 const uint16_t word = static_cast<uint16_t>(bytes.b3 | (bytes.b4 << 8));
52 out.px = static_cast<uint8_t>((word >> 1) & 0x3F);
53 out.py = static_cast<uint8_t>((word >> 7) & 0x3F);
54 out.draw_layer = static_cast<uint8_t>((word >> 13) & 0x01);
55 out.behavior_layer = static_cast<uint8_t>((word >> 14) & 0x01);
56 return out;
57}
58
60 const PushableBlockEntry& entry) {
62 out.b1 = static_cast<uint8_t>(entry.room_id & 0xFF);
63 out.b2 = static_cast<uint8_t>((entry.room_id >> 8) & 0xFF);
64 const uint16_t word = static_cast<uint16_t>(
65 ((entry.px & 0x3F) << 1) | ((entry.py & 0x3F) << 7) |
66 ((entry.draw_layer & 0x01) << 13) |
67 ((entry.behavior_layer & 0x01) << 14));
68 out.b3 = static_cast<uint8_t>(word & 0xFF);
69 out.b4 = static_cast<uint8_t>((word >> 8) & 0xFF);
70 return out;
71}
72
73} // namespace zelda3
74} // namespace yaze
75
76#endif // YAZE_ZELDA3_DUNGEON_DUNGEON_BLOCK_CODEC_H_
PushableBlockBytes EncodePushableBlockEntry(const PushableBlockEntry &entry)
PushableBlockEntry DecodePushableBlockEntry(const PushableBlockBytes &bytes)