Files
yaze/docs/internal/zelda3/dungeon-spec.md

22 KiB

Zelda 3 (Link to the Past) Dungeon System Specification

Purpose: This document provides a comprehensive specification for the ALTTP dungeon/underworld system based on analysis of:

  • yaze - C++ dungeon editor implementation (partial)
  • ZScream - C# reference implementation (complete)
  • usdasm - Assembly disassembly of bank_01.asm (ground truth)

Goal: Close the gaps in yaze's dungeon editor to achieve correct room rendering.


Table of Contents

  1. Room Data Structure
  2. Room Header Format
  3. Object System
  4. Layer System
  5. Door System
  6. Sprites
  7. Items and Chests
  8. Graphics and Tilesets
  9. Implementation Gaps in yaze
  10. ROM Address Reference

1. Room Data Structure

1.1 Overview

A dungeon room in ALTTP consists of:

  • Room Header (14 bytes) - Metadata, properties, warp destinations
  • Object Data - Tile objects for walls, floors, decorations
  • Sprite Data - Enemies, NPCs, interactive entities
  • Item Data - Chest contents, pot items, key drops
  • Door Data - Connections between rooms

1.2 Total Rooms

Total Rooms: 296 (0x00 - 0x127)
Room Size: 512x512 pixels (64x64 tiles at 8x8 pixels each)

1.3 Room Memory Layout

Room Object Data:
+0x00: Floor nibbles (floor2 << 4 | floor1)
+0x01: Layout byte ((value >> 2) & 0x07)
+0x02+: Object stream (3 bytes per object)

Layer Separators:
- 0xFF 0xFF: Marks transition to next layer
- 0xF0 0xFF: Begin door list
- Layer 3 reached or 0xFF 0xFF at boundary: End of data

2. Room Header Format

2.1 Header Structure (14 bytes)

Offset Bits Field Description
0x00 7-5 bg2 Background 2 / Layer merge type (0-7)
0x00 4-2 collision Collision type (0-4)
0x00 0 light Light flag (if set, bg2 = 0x08 Dark Room)
0x01 5-0 palette Palette ID (0-63, masked 0x3F)
0x02 7-0 blockset Tileset/Blockset ID (0-23)
0x03 7-0 spriteset Spriteset ID (0-64)
0x04 7-0 effect Room effect (0-7)
0x05 7-0 tag1 Tag 1 trigger (0-64)
0x06 7-0 tag2 Tag 2 trigger (0-64)
0x07 1-0 holewarp_plane Pit warp plane (0-3)
0x07 3-2 staircase_plane[0] Staircase 1 plane (0-3)
0x07 5-4 staircase_plane[1] Staircase 2 plane (0-3)
0x07 7-6 staircase_plane[2] Staircase 3 plane (0-3)
0x08 1-0 staircase_plane[3] Staircase 4 plane (0-3)
0x09 7-0 holewarp Hole warp destination room (0-255)
0x0A 7-0 staircase_rooms[0] Staircase 1 destination room
0x0B 7-0 staircase_rooms[1] Staircase 2 destination room
0x0C 7-0 staircase_rooms[2] Staircase 3 destination room
0x0D 7-0 staircase_rooms[3] Staircase 4 destination room

2.2 Header Address Calculation

// Step 1: Get master pointer
int header_pointer = ROM[kRoomHeaderPointer, 3];  // 24-bit
header_pointer = SNEStoPC(header_pointer);

// Step 2: Index into pointer table
int table_offset = header_pointer + (room_id * 2);
int address = (ROM[kRoomHeaderPointerBank] << 16) | ROM[table_offset, 2];

// Step 3: Convert to PC address
int header_location = SNEStoPC(address);

2.3 Layer Merge Types (bg2 field)

ID Name Layer2OnTop Layer2Translucent Layer2Visible
0x00 Off true false false
0x01 Parallax true false false
0x02 Dark true true true
0x03 On top true true false
0x04 Translucent true true true
0x05 Addition true true true
0x06 Normal true false false
0x07 Transparent true true true
0x08 Dark room true true true

Note: When light flag is set, bg2 is overridden to 0x08 (Dark room).

2.4 Collision Types

ID Name
0 One_Collision
1 Both
2 Both_With_Scroll
3 Moving_Floor_Collision
4 Moving_Water_Collision

