backend-infra-engineer: Release v0.3.9-hotfix7 snapshot

This commit is contained in:
scawful
2025-11-23 13:37:10 -05:00
parent c8289bffda
commit 2934c82b75
202 changed files with 34914 additions and 845 deletions

View File

@@ -0,0 +1,61 @@
# Dungeon Editor System Architecture
**Status**: Draft
**Last Updated**: 2025-11-21
**Related Code**: `src/app/editor/dungeon/`, `src/zelda3/dungeon/`, `test/integration/dungeon_editor_v2_test.cc`, `test/e2e/dungeon_editor_smoke_test.cc`
## Overview
DungeonEditorV2 is the ImGui-based dungeon editor for *A Link to the Past*. It uses a card/docking
layout and delegates most logic to small components:
- **DungeonRoomLoader** (`dungeon_room_loader.{h,cc}`): Reads rooms/entrances from the ROM, caches per-room palette metadata, and (optionally) loads all rooms in parallel.
- **DungeonRoomSelector** (`dungeon_room_selector.{h,cc}`): Lists rooms, matrix navigation, and entrance jump-to.
- **DungeonCanvasViewer** (`dungeon_canvas_viewer.{h,cc}`): Renders BG1/BG2 bitmaps per room, manages per-room layer visibility, and drives mouse interaction.
- **DungeonObjectInteraction** (`dungeon_object_interaction.{h,cc}`): Selection, multi-select, drag/move, copy/paste, and ghost previews on the canvas.
- **DungeonObjectSelector** (`dungeon_object_selector.{h,cc}`): Asset-browser style object picker and compact editors for sprites/items/doors/chests/properties (UI only).
- **ObjectEditorCard** (`object_editor_card.{h,cc}`): Unified object editor card.
- **DungeonEditorSystem** (`zelda3/dungeon/dungeon_editor_system.{h,cc}`): Planned orchestration layer for sprites/items/doors/chests/room properties (mostly stubbed today).
- **Room Model** (`zelda3/dungeon/room.{h,cc}`): Holds room metadata, objects, sprites, background buffers, and encodes objects back to ROM.
The editor acts as a coordinator: it wires callbacks between selector/interaction/canvas, tracks
tabbed room cards, and queues texture uploads through `gfx::Arena`.
## Data Flow (intended)
1. **Load**
- `DungeonRoomLoader::LoadRoom` loads room headers/objects/sprites for a room on demand.
- `DungeonRoomLoader::LoadRoomEntrances` fills `entrances_` for navigation.
- Palettes are pulled from `Rom::palette_group().dungeon_main`.
2. **Render**
- `Room::LoadRoomGraphics` pulls blockset tiles into the rooms private BG1/BG2 buffers.
- `Room::RenderRoomGraphics` renders objects into BG buffers; `DungeonCanvasViewer` queues textures and draws with grid/overlays.
3. **Interact**
- `DungeonObjectSelector` emits a preview object; `DungeonCanvasViewer` hands it to `DungeonObjectInteraction` for ghosting and placement.
- Selection/drag/copy/paste adjust `RoomObject` instances directly, then invalidate room graphics to trigger re-render.
4. **Save**
- `DungeonEditorV2::Save` currently saves palettes via `PaletteManager` then calls `Room::SaveObjects()` for all rooms.
- Other entities (sprites, doors, chests, entrances, items, room metadata) are not persisted yet.
## Current Limitations / Gaps
- **Undo/Redo**: `DungeonEditorV2` methods return `Unimplemented`; no command history is wired.
- **Persistence coverage**: Only tile objects (and palettes) are written back. Sprites, doors, chests, entrances, collision, pot drops, and room metadata are UI-only stubs through `DungeonEditorSystem`.
- **DungeonEditorSystem**: Exists as API scaffolding but does not load/save or render; panels in `DungeonObjectSelector` cannot commit changes to the ROM.
- **Object previews**: Selector uses primitive rectangles; no `ObjectDrawer`/real tiles are shown.
- **Tests**: Integration/E2E cover loading and card plumbing but not ROM writes for doors/chests/entrances or undo/redo flows.
## Suggested Next Steps
1. **Wire DungeonEditorSystem**: Initialize it in `DungeonEditorV2::Load`, back it with real ROM I/O for sprites/doors/chests/entrances/items/room properties, and sync UI panels to it.
2. **Undo/Redo**: Add a command stack (objects/sprites/palettes/metadata) and route `Ctrl+Z/Ctrl+Shift+Z`; re-use patterns from overworld editor if available.
3. **Save Pipeline**: Extend `DungeonEditorV2::Save` to call DungeonEditorSystem save hooks and verify round-trips in tests.
4. **Object Rendering**: Replace rectangle previews in `DungeonObjectSelector` with `ObjectDrawer`-based thumbnails to match in-canvas visuals.
5. **Test Coverage**: Add integration tests that:
- Place/delete objects and verify `Room::EncodeObjects` output changes in ROM.
- Add doors/chests/entrances and assert persistence once implemented.
- Exercise undo/redo on object placement and palette edits.
6. **Live Emulator Preview (optional)**: Keep `DungeonObjectEmulatorPreview` as a hook for live patching when the emulator integration lands.

View File

