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

208 lines
5.9 KiB
Markdown

# 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
```bash
# 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:
```bash
# 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:
```bash
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:
```bash
z3ed rom-compare --rom=target.sfc --baseline=vanilla.sfc --verbose --show-diff
```
---
## Detection
yaze detects expanded pointer tables by checking for the marker byte:
```cpp
// 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:
```cpp
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
```bash
# 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