2.5 Room Effects

ID Name Description
0 Nothing No effect
1 One Effect 1
2 Moving_Floor Animated floor tiles
3 Moving_Water Animated water tiles
4 Four Effect 4
5 Red_Flashes Red screen flashes
6 Torch_Show_Floor Floor revealed by torches
7 Ganon_Room Final boss room effect

2.6 Room Tags (65 types)

Key tags include:

  • NW_Kill_Enemy_to_Open (1) - Kill all enemies in NW quadrant
  • Clear_Quadrant_to_Open (various) - Quadrant-based triggers
  • Push_Block_to_Open (various) - Block puzzle triggers
  • Water_Gate - Water level control
  • Agahnim_Room - Boss room setup
  • Holes_0 through Holes_2 - Pit configurations

3. Object System

3.1 Object Subtypes

ALTTP uses three object subtypes with different encoding and capabilities:

Subtype ID Range Count Scalable Description
1 0x00-0xF7 248 Yes Standard room objects
2 0x100-0x13F 64 No Fixed-size objects
3 0xF80-0xFFF 128 No Special/complex objects

3.2 Object Encoding (3 bytes)

Subtype 1 (b3 < 0xF8):

Byte 1: xxxxxxss  (x = X position bits 7-2, s = size X bits 1-0)
Byte 2: yyyyyyss  (y = Y position bits 7-2, s = size Y bits 1-0)
Byte 3: iiiiiiii  (object ID 0x00-0xF7)

Decoding:
  posX = (b1 & 0xFC) >> 2
  posY = (b2 & 0xFC) >> 2
  sizeX = b1 & 0x03
  sizeY = b2 & 0x03
  sizeXY = (sizeX << 2) + sizeY
  object_id = b3

Subtype 2 (b1 >= 0xFC):

Byte 1: 111111xx  (x = X position bits 5-4)
Byte 2: xxxxyyyy  (x = X position bits 3-0, y = Y position bits 5-2)
Byte 3: yyiiiiii  (y = Y position bits 1-0, i = object ID low 6 bits)

Decoding:
  posX = ((b2 & 0xF0) >> 4) + ((b1 & 0x03) << 4)
  posY = ((b2 & 0x0F) << 2) + ((b3 & 0xC0) >> 6)
  object_id = (b3 & 0x3F) | 0x100

Subtype 3 (b3 >= 0xF8):

Byte 1: xxxxxxii  (x = X position bits 7-2, i = size/ID bits 1-0)
Byte 2: yyyyyyii  (y = Y position bits 7-2, i = size/ID bits 3-2)
Byte 3: 11111iii  (i = ID bits 6-4, marker 0xF8-0xFF)

Decoding:
  posX = (b1 & 0xFC) >> 2
  posY = (b2 & 0xFC) >> 2
  object_id = ((b3 << 4) | 0x80 + (((b2 & 0x03) << 2) + (b1 & 0x03))) - 0xD80
  // Results in 0x200-0x27E range

3.3 Object Data Tables (bank_01.asm)

Address        | Size  | Content
---------------|-------|------------------------------------------
$018000-$0181FE| 512B  | Subtype 1 data offsets (256 entries x 2)
$018200-$0183EE| 494B  | Subtype 1 routine pointers (256 entries)
$0183F0-$01846E| 128B  | Subtype 2 data offsets (64 entries x 2)
$018470-$0184EE| 128B  | Subtype 2 routine pointers (64 entries)
$0184F0-$0185EE| 256B  | Subtype 3 data offsets (128 entries x 2)
$0185F0-$0186EE| 256B  | Subtype 3 routine pointers (128 entries)

3.4 Drawing Routines

There are 24+ unique drawing patterns used by objects:

Routine Pattern Multiplier Objects Using
Rightwards2x2_1to15or32 2x2 horizontal s*2 Walls, floors
Rightwards2x4_1to15or26 2x4 horizontal s*2 Taller walls
Downwards2x2_1to15or32 2x2 vertical s*2 Vertical features
Downwards4x2_1to15or26 4x2 vertical s*2 Wide columns
DiagonalAcute_1to16 Diagonal / +1,+1 Stairs up-right
DiagonalGrave_1to16 Diagonal \ +1,-1 Stairs down-right
4x4 Fixed 4x4 none Type 2 objects
3x4 Fixed 3x4 none Doors, decorations
Single2x2 Single 2x2 none Small decorations
Single2x3Pillar Single 2x3 none Pillars

