Files
yaze/docs/internal/zelda3/overworld-tail-expansion.md

5.9 KiB

Overworld Tail Map Expansion (0xA0-0xBF)

Consolidated: 2025-12-08 Status: IMPLEMENTED Owner: zelda3-hacking-expert / overworld-specialist Purpose: Enable editing of special-world tail map slots (0xA0-0xBF) without corrupting existing data


Quick Start

# Apply tail expansion to a ZSCustomOverworld v3 ROM
z3ed overworld-doctor --rom=zelda3.sfc --apply-tail-expansion --output=expanded.sfc

# Or apply manually with Asar (after ZSCustomOverworld v3)
asar assets/patches/Overworld/TailMapExpansion.asm zelda3.sfc

Prerequisites:

  1. ZSCustomOverworld v3 must be applied first
  2. ROM must be 2MB (standard expanded size)

Problem Statement

The vanilla pointer tables only have 160 entries (maps 0x00-0x9F).

Original High Table: PC 0x1794D (SNES $02:F94D), 160 entries x 3 bytes
Original Low Table:  PC 0x17B2D (SNES $02:FB2D), 160 entries x 3 bytes

Writing to entries for maps 0xA0+ would overwrite existing data, corrupting Light World maps 0x00-0x1F.


Solution: TailMapExpansion.asm

The TailMapExpansion.asm patch relocates pointer tables to safe free space with 192 entries:

Component PC Address SNES Address Size
Detection Marker 0x1423FF $28:A3FF 1 byte (value: 0xEA)
New High Table 0x142400 $28:A400 576 bytes (192 x 3)
New Low Table 0x142640 $28:A640 576 bytes (192 x 3)
Blank Map High 0x180000 $30:8000 188 bytes
Blank Map Low 0x181000 $30:9000 4 bytes

Game Code Patches

The patch updates 8 LDA instructions in bank $02:

Function 1: Overworld_DecompressAndDrawOneQuadrant

PC Address Original Patched
0x1F59D LDA.l $02F94D,X LDA.l $28A400,X
0x1F5A3 LDA.l $02F94E,X LDA.l $28A401,X
0x1F5C8 LDA.l $02FB2D,X LDA.l $28A640,X
0x1F5CE LDA.l $02FB2E,X LDA.l $28A641,X

Function 2: Secondary quadrant loader

PC Address Original Patched
0x1F7E3 LDA.l $02F94D,X LDA.l $28A400,X
0x1F7E9 LDA.l $02F94E,X LDA.l $28A401,X
0x1F80E LDA.l $02FB2D,X LDA.l $28A640,X
0x1F814 LDA.l $02FB2E,X LDA.l $28A641,X

ROM Layout (LoROM, no header)

Expanded Data Regions (DO NOT OVERWRITE)

Region PC Start PC End SNES Contents
Tile16 Expanded 0x1E8000 0x1EFFFF $3D:0000-$3D:FFFF 4096 tile16 entries
Map32 BL Expanded 0x1F0000 0x1F7FFF $3E:0000-$3E:7FFF Map32 bottom-left tiles
Map32 BR Expanded 0x1F8000 0x1FFFFF $3E:8000-$3F:FFFF Map32 bottom-right tiles
ZSCustom Tables 0x140000 0x1421FF $28:8000+ Custom overworld arrays
Overlay Space 0x120000 0x12FFFF $24:8000+ Expanded overlay data

Safe Free Space

PC Start PC End Size SNES Bank Notes
0x1422B2 0x17FFFF ~245 KB $28-$2F Primary free space
0x180000 0x1DFFFF 384 KB $30-$3B Additional free space

WARNING: Do NOT use addresses 0x1E0000+ for new data!


CLI Tools

overworld-doctor

Full diagnostic and repair:

# Diagnose only
z3ed overworld-doctor --rom=file.sfc --verbose

# Compare with baseline
z3ed overworld-doctor --rom=file.sfc --baseline=vanilla.sfc

# Apply tile16 fixes
z3ed overworld-doctor --rom=file.sfc --fix --output=fixed.sfc

# Apply tail map expansion
z3ed overworld-doctor --rom=file.sfc --apply-tail-expansion --output=expanded.sfc

# Preview tail expansion (dry run)
z3ed overworld-doctor --rom=file.sfc --apply-tail-expansion --dry-run

overworld-validate

Validate map pointers and decompression:

z3ed overworld-validate --rom=file.sfc              # Check maps 0x00-0x9F
z3ed overworld-validate --rom=file.sfc --include-tail  # Include 0xA0-0xBF
z3ed overworld-validate --rom=file.sfc --check-tile16  # Check tile16 region

rom-compare

Compare two ROMs:

z3ed rom-compare --rom=target.sfc --baseline=vanilla.sfc --verbose --show-diff

Detection

yaze detects expanded pointer tables by checking for the marker byte:

// In src/zelda3/overworld/overworld.h
bool HasExpandedPointerTables() const {
  return rom_->data()[0x1423FF] == 0xEA;
}

The loading code checks BOTH conditions before allowing tail map access:

const bool allow_special_tail =
    core::FeatureFlags::get().overworld.kEnableSpecialWorldExpansion &&
    HasExpandedPointerTables();

Source Files

File Purpose
assets/patches/Overworld/TailMapExpansion.asm ASM patch file
src/zelda3/overworld/overworld.h HasExpandedPointerTables() method
src/zelda3/overworld/overworld.cc Tail map guards
src/cli/handlers/tools/overworld_doctor_commands.cc CLI apply logic
src/cli/handlers/tools/overworld_validate_commands.cc Validator command
src/cli/handlers/tools/rom_compare_commands.cc ROM comparison
src/cli/handlers/tools/diagnostic_types.h Constants

Testing

# 1. Check ROM baseline
z3ed rom-doctor --rom vanilla.sfc --format json

# 2. Apply tail expansion patch
z3ed overworld-doctor --rom vanilla.sfc --apply-tail-expansion --output expanded.sfc

# 3. Verify expansion marker
z3ed rom-doctor --rom expanded.sfc --format json | jq '.expanded_pointer_tables'

# 4. Run integration tests
z3ed test-run --label rom_dependent --format json

Known Issues / History

Previous Errors (FIXED)

The original padding attempted to use addresses inside the expanded tile16 region:

  • 0x1E878B, 0x1E95A3, 0x1ED6F3, 0x1EF540

These caused tile corruption. The overworld-doctor CLI tool can detect and repair this corruption.

Remaining Tasks

  1. Integration testing with fully patched ROM
  2. GUI indicator in Overworld Editor showing tail map availability
  3. Unit tests for marker detection and guard logic