@@ -0,0 +1,69 @@
# Message System Architecture
**Status**: Draft
**Last Updated**: 2025-11-21
**Related Code**: `src/app/editor/message/`, `src/cli/handlers/game/message.cc`
This document outlines the architecture of the Message (Text) System in YAZE.
## Overview
The Message System manages the in-game dialogue and narration. ALttP uses a custom text engine with:
* **Proportional Font**: Variable width characters.
* **Dictionary Compression**: Common words/phrases are stored in a dictionary and referenced by single bytes to save space.
* **Command Codes**: Byte sequences control window layout, scrolling, text speed, and player interaction.
## Data Structures
### 1. MessageData
Represents a single dialogue entry.
* **ID**: Message index (0-396 in vanilla).
* **Address**: ROM offset.
* **RawString**: Human-readable text with dictionary tokens (e.g., `[D:01]`).
* **ContentsParsed**: Fully expanded text (e.g., `Link`).
* **Data**: Raw ROM bytes.
### 2. DictionaryEntry
A phrase used for compression.
* **ID**: Index (0x00 - 0x60).
* **Contents**: The text (e.g., " the ").
* **Token**: Representation in `RawString` (e.g., `[D:00]`).
### 3. TextElement
Represents special control codes or characters.
* **Commands**: `[W:02]` (Window Border), `[SPD:01]` (Scroll Speed).
* **Special Chars**: `[UP]` (Arrow), `[A]` (Button), `...` (Ellipsis).
## ROM Layout (Vanilla)
* **Bank 0E (0xE0000)**: Primary text data block (32KB).
* **Bank 0E (0x75F40)**: Secondary text data block (5.3KB).
* **Dictionary**: Pointers at `0x74703`.
* **Font Graphics**: 2BPP tiles at `0x70000`.
* **Character Widths**: Table at `0x74ADF`.
## Pipeline
### Loading
1. **Read**: `ReadAllTextData` scans the ROM text blocks.
2. **Parse**: Bytes are mapped to characters using `CharEncoder`.
3. **Expand**: Dictionary references (`0x88`+) are looked up and replaced with `[D:XX]` tokens.
4. **Preview**: `MessagePreview` renders the text to a bitmap using the font graphics and width table.
### Saving
1. **Parse**: User text is converted to bytes.
2. **Optimize**: `OptimizeMessageForDictionary` scans the text for dictionary phrases and replaces them with single-byte references.
3. **Write**: Data is written sequentially to the ROM text blocks. If the first block overflows, it spills into the second block.
## Editor UI
* **Message List**: Displays all messages with ID and preview.
* **Editor**: Multiline text input. Buttons to insert commands/special chars.
* **Preview**: Live rendering of the message box as it would appear in-game.
* **Dictionary**: Read-only view of dictionary entries.
## Limitations
* **Hardcoded Limits**: The text block sizes are fixed for vanilla.
* **Translation**: No specific tooling for side-by-side translation.
* **Export**: Limited to binary "Expanded Messages" format; no JSON/YAML support.

View File

@@ -0,0 +1,61 @@
# Music System Architecture
**Status**: Draft
**Last Updated**: 2025-11-21
**Related Code**: `src/zelda3/music/`, `src/app/editor/music/`
This document outlines the architecture of the Music System in YAZE, covering both the editor and the underlying engine.
## Overview
The Music System is designed to edit the soundtrack of *A Link to the Past*, which runs on the SNES **N-SPC** audio engine. The system consists of:
1. **Tracker Backend** (`src/zelda3/music/`): Parses binary ROM data into editable structures.
2. **Music Editor** (`src/app/editor/music/`): Provides a UI for playback and modification.
3. **Emulator Integration**: Uses the internal `Spc700` emulation for live preview.
## Core Components
### 1. The Tracker (`tracker.h`, `tracker.cc`)
Derived from the legacy "Hyrule Magic" C codebase, this class handles the low-level complexity of the N-SPC format.
* **Data Structures**:
* `SpcCommand`: A doubly-linked list node representing a single music event (note, rest, command).
* `Song`: A collection of `SongPart`s (tracks), typically 8 channels.
* `SongRange`: Metadata mapping a ROM address range to parsed commands.
* `ZeldaInstrument`: ADSR and sample definitions.
* **Parsing**:
* `LoadSongs`: Iterates through the game's pointer tables (Banks 0x1A, 0x1B) to load all music.
* `LoadSpcCommand`: Recursive descent parser for the byte-code stream.
* **Serialization**:
* `SaveSongs`: Re-packs the linked lists into binary blocks.
* `AllocSpcBlock`: Manages memory for the binary output.
### 2. Music Editor (`music_editor.cc`)
The frontend GUI built with ImGui.
* **Playback**:
* `PlaySong(int id)`: Writes to game RAM (`$7E012C`) to trigger the in-game song request mechanism via the emulator.
* **Visualization**:
* `DrawPianoRoll`: Renders note data (currently a placeholder).
* `DrawToolset`: Transport controls (Play/Stop/Rewind).
### 3. SPC700 Audio Engine
The SNES audio subsystem (APU) runs independently of the main CPU.
* **Communication**: The CPU uploads music data to the APU RAM (ARAM) via a handshake protocol on ports `$2140-$2143`.
* **Banks**:
* **Overworld**: Bank `$1A`
* **Underworld**: Bank `$1B`
* **Credits**: Bank `$1A` (offset)
## Data Flow
1. **Loading**: `MusicEditor::Initialize` -> `Tracker::LoadSongs` -> Parses ROM -> Populates `std::vector<Song>`.
2. **Editing**: User modifies `SpcCommand` linked lists (Not yet fully implemented in UI).
3. **Preview**: User clicks "Play". Editor writes ID to emulated RAM. Emulator NMI handler sees ID, uploads data to SPC700.
4. **Saving**: `Tracker::SaveSongs` -> Serializes commands -> Writes to ROM buffer -> Fixes pointers.
## Limitations
* **Vanilla-Centric**: The `Tracker` currently assumes vanilla bank sizes and offsets.
* **Legacy Code**: The parsing logic is essentially a C port and uses raw pointers/malloc heavily.
* **No Expansion**: Does not support the "Expanded Music" hack (relocated pointers) or "NewSPC" engine.