3.5 Size Handling

// Default size when size byte is 0
if (size == 0) {
    size = 32;  // For most subtype 1 objects
}

// Some objects use size + 1 for iteration count
for (int s = 0; s < size + 1; s++) { ... }

// Size masking for some routines
int effective_size = size & 0x0F;  // Use lower 4 bits only

3.6 Layer Assignment

Objects are assigned to layers via the object stream:

  • Objects before first 0xFF 0xFF marker: Layer 0 (BG1)
  • Objects after first 0xFF 0xFF marker: Layer 1 (BG2)
  • Objects after second 0xFF 0xFF marker: Layer 2 (BG3/Sprites)

Some objects have allBgs = true flag and draw to both BG1 and BG2.

3.7 Common Object IDs

Subtype 1 (Scalable):

ID Name
0x00-0x07 Wall segments (N/S/E/W)
0x08-0x0B Pit edges
0x0C-0x20 Diagonal walls
0x21-0x30 Rails and supports
0x31-0x40 Carpets and trim
0xD0-0xD7 Floor types (8 patterns)
0xE0-0xE7 Ceiling types
0xF0-0xF3 Conveyor belts (4 directions)

Subtype 2 (Fixed):

ID Name
0x100-0x107 Corners (concave/convex)
0x108-0x10F Braziers and statues
0x110-0x117 Star tiles
0x118-0x11F Torches and furniture
0x120-0x127 Stairs (inter/intra room)
0x128-0x12F Blocks and platforms

Subtype 3 (Special):

ID Name
0x200-0x207 Waterfall faces
0x208-0x20F Somaria paths
0x210-0x217 Item piles (rupees)
0x218-0x21F Chests (various)
0x220-0x227 Pipes and conveyors
0x228-0x22F Pegs and switches

4. Layer System

4.1 Layer Architecture

ALTTP uses a 3-layer system matching SNES hardware:

Layer SNES Name Memory Purpose
BG1 Background 1 $7E2000 Main room layout
BG2 Background 2 $7E4000 Overlay layer
BG3 Background 3 Sprites Sprites/effects

4.2 Tilemap Memory Layout

Upper Layer (BG1/BG2): $7E2000-$7E27FF (2KB per layer)
Lower Layer (BG3):     $7E4000-$7E47FF (2KB per layer)

Grid Structure: 4 columns x 3 rows of 32x32 blocks
Offset: $100 bytes per block horizontally
        $1C0 bytes per block vertically

Each tilemap entry: 16-bit word
  Bits [9:0]:   Tile index (0-1023)
  Bit  [10]:    Priority bit
  Bits [13:11]: Palette select (0-7)
  Bit  [14]:    Horizontal flip
  Bit  [15]:    Vertical flip

4.3 Layer Merge Behavior

The bg2 field controls how layers are composited:

switch (bg2) {
    case 0x00: // Off - BG2 hidden
    case 0x01: // Parallax - BG2 scrolls differently
    case 0x02: // Dark - Dark room effect
    case 0x03: // On top - BG2 overlays BG1
    case 0x04: // Translucent - BG2 semi-transparent
    case 0x05: // Addition - Additive blending
    case 0x06: // Normal - Standard display
    case 0x07: // Transparent - BG2 transparent
    case 0x08: // Dark room - Special dark room
}

4.4 Floor Rendering

Floor tiles are specified by the first byte of room object data:

uint8_t floor_byte = ROM[object_data_ptr];
uint8_t floor1 = floor_byte & 0x0F;  // Low nibble
uint8_t floor2 = floor_byte >> 4;    // High nibble

Floor patterns (0-15) reference tile graphics at tile_address_floor.


5. Door System

5.1 Door Data Format

Doors are encoded as 2-byte entries after the 0xF0 0xFF marker:

Byte 1: Door position (0-255)
Byte 2: Door type and direction
  Bits [3:0]: Direction (0=North, 1=South, 2=West, 3=East)
  Bits [7:4]: Door type/subtype

5.2 Door Types

