backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)
This commit is contained in:
@@ -0,0 +1,482 @@
|
||||
# YAZE Dungeon System - Complete Technical Reference
|
||||
|
||||
Comprehensive reference for AI agents working on the YAZE Dungeon editing system.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Overview
|
||||
|
||||
### File Structure
|
||||
```
|
||||
src/zelda3/dungeon/
|
||||
├── dungeon.h/cc # Main Dungeon class
|
||||
├── room.h/cc # Room class (1,337 lines)
|
||||
├── room_object.h/cc # RoomObject encoding (633+249 lines)
|
||||
├── object_drawer.h/cc # Object rendering (210+972 lines)
|
||||
├── object_parser.h/cc # ROM tile parsing (172+387 lines)
|
||||
├── room_entrance.h # Entrance data (367 lines)
|
||||
├── dungeon_rom_addresses.h # ROM address constants (108 lines)
|
||||
|
||||
src/app/editor/dungeon/
|
||||
├── dungeon_editor_v2.h/cc # Main editor (card-based)
|
||||
├── dungeon_room_loader.h/cc # ROM data loading
|
||||
├── dungeon_room_selector.h/cc # Room selection UI
|
||||
├── dungeon_canvas_viewer.h/cc # Canvas rendering
|
||||
├── dungeon_object_selector.h/cc # Object palette
|
||||
├── dungeon_object_interaction.h/cc # Mouse interactions
|
||||
```
|
||||
|
||||
### Data Model
|
||||
```
|
||||
Dungeon
|
||||
└── rooms_[296]
|
||||
└── Room
|
||||
├── tile_objects_[] (RoomObject instances)
|
||||
├── sprites_[]
|
||||
├── chests_in_room_[]
|
||||
├── z3_staircases_[]
|
||||
├── bg1_buffer_ (512x512 pixels)
|
||||
├── bg2_buffer_ (512x512 pixels)
|
||||
└── current_gfx16_[] (16KB graphics)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Room Structure
|
||||
|
||||
### Room Count & Organization
|
||||
- **Total Rooms:** 296 (indices 0x00-0x127)
|
||||
- **Canvas Size:** 512x512 pixels (64x64 tiles)
|
||||
- **Layers:** BG1, BG2, BG3
|
||||
|
||||
### Room Properties
|
||||
```cpp
|
||||
// room.h
|
||||
int room_id_; // Room index (0-295)
|
||||
uint8_t blockset; // Graphics blockset ID
|
||||
uint8_t spriteset; // Sprite set ID
|
||||
uint8_t palette; // Palette ID (0-63)
|
||||
uint8_t layout; // Layout template (0-7)
|
||||
uint8_t floor1, floor2; // Floor graphics (nibbles)
|
||||
uint16_t message_id_; // Associated message
|
||||
|
||||
// Behavioral
|
||||
CollisionKey collision_type; // Collision enum
|
||||
EffectKey effect_type; // Visual effect enum
|
||||
TagKey tag1, tag2; // Special condition tags
|
||||
LayerMergeType layer_merge; // BG1/BG2 blend mode
|
||||
```
|
||||
|
||||
### Layer Merge Types
|
||||
```cpp
|
||||
enum LayerMergeType {
|
||||
LayerMerge00 = 0x00, // Off - Layer 2 invisible
|
||||
LayerMerge01 = 0x01, // Parallax scrolling
|
||||
LayerMerge02 = 0x02, // Dark overlay
|
||||
LayerMerge03 = 0x03, // On top (translucent)
|
||||
LayerMerge04 = 0x04, // Translucent blend
|
||||
LayerMerge05 = 0x05, // Addition blend
|
||||
LayerMerge06 = 0x06, // Normal overlay
|
||||
LayerMerge07 = 0x07, // Transparent
|
||||
LayerMerge08 = 0x08, // Dark room effect
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Object Encoding System
|
||||
|
||||
### 3-Byte Object Format
|
||||
|
||||
Objects are stored as 3 bytes in ROM with three distinct encoding types:
|
||||
|
||||
#### Type 1: Standard Objects (ID 0x00-0xFF)
|
||||
```
|
||||
Byte format: xxxxxxss | yyyyyyss | iiiiiiii
|
||||
b1 b2 b3
|
||||
|
||||
Decoding:
|
||||
x = (b1 & 0xFC) >> 2 // 6 bits (0-63)
|
||||
y = (b2 & 0xFC) >> 2 // 6 bits (0-63)
|
||||
size = ((b1 & 0x03) << 2) | (b2 & 0x03) // 4 bits (0-15)
|
||||
id = b3 // 8 bits
|
||||
```
|
||||
|
||||
#### Type 2: Extended Objects (ID 0x100-0x1FF)
|
||||
```
|
||||
Indicator: b1 >= 0xFC
|
||||
|
||||
Byte format: 111111xx | xxxxyyyy | yyiiiiii
|
||||
b1 b2 b3
|
||||
|
||||
Decoding:
|
||||
id = (b3 & 0x3F) | 0x100
|
||||
x = ((b2 & 0xF0) >> 4) | ((b1 & 0x03) << 4)
|
||||
y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6)
|
||||
size = 0 // No size parameter
|
||||
```
|
||||
|
||||
#### Type 3: Rare Objects (ID 0xF00-0xFFF)
|
||||
```
|
||||
Indicator: b3 >= 0xF8
|
||||
|
||||
Byte format: xxxxxxii | yyyyyyii | 11111iii
|
||||
b1 b2 b3
|
||||
|
||||
Decoding:
|
||||
id = (b3 << 4) | 0x80 | ((b2 & 0x03) << 2) | (b1 & 0x03)
|
||||
x = (b1 & 0xFC) >> 2
|
||||
y = (b2 & 0xFC) >> 2
|
||||
size = ((b1 & 0x03) << 2) | (b2 & 0x03)
|
||||
```
|
||||
|
||||
### Object Categories
|
||||
|
||||
| Type | ID Range | Examples |
|
||||
|------|----------|----------|
|
||||
| Type 1 | 0x00-0xFF | Walls, floors, decorations |
|
||||
| Type 2 | 0x100-0x1FF | Corners, stairs, furniture |
|
||||
| Type 3 | 0xF00-0xFFF | Chests, pipes, special objects |
|
||||
|
||||
---
|
||||
|
||||
## 4. ObjectDrawer Rendering System
|
||||
|
||||
### Class Structure
|
||||
```cpp
|
||||
// object_drawer.h
|
||||
class ObjectDrawer {
|
||||
// Entry point
|
||||
absl::Status DrawObject(const RoomObject& object,
|
||||
gfx::BackgroundBuffer& bg1,
|
||||
gfx::BackgroundBuffer& bg2,
|
||||
const gfx::PaletteGroup& palette_group);
|
||||
|
||||
// Data
|
||||
Rom* rom_;
|
||||
const uint8_t* room_gfx_buffer_; // current_gfx16_
|
||||
std::unordered_map<int16_t, int> object_to_routine_map_;
|
||||
std::vector<DrawRoutine> draw_routines_;
|
||||
};
|
||||
```
|
||||
|
||||
### Draw Routine Status
|
||||
|
||||
| # | Routine Name | Status | Lines |
|
||||
|---|--------------|--------|-------|
|
||||
| 0 | DrawRightwards2x2_1to15or32 | COMPLETE | 302-321 |
|
||||
| 1 | DrawRightwards2x4_1to15or26 | COMPLETE | 323-348 |
|
||||
| 2 | DrawRightwards2x4spaced4_1to16 | COMPLETE | 350-373 |
|
||||
| 3 | DrawRightwards2x4spaced4_BothBG | **STUB** | 375-381 |
|
||||
| 4 | DrawRightwards2x2_1to16 | COMPLETE | 383-399 |
|
||||
| 5 | DrawDiagonalAcute_1to16 | **UNCLEAR** | 401-417 |
|
||||
| 6 | DrawDiagonalGrave_1to16 | **UNCLEAR** | 419-435 |
|
||||
| 7 | DrawDownwards2x2_1to15or32 | COMPLETE | 689-708 |
|
||||
| 8 | DrawDownwards4x2_1to15or26 | COMPLETE | 710-753 |
|
||||
| 9 | DrawDownwards4x2_BothBG | **STUB** | 755-761 |
|
||||
| 10 | DrawDownwardsDecor4x2spaced4 | COMPLETE | 763-782 |
|
||||
| 11 | DrawDownwards2x2_1to16 | COMPLETE | 784-799 |
|
||||
| 12 | DrawDownwardsHasEdge1x1 | COMPLETE | 801-813 |
|
||||
| 13 | DrawDownwardsEdge1x1 | COMPLETE | 815-827 |
|
||||
| 14 | DrawDownwardsLeftCorners | COMPLETE | 829-842 |
|
||||
| 15 | DrawDownwardsRightCorners | COMPLETE | 844-857 |
|
||||
| 16 | DrawRightwards4x4_1to16 | COMPLETE | 534-550 |
|
||||
| - | CustomDraw | **STUB** | 524-532 |
|
||||
| - | DrawDoorSwitcherer | **STUB** | 566-575 |
|
||||
|
||||
### INCOMPLETE: BothBG Routines (4 locations)
|
||||
|
||||
These routines should draw to BOTH BG1 and BG2 but currently only call single-layer version:
|
||||
|
||||
```cpp
|
||||
// Line 375-381: DrawRightwards2x4spaced4_1to16_BothBG
|
||||
// Line 437-442: DrawDiagonalAcute_1to16_BothBG
|
||||
// Line 444-449: DrawDiagonalGrave_1to16_BothBG
|
||||
// Line 755-761: DrawDownwards4x2_1to16_BothBG
|
||||
|
||||
// Current (WRONG):
|
||||
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||
const RoomObject& obj, gfx::BackgroundBuffer& bg, // Only 1 buffer!
|
||||
std::span<const gfx::TileInfo> tiles) {
|
||||
// Just calls single-layer version - misses BG2
|
||||
DrawRightwards2x4spaced4_1to16(obj, bg, tiles);
|
||||
}
|
||||
|
||||
// Should be:
|
||||
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||
const RoomObject& obj,
|
||||
gfx::BackgroundBuffer& bg1, // Both buffers
|
||||
gfx::BackgroundBuffer& bg2,
|
||||
std::span<const gfx::TileInfo> tiles) {
|
||||
DrawRightwards2x4spaced4_1to16(obj, bg1, tiles);
|
||||
DrawRightwards2x4spaced4_1to16(obj, bg2, tiles);
|
||||
}
|
||||
```
|
||||
|
||||
### UNCLEAR: Diagonal Routines
|
||||
|
||||
```cpp
|
||||
// Lines 401-417, 419-435
|
||||
// Issues:
|
||||
// - Hardcoded +6 and 5 iterations (why?)
|
||||
// - Coordinate formula may produce negative Y
|
||||
// - Only uses 4 tiles from larger span
|
||||
// - No bounds checking
|
||||
|
||||
for (int s = 0; s < size + 6; s++) { // Why +6?
|
||||
for (int i = 0; i < 5; i++) { // Why 5?
|
||||
WriteTile8(bg, obj.x_ + s, obj.y_ + (i - s), tiles[i % 4]);
|
||||
// ^^ (i - s) can be negative when s > i
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Tile Rendering Pipeline
|
||||
|
||||
### WriteTile8() - Tile to Pixel Conversion
|
||||
```cpp
|
||||
// object_drawer.cc lines 863-883
|
||||
void WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
|
||||
const gfx::TileInfo& tile_info) {
|
||||
// tile coords → pixel coords: tile_x * 8, tile_y * 8
|
||||
DrawTileToBitmap(bitmap, tile_info, tile_x * 8, tile_y * 8, room_gfx_buffer_);
|
||||
}
|
||||
```
|
||||
|
||||
### DrawTileToBitmap() - Pixel Rendering
|
||||
```cpp
|
||||
// object_drawer.cc lines 890-970
|
||||
// Key steps:
|
||||
// 1. Graphics sheet lookup: tile_info.id_ → (sheet_x, sheet_y)
|
||||
// 2. Palette offset: (palette & 0x0F) * 8
|
||||
// 3. Per-pixel with mirroring support
|
||||
// 4. Color 0 = transparent (skipped)
|
||||
|
||||
int tile_sheet_x = (tile_info.id_ % 16) * 8; // 0-127 pixels
|
||||
int tile_sheet_y = (tile_info.id_ / 16) * 8; // 0-127 pixels
|
||||
uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8;
|
||||
|
||||
for (int py = 0; py < 8; py++) {
|
||||
for (int px = 0; px < 8; px++) {
|
||||
int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px;
|
||||
int src_y = tile_info.vertical_mirror_ ? (7 - py) : py;
|
||||
// Read pixel, apply palette, write to bitmap
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Palette Application (CRITICAL)
|
||||
```cpp
|
||||
// object_drawer.cc lines 71-115
|
||||
// Palette must be applied AFTER drawing, BEFORE SDL sync
|
||||
|
||||
// 1. Draw all objects (writes palette indices 0-255)
|
||||
for (auto& obj : objects) {
|
||||
DrawObject(obj, bg1, bg2, palette_group);
|
||||
}
|
||||
|
||||
// 2. Apply dungeon palette to convert indices → RGB
|
||||
bg1_bmp.SetPalette(dungeon_palette);
|
||||
bg2_bmp.SetPalette(dungeon_palette);
|
||||
|
||||
// 3. Sync to SDL surfaces
|
||||
SDL_LockSurface(bg1_bmp.surface());
|
||||
memcpy(bg1_bmp.surface()->pixels, bg1_bmp.mutable_data().data(), ...);
|
||||
SDL_UnlockSurface(bg1_bmp.surface());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. ROM Addresses
|
||||
|
||||
### Room Data
|
||||
```cpp
|
||||
kRoomObjectLayoutPointer = 0x882D // Layout pointer table
|
||||
kRoomObjectPointer = 0x874C // Object data pointer
|
||||
kRoomHeaderPointer = 0xB5DD // Room headers (3-byte long)
|
||||
kRoomHeaderPointerBank = 0xB5E7 // Bank byte
|
||||
```
|
||||
|
||||
### Palette & Graphics
|
||||
```cpp
|
||||
kDungeonsMainBgPalettePointers = 0xDEC4B
|
||||
kDungeonsPalettes = 0xDD734
|
||||
kGfxGroupsPointer = 0x6237
|
||||
kTileAddress = 0x1B52 // Main tile graphics
|
||||
kTileAddressFloor = 0x1B5A // Floor tile graphics
|
||||
```
|
||||
|
||||
### Object Subtypes
|
||||
```cpp
|
||||
kRoomObjectSubtype1 = 0x0F8000 // Standard objects
|
||||
kRoomObjectSubtype2 = 0x0F83F0 // Extended objects
|
||||
kRoomObjectSubtype3 = 0x0F84F0 // Rare objects
|
||||
kRoomObjectTileAddress = 0x091B52 // Tile data
|
||||
```
|
||||
|
||||
### Special Objects
|
||||
```cpp
|
||||
kBlocksPointer[1-4] = 0x15AFA-0x15B0F
|
||||
kChestsDataPointer1 = 0xEBFB
|
||||
kTorchData = 0x2736A
|
||||
kPitPointer = 0x394AB
|
||||
kDoorPointers = 0xF83C0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. TODOs in room_object.h (30+ items)
|
||||
|
||||
### Unknown Objects Needing Verification
|
||||
|
||||
| Line | ID | Description |
|
||||
|------|-----|-------------|
|
||||
| 234 | 0x35 | "WEIRD DOOR" - needs investigation |
|
||||
| 252-255 | 0x49-0x4C | "Unknown" Type 1 objects |
|
||||
| 350-353 | 0xC4-0xC7 | "Diagonal layer 2 mask B" - needs verify |
|
||||
| 392-395 | 0xDE-0xE1 | "Moving wall flag" - WTF IS THIS? |
|
||||
| 466-476 | Type 2 | Multiple "Unknown" objects |
|
||||
| 480 | 0x30 | "Intraroom stairs north B" - verify layer |
|
||||
| 486 | 0x36 | "Water ladder (south)" - needs verify |
|
||||
| 512-584 | Type 3 | Multiple "Unknown" objects |
|
||||
|
||||
---
|
||||
|
||||
## 8. DungeonEditorV2 Architecture
|
||||
|
||||
### Card-Based Component System
|
||||
```cpp
|
||||
DungeonEditorV2 (Coordinator)
|
||||
├── DungeonRoomLoader // ROM data loading
|
||||
├── DungeonRoomSelector // Room list/selection
|
||||
├── DungeonCanvasViewer // 512x512 canvas
|
||||
├── DungeonObjectSelector // Object palette
|
||||
├── DungeonObjectInteraction // Mouse handling
|
||||
├── ObjectEditorCard // Property editing
|
||||
└── PaletteEditorWidget // Color editing
|
||||
```
|
||||
|
||||
### Card Types
|
||||
```cpp
|
||||
show_control_panel_ // Room/entrance selection
|
||||
show_room_selector_ // Room list
|
||||
show_room_matrix_ // 16x19 visual layout
|
||||
show_entrances_list_ // Entrance/spawn list
|
||||
show_room_graphics_ // Blockset/palette
|
||||
show_object_editor_ // Object placement
|
||||
show_palette_editor_ // Palette colors
|
||||
show_debug_controls_ // Debug options
|
||||
```
|
||||
|
||||
### Undo/Redo System
|
||||
```cpp
|
||||
// Per-room object snapshots
|
||||
std::unordered_map<int, std::vector<std::vector<RoomObject>>> undo_history_;
|
||||
std::unordered_map<int, std::vector<std::vector<RoomObject>>> redo_history_;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Room Loading Flow
|
||||
|
||||
```
|
||||
LoadRoomFromRom(room_id)
|
||||
│
|
||||
├── Resolve room header pointer (0xB5DD + room_id * 3)
|
||||
│
|
||||
├── Parse header bytes:
|
||||
│ ├── BG2 type, collision, light flag
|
||||
│ ├── Palette, blockset, spriteset
|
||||
│ ├── Effect type, tags
|
||||
│ └── Staircase data
|
||||
│
|
||||
├── Load graphics sheets (16 blocks)
|
||||
│
|
||||
└── LoadObjects()
|
||||
│
|
||||
├── Read floor/layout header (2 bytes)
|
||||
│
|
||||
├── Parse object stream:
|
||||
│ ├── 3 bytes per object
|
||||
│ ├── 0xFF 0xFF = layer boundary
|
||||
│ └── 0xF0 0xFF = door section
|
||||
│
|
||||
└── Handle special objects:
|
||||
├── Staircases
|
||||
├── Chests
|
||||
├── Doors
|
||||
├── Torches
|
||||
└── Blocks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Rendering Pipeline
|
||||
|
||||
```
|
||||
1. LoadRoomGraphics()
|
||||
└── Build graphics sheet list from blockset
|
||||
|
||||
2. CopyRoomGraphicsToBuffer()
|
||||
└── Copy ROM sheets → current_gfx16_[]
|
||||
|
||||
3. RenderRoomGraphics()
|
||||
├── Check dirty flags
|
||||
├── LoadLayoutTilesToBuffer()
|
||||
├── Draw floor to bg1/bg2 buffers
|
||||
└── RenderObjectsToBackground()
|
||||
└── ObjectDrawer::DrawObjectList()
|
||||
|
||||
4. Present (Canvas Viewer)
|
||||
├── Process deferred texture queue
|
||||
├── Create/update GPU textures
|
||||
└── Render to ImGui canvas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Known Issues Summary
|
||||
|
||||
### BothBG Support (4 stubs)
|
||||
- Line 380: `DrawRightwards2x4spaced4_1to16_BothBG`
|
||||
- Line 441: `DrawDiagonalAcute_1to16_BothBG`
|
||||
- Line 448: `DrawDiagonalGrave_1to16_BothBG`
|
||||
- Line 760: `DrawDownwards4x2_1to16_BothBG`
|
||||
|
||||
**Fix:** Change signature to accept both `bg1` and `bg2` buffers.
|
||||
|
||||
### Diagonal Logic (2 routines)
|
||||
- Lines 401-435: Hardcoded constants, potential negative coords
|
||||
- **Needs:** Game verification or ZScream reference
|
||||
|
||||
### Custom/Door Stubs (2 routines)
|
||||
- Line 524-532: `CustomDraw` - only draws first tile
|
||||
- Line 566-575: `DrawDoorSwitcherer` - only draws first tile
|
||||
|
||||
### Object Names (30+ unknowns)
|
||||
- Multiple objects need in-game verification
|
||||
- See section 7 for full list
|
||||
|
||||
---
|
||||
|
||||
## 12. Testing
|
||||
|
||||
### Run Dungeon Tests
|
||||
```bash
|
||||
# Unit tests
|
||||
ctest --test-dir build -R "dungeon\|Dungeon" -V
|
||||
|
||||
# E2E tests
|
||||
ctest --test-dir build -R "DungeonEditor" -V
|
||||
```
|
||||
|
||||
### E2E Test Files
|
||||
- `test/e2e/dungeon_editor_smoke_test.cc`
|
||||
- `test/e2e/dungeon_canvas_interaction_test.cc`
|
||||
- `test/e2e/dungeon_layer_rendering_test.cc`
|
||||
- `test/e2e/dungeon_object_drawing_test.cc`
|
||||
|
||||
### Test Design Doc
|
||||
`docs/internal/testing/dungeon-gui-test-design.md` (1000+ lines)
|
||||
Reference in New Issue
Block a user