Type Name Description
0x00 Regular Standard door
0x02 Regular2 Standard door variant
0x06 EntranceDoor Entrance from overworld
0x08 WaterfallTunnel Behind waterfall
0x0A EntranceLarge Large entrance
0x0C EntranceLarge2 Large entrance variant
0x0E EntranceCave Cave entrance
0x12 ExitToOW Exit to overworld
0x14 ThroneRoom Throne room door
0x16 PlayerBgChange Layer toggle door
0x18 ShuttersTwoWay Two-way shutter
0x1A InvisibleDoor Hidden door
0x1C SmallKeyDoor Requires small key
0x20-0x26 StairMaskLocked Locked stair doors
0x28 BreakableWall Bomb-able wall
0x30 LgExplosion Large explosion door
0x32 Slashable Sword-cuttable
0x40 RegularDoor33 Regular variant
0x44 Shutter One-way shutter
0x46 WarpRoomDoor Warp tile door
0x48-0x4A ShutterTrap Trap shutter doors

5.3 Door Graphics Addresses

Direction | Graphics Pointer
----------|------------------
North     | $014D9E (kDoorGfxUp)
South     | $014E06 (kDoorGfxDown)
West      | $014E66 (kDoorGfxLeft)
East      | $014EC6 (kDoorGfxRight)

5.4 Door Rendering Dimensions

Direction Width Height
North/South 4 tiles 3 tiles
East/West 3 tiles 4 tiles

6. Sprites

6.1 Sprite Data Structure

struct Sprite {
    uint8_t id;        // Sprite type (0x00-0xF3)
    uint8_t x;         // X position (0-63)
    uint8_t y;         // Y position (0-63)
    uint8_t subtype;   // Subtype flags
    uint8_t layer;     // Layer (0-2)
    uint8_t key_drop;  // Key drop (0=none, 1=small, 2=big)
    bool overlord;     // Is overlord sprite
};

6.2 Sprite Encoding

Sprites are stored as 3-byte entries:

Byte 1: Y position (bits 7-1), Layer flag (bit 0)
Byte 2: X position (bits 7-1), Subtype high (bit 0)
Byte 3: Sprite ID (0x00-0xFF)

Special handling:
- Overlord check: (subtype & 0x07) == 0x07
- Key drop at sprite ID 0xE4:
  - Position (0x00, 0x1E) = small key drop
  - Position (0x00, 0x1D) = big key drop

6.3 Overlord Sprites

When (subtype & 7) == 7, the sprite is an "overlord" with special behavior. Overlord IDs 0x01-0x1A have separate name tables.

6.4 Key Drop Mechanics

// Detection during sprite loading
if (sprite_id == 0xE4) {
    if (x == 0x00 && y == 0x1E) {
        key_drop = 1;  // Small key
    } else if (x == 0x00 && y == 0x1D) {
        key_drop = 2;  // Big key
    }
}

7. Items and Chests

7.1 Chest Data

Chests are stored separately from room objects:

struct ChestData {
    uint16_t room_id;   // Room containing chest
    uint8_t x;          // X position
    uint8_t y;          // Y position
    uint8_t item_id;    // Item contained
    bool is_big;        // Big chest flag
};

7.2 Item Types (Pot Items)

ID Item ID Item
0 Nothing 14 Small magic
1 Rupee (green) 15 Big magic
2 Rock crab 16 Bomb refill
3 Bee 17 Arrow refill
4 Random 18 Fairy
5 Bomb 19 Key
6 Heart 20 Fairy*8
7 Blue rupee 21-22 Various
8 Key*8 23 Hole
9 Arrow 24 Warp
10 1 bomb 25 Staircase
11 Heart 26 Bombable
12 Rupee (blue) 27 Switch
13 Heart variant

7.3 Item Encoding

Items with ID >= 0x80 use special encoding:

if (id & 0x80) {
    int actual_id = ((id - 0x80) / 2) + 23;
}

7.4 Item ROM Addresses

Chest Pointers:     $01EBF6 (kChestsLengthPointer)
Chest Data:         $01EBFB (kChestsDataPointer1)
Room Items:         $01DB69 (kRoomItemsPointers)

8. Graphics and Tilesets

8.1 Graphics Organization

Sheet Count:          223 sheets
Uncompressed Size:    2048 bytes (0x800) per sheet
3BPP Size:            1536 bytes (0x600) per sheet

8.2 Key Graphics Addresses

Tile Address:         $009B52 (kTileAddress)
Tile Address Floor:   $009B5A (kTileAddressFloor)
Subtype 1 Tiles:      $018000 (kRoomObjectSubtype1)
Subtype 2 Tiles:      $0183F0 (kRoomObjectSubtype2)
Subtype 3 Tiles:      $0184F0 (kRoomObjectSubtype3)
GFX Groups:           $006237 (kGfxGroupsPointer)

8.3 Palette Configuration

Palettes Per Group:   16
Colors Per Palette:   16
Total Palette Size:   256 colors
Half Palette Size:    8 colors

Dungeon Main BG:      $0DEC4B (kDungeonsMainBgPalettePointers)
Dungeon Palettes:     $0DD734 (kDungeonsPalettes)

8.4 Tile Info Format

struct TileInfo {
    uint16_t id;           // Tile index (10 bits)
    uint8_t palette;       // Palette (3 bits)
    bool h_flip;           // Horizontal mirror
    bool v_flip;           // Vertical mirror
    bool priority;         // Priority bit
};

// Decode from 16-bit word
TileInfo decode(uint16_t word) {
    TileInfo t;
    t.id = word & 0x03FF;
    t.priority = (word >> 10) & 1;
    t.palette = (word >> 11) & 0x07;
    t.h_flip = (word >> 14) & 1;
    t.v_flip = (word >> 15) & 1;
    return t;
}

9. Implementation Gaps in yaze

9.1 Critical Gaps (Must Fix for Correct Rendering)

Gap Severity Description ZScream Reference
Type 3 Objects Critical Stub implementation, simplified drawing Subtype3_Draw.cs
Door Rendering Critical LoadDoors() is stub, no type handling Doors_Draw.cs
all_bgs Flag Ignored Critical Uses hardcoded routine IDs (3,9,17,18) instead Room_Object.cs allBgs
Floor Rendering High Floor values loaded but not rendered Room.cs floor1/floor2
Type 2 Complex Layouts High Missing column, bed, spiral stair handling Subtype2_Multiple.cs
Layer Merge Effects High Flags exist but not applied during render LayerMergeType.cs

9.2 Missing Systems

System Status in yaze ZScream Implementation
Pot Items Not implemented Items_Draw.cs (28 types)
Key Drop Visualization Detection only, no draw Sprite.cs DrawKey()
Door Graphics Generic object render Doors_Draw.cs (40+ types)
Item-Sprite Linking Not implemented PotItem.cs
Selection State Not tracked Room_Object.cs selected
Unique Sprite IDs Not tracked ROM.uniqueSpriteID

9.3 Architectural Differences

Aspect yaze Approach ZScream Approach Recommendation
Object Classes Single RoomObject class Per-ID classes (object_00, etc.) Keep unified, add type handlers
Draw Routines 38 shared lambdas 256+ override methods Keep yaze approach
Tile Loading On-demand parser Pre-loaded static arrays Keep yaze approach
Layer Selection Binary choice (BG1/BG2) Enum with BG3 Add BG3 support

9.4 Fix Priority List

Phase 1: Core Rendering

  1. Fix all_bgs_ flag usage instead of hardcoded routine IDs
  2. Implement proper floor rendering from floor1/floor2 values
  3. Complete Type 3 object drawing (Somaria paths, etc.)
  4. Add missing Type 2 object patterns

Phase 2: Doors 5. Implement door type classification system 6. Add special door graphics (caves, holes, hidden walls) 7. Mirror effect for bidirectional doors 8. Layer-specific door rendering

Phase 3: Items & Sprites 9. Implement PotItem system (28 types) 10. Add key drop visualization 11. Link items to sprites for drops 12. Selection state tracking

Phase 4: Polish 13. Layer merge effect application 14. BG3 layer support 15. Complete bounds checking 16. Dimension calculation for complex objects


10. ROM Address Reference

10.1 Room Data

constexpr int kRoomObjectLayoutPointer = 0x882D;
constexpr int kRoomObjectPointer = 0x874C;
constexpr int kRoomHeaderPointer = 0xB5DD;
constexpr int kRoomHeaderPointerBank = 0xB5E7;
constexpr int kNumberOfRooms = 296;

10.2 Graphics

constexpr int kTileAddress = 0x001B52;
constexpr int kTileAddressFloor = 0x001B5A;
constexpr int kRoomObjectSubtype1 = 0x8000;
constexpr int kRoomObjectSubtype2 = 0x83F0;
constexpr int kRoomObjectSubtype3 = 0x84F0;
constexpr int kGfxGroupsPointer = 0x6237;

10.3 Palettes

constexpr int kDungeonsMainBgPalettePointers = 0xDEC4B;
constexpr int kDungeonsPalettes = 0xDD734;

10.4 Sprites & Items

constexpr int kRoomItemsPointers = 0xDB69;
constexpr int kRoomsSpritePointer = 0x4C298;
constexpr int kSpriteBlocksetPointer = 0x5B57;
constexpr int kSpritesData = 0x4D8B0;
constexpr int kDungeonSpritePointers = 0x090000;

10.5 Blocks & Features

constexpr int kBlocksLength = 0x8896;
constexpr int kBlocksPointer1 = 0x15AFA;
constexpr int kBlocksPointer2 = 0x15B01;
constexpr int kBlocksPointer3 = 0x15B08;
constexpr int kBlocksPointer4 = 0x15B0F;

10.6 Chests & Torches

constexpr int kChestsLengthPointer = 0xEBF6;
constexpr int kChestsDataPointer1 = 0xEBFB;
constexpr int kTorchData = 0x2736A;
constexpr int kTorchesLengthPointer = 0x88C1;

10.7 Pits & Doors

constexpr int kPitPointer = 0x394AB;
constexpr int kPitCount = 0x394A6;
constexpr int kDoorPointers = 0xF83C0;
constexpr int kDoorGfxUp = 0x4D9E;
constexpr int kDoorGfxDown = 0x4E06;
constexpr int kDoorGfxLeft = 0x4E66;
constexpr int kDoorGfxRight = 0x4EC6;

Appendix A: Object ID Quick Reference

Subtype 1 (0x00-0xF7) - Scalable

Range Category
0x00-0x07 Wall segments
0x08-0x0B Pit edges
0x0C-0x20 Diagonal walls (allBgs=true)
0x21-0x30 Rails and supports
0x31-0x40 Carpets and trim
0x41-0x50 Decorations
0xD0-0xD7 Floor patterns
0xE0-0xE7 Ceiling patterns
0xF0-0xF3 Conveyor belts

Subtype 2 (0x100-0x13F) - Fixed Size

Range Category
0x100-0x107 Corners
0x108-0x10F Braziers/statues
0x110-0x117 Star tiles
0x118-0x11F Torches/furniture
0x120-0x127 Stairs
0x128-0x12F Blocks/platforms
0x130-0x13F Misc decorations

Subtype 3 (0x200-0x27E) - Special

Range Category
0x200-0x207 Waterfall faces
0x208-0x20F Somaria paths
0x210-0x217 Item piles
0x218-0x21F Chests
0x220-0x227 Pipes/conveyors
0x228-0x22F Pegs/switches
0x230-0x23F Boss objects
0x240-0x27E Misc special

Appendix B: Drawing Routine Reference

ID Routine Name Pattern Objects
0 Rightwards2x2_1to15or32 2x2 horizontal 0x00, walls
1 Rightwards2x4_1to15or26 2x4 horizontal 0x01-0x02
2 Downwards2x2_1to15or32 2x2 vertical vertical walls
3 Rightwards2x2_BothBG 2x2 both layers 0x03-0x04
4 Rightwards2x4spaced4_1to16 2x4 spaced 0x05-0x06
5 DiagonalAcute_1to16 Diagonal / stairs
6 DiagonalGrave_1to16 Diagonal \ stairs
7 Downwards4x2_1to15or26 4x2 vertical wide columns
8 4x4 Fixed 4x4 Type 2 objects
9 Downwards4x2_BothBG 4x2 both layers special walls
17 DiagonalAcute_BothBG Diagonal both diagonal walls
18 DiagonalGrave_BothBG Diagonal both diagonal walls

Document generated from analysis of yaze, ZScream, and usdasm codebases. Last updated: 2025-12-01