backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)

This commit is contained in:
scawful
2025-12-22 00:20:49 +00:00
parent 2934c82b75
commit 5c4cd57ff8
1259 changed files with 239160 additions and 43801 deletions

28
docs/public/README.md Normal file
View File

@@ -0,0 +1,28 @@
# Public Docs Guide (Doxygen-Friendly)
Purpose: keep `docs/public` readable, accurate, and exportable via Doxygen.
## Authoring checklist
- One H1 per page; keep openings short (23 lines) and outcome-focused.
- Lead with “who/what/why” and a minimal quick-start or TL;DR when relevant.
- Prefer short sections and bullet lists over walls of text; keep code blocks minimal and copy-pastable.
- Use relative links within `docs/public`; point to `docs/internal` only when the public doc would otherwise be incomplete.
- Label platform-specific steps explicitly (macOS/Linux/Windows) and validate against current `CMakePresets.json`.
- Avoid agent-only workflow details; those belong in `docs/internal`.
## Doxygen structure
- `docs/public/index.md` serves as the `@mainpage` with concise navigation.
- Keep headings shallow (H1H3) so Doxygen generates clean TOCs.
- Include a short “New here?” or quick-start block on entry pages to aid scanning.
## Accuracy cadence
- Re-verify build commands and presets after CMake or CI changes.
- Review public docs at least once per release cycle; archive or rewrite stale guidance instead of adding new pages.
## Naming & formatting
- Use kebab-case filenames; reserve ALL-CAPS for anchors like README/CONTRIBUTING/AGENTS/GEMINI/CLAUDE.
- Prefer present tense, active voice, and consistent terminology (e.g., “yaze”, “z3ed”, “preset”).
## When to add vs. link
- Add a new public page only if it benefits external readers; otherwise, link to the relevant internal doc.
- For experimental or rapidly changing features, keep the source in `docs/internal` and expose a short, stable summary here.

View File

@@ -1,89 +1,124 @@
# Build Instructions
# Build from Source
yaze uses a modern CMake build system with presets for easy configuration. This guide explains the
environment checks, dependencies, and platform-specific considerations. For concise per-platform
commands, always start with the [Build & Test Quick Reference](quick-reference.md).
YAZE uses a modern CMake build system with presets for easy configuration. This guide covers environment setup, dependencies, and platform-specific considerations.
> **Quick Start:** For concise build commands, see the [Build and Test Quick Reference](quick-reference.md).
---
## 1. Environment Verification
**Before your first build**, run the verification script to ensure your environment is configured correctly.
Before your first build, run the verification script to ensure your environment is configured correctly.
### Windows (PowerShell)
```powershell
.\scripts\verify-build-environment.ps1
# With automatic fixes
.\scripts\verify-build-environment.ps1 -FixIssues
```
> Tip: After verification, run `.\scripts\setup-vcpkg-windows.ps1` to bootstrap vcpkg, ensure `clang-cl`/Ninja are installed, and cache the `x64-windows` triplet.
### macOS & Linux (Bash)
> **Tip:** After verification, run `.\scripts\setup-vcpkg-windows.ps1` to bootstrap vcpkg, install clang-cl/Ninja, and cache the x64-windows triplet.
### macOS and Linux
```bash
./scripts/verify-build-environment.sh
# With automatic fixes
./scripts\verify-build-environment.sh --fix
./scripts/verify-build-environment.sh --fix
```
The script checks for required tools like CMake, a C++23 compiler, and platform-specific dependencies.
The script checks for CMake, a C++23 compiler, and platform-specific dependencies.
---
## 2. Using Presets
- Pick the preset that matches your platform/workflow (debug: `mac-dbg` / `lin-dbg` / `win-dbg`,
AI-enabled: `mac-ai` / `win-ai`, release: `*-rel`, etc.).
- Configure with `cmake --preset <name>` and build with `cmake --build --preset <name> [--target …]`.
- Add `-v` to a preset name (e.g., `mac-dbg-v`) to surface compiler warnings.
- Need a full matrix? See the [CMake Presets Guide](presets.md) for every preset and the quick
reference for ready-to-run command snippets.
Select a preset that matches your platform and workflow:
## Feature Toggles & Windows Profiles
| Workflow | Presets |
|----------|---------|
| Debug builds | `mac-dbg`, `lin-dbg`, `win-dbg` |
| AI-enabled builds | `mac-ai`, `lin-ai`, `win-ai` |
| Release builds | `mac-rel`, `lin-rel`, `win-rel` |
| Development (ROM tests) | `mac-dev`, `lin-dev`, `win-dev` |
**Build Commands:**
```bash
cmake --preset <name> # Configure
cmake --build --preset <name> --target yaze # Build
```
Add `-v` suffix (e.g., `mac-dbg-v`) to enable verbose compiler warnings.
See the [CMake Presets Guide](presets.md) for the complete preset reference.
---
## Feature Toggles
### Windows Presets
| Preset | Purpose |
| --- | --- |
| `win-dbg`, `win-rel`, `ci-windows` | Core builds without agent UI, gRPC, or AI runtimes. Fastest option for MSVC/clang-cl. |
| `win-ai`, `win-vs-ai` | Full agent stack for local development (UI panels + remote automation + AI runtime). |
| `ci-windows-ai` | Nightly/weekly CI preset that exercises the entire automation stack on Windows. |
|--------|---------|
| `win-dbg`, `win-rel` | Core builds without agent UI or AI. Fastest option. |
| `win-ai`, `win-vs-ai` | Full agent stack (UI + automation + AI runtime) |
| `ci-windows-ai` | CI preset for the complete automation stack |
### Agent Feature Flags
### CMake Feature Flags
| Option | Default | Effect |
| --- | --- | --- |
| `YAZE_BUILD_AGENT_UI` | `ON` when `YAZE_BUILD_GUI=ON` | Builds the ImGui widgets used by the chat/agent panels. |
| `YAZE_ENABLE_REMOTE_AUTOMATION` | `ON` for `*-ai` presets | Adds gRPC/protobuf services plus GUI automation clients. |
| `YAZE_ENABLE_AI_RUNTIME` | `ON` for `*-ai` presets | Enables Gemini/Ollama transports, proposal planning, and advanced routing logic. |
| `YAZE_ENABLE_AGENT_CLI` | `ON` when `YAZE_BUILD_CLI=ON` | Compiles the conversational agent stack consumed by `z3ed`. |
| Option | Default | Description |
|--------|---------|-------------|
| `YAZE_BUILD_AGENT_UI` | ON with GUI | ImGui chat/agent panels |
| `YAZE_ENABLE_REMOTE_AUTOMATION` | ON for `*-ai` | gRPC services and automation |
| `YAZE_ENABLE_AI_RUNTIME` | ON for `*-ai` | Gemini/Ollama AI providers |
| `YAZE_ENABLE_AGENT_CLI` | ON with CLI | z3ed agent commands |
Combine these switches to match your workflow: keep everything `OFF` for lightweight GUI hacking or turn them `ON` for automation-heavy work with sketchybar/yabai/skhd, tmux, or remote runners.
Keep features `OFF` for lightweight GUI development, or enable them for automation workflows.
---
## 3. Dependencies
- **Required**: CMake 3.16+, C++23 Compiler (GCC 13+, Clang 16+, MSVC 2019+), Git.
- **Bundled**: All other dependencies (SDL2, ImGui, Asar, nlohmann/json, cpp-httplib, GoogleTest, etc.) live under the `ext/` directory or are managed by CMake's `FetchContent`. No external package manager is required for a basic build.
- **Optional**:
- **gRPC**: For GUI test automation. Can be enabled with `-DYAZE_WITH_GRPC=ON`.
- **vcpkg (Windows)**: Can be used for faster gRPC builds on Windows (optional).
### Required
- **CMake** 3.16 or later
- **C++23 Compiler**: GCC 13+, Clang 16+, or MSVC 2022 17.4+
- **Git**
### Bundled (No Installation Required)
All core dependencies are included in the `ext/` directory or fetched automatically:
- SDL2, ImGui, Asar, nlohmann/json, cpp-httplib, GoogleTest
### Optional
- **gRPC**: Required for GUI automation and AI features. Enable with `-DYAZE_ENABLE_GRPC=ON`.
- **vcpkg (Windows)**: Speeds up gRPC builds on Windows.
---
## 4. Platform Setup
### macOS
```bash
# Install Xcode Command Line Tools
xcode-select --install
# Recommended: Install build tools via Homebrew
# Install build tools via Homebrew
brew install cmake pkg-config
# For sandboxed/offline builds: Install dependencies to avoid network fetch
# Optional: For sandboxed/offline builds
brew install yaml-cpp googletest
```
**Note**: When building in sandboxed/offline environments (e.g., via Claude Code or restricted networks), install `yaml-cpp` and `googletest` via Homebrew to avoid GitHub fetch failures. The build system automatically detects Homebrew installations and uses them as fallback:
- yaml-cpp: `/opt/homebrew/opt/yaml-cpp`, `/usr/local/opt/yaml-cpp`
- googletest: `/opt/homebrew/opt/googletest`, `/usr/local/opt/googletest`
> **Note:** In sandboxed environments, install yaml-cpp and googletest via Homebrew to avoid network fetch failures. The build system auto-detects Homebrew installations at `/opt/homebrew/opt/` (Apple Silicon) or `/usr/local/opt/` (Intel).
### Linux (Ubuntu/Debian)
```bash
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build pkg-config \
@@ -91,15 +126,21 @@ sudo apt-get install -y build-essential cmake ninja-build pkg-config \
```
### Windows
1. **Install Visual Studio 2022** with the “Desktop development with C++” workload (requires MSVC + MSBuild).
2. **Install Ninja** (recommended): `choco install ninja` or enable the “CMake tools for Windows” optional component.
3. Run the verifier: `.\scripts\verify-build-environment.ps1 -FixIssues` this checks Visual Studio workloads, Ninja, clang-cl, Git settings, and vcpkg cache.
4. Bootstrap vcpkg once: `.\scripts\setup-vcpkg-windows.ps1` (prefetches SDL2, yaml-cpp, etc.).
5. Use the `win-*` presets (Ninja) or `win-vs-*` presets (Visual Studio generator) as needed. For AI/gRPC features, prefer `win-ai` / `win-vs-ai`.
6. For quick validation, run the PowerShell helper:
1. **Install Visual Studio 2022** with "Desktop development with C++" workload
2. **Install Ninja** (recommended): `choco install ninja` or via Visual Studio Installer
3. **Run the verifier:**
```powershell
pwsh -File scripts/agents/windows-smoke-build.ps1 -Preset win-ai -Target z3ed
.\scripts\verify-build-environment.ps1 -FixIssues
```
4. **Bootstrap vcpkg:**
```powershell
.\scripts\setup-vcpkg-windows.ps1
```
5. **Build:**
- Use `win-*` presets with Ninja generator
- Use `win-vs-*` presets for Visual Studio IDE
- For AI features, use `win-ai` or `win-vs-ai`
## 5. Testing

View File

@@ -0,0 +1,71 @@
# YAZE Installation Options (Distribution Guide)
Status: Draft
Audience: Users/distributors who want alternatives to direct GitHub release binaries.
## Overview
YAZE is distributed primarily via GitHub release binaries. This guide summarizes current install paths and outlines packaging-friendly options per platform. Use the table to pick what is available today vs. what would require packaging work.
## Platform Matrix
| Platform | Status | Recommended Path | Notes |
|----------|--------|------------------|-------|
| macOS (Intel/Apple) | Available | GitHub release tarball; custom Homebrew tap (see below) | Prefer Apple silicon builds; Intel works under Rosetta. |
| Windows (x64) | Available | GitHub release zip; vcpkg-from-source (community) | No official winget/choco package yet. |
| Linux (x86_64) | Available | GitHub release AppImage (if provided) or build from source | Test on Ubuntu/Debian/Fedora; Wayland users may need XWayland. |
| Web (WASM) | Preview | Hosted demo or local `npm http-server` of `build-wasm` artifact | Requires modern browser; no install. |
## macOS
### 1) Release binary (recommended)
1. Download the macOS tarball from GitHub releases.
2. `tar -xf yaze-<version>-macos.tar.gz && cd yaze-<version>-macos`
3. Run `./yaze.app/Contents/MacOS/yaze` (GUI) or `./bin/z3ed` (CLI).
### 2) Homebrew (custom tap)
- If you publish a tap: `brew tap <your/tap>` then `brew install yaze`.
- Sample formula inputs:
- URL: GitHub release tarball.
- Dependencies: `cmake`, `ninja`, `pkg-config`, `sdl2`, `glew`, `glm`, `ftxui`, `abseil`, `protobuf`, `gtest`.
- For development builds: `cmake --preset mac-dbg` then `cmake --build --preset mac-dbg`.
## Windows
### 1) Release zip (recommended)
1. Download the Windows zip from GitHub releases.
2. Extract to a writable directory.
3. Run `yaze.exe` (GUI) or `z3ed.exe` (CLI) from the `bin` folder.
### 2) vcpkg-from-source (DIY)
If you prefer source builds with vcpkg dependencies:
1. Install vcpkg and integrate: `vcpkg integrate install`.
2. Configure: `cmake --preset win-dbg -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake`.
3. Build: `cmake --build --preset win-dbg`.
4. Run tests (optional): `ctest --test-dir build`.
## Linux
### 1) Release AppImage (if available)
- `chmod +x yaze-<version>-linux.AppImage && ./yaze-<version>-linux.AppImage`
- If graphics fail under Wayland, try `XWAYLAND_FORCE=1 ./yaze-<version>-linux.AppImage`.
### 2) Build from source
Prereqs: `cmake`, `ninja-build`, `pkg-config`, `libsdl2-dev`, `libglew-dev`, `libglm-dev`, `protobuf-compiler`, `libprotobuf-dev`, `libabsl-dev`, `libftxui-dev` (or build from source), `zlib1g-dev`.
```
cmake --preset lin-dbg
cmake --build --preset lin-dbg
ctest --test-dir build -L stable # optional
```
## Web (WASM Preview)
- Use the published web build (if provided) or self-host the `build-wasm` output:
```
cd build-wasm && npx http-server .
```
- Open the local URL in a modern browser; no installation required.
## Packaging Notes
- Prefer static/runtime-complete bundles for end users (AppImage on Linux, app bundle on macOS, zip on Windows).
- When creating packages (Homebrew/Chocolatey/winget), pin the release URL and checksum and align dependencies to the CMake presets (`mac-*/lin-*/win-*`).
- Keep CLI and GUI in the same archive to avoid mismatched versions; CLI entry is `z3ed`, GUI entry is `yaze`.
## Quick Links
- Build quick reference: `docs/public/build/quick-reference.md`
- CMake presets: `CMakePresets.json`
- Tests (optional after build): `ctest --test-dir build -L stable`

View File

@@ -1,6 +1,6 @@
# Platform Compatibility & CI/CD Fixes
# Platform Compatibility & CI/CD
**Last Updated**: October 9, 2025
**Last Updated**: November 27, 2025
---

View File

@@ -114,8 +114,7 @@ By default, all presets suppress compiler warnings with `-w` for a cleaner build
## Build Directories
Most presets use `build/` directory. Exceptions:
- `mac-rooms`: Uses `build_rooms/` to avoid conflicts
Most presets use `build/`. WASM presets use `build-wasm/`. Use `CMakeUserPresets.json` for custom directories.
## Feature Flags

View File

@@ -1,149 +1,185 @@
# Build & Test Quick Reference
# Build and Test Quick Reference
Use this document as the single source of truth for configuring, building, and testing YAZE across
platforms. Other guides (README, CLAUDE.md, GEMINI.md, etc.) should link here instead of duplicating
steps.
This document is the single source of truth for configuring, building, and testing YAZE on all platforms. Other documentation files link here rather than duplicating these instructions.
## 1. Environment Prep
- Clone with submodules: `git clone --recursive https://github.com/scawful/yaze.git`
- Run the verifier once per machine:
- macOS/Linux: `./scripts/verify-build-environment.sh --fix`
- Windows PowerShell: `.\scripts\verify-build-environment.ps1 -FixIssues`
---
## 1. Environment Setup
### Clone the Repository
```bash
git clone --recursive https://github.com/scawful/yaze.git
cd yaze
```
### Verify Your Environment
Run the verification script once per machine to check dependencies and fix common issues:
**macOS / Linux:**
```bash
./scripts/verify-build-environment.sh --fix
```
**Windows (PowerShell):**
```powershell
.\scripts\verify-build-environment.ps1 -FixIssues
```
---
## 2. Build Presets
Use `cmake --preset <name>` followed by `cmake --build --preset <name> [--target …]`.
| Preset | Platform(s) | Notes |
|-------------|-------------|-------|
| `mac-dbg`, `lin-dbg`, `win-dbg` | macOS/Linux/Windows | Standard debug builds, tests on by default. |
| `mac-ai`, `lin-ai`, `win-ai` | macOS/Linux/Windows | Enables gRPC, agent UI, `z3ed`, and AI runtime. |
| `mac-rel`, `lin-rel`, `win-rel` | macOS/Linux/Windows | Optimized release builds. |
| `mac-dev`, `lin-dev`, `win-dev` | macOS/Linux/Windows | Development builds with ROM-dependent tests enabled. |
| `mac-uni` | macOS | Universal binary (ARM64 + x86_64) for distribution. |
| `ci-*` presets | Platform-specific | Mirrors CI matrix; see `CMakePresets.json`. |
YAZE uses CMake presets for consistent builds. Configure with `cmake --preset <name>`, then build with `cmake --build --preset <name>`.
**Verbose builds**: add `-v` suffix (e.g., `mac-dbg-v`, `lin-dbg-v`, `win-dbg-v`) to turn off compiler warning suppression.
### Available Presets
## 3. AI/Assistant Build Policy
- Human developers typically use `build` or `build_test` directories.
- AI assistants **must use dedicated directories** (`build_ai`, `build_agent`, etc.) to avoid
clobbering user builds.
- When enabling AI features, prefer the `*-ai` presets and target only the binaries you need
(`yaze`, `z3ed`, `yaze_test`, …).
- Windows helpers: use `scripts/agents/windows-smoke-build.ps1` for quick builds and `scripts/agents/run-tests.sh` (or its PowerShell equivalent) for test runs so preset + generator settings stay consistent.
| Preset | Platform | Description |
|--------|----------|-------------|
| `mac-dbg`, `lin-dbg`, `win-dbg` | macOS / Linux / Windows | Standard debug builds with tests enabled |
| `mac-ai`, `lin-ai`, `win-ai` | macOS / Linux / Windows | Full AI stack: gRPC, agent UI, z3ed CLI, AI runtime |
| `mac-rel`, `lin-rel`, `win-rel` | macOS / Linux / Windows | Optimized release builds |
| `mac-dev`, `lin-dev`, `win-dev` | macOS / Linux / Windows | Development builds with ROM-dependent tests |
| `mac-uni` | macOS | Universal binary (ARM64 + x86_64) for distribution |
| `mac-test`, `lin-test`, `win-test` | All | Optimized builds for fast test iteration |
| `ci-*` | Platform-specific | CI/CD configurations (see CMakePresets.json) |
## 4. Common Commands
**Tip:** Add `-v` suffix (e.g., `mac-dbg-v`) to enable verbose compiler warnings.
---
## 3. Build Directory Policy
| Build Type | Default Directory |
|------------|-------------------|
| Native (desktop/CLI) | `build/` |
| WASM | `build-wasm/` |
If you need per-user or per-agent isolation, create a local `CMakeUserPresets.json` that points `binaryDir` to a custom path.
Example:
```bash
cp CMakeUserPresets.json.example CMakeUserPresets.json
export YAZE_BUILD_ROOT="$HOME/.cache/yaze"
cmake --preset dev-local
cmake --build --preset dev-local --target yaze
```
For AI-enabled builds, use the `*-ai` presets and specify only the targets you need:
```bash
cmake --build --preset mac-ai --target yaze z3ed
```
**Windows Helper Scripts:**
- Quick builds: `scripts/agents/windows-smoke-build.ps1`
- Test runs: `scripts/agents/run-tests.sh` (or PowerShell equivalent)
---
## 4. Common Build Commands
### Standard Debug Build
**macOS:**
```bash
# Debug GUI build (macOS)
cmake --preset mac-dbg
cmake --build --preset mac-dbg --target yaze
```
# Debug GUI build (Linux)
**Linux:**
```bash
cmake --preset lin-dbg
cmake --build --preset lin-dbg --target yaze
```
# Debug GUI build (Windows)
**Windows:**
```bash
cmake --preset win-dbg
cmake --build --preset win-dbg --target yaze
```
# AI-enabled build with gRPC (macOS)
### AI-Enabled Build (with gRPC and z3ed CLI)
**macOS:**
```bash
cmake --preset mac-ai
cmake --build --preset mac-ai --target yaze z3ed
```
# AI-enabled build with gRPC (Linux)
**Linux:**
```bash
cmake --preset lin-ai
cmake --build --preset lin-ai --target yaze z3ed
```
# AI-enabled build with gRPC (Windows)
**Windows:**
```bash
cmake --preset win-ai
cmake --build --preset win-ai --target yaze z3ed
```
---
## 5. Testing
### Default Tests (Always Available)
YAZE uses CTest with GoogleTest. Tests are organized by category using labels.
Default test suites run automatically with debug/dev presets. Include stable unit/integration tests and GUI smoke tests:
### Quick Start
```bash
# Build stable test suite (always included in debug presets)
cmake --build --preset mac-dbg --target yaze_test_stable
# Run stable tests (fast, no ROM required)
ctest --test-dir build -L stable -j4
# Run with ctest (recommended approach)
ctest --preset mac-dbg -L stable # Stable tests only
ctest --preset mac-dbg -L gui # GUI smoke tests
ctest --test-dir build -L "stable|gui" # Both stable + GUI
# Run all enabled tests
ctest --test-dir build --output-on-failure
# Run tests matching a pattern
ctest --test-dir build -R "Dungeon"
```
### Optional: ROM-Dependent Tests
### Test Categories
For tests requiring Zelda3 ROM file (ASAR ROM tests, complete edit workflows, ZSCustomOverworld upgrades):
| Category | Command | Description |
|----------|---------|-------------|
| Stable | `ctest --test-dir build -L stable` | Core unit tests, always available |
| GUI | `ctest --test-dir build -L gui` | GUI smoke tests |
| ROM-dependent | `ctest --test-dir build -L rom_dependent` | Requires a Zelda 3 ROM |
| Experimental | `ctest --test-dir build -L experimental` | AI/experimental features |
### Enabling ROM-Dependent Tests
```bash
# Configure with ROM path
cmake --preset mac-dbg -DYAZE_ENABLE_ROM_TESTS=ON -DYAZE_TEST_ROM_PATH=~/zelda3.sfc
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Build ROM test suite
cmake --build --preset mac-dbg --target yaze_test_rom_dependent
# Run ROM tests
# Build and run
cmake --build --preset mac-dev --target yaze_test
ctest --test-dir build -L rom_dependent
```
### Optional: Experimental AI Tests
For AI-powered feature tests (requires `YAZE_ENABLE_AI_RUNTIME=ON`):
```bash
# Use AI-enabled preset
cmake --preset mac-ai
# Build experimental test suite
cmake --build --preset mac-ai --target yaze_test_experimental
# Run AI tests
ctest --test-dir build -L experimental
```
### Test Commands Reference
```bash
# Stable tests only (recommended for quick iteration)
ctest --test-dir build -L stable -j4
# All enabled tests (respects preset configuration)
ctest --test-dir build --output-on-failure
# GUI smoke tests
ctest --test-dir build -L gui
# Headless GUI tests (CI mode)
ctest --test-dir build -L headless_gui
# Tests matching pattern
ctest --test-dir build -R "Dungeon"
# Verbose output
ctest --test-dir build --verbose
```
### Test Organization by Preset
### Test Coverage by Preset
| Preset | Stable | GUI | ROM-Dep | Experimental |
|--------|--------|-----|---------|--------------|
| `mac-dbg`, `lin-dbg`, `win-dbg` | Yes | Yes | No | No |
| `mac-ai`, `lin-ai`, `win-ai` | Yes | Yes | No | Yes |
| `mac-dev`, `lin-dev`, `win-dev` | Yes | Yes | Yes | No |
| `mac-rel`, `lin-rel`, `win-rel` | No | No | No | No |
|--------|:------:|:---:|:-------:|:------------:|
| `*-dbg` | Yes | Yes | No | No |
| `*-ai` | Yes | Yes | No | Yes |
| `*-dev` | Yes | Yes | Yes | No |
| `*-rel` | No | No | No | No |
### Environment Variables
- `YAZE_TEST_ROM_PATH` - Set ROM path for ROM-dependent tests (or use `-DYAZE_TEST_ROM_PATH=...` in CMake)
- `YAZE_SKIP_ROM_TESTS` - Skip ROM tests if set (useful for CI without ROM)
- `YAZE_ENABLE_UI_TESTS` - Enable GUI tests (default if display available)
| Variable | Purpose |
|----------|---------|
| `YAZE_TEST_ROM_PATH` | Path to ROM for ROM-dependent tests |
| `YAZE_SKIP_ROM_TESTS` | Skip ROM tests (useful for CI) |
| `YAZE_ENABLE_UI_TESTS` | Enable GUI tests (auto-detected if display available) |
## 6. Troubleshooting & References
- Detailed troubleshooting: `docs/public/build/troubleshooting.md`
- Platform compatibility: `docs/public/build/platform-compatibility.md`
- Internal agents must follow coordination protocol in
`docs/internal/agents/coordination-board.md` before running builds/tests.
---
## 6. Further Reading
- **[Build Troubleshooting](troubleshooting.md)** - Solutions for common build issues
- **[Platform Compatibility](platform-compatibility.md)** - Platform-specific notes and CI/CD details
- **[CMake Presets Guide](presets.md)** - Complete preset reference
- **[Testing Guide](../developer/testing-guide.md)** - Comprehensive testing documentation

84
docs/public/cli/README.md Normal file
View File

@@ -0,0 +1,84 @@
# z3ed CLI Reference
The `z3ed` command-line tool provides ROM inspection, validation, AI-assisted editing, and automation capabilities.
---
## Command Categories
### Doctor Suite (Diagnostics)
Validate and repair ROM data integrity.
- [Doctor Commands](doctor-commands.md) - `rom-doctor`, `dungeon-doctor`, `overworld-doctor`, `rom-compare`
### Test Infrastructure
Machine-readable test discovery and execution.
- [Test Commands](test-commands.md) - `test-list`, `test-run`, `test-status`
### Inspection Tools
| Command | Description |
|---------|-------------|
| `hex-read` | Read raw bytes from ROM |
| `hex-search` | Search for byte patterns |
| `palette-get-colors` | Extract palette data |
| `sprite-list` | List sprites |
| `music-list` | List music tracks |
| `dialogue-list` | List dialogue entries |
### Overworld Tools
| Command | Description |
|---------|-------------|
| `overworld-find-tile` | Find tile usage across maps |
| `overworld-describe-map` | Describe map properties |
| `overworld-list-warps` | List warp points |
| `overworld-list-sprites` | List overworld sprites |
### Dungeon Tools
| Command | Description |
|---------|-------------|
| `dungeon-list-sprites` | List dungeon sprites |
| `dungeon-describe-room` | Describe room properties |
| `dungeon-list-objects` | List room objects |
---
## Common Flags
| Flag | Description |
|------|-------------|
| `--rom <path>` | Path to ROM file |
| `--format json\|text` | Output format |
| `--verbose` | Detailed output |
| `--help` | Show command help |
---
## Examples
```bash
# List all commands
z3ed help
# Get help for a command
z3ed help rom-doctor
# JSON output for scripting
z3ed rom-doctor --rom zelda3.sfc --format json
# Parse with jq
z3ed rom-doctor --rom zelda3.sfc --format json | jq '.checksum_valid'
```
---
## Related Documentation
- [z3ed CLI Guide](../usage/z3ed-cli.md) - Usage tutorials and workflows
- [Getting Started](../overview/getting-started.md) - Quick start guide

View File

@@ -0,0 +1,190 @@
# z3ed Doctor Commands
The doctor command suite provides diagnostic and repair tools for ROM data integrity. All commands support structured JSON output for automation.
## Available Doctor Commands
| Command | Description |
|---------|-------------|
| `overworld-doctor` | Diagnose/repair overworld data (tile16, pointers, ZSCustom features) |
| `overworld-validate` | Validate map32 pointers and decompression |
| `dungeon-doctor` | Diagnose dungeon room data (objects, sprites, chests) |
| `rom-doctor` | Validate ROM file integrity (header, checksums, expansions) |
| `rom-compare` | Compare two ROMs for differences |
## Common Flags
All doctor commands support:
- `--rom <path>` - Path to ROM file (required)
- `--format json|text` - Output format (default: text)
- `--verbose` - Show detailed output
## overworld-doctor
Diagnose and repair overworld data corruption.
```bash
# Basic diagnosis
z3ed overworld-doctor --rom zelda3.sfc
# Compare against vanilla baseline
z3ed overworld-doctor --rom zelda3.sfc --baseline vanilla.sfc
# Apply fixes with dry-run preview
z3ed overworld-doctor --rom zelda3.sfc --fix --output fixed.sfc --dry-run
# JSON output for agents
z3ed overworld-doctor --rom zelda3.sfc --format json
```
### Detects
- ZSCustomOverworld version (Vanilla, v2, v3)
- Expanded tile16/tile32 regions
- Expanded pointer tables (tail map support)
- Tile16 corruption at known problem addresses
- Map pointer validity for all 160+ maps
## dungeon-doctor
Diagnose dungeon room data integrity.
```bash
# Sample key rooms (fast)
z3ed dungeon-doctor --rom zelda3.sfc
# Analyze all 296 rooms
z3ed dungeon-doctor --rom zelda3.sfc --all
# Analyze specific room
z3ed dungeon-doctor --rom zelda3.sfc --room 0x10
# JSON output
z3ed dungeon-doctor --rom zelda3.sfc --format json --verbose
```
### Validates
- Room header pointers
- Object counts (max 400 before lag)
- Sprite counts (max 64 per room)
- Chest counts (max 6 per room for item flags)
- Object bounds (0-63 for x/y coordinates)
### Sample Output (Text)
```
╔═══════════════════════════════════════════════════════════════╗
║ DUNGEON DOCTOR ║
╠═══════════════════════════════════════════════════════════════╣
║ Rooms Analyzed: 19 ║
║ Valid Rooms: 19 ║
║ Rooms with Warnings: 0 ║
║ Rooms with Errors: 0 ║
╠═══════════════════════════════════════════════════════════════╣
║ Total Objects: 890 ║
║ Total Sprites: 98 ║
╚═══════════════════════════════════════════════════════════════╝
```
## rom-doctor
Validate ROM file integrity and expansion status.
```bash
# Basic validation
z3ed rom-doctor --rom zelda3.sfc
# Verbose with all findings
z3ed rom-doctor --rom zelda3.sfc --verbose
# JSON output for CI/automation
z3ed rom-doctor --rom zelda3.sfc --format json
```
### Validates
- SNES header (title, map mode, country)
- Checksum verification (complement XOR checksum = 0xFFFF)
- ROM size (vanilla 1MB vs expanded 2MB)
- ZSCustomOverworld version detection
- Expansion flags (tile16, tile32, pointer tables)
- Free space analysis in expansion region
### Sample Output (Text)
```
╔═══════════════════════════════════════════════════════════════╗
║ ROM DOCTOR ║
╠═══════════════════════════════════════════════════════════════╣
║ ROM Title: THE LEGEND OF ZELDA ║
║ Size: 0x200000 bytes (2048 KB) ║
║ Map Mode: LoROM ║
║ Country: USA ║
╠═══════════════════════════════════════════════════════════════╣
║ Checksum: 0xAF0D (complement: 0x50F2) - VALID ║
║ ZSCustomOverworld: Vanilla ║
║ Expanded Tile16: NO ║
║ Expanded Tile32: NO ║
║ Expanded Ptr Tables: NO ║
╚═══════════════════════════════════════════════════════════════╝
```
## rom-compare
Compare two ROMs to identify differences.
```bash
# Basic comparison
z3ed rom-compare --rom my_rom.sfc --baseline vanilla.sfc
# Show detailed byte differences
z3ed rom-compare --rom my_rom.sfc --baseline vanilla.sfc --show-diff
# JSON output
z3ed rom-compare --rom my_rom.sfc --baseline vanilla.sfc --format json
```
## Diagnostic Schema
All doctor commands produce findings with consistent structure:
```json
{
"findings": [
{
"id": "tile16_corruption",
"severity": "error",
"message": "Corrupted tile16 at 0x1E878B",
"location": "0x1E878B",
"suggested_action": "Run with --fix to zero corrupted entries",
"fixable": true
}
],
"summary": {
"total_findings": 1,
"critical": 0,
"errors": 1,
"warnings": 0,
"info": 0,
"fixable": 1
}
}
```
### Severity Levels
- `info` - Informational, no action needed
- `warning` - Potential issue, may need attention
- `error` - Problem detected, should be fixed
- `critical` - Severe issue, requires immediate attention
## Agent Usage
For AI agents consuming doctor output:
```bash
# Get structured JSON for parsing
z3ed rom-doctor --rom zelda3.sfc --format json
# Chain with jq for specific fields
z3ed rom-doctor --rom zelda3.sfc --format json | jq '.checksum_valid'
# Check exit code for pass/fail
z3ed rom-doctor --rom zelda3.sfc --format json && echo "ROM OK"
```

View File

@@ -0,0 +1,237 @@
# z3ed Test Commands
The test command suite provides machine-readable test discovery and execution for CI/CD and agent automation.
## Available Test Commands
| Command | Description | Requires ROM |
|---------|-------------|--------------|
| `test-list` | List available test suites with labels and requirements | No |
| `test-run` | Run tests with structured output | No |
| `test-status` | Show test configuration status | No |
## test-list
Discover available test suites and their requirements.
```bash
# Human-readable list
z3ed test-list
# Machine-readable JSON for agents
z3ed test-list --format json
# Filter by label
z3ed test-list --label stable
```
### Sample Output (Text)
```
=== Available Test Suites ===
stable Core unit and integration tests (fast, reliable)
Requirements: None
gui GUI smoke tests (ImGui framework validation)
Requirements: SDL display or headless
z3ed z3ed CLI self-test and smoke tests
Requirements: z3ed target built
headless_gui GUI tests in headless mode (CI-safe)
Requirements: None
rom_dependent Tests requiring actual Zelda3 ROM
Requirements: YAZE_ENABLE_ROM_TESTS=ON + ROM path
⚠ Requires ROM file
experimental AI runtime features and experiments
Requirements: YAZE_ENABLE_AI_RUNTIME=ON
⚠ Requires AI runtime
benchmark Performance and optimization tests
Requirements: None
```
### Sample Output (JSON)
```json
{
"suites": [
{
"label": "stable",
"description": "Core unit and integration tests (fast, reliable)",
"requirements": "None",
"requires_rom": false,
"requires_ai": false
},
{
"label": "rom_dependent",
"description": "Tests requiring actual Zelda3 ROM",
"requirements": "YAZE_ENABLE_ROM_TESTS=ON + ROM path",
"requires_rom": true,
"requires_ai": false
}
],
"total_tests_discovered": 42,
"build_directory": "build"
}
```
## test-run
Run tests and get structured results.
```bash
# Run stable tests (default)
z3ed test-run
# Run specific label
z3ed test-run --label gui
# Run with preset
z3ed test-run --label stable --preset mac-test
# Verbose output
z3ed test-run --label stable --verbose
# JSON output for CI
z3ed test-run --label stable --format json
```
### Sample Output (JSON)
```json
{
"build_directory": "build",
"label": "stable",
"preset": "default",
"tests_passed": 42,
"tests_failed": 0,
"tests_total": 42,
"success": true
}
```
### Exit Codes
- `0` - All tests passed
- `1` - One or more tests failed or error occurred
## test-status
Show current test configuration.
```bash
# Human-readable status
z3ed test-status
# JSON for agents
z3ed test-status --format json
```
### Sample Output (Text)
```
╔═══════════════════════════════════════════════════════════════╗
║ TEST CONFIGURATION ║
╠═══════════════════════════════════════════════════════════════╣
║ ROM Path: (not set) ║
║ Skip ROM Tests: NO ║
║ UI Tests Enabled: NO ║
║ Active Preset: mac-test (fast) ║
╠═══════════════════════════════════════════════════════════════╣
║ Available Build Directories: ║
║ ✓ build ║
╠═══════════════════════════════════════════════════════════════╣
║ Available Test Suites: ║
║ ✓ stable ║
║ ✓ gui ║
║ ✓ z3ed ║
║ ✗ rom_dependent (needs ROM) ║
║ ✓ experimental (needs AI) ║
╚═══════════════════════════════════════════════════════════════╝
```
### Sample Output (JSON)
```json
{
"rom_path": "not set",
"skip_rom_tests": false,
"ui_tests_enabled": false,
"build_directories": ["build"],
"active_preset": "mac-test (fast)",
"available_suites": ["stable", "gui", "z3ed", "headless_gui", "experimental", "benchmark"]
}
```
## Environment Variables
The test commands respect these environment variables:
| Variable | Description |
|----------|-------------|
| `YAZE_TEST_ROM_PATH` | Path to Zelda3 ROM for ROM-dependent tests |
| `YAZE_SKIP_ROM_TESTS` | Set to `1` to skip ROM tests |
| `YAZE_ENABLE_UI_TESTS` | Set to `1` to enable UI tests |
## Quick Start
### For Developers
```bash
# Configure fast test build
cmake --preset mac-test
# Build test targets
cmake --build --preset mac-test
# Run stable tests
z3ed test-run --label stable
```
### For CI/Agents
```bash
# Check what's available
z3ed test-list --format json
# Run stable suite and capture results
z3ed test-run --label stable --format json > test-results.json
# Check exit code
if [ $? -eq 0 ]; then echo "All tests passed"; fi
```
### With ROM Tests
```bash
# Set ROM path
export YAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Configure with ROM tests enabled
cmake --preset mac-dev -DYAZE_ENABLE_ROM_TESTS=ON
# Run ROM-dependent tests
z3ed test-run --label rom_dependent
```
## Integration with ctest
The test commands wrap `ctest` internally. You can also use ctest directly:
```bash
# Equivalent to z3ed test-run --label stable
ctest --test-dir build -L stable
# Run all tests
ctest --test-dir build --output-on-failure
# Run specific pattern
ctest --test-dir build -R "RomTest"
```
## Test Labels Reference
| Label | Description | CI Stage |
|-------|-------------|----------|
| `stable` | Core unit + integration tests | PR/Push |
| `gui` | GUI smoke tests | PR/Push |
| `z3ed` | CLI self-tests | PR/Push |
| `headless_gui` | CI-safe GUI tests | PR/Push |
| `rom_dependent` | Tests requiring ROM | Nightly |
| `experimental` | AI features | Nightly |
| `benchmark` | Performance tests | Nightly |

View File

@@ -0,0 +1,572 @@
# Collaboration Server Setup Guide
This guide explains how to set up a WebSocket server for yaze's real-time collaboration feature, enabling multiple users to edit ROMs together.
## Quick Start with yaze-server
The official collaboration server is **[yaze-server](https://github.com/scawful/yaze-server)**, a Node.js WebSocket server with:
- Real-time session management
- AI agent integration (Gemini/Genkit)
- ROM synchronization and diff broadcasting
- Rate limiting and security features
### Local Development
```bash
git clone https://github.com/scawful/yaze-server.git
cd yaze-server
npm ci
npm start
# Server runs on ws://localhost:8765 (default port 8765)
```
### Production Deployment
For production, deploy yaze-server behind an SSL proxy when possible:
- **halext-server**: `ws://org.halext.org:8765` (pm2 process `yaze-collab`, no TLS on 8765 today; front with nginx/Caddy for `wss://` if desired)
- **Self-hosted**: Deploy to Railway, Render, Fly.io, or your own VPS
### Current halext deployment (ssh halext-server)
- Process: pm2 `yaze-collab`
- Port: `8765` (plain WS/HTTP; add TLS proxy for WSS)
- Health: `http://org.halext.org:8765/health`, metrics at `/metrics`
- AI: enable with `GEMINI_API_KEY` or `AI_AGENT_ENDPOINT` + `ENABLE_AI_AGENT=true`
### Server v2.1 Features
- **Persistence**: Configurable SQLite storage (`SQLITE_DB_PATH` env var)
- **Admin API**: Protected endpoints for session/room management
- **Enhanced Health**: AI status, TLS detection, persistence info in `/health`
- **Configurable Limits**: Tunable rate limits via environment variables
---
## Overview
The yaze web app (WASM build) supports real-time collaboration through WebSocket connections. Since GitHub Pages only serves static files, you'll need a separate WebSocket server to enable this feature.
## Architecture
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User A │ │ WebSocket │ │ User B │
│ (Browser) │◄───►│ Server │◄───►│ (Browser) │
│ yaze WASM │ │ (Your Server) │ │ yaze WASM │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## Protocol Specification
The halext deployment (`yaze-collab` pm2 on port 8765) runs the official **yaze-server v2.0** and speaks two compatible protocols:
- **WASM compatibility protocol** used by the current web/WASM build (flat `type` JSON messages, no `payload` wrapper).
- **Session/AI protocol** used by advanced clients (desktop/editor + AI/ROM sync/proposals) with `{ type, payload }` envelopes.
### WASM compatibility protocol (web build)
All messages are JSON with a `type` field and flat attributes.
**Client → Server**
- `create`: `room`, `name?`, `user`, `user_id`, `color?`, `password?`
- `join`: `room`, `user`, `user_id`, `color?`, `password?`
- `leave`: `room`, `user_id`
- `change`: `room`, `user_id`, `offset`, `old_data`, `new_data`, `timestamp?`
- `cursor`: `room`, `user_id`, `editor`, `x`, `y`, `map_id`
- `ping`: optional keep-alive (`{ "type": "ping" }`)
**Server → Client**
- `create_response`: `{ "type": "create_response", "success": true, "session_name": "..." }`
- `join_response`: `{ "type": "join_response", "success": true, "session_name": "..." }`
- `users`: `{ "type": "users", "list": [{ "id": "...", "name": "...", "color": "#4ECDC4", "active": true }] }`
- `change`: Echoed to room with `timestamp` added
- `cursor`: Broadcast presence updates
- `error`: `{ "type": "error", "message": "...", "payload": { "error": "..." } }`
- `pong`: `{ "type": "pong", "payload": { "timestamp": 1700000000000 } }`
**Notes**
- Passwords are supported (`password` hashed server-side); rooms are deleted when empty.
- Rate limits: 100 messages/min/IP; 10 join/host attempts/min/IP.
- Size limits: ROM diffs ≤ 5 MB, snapshots ≤ 10 MB. Heartbeat every 30s terminates dead sockets.
### Session/AI protocol (advanced clients)
Messages use `{ "type": "...", "payload": { ... } }`.
**Key client messages**
- `host_session`: `session_name`, `username`, `rom_hash?`, `ai_enabled? (default true)`, `session_password?`
- `join_session`: `session_code`, `username`, `session_password?`
- `chat_message`: `sender`, `message`, `message_type?`, `metadata?`
- `rom_sync`: `sender`, `diff_data` (base64), `rom_hash`
- `snapshot_share`: `sender`, `snapshot_data` (base64), `snapshot_type`
- `proposal_share` / `proposal_vote` / `proposal_update`
- `ai_query`: `username`, `query` (requires `ENABLE_AI_AGENT` plus `GEMINI_API_KEY` or `AI_AGENT_ENDPOINT`)
- `leave_session`, `ping`
**Key server broadcasts**
- `session_hosted`, `session_joined`, `participant_joined`, `participant_left`
- `chat_message`, `rom_sync`, `snapshot_shared`
- `proposal_shared`, `proposal_vote_received`, `proposal_updated`
- `ai_response` (only when AI is enabled and configured)
- `pong`, `error`, `server_shutdown`
See `yaze-server/README.md` for full payload examples.
---
## Deployment Options
### Self-Hosted (VPS/Dedicated Server)
1. Install Node.js 18+
2. Clone/copy the server code and install deps:
```bash
npm ci
```
3. Configure environment variables:
**Core Settings:**
```bash
PORT=8765 # WebSocket/HTTP port (default: 8765)
ENABLE_AI_AGENT=true # Enable AI query handling (default: true)
GEMINI_API_KEY=your_api_key # Gemini API key for AI responses
AI_AGENT_ENDPOINT=http://... # Alternative: external AI endpoint
```
**Persistence (v2.1+):**
```bash
SQLITE_DB_PATH=/var/lib/yaze-collab.db # File-based persistence (default: :memory:)
```
**Rate Limiting:**
```bash
RATE_LIMIT_MAX_MESSAGES=100 # Messages per minute per IP (default: 100)
JOIN_LIMIT_MAX_ATTEMPTS=10 # Join/host attempts per minute per IP (default: 10)
```
**Admin API:**
```bash
ADMIN_API_KEY=your_secret_key # Protect admin endpoints (optional)
```
4. Run with PM2 for process management:
```bash
npm install -g pm2
pm2 start server.js --name yaze-collab --env production
pm2 save
```
5. Add TLS reverse proxy (recommended)
**nginx example (translate `wss://` to local `ws://localhost:8765`):**
```nginx
server {
listen 443 ssl;
server_name collab.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /ws {
proxy_pass http://localhost:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
}
```
**Caddy example:**
```caddy
collab.yourdomain.com {
reverse_proxy /ws localhost:8765 {
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
tls you@example.com
}
```
**Why:** avoids mixed-content errors in browsers, encrypts ROM diffs/chat/passwords, and centralizes cert/ALPN handling.
### Platform-as-a-Service
| Platform | Pros | Cons |
|----------|------|------|
| **Railway** | Easy deploy, free tier | Limited free hours |
| **Render** | Free tier, auto-deploy | Spins down on inactivity |
| **Fly.io** | Global edge, generous free | More complex setup |
| **Deno Deploy** | Free, edge deployment | Deno runtime only |
| **Cloudflare Workers** | Free tier, global edge | Durable Objects cost |
---
## Client Configuration
### Method 1: JavaScript Configuration (Recommended)
Add before loading yaze:
```html
<script>
window.YAZE_CONFIG = {
collaboration: {
serverUrl: 'ws://org.halext.org:8765' // use wss:// if you front with TLS
}
};
</script>
```
### Method 2: Meta Tag
```html
<meta name="yaze-collab-server" content="ws://org.halext.org:8765">
```
### Method 3: Runtime Configuration
In your integration code:
```cpp
auto& collab = WasmCollaboration::GetInstance();
collab.SetWebSocketUrl("ws://org.halext.org:8765");
```
---
## Security Considerations
- Transport: terminate TLS in front of the server (`wss://`). The halext deployment currently runs plain `ws://org.halext.org:8765`; add nginx/Caddy to secure it.
- Built-in guardrails: 100 messages/min/IP, 10 join/host attempts/min/IP, 5 MB ROM diff limit, 10 MB snapshot limit, 30s heartbeat that drops dead sockets.
- Passwords: supported on both protocols (`password` for WASM, `session_password` for full sessions). Hashing is SHA-256 on the server side.
- AI: only enabled when `ENABLE_AI_AGENT=true` **and** `GEMINI_API_KEY` or `AI_AGENT_ENDPOINT` is set. Leave unset to disable AI endpoints.
- Persistence: halext uses in-memory SQLite (sessions reset on restart). For durability, run sqlite on disk (`SQLITE_DB_PATH=/var/lib/yaze-collab.db`) or swap to Postgres/MySQL with a lightweight adapter. Add backups/retention for audit.
- Authentication: front the service with an auth gateway if you need verified identities; yaze-server does not issue tokens itself.
---
## Troubleshooting
- **Handshake issues:** Match the scheme to the deployment. halext runs `ws://org.halext.org:8765`; use `wss://` only when you have a TLS proxy forwarding `Upgrade` headers.
- **Health checks:** `curl http://org.halext.org:8765/health` and `/metrics` to confirm the service is live.
- **TLS errors:** If you front with nginx/Caddy, ensure HTTP/1.1, `Upgrade`/`Connection` headers, and a valid certificate. Remove `wss://` if you have not enabled TLS.
- **Disconnects/rate limits:** Server sends heartbeats every 30s and enforces limits. Check `pm2 logs yaze-collab` on halext for details.
- **Performance:** Keep diffs under 5 MB, snapshots under 10 MB, and batch cursor updates on the client. Enable compression at the proxy if needed.
---
## Operations Playbook (halext-friendly)
- **Status:** `curl http://org.halext.org:8765/health` and `/metrics`; add `/metrics` scrape to Prometheus if available.
- **Logs:** `pm2 logs yaze-collab` (rotate externally if needed).
- **Restart/Redeploy:** `pm2 restart yaze-collab`; `pm2 list` to verify uptime.
- **Admin actions:** Use Admin API (see below) or block abusive IPs at the proxy.
- **Scaling path:** add Redis pub/sub for multi-instance broadcast; place proxy in front with sticky room affinity if you shard.
---
## Admin API (v2.1+)
Protected endpoints for server administration. Set `ADMIN_API_KEY` to require authentication.
### Authentication
Include the key in requests:
```bash
curl -H "X-Admin-Key: your_secret_key" http://localhost:8765/admin/sessions
# Or as query param: http://localhost:8765/admin/sessions?admin_key=your_secret_key
```
### Endpoints
**List all sessions/rooms:**
```bash
GET /admin/sessions
# Response: { sessions: [...], wasm_rooms: [...], total_connections: N }
```
**List users in a session:**
```bash
GET /admin/sessions/:code/users
# Response: { code: "ABC123", type: "full"|"wasm", users: [...] }
```
**Close a session (kick all users):**
```bash
DELETE /admin/sessions/:code
# Body: { "reason": "Maintenance" } (optional)
# Response: { success: true, code: "ABC123", reason: "..." }
```
**Kick a specific user:**
```bash
DELETE /admin/sessions/:code/users/:userId
# Body: { "reason": "Violation of rules" } (optional)
# Response: { success: true, code: "ABC123", userId: "user-123", reason: "..." }
```
**Broadcast message to session:**
```bash
POST /admin/sessions/:code/broadcast
# Body: { "message": "Server maintenance in 5 minutes", "message_type": "admin" }
# Response: { success: true, code: "ABC123", recipients: N }
```
---
## Halext TLS Deployment Guide
Step-by-step guide to add WSS (TLS) to the halext deployment.
### Prerequisites
- SSH access to halext-server
- Domain DNS pointing to server (e.g., `collab.halext.org` or use existing `org.halext.org`)
- Certbot or existing SSL certificates
### Option A: nginx reverse proxy
1. **Install nginx (if not present):**
```bash
sudo apt update && sudo apt install nginx certbot python3-certbot-nginx
```
2. **Create nginx config:**
```bash
sudo nano /etc/nginx/sites-available/yaze-collab
```
```nginx
server {
listen 80;
server_name collab.halext.org; # or org.halext.org
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name collab.halext.org;
ssl_certificate /etc/letsencrypt/live/collab.halext.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/collab.halext.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# WebSocket proxy
location / {
proxy_pass http://127.0.0.1:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
}
```
3. **Enable site and get certificate:**
```bash
sudo ln -s /etc/nginx/sites-available/yaze-collab /etc/nginx/sites-enabled/
sudo certbot --nginx -d collab.halext.org
sudo nginx -t && sudo systemctl reload nginx
```
4. **Update client config to use WSS:**
```javascript
window.YAZE_CONFIG = {
collaboration: { serverUrl: 'wss://collab.halext.org' }
};
```
### Option B: Caddy (simpler, auto-TLS)
1. **Install Caddy:**
```bash
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
```
2. **Create Caddyfile:**
```bash
sudo nano /etc/caddy/Caddyfile
```
```caddy
collab.halext.org {
reverse_proxy localhost:8765
# Caddy handles TLS automatically
}
```
3. **Reload Caddy:**
```bash
sudo systemctl reload caddy
```
### Verify TLS is working
```bash
# Check health endpoint shows TLS detected
curl -s https://collab.halext.org/health | jq '.tls'
# Expected: { "detected": true, "note": "Request via TLS proxy" }
# Test WebSocket connection
wscat -c wss://collab.halext.org
```
---
## PM2 Ecosystem File
For more control, use a PM2 ecosystem file:
**ecosystem.config.js:**
```javascript
module.exports = {
apps: [{
name: 'yaze-collab',
script: 'server.js',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'development',
PORT: 8765
},
env_production: {
NODE_ENV: 'production',
PORT: 8765,
SQLITE_DB_PATH: '/var/lib/yaze-collab/sessions.db',
ENABLE_AI_AGENT: 'true',
// GEMINI_API_KEY: 'your_key_here',
// ADMIN_API_KEY: 'your_admin_key'
}
}]
};
```
**Usage:**
```bash
pm2 start ecosystem.config.js --env production
pm2 save
pm2 startup # Enable startup on boot
```
---
## Persistence & Backup
### Enable file-based persistence
```bash
# Create data directory
sudo mkdir -p /var/lib/yaze-collab
sudo chown $(whoami) /var/lib/yaze-collab
# Set environment variable
export SQLITE_DB_PATH=/var/lib/yaze-collab/sessions.db
pm2 restart yaze-collab --update-env
```
### Backup strategy
```bash
# Daily backup cron job
echo "0 3 * * * sqlite3 /var/lib/yaze-collab/sessions.db '.backup /backups/yaze-collab-$(date +%Y%m%d).db'" | crontab -
# Retain last 7 days
echo "0 4 * * * find /backups -name 'yaze-collab-*.db' -mtime +7 -delete" | crontab -e
```
### Health endpoint (v2.1+)
The `/health` endpoint now reports persistence status:
```json
{
"status": "healthy",
"version": "2.1",
"persistence": {
"type": "file",
"path": "/var/lib/yaze-collab/sessions.db"
},
"ai": {
"enabled": true,
"configured": true,
"provider": "gemini"
},
"tls": {
"detected": true,
"note": "Request via TLS proxy"
}
}
```
## Client UX hints
- Surface server status in the web UI by calling `/health` once on load and showing: server reachable, AI enabled/disabled (from health/metrics), and whether TLS is in use.
- Default `window.YAZE_CONFIG.collaboration.serverUrl` to `wss://collab.yourdomain.com/ws` when a TLS proxy is present; fall back to `ws://localhost:8765` for local dev.
- Show a small banner when AI is disabled or when the connection is downgraded to plain WS to set user expectations.
---
## Example: Complete Docker Deployment
**Dockerfile:**
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY server.js .
EXPOSE 8765
CMD ["node", "server.js"]
```
**docker-compose.yml:**
```yaml
version: '3.8'
services:
collab:
build: .
ports:
- "8765:8765"
restart: unless-stopped
environment:
- NODE_ENV=production
- ENABLE_AI_AGENT=true
# Uncomment one of the following if AI responses are desired
# - GEMINI_API_KEY=your_api_key
# - AI_AGENT_ENDPOINT=http://ai-service:5000
```
Deploy:
```bash
docker-compose up -d
```
---
## Testing Your Server
Use wscat to test:
```bash
npm install -g wscat
wscat -c ws://org.halext.org:8765
# Send create message
{"type":"create","room":"TEST01","name":"Test","user":"TestUser","user_id":"test-123","color":"#FF0000"}
# Check response
# < {"type":"create_response","success":true,"session_name":"Test"}
# Test full session protocol (AI disabled)
{"type":"host_session","payload":{"session_name":"DocsCheck","username":"tester","ai_enabled":false}}
```
Health check:
```bash
curl http://org.halext.org:8765/health
curl http://org.halext.org:8765/metrics
```
Or use the browser console on your yaze deployment:
```javascript
window.YAZE_CONFIG = {
collaboration: { serverUrl: 'ws://org.halext.org:8765' }
};
// Then use the collaboration UI in yaze
```

View File

@@ -11,6 +11,16 @@ YAZE includes two primary AI assistance modes:
Both modes use the same underlying AI service (Ollama or Gemini) and tool infrastructure, but target different workflows.
## Choosing the right agent persona
- Personas live in `.claude/agents/<agent-id>.md`; open the matching file as your system prompt before a session (available to all agents, not just Claude).
- **ai-infra-architect**: AI/agent infra, MCP/gRPC, z3ed tooling, model plumbing.
- **backend-infra-engineer**: Build/packaging/toolchains, CI reliability, release plumbing.
- **imgui-frontend-engineer**: ImGui/editor UI, renderer/backends, canvas/docking UX.
- **snes-emulator-expert**: Emulator core (CPU/APU/PPU), performance/accuracy/debugging.
- **zelda3-hacking-expert**: Gameplay/ROM logic, data formats, hacking workflows.
- **test-infrastructure-expert**: Test harnesses, CTest/gMock infra, flake/bloat triage.
- **docs-janitor**: Docs/process hygiene, onboarding, checklists.
## Prerequisites
### Build Requirements
@@ -78,7 +88,7 @@ cmake --build --preset mac-ai --target z3ed
```bash
# You encounter a compilation error
cmake --build build_ai
cmake --build build
# [ERROR] src/app/gfx/snes_color.cc:45: error: 'Arena' was not declared
# Use z3ed to analyze and suggest fixes
@@ -141,7 +151,7 @@ The agent automatically analyzes compilation failures:
```bash
z3ed agent chat --rom zelda3.sfc
> cmake --build build_ai failed with:
> cmake --build build failed with:
> error: 'gfx::Arena' has not been declared in snes_color.cc:45
# AI will:

View File

@@ -2,25 +2,33 @@
This guide summarizes the architecture and implementation standards used across the editor codebase.
## Editor Status (October 2025)
## Editor Status (November 2025)
| Editor | State | Notes |
|-------------------|--------------|-------|
| Overworld | Stable | Full feature set; continue regression testing after palette fixes. |
| Message | Stable | Re-test rendering after recent palette work. |
| Emulator | Stable | UI and core subsystems aligned with production builds. |
| Palette | Stable | Serves as the source of truth for palette helpers. |
| Assembly | Stable | No outstanding refactors. |
| Dungeon | Experimental | Requires thorough manual coverage before release. |
| Graphics | Experimental | Large rendering changes in flight; validate texture pipeline. |
| Sprite | Experimental | UI patterns still migrating to the new card system. |
| Editor | State | Panels | Notes |
|-------------------|--------------|--------|-------|
| Overworld | Stable | 8 | Full feature set with tile16 editor, scratch space. |
| Message | Stable | 4 | Message list, editor, font atlas, dictionary panels. |
| Emulator | Stable | 10 | CPU, PPU, Memory debuggers; AI agent integration. |
| Palette | Stable | 11 | Source of truth for palette helpers. |
| Assembly | Stable | 2 | File browser and editor panels. |
| Dungeon | Stable | 8 | Room selector, matrix, graphics, object editor. |
| Graphics | Stable | 4 | Sheet editor, browser, player animations. |
| Sprite | Stable | 2 | Vanilla and custom sprite panels. |
| Screen | Stable | 5 | Dungeon maps, inventory, title screen, etc. |
| Music | Experimental | 3 | Tracker, instrument editor, assembly view. |
### Screen Editor Notes
### Recent Improvements (v0.3.9)
- **Title screen**: Vanilla ROM tilemap parsing remains broken. Implement a DMA
parser and confirm the welcome screen renders before enabling painting.
- **Overworld map**: Mode 7 tiling, palette switching, and custom map import/export are in place. The next milestone is faster tile painting.
- **Dungeon map**: Rendering is wired up; tile painting and ROM write-back are still pending.
- **EditorManager Refactoring**: 90% feature parity with 44% code reduction
- **Panel-Based UI**: All 34 editor panels (formerly cards) with X-button close, multi-session support
- **SDL3 Backend Infrastructure**: 17 abstraction files for future migration
- **WASM Web Port**: Real-time collaboration via WebSocket
- **AI Agent Tools**: Phases 1-4 complete (meta-tools, schemas, validation)
### Known Issues
- **Dungeon object rendering**: Regression with object visibility
- **ZSOW v3 palettes**: Large-area palette issues being investigated
## 1. Core Architectural Patterns
@@ -139,18 +147,18 @@ Google-style C++23 guidelines while accommodating ROM hacking patterns.
### 5.1. Quick Debugging with Startup Flags
To accelerate your debugging workflow, use command-line flags to jump directly to specific editors and open relevant UI cards:
To accelerate your debugging workflow, use command-line flags to jump directly to specific editors and open relevant UI panels:
```bash
# Quick dungeon room testing
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0"
# Compare multiple rooms
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
# Full dungeon workspace
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Object Editor,Palette Editor"
# Enable debug logging
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Dungeon
@@ -158,9 +166,9 @@ To accelerate your debugging workflow, use command-line flags to jump directly t
**Available Editors**: Assembly, Dungeon, Graphics, Music, Overworld, Palette, Screen, Sprite, Message, Hex, Agent, Settings
**Dungeon Editor Cards**: Rooms List, Room Matrix, Entrances List, Room Graphics, Object Editor, Palette Editor, Room N (where N is room ID 0-319)
**Dungeon Editor Panels**: Rooms List, Room Matrix, Entrances List, Room Graphics, Object Editor, Palette Editor, Room N (where N is room ID 0-319)
See [debugging-startup-flags.md](debugging-startup-flags.md) for complete documentation.
See [Startup Debugging Flags](debug-flags.md) for complete documentation, including panel visibility overrides (`--startup_welcome/--startup_dashboard/--startup_sidebar`).
### 5.2. Testing Strategies

View File

@@ -1,6 +1,6 @@
# YAZE Startup Debugging Flags
This guide explains how to use command-line flags to quickly open specific editors and cards during development for faster debugging workflows.
This guide explains how to use command-line flags to quickly open specific editors and panels during development for faster debugging workflows.
## Basic Usage
@@ -24,6 +24,13 @@ Enable debug logging with verbose output.
./yaze --debug --log_file=yaze_debug.log
```
### `--log_level`, `--log_categories`, `--log_to_console`
Control verbosity and filter by subsystem. Categories can be allowlisted or blocked by prefixing with `-`:
```bash
./yaze --log_level=debug --log_categories="EditorManager,-Audio" --log_to_console
```
### `--editor`
Open a specific editor on startup. This saves time by skipping manual navigation through the UI.
@@ -46,10 +53,12 @@ Open a specific editor on startup. This saves time by skipping manual navigation
./yaze --rom_file=zelda3.sfc --editor=Dungeon
```
### `--cards`
Open specific cards/panels within an editor. Most useful with the Dungeon editor.
### `--open_panels`
Open specific panels within an editor. Matching is case-insensitive and accepts either display names
or stable panel IDs (e.g., `dungeon.room_list`, `emulator.cpu_debugger`). `Room N` tokens will open
the corresponding dungeon room card.
**Dungeon Editor Cards:**
**Dungeon Editor Panels:**
- `Rooms List` - Shows the list of all dungeon rooms
- `Room Matrix` - Shows the dungeon room layout matrix
- `Entrances List` - Shows dungeon entrance configurations
@@ -60,7 +69,15 @@ Open specific cards/panels within an editor. Most useful with the Dungeon editor
**Example:**
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Rooms List,Room 0"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Rooms List,Room 0"
```
### `--startup_welcome`, `--startup_dashboard`, `--startup_sidebar`
Control startup chrome visibility. Each accepts `auto`, `show`, or `hide`:
```bash
./yaze --rom_file=zelda3.sfc --editor=Overworld \
--startup_welcome=hide --startup_dashboard=show --startup_sidebar=hide
```
## Common Debugging Scenarios
@@ -69,14 +86,14 @@ Open specific cards/panels within an editor. Most useful with the Dungeon editor
Open a specific dungeon room for testing:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room Graphics"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room Graphics"
```
### 2. Multiple Room Comparison
Compare multiple rooms side-by-side:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
```
### 3. Full Dungeon Editor Workspace
@@ -84,7 +101,7 @@ Open all dungeon editor tools:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
```
### 4. Debug Mode with Logging
@@ -92,7 +109,7 @@ Enable full debug output while working:
```bash
./yaze --rom_file=zelda3.sfc --debug --log_file=debug.log \
--editor=Dungeon --cards="Room 0"
--editor=Dungeon --open_panels="Room 0"
```
### 5. Quick Overworld Editing
@@ -123,16 +140,15 @@ All flags can be combined for powerful debugging setups:
--debug \
--log_file=room_105_debug.log \
--editor=Dungeon \
--cards="Room 105,Room Graphics,Palette Editor,Object Editor"
--open_panels="Room 105,Room Graphics,Palette Editor,Object Editor"
```
## Notes
- Card names are case-sensitive and must match exactly
- Use quotes around comma-separated card lists
- Invalid editor or card names will be logged as warnings but won't crash the application
- The `--cards` flag is currently only implemented for the Dungeon editor
- Room IDs range from 0-319 in the vanilla game
- Panel tokens are matched case-insensitively against IDs and display names
- Use quotes around comma-separated panel lists
- Invalid editor or panel names will be logged as warnings but won't crash the application
- `Room N` shortcuts use the dungeon room ID range (0-319 in vanilla)
## Troubleshooting
@@ -141,11 +157,10 @@ All flags can be combined for powerful debugging setups:
- Verify ROM loaded successfully
- Check log output with `--debug`
**Cards don't appear:**
**Panels don't appear:**
- Ensure editor is set (e.g., `--editor=Dungeon`)
- Check card name spelling
- Some cards require a loaded ROM
**Want to add more card support?**
See `EditorManager::OpenEditorAndCardsFromFlags()` in `src/app/editor/editor_manager.cc`
- Check panel name spelling
- Some panels require a loaded ROM
**Want to add more panel support?**
See `EditorManager::OpenEditorAndPanelsFromFlags()` in `src/app/editor/editor_manager.cc`

View File

@@ -1,6 +1,6 @@
# E5 - Debugging and Testing Guide
**Last Updated**: October 9, 2025
**Last Updated**: December 5, 2025
**Status**: Active
This document provides a comprehensive guide to debugging and testing the `yaze` application. It covers strategies for developers and provides the necessary information for AI agents to interact with, test, and validate the application.
@@ -30,20 +30,19 @@ Categories allow you to filter logs to focus on a specific subsystem. Common cat
You can control logging behavior using command-line flags when launching `yaze` or `yaze_test`.
- **Enable Verbose Debug Logging**:
- **Set Log Level & Categories** (allowlist or blocklist by prefixing with `-`; tokens are trimmed and case-sensitive):
```bash
./build/bin/yaze --debug
./build/bin/yaze --log_level=debug --log_categories="OverworldEditor,-Audio"
```
- **Log to a File**:
- **Log to a File (and mirror to console if needed)**:
```bash
./build/bin/yaze --log_file=yaze_debug.log
./build/bin/yaze --log_file=yaze_debug.log --log_to_console
```
- **Filter by Category**:
- **Enable Verbose Debug Logging** (forces console logging and `debug` level):
```bash
# Only show logs from the APU and CPU emulator components
./build/bin/yaze_emu --emu_debug_apu=true --emu_debug_cpu=true
./build/bin/yaze --debug --log_file=yaze_debug.log
```
**Best Practice**: When debugging a specific component, add detailed `LOG_DEBUG` statements with a unique category. Then, run `yaze` with the appropriate flags to isolate the output.
@@ -66,35 +65,33 @@ The `yaze` ecosystem provides several executables and flags to streamline testin
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness
```
- **Open a Specific Editor and Cards**: To quickly test a specific editor and its components, use the `--editor` and `--cards` flags. This is especially useful for debugging complex UIs like the Dungeon Editor.
- **Open a Specific Editor and Panels**: Use `--editor` and `--open_panels` (panel IDs or display names, comma-separated, case-insensitive) to land exactly where you need. Combine with startup visibility flags to hide chrome for automation:
```bash
# Open the Dungeon Editor with the Room Matrix and two specific room cards
./build/bin/yaze --rom_file zelda3.sfc --editor=Dungeon --cards="Room Matrix,Room 0,Room 105"
# Available editors: Assembly, Dungeon, Graphics, Music, Overworld, Palette,
# Screen, Sprite, Message, Hex, Agent, Settings
# Dungeon editor cards: Rooms List, Room Matrix, Entrances List, Room Graphics,
# Object Editor, Palette Editor, Room N (where N is room ID)
# Open the Dungeon editor with a couple panels pre-visible
./build/bin/yaze --rom_file zelda3.sfc --editor=Dungeon --open_panels="dungeon.room_list,Room 105"
# You can also hide startup chrome for automation runs
./build/bin/yaze --rom_file zelda3.sfc --editor=Overworld \
--open_panels="overworld.map_canvas" --startup_welcome=hide --startup_dashboard=hide
```
**Quick Examples**:
```bash
# Fast dungeon room testing
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0"
# Compare multiple rooms side-by-side
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
# Full dungeon workspace with all tools
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
--open_panels="Rooms List,Room Matrix,Object Editor,Palette Editor"
# Jump straight to overworld editing
./build/bin/yaze --rom_file=zelda3.sfc --editor=Overworld
```
For a complete reference, see [docs/debugging-startup-flags.md](debugging-startup-flags.md).
For a complete reference, see [Startup Debugging Flags](debug-flags.md).
### Running Automated C++ Tests
@@ -102,22 +99,22 @@ The `yaze_test` executable is used to run the project's suite of unit, integrati
- **Run All Tests**:
```bash
./build_ai/bin/yaze_test
./build/bin/yaze_test
```
- **Run Specific Categories**:
```bash
# Run only fast, dependency-free unit tests
./build_ai/bin/yaze_test --unit
./build/bin/yaze_test --unit
# Run tests that require a ROM file
./build_ai/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
./build/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
```
- **Run GUI-based E2E Tests**:
```bash
# Run E2E tests and watch the GUI interactions
./build_ai/bin/yaze_test --e2e --show-gui
./build/bin/yaze_test --e2e --show-gui
```
### Inspecting ROMs with `z3ed`
@@ -168,7 +165,7 @@ This will return a list of widget IDs (e.g., `Dungeon/Canvas/Map`) that can be u
**Tip**: You can also launch `yaze` with the `--editor` flag to automatically open a specific editor:
```bash
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --editor=Dungeon --cards="Room 0"
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --editor=Dungeon --open_panels="Room 0"
```
#### Step 3: Record or Write a Test Script
@@ -193,7 +190,7 @@ An agent can either generate a test script from scratch or use a pre-recorded on
```bash
# Start yaze with the room already open
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness \
--editor=Dungeon --cards="Room 105"
--editor=Dungeon --open_panels="Room 105"
# Then your test script just needs to validate the state
{"action": "assert_visible", "target": "Room Card 105"}

View File

@@ -1248,8 +1248,8 @@ class MusicEditor {
```bash
cd /Users/scawful/Code/yaze
cmake --build build_ai --target yaze -j12
./build_ai/bin/yaze.app/Contents/MacOS/yaze
cmake --build build --target yaze -j12
./build/bin/yaze.app/Contents/MacOS/yaze
```
### Platform-Specific

View File

@@ -1,4 +1,8 @@
# G5 - GUI Consistency and Card-Based Architecture Guide
# G5 - GUI Consistency and Panel-Based Architecture Guide
> Note: The project is migrating from **Card** terminology to **Panel**
> (`PanelWindow`, `PanelManager`, `PanelDescriptor`). This guide still shows
> legacy names; mentally substitute Panel for Card while Phase 2 lands.
This guide establishes standards for GUI consistency across all yaze editors, focusing on the modern card-based architecture, theming system, and layout patterns.
@@ -11,13 +15,14 @@ This guide establishes standards for GUI consistency across all yaze editors, fo
5. [GUI Library Architecture](#5-gui-library-architecture)
6. [Themed Widget System](#6-themed-widget-system)
7. [Begin/End Patterns](#7-beginend-patterns)
8. [Currently Integrated Editors](#8-currently-integrated-editors)
9. [Layout Helpers](#9-layout-helpers)
10. [Workspace Management](#10-workspace-management)
11. [Future Editor Improvements](#11-future-editor-improvements)
12. [Migration Checklist](#12-migration-checklist)
13. [Code Examples](#13-code-examples)
14. [Common Pitfalls](#14-common-pitfalls)
8. [Avoiding Duplicate Rendering](#8-avoiding-duplicate-rendering)
9. [Currently Integrated Editors](#9-currently-integrated-editors)
10. [Layout Helpers](#10-layout-helpers)
11. [Workspace Management](#11-workspace-management)
12. [Future Editor Improvements](#12-future-editor-improvements)
13. [Migration Checklist](#13-migration-checklist)
14. [Code Examples](#14-code-examples)
15. [Common Pitfalls](#15-common-pitfalls)
## 1. Introduction
@@ -532,17 +537,43 @@ if (ImGui::BeginTable("##MyTable", 3, ImGuiTableFlags_Borders)) {
}
```
**Child Window Pattern:**
**Child Window Pattern (CRITICAL):**
⚠️ **This is the most commonly misused pattern.** Unlike tables, `EndChild()` must ALWAYS be called after `BeginChild()`, regardless of the return value.
```cpp
// ✅ CORRECT: EndChild OUTSIDE the if block
if (ImGui::BeginChild("##ScrollRegion", ImVec2(0, 200), true)) {
// Scrollable content
// Scrollable content - only drawn when visible
for (int i = 0; i < 100; i++) {
ImGui::Text("Item %d", i);
}
}
ImGui::EndChild();
ImGui::EndChild(); // ALWAYS called, even if BeginChild returned false
// ❌ WRONG: EndChild INSIDE the if block - causes state corruption!
if (ImGui::BeginChild("##ScrollRegion", ImVec2(0, 200), true)) {
for (int i = 0; i < 100; i++) {
ImGui::Text("Item %d", i);
}
ImGui::EndChild(); // BUG: Not called when BeginChild returns false!
}
```
**Why this matters:** When `BeginChild()` returns false (child window is clipped or not visible), ImGui still expects `EndChild()` to be called to properly clean up internal state. Failing to call it corrupts ImGui's window stack, which can cause seemingly unrelated errors like table assertions or missing UI elements.
**Pattern Comparison:**
| Function | Call End when Begin returns false? |
|----------|-----------------------------------|
| `BeginChild()` / `EndChild()` | ✅ **YES - ALWAYS** |
| `Begin()` / `End()` (windows) | ✅ **YES - ALWAYS** |
| `BeginTable()` / `EndTable()` | ❌ **NO - only if Begin returned true** |
| `BeginTabBar()` / `EndTabBar()` | ❌ **NO - only if Begin returned true** |
| `BeginTabItem()` / `EndTabItem()` | ❌ **NO - only if Begin returned true** |
| `BeginPopup()` / `EndPopup()` | ❌ **NO - only if Begin returned true** |
| `BeginMenu()` / `EndMenu()` | ❌ **NO - only if Begin returned true** |
### Toolset Begin/End
```cpp
@@ -582,7 +613,169 @@ struct ScopedCard {
};
```
## 8. Currently Integrated Editors
## 8. Avoiding Duplicate Rendering
### Overview
Duplicate rendering occurs when the same UI content is drawn multiple times per frame. This wastes GPU resources and can cause visual glitches, flickering, or assertion errors in ImGui.
### Common Causes
1. **Calling draw functions from multiple places**
2. **Forgetting to check visibility flags**
3. **Shared functions called by different cards**
4. **Rendering in callbacks that fire every frame**
### Pattern 1: Shared Draw Functions
When multiple cards need similar content, don't call the same draw function from multiple places:
```cpp
// ❌ WRONG: DrawMetadata called twice when both cards are visible
void DrawCardA() {
gui::EditorCard card("Card A", ICON_MD_A);
if (card.Begin()) {
DrawCanvas();
DrawMetadata(); // Called here...
}
card.End();
}
void DrawCardB() {
gui::EditorCard card("Card B", ICON_MD_B);
if (card.Begin()) {
DrawMetadata(); // ...AND here! Duplicate!
DrawCanvas();
}
card.End();
}
// ✅ CORRECT: Each card has its own content
void DrawCardA() {
gui::EditorCard card("Card A", ICON_MD_A);
if (card.Begin()) {
DrawCanvas();
// Card A specific content only
}
card.End();
}
void DrawCardB() {
gui::EditorCard card("Card B", ICON_MD_B);
if (card.Begin()) {
DrawMetadata(); // Card B specific content only
}
card.End();
}
```
### Pattern 2: Nested Function Calls
Watch out for functions that call other functions with overlapping content:
```cpp
// ❌ WRONG: DrawSpriteCanvas calls DrawMetadata, then DrawCustomSprites
// also calls DrawMetadata AND DrawSpriteCanvas
void DrawSpriteCanvas() {
// ... canvas code ...
DrawAnimationFrames();
DrawCustomSpritesMetadata(); // BUG: This shouldn't be here!
}
void DrawCustomSprites() {
if (BeginTable(...)) {
TableNextColumn();
DrawCustomSpritesMetadata(); // First call
TableNextColumn();
DrawSpriteCanvas(); // Calls DrawCustomSpritesMetadata again!
EndTable();
}
}
// ✅ CORRECT: Each function has clear, non-overlapping responsibilities
void DrawSpriteCanvas() {
// ... canvas code ...
DrawAnimationFrames();
// NO DrawCustomSpritesMetadata here!
}
void DrawCustomSprites() {
if (BeginTable(...)) {
TableNextColumn();
DrawCustomSpritesMetadata(); // Only place it's called
TableNextColumn();
DrawSpriteCanvas(); // Just draws the canvas
EndTable();
}
}
```
### Pattern 3: Expensive Per-Frame Operations
Don't call expensive rendering operations every frame unless necessary:
```cpp
// ❌ WRONG: RenderRoomGraphics called every frame when card is visible
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
auto& room = rooms_[current_room_id_];
room.RenderRoomGraphics(); // Expensive! Called every frame!
DrawRoomGfxCanvas();
}
graphics_card.End();
}
// ✅ CORRECT: Only render when room data changes
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
auto& room = rooms_[current_room_id_];
// RenderRoomGraphics is called in DrawRoomTab when room loads
// or when data changes - NOT every frame here
DrawRoomGfxCanvas(); // Just displays already-rendered data
}
graphics_card.End();
}
```
### Pattern 4: Visibility Flag Checks
Always check visibility before drawing:
```cpp
// ❌ WRONG: Card drawn without visibility check
void Update() {
DrawMyCard(); // Always called!
}
// ✅ CORRECT: Check visibility first
void Update() {
if (show_my_card_) {
DrawMyCard();
}
}
```
### Debugging Duplicate Rendering
1. **Add logging to draw functions:**
```cpp
void DrawMyContent() {
LOG_DEBUG("UI", "DrawMyContent called"); // Count calls per frame
// ...
}
```
2. **Check for multiple card instances:**
```cpp
// Search for multiple cards with similar names
grep -n "EditorCard.*MyCard" src/app/editor/
```
3. **Trace call hierarchy:**
- Use a debugger or add call stack logging
- Look for functions that call each other unexpectedly
## 9. Currently Integrated Editors
The card system is integrated across 11 of 13 editors:
@@ -604,7 +797,7 @@ The card system is integrated across 11 of 13 editors:
- **SettingsEditor** - Monolithic settings window, low usage frequency
- **AgentEditor** - Complex AI agent UI, under active development
## 9. Layout Helpers
## 10. Layout Helpers
### Overview
@@ -691,7 +884,7 @@ if (ImGui::BeginTable("##Grid", 2, ImGuiTableFlags_SizingStretchSame)) {
}
```
## 10. Workspace Management
## 11. Workspace Management
The workspace manager provides comprehensive window and layout operations:
@@ -712,7 +905,7 @@ workspace_manager_.ExecuteWorkspaceCommand(command_id);
// Supports: w.s (show all), w.h (hide all), l.s (save layout), etc.
```
## 11. Future Editor Improvements
## 12. Future Editor Improvements
This section outlines remaining improvements for editors not yet fully integrated.
@@ -734,7 +927,7 @@ This section outlines remaining improvements for editors not yet fully integrate
2. Integrate with EditorCardManager
3. Add keyboard shortcuts for common operations
## 12. Migration Checklist
## 13. Migration Checklist
Use this checklist when converting an editor to the card-based architecture:
@@ -811,7 +1004,7 @@ Use this checklist when converting an editor to the card-based architecture:
- [ ] Add example to this guide if pattern is novel
- [ ] Update CLAUDE.md if editor behavior changed significantly
## 13. Code Examples
## 14. Code Examples
### Complete Editor Implementation
@@ -1093,7 +1286,7 @@ void MyEditor::DrawPropertiesCard() {
} // namespace yaze
```
## 14. Common Pitfalls
## 15. Common Pitfalls
### 1. Forgetting Bidirectional Visibility Sync
@@ -1197,6 +1390,113 @@ if (card.Begin()) {
card.End(); // ALWAYS called
```
### 5a. BeginChild/EndChild Mismatch (Most Common Bug!)
**Problem:** `EndTable() call should only be done while in BeginTable() scope` assertion, or other strange ImGui crashes.
**Cause:** `EndChild()` placed inside the if block instead of outside.
**Why it's confusing:** Unlike `BeginTable()`, the `BeginChild()` function requires `EndChild()` to be called regardless of the return value. Many developers assume all Begin/End pairs work the same way.
**Solution:**
```cpp
// ❌ WRONG - EndChild inside if block
void DrawList() {
if (ImGui::BeginChild("##List", ImVec2(0, 0), true)) {
for (int i = 0; i < items.size(); i++) {
ImGui::Selectable(items[i].c_str());
}
ImGui::EndChild(); // BUG! Not called when BeginChild returns false!
}
}
// ✅ CORRECT - EndChild outside if block
void DrawList() {
if (ImGui::BeginChild("##List", ImVec2(0, 0), true)) {
for (int i = 0; i < items.size(); i++) {
ImGui::Selectable(items[i].c_str());
}
}
ImGui::EndChild(); // ALWAYS called!
}
```
**Files where this bug was found and fixed:**
- `sprite_editor.cc` - `DrawSpriteCanvas()`, `DrawSpritesList()`
- `dungeon_editor_v2.cc` - `DrawRoomsListCard()`, `DrawEntrancesListCard()`
- `assembly_editor.cc` - `DrawCurrentFolder()`
- `object_editor_card.cc` - `DrawTemplatesTab()`
### 5b. Duplicate Rendering in Shared Functions
**Problem:** UI elements appear twice, performance degradation, visual glitches.
**Cause:** A draw function is called from multiple places, or a function calls another function that draws the same content.
**Example of the bug:**
```cpp
// DrawSpriteCanvas was calling DrawCustomSpritesMetadata
// DrawCustomSprites was also calling DrawCustomSpritesMetadata AND DrawSpriteCanvas
// Result: DrawCustomSpritesMetadata rendered twice!
void DrawSpriteCanvas() {
// ... canvas drawing ...
DrawAnimationFrames();
DrawCustomSpritesMetadata(); // ❌ BUG: Also called by DrawCustomSprites!
}
void DrawCustomSprites() {
TableNextColumn();
DrawCustomSpritesMetadata(); // First call
TableNextColumn();
DrawSpriteCanvas(); // ❌ Calls DrawCustomSpritesMetadata AGAIN!
}
```
**Solution:** Each function should have clear, non-overlapping responsibilities:
```cpp
void DrawSpriteCanvas() {
// ... canvas drawing ...
DrawAnimationFrames();
// NO DrawCustomSpritesMetadata here - it belongs in DrawCustomSprites only
}
void DrawCustomSprites() {
TableNextColumn();
DrawCustomSpritesMetadata(); // Only place it's called
TableNextColumn();
DrawSpriteCanvas(); // Just draws canvas + animations
}
```
### 5c. Expensive Operations Called Every Frame
**Problem:** Low FPS, high CPU usage when certain cards are visible.
**Cause:** Expensive operations like `RenderRoomGraphics()` called unconditionally every frame.
**Solution:**
```cpp
// ❌ WRONG - Renders every frame
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
room.RenderRoomGraphics(); // Expensive! Called 60x per second!
DrawCanvas();
}
graphics_card.End();
}
// ✅ CORRECT - Only render when needed
void DrawRoomGraphicsCard() {
if (graphics_card.Begin()) {
// RenderRoomGraphics is called in DrawRoomTab when room loads,
// or when room data changes - NOT every frame
DrawCanvas(); // Just displays already-rendered data
}
graphics_card.End();
}
```
### 6. Not Testing Minimize-to-Icon
**Problem:** Control panel can't be reopened after minimizing.
@@ -1329,4 +1629,4 @@ For questions or suggestions about GUI consistency, please open an issue on GitH
---
**Last Updated**: October 13, 2025
**Last Updated**: November 26, 2025

View File

@@ -52,14 +52,85 @@ struct PaletteGroupMap {
#### Structure
- **20 dungeon palettes** in the `dungeon_main` group
- **90 colors per palette** (full SNES palette for BG layers)
- **ROM Location**: `kDungeonMainPalettes` (check `snes_palette.cc` for exact address)
- **180 bytes per palette** (90 colors × 2 bytes per color)
- **ROM Location**: `kDungeonMainPalettes = 0xDD734`
#### Usage
#### Palette Lookup System (CRITICAL)
**IMPORTANT**: Room headers store a "palette set ID" (0-71), NOT a direct palette index!
The game uses a **two-level lookup system** to convert room palette properties to actual
dungeon palette indices:
1. **Palette Set Table** (`paletteset_ids` at ROM `0x75460`)
- 72 entries, each 4 bytes: `[bg_palette_offset, aux1, aux2, aux3]`
- The first byte is a **byte offset** into the palette pointer table
2. **Palette Pointer Table** (ROM `0xDEC4B`)
- Contains 16-bit words that, when divided by 180, give the palette index
- Each word = ROM offset into dungeon palette data
**Correct Lookup Algorithm**:
```cpp
// Loading a dungeon palette
constexpr uint32_t kPalettesetIds = 0x75460;
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B;
// room.palette is 0-71 (palette set ID, NOT palette index!)
uint8_t byte_offset = paletteset_ids[room.palette][0]; // Step 1
uint16_t word = rom.ReadWord(kDungeonPalettePointerTable + byte_offset); // Step 2
int palette_id = word / 180; // Step 3: convert ROM offset to palette index
```
**Example Lookup**:
```
Room palette property = 16
→ paletteset_ids[16][0] = 0x10 (byte offset 16)
→ Word at 0xDEC4B + 16 = 0x05A0 (1440)
→ Palette ID = 1440 / 180 = 8
→ Use dungeon_main[8], NOT dungeon_main[16]!
```
**The Pointer Table (0xDEC4B)**:
| Offset | Word | Palette ID |
|--------|--------|------------|
| 0 | 0x0000 | 0 |
| 2 | 0x00B4 | 1 |
| 4 | 0x0168 | 2 |
| 6 | 0x021C | 3 |
| ... | ... | ... |
| 38 | 0x0D5C | 19 |
#### Common Pitfall: Direct Palette ID Usage
**WRONG** (causes purple/wrong colors for palette sets 16+):
```cpp
// BUG: Uses byte offset directly as palette ID!
palette_id = paletteset_ids[room.palette][0];
```
**CORRECT**:
```cpp
auto offset = paletteset_ids[room.palette][0];
auto word = rom->ReadWord(0xDEC4B + offset);
palette_id = word.value() / 180;
```
#### Standard Usage
```cpp
// Loading a dungeon palette (with proper lookup)
auto& dungeon_pal_group = rom->palette_group().dungeon_main;
int num_palettes = dungeon_pal_group.size(); // Should be 20
int palette_id = room.palette; // Room's palette ID (0-19)
// Perform the two-level lookup
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B;
int palette_id = room.palette; // Default fallback
if (room.palette < paletteset_ids.size()) {
auto offset = paletteset_ids[room.palette][0];
auto word = rom->ReadWord(kDungeonPalettePointerTable + offset);
if (word.ok()) {
palette_id = word.value() / 180;
}
}
// IMPORTANT: Use operator[] not palette() method!
auto palette = dungeon_pal_group[palette_id]; // Returns reference
@@ -302,6 +373,10 @@ constexpr uint32_t kDungeonMainPalettes = 0xDD734;
constexpr uint32_t kHardcodedGrassLW = 0x5FEA9;
constexpr uint32_t kTriforcePalette = 0xF4CD0;
constexpr uint32_t kOverworldMiniMapPalettes = 0x55B27;
// Dungeon palette lookup tables (critical for room rendering!)
constexpr uint32_t kPalettesetIds = 0x75460; // 72 entries × 4 bytes
constexpr uint32_t kDungeonPalettePointerTable = 0xDEC4B; // Palette ROM offsets
```
## Graphics Sheet Palette Application
@@ -351,3 +426,103 @@ bitmap.mutable_data() = new_data;
// CORRECT - Updates both vector and surface
bitmap.set_data(new_data);
```
## Bitmap Dual Palette System
### Understanding the Two Palette Storage Mechanisms
The `Bitmap` class has **two separate palette storage locations**, which can cause confusion:
| Storage | Location | Populated By | Used For |
|---------|----------|--------------|----------|
| Internal SnesPalette | `bitmap.palette_` | `SetPalette(SnesPalette)` | Serialization, palette editing |
| SDL Surface Palette | `surface_->format->palette` | Both `SetPalette` overloads | Actual rendering to textures |
### The Problem: Empty palette() Returns
When dungeon rooms apply palettes to their layer buffers, they use `SetPalette(vector<SDL_Color>)`:
```cpp
// In room.cc - CreateAllGraphicsLayers()
auto set_dungeon_palette = [](gfx::Bitmap& bmp, const gfx::SnesPalette& pal) {
std::vector<SDL_Color> colors(256);
for (size_t i = 0; i < pal.size() && i < 256; ++i) {
ImVec4 rgb = pal[i].rgb();
colors[i] = { static_cast<Uint8>(rgb.x), static_cast<Uint8>(rgb.y),
static_cast<Uint8>(rgb.z), 255 };
}
colors[255] = {0, 0, 0, 0}; // Transparent
bmp.SetPalette(colors); // Uses SDL_Color overload!
};
```
This means `bitmap.palette().size()` returns **0** even though the bitmap renders correctly!
### Solution: Extract Palette from SDL Surface
When you need to copy a palette between bitmaps (e.g., for layer compositing), extract it from the SDL surface:
```cpp
void CopyPaletteBetweenBitmaps(const gfx::Bitmap& src, gfx::Bitmap& dst) {
SDL_Surface* src_surface = src.surface();
if (!src_surface || !src_surface->format) return;
SDL_Palette* src_pal = src_surface->format->palette;
if (!src_pal || src_pal->ncolors == 0) return;
// Extract palette colors into a vector
std::vector<SDL_Color> colors(256);
int colors_to_copy = std::min(src_pal->ncolors, 256);
for (int i = 0; i < colors_to_copy; ++i) {
colors[i] = src_pal->colors[i];
}
// Apply to destination bitmap
dst.SetPalette(colors);
}
```
### Layer Compositing with Correct Palettes
When merging multiple layers into a single composite bitmap (as done in `RoomLayerManager::CompositeToOutput()`), the correct approach is:
1. Create/clear the output bitmap
2. For each visible layer:
- Extract the SDL palette from the first layer with a valid surface
- Apply it to the output bitmap using `SetPalette(vector<SDL_Color>)`
- Composite the pixel data (skip transparent indices 0 and 255)
3. Sync pixel data to surface with `UpdateSurfacePixels()`
4. Mark as modified for texture update
**Example from RoomLayerManager**:
```cpp
void RoomLayerManager::CompositeToOutput(Room& room, gfx::Bitmap& output) const {
// Create output bitmap
output.Create(512, 512, 8, std::vector<uint8_t>(512*512, 255));
bool palette_copied = false;
for (auto layer_type : GetDrawOrder()) {
auto& buffer = GetLayerBuffer(room, layer_type);
const auto& src_bitmap = buffer.bitmap();
// Copy palette from first visible layer
if (!palette_copied && src_bitmap.surface()) {
ApplySDLPaletteToBitmap(src_bitmap.surface(), output);
palette_copied = true;
}
// Composite pixels...
}
output.UpdateSurfacePixels();
output.set_modified(true);
}
```
### Best Practices for Palette Handling
1. **Don't assume palette() has data**: Always check `palette().size() > 0` before using it
2. **Use SDL surface as authoritative source**: For rendering-related palette operations
3. **Use SetPalette(SnesPalette) for persistence**: When the palette needs to be saved or edited
4. **Use SetPalette(vector<SDL_Color>) for performance**: When you already have SDL colors
5. **Always call UpdateSurfacePixels()**: After modifying pixel data and before rendering

View File

@@ -1,14 +1,16 @@
# A1 - Testing Guide
# Testing Guide
This guide provides a comprehensive overview of the testing framework for the yaze project, including the test organization, execution methods, and the end-to-end GUI automation system.
This guide covers the testing framework for YAZE, including test organization, execution, and the GUI automation system.
## 1. Test Organization
---
The test suite is organized into a clear directory structure that separates tests by their purpose and dependencies. This is the primary way to understand the nature of a test.
## Test Organization
Tests are organized by purpose and dependencies:
```
test/
├── unit/ # Unit tests for individual components
├── unit/ # Isolated component tests
│ ├── core/ # Core functionality (asar, hex utils)
│ ├── cli/ # Command-line interface tests
│ ├── emu/ # Emulator component tests
@@ -16,138 +18,132 @@ test/
│ ├── gui/ # GUI widget tests
│ ├── rom/ # ROM data structure tests
│ └── zelda3/ # Game-specific logic tests
├── integration/ # Tests for interactions between components
│ ├── ai/ # AI agent and vision tests
├── integration/ # Component interaction tests
│ ├── ai/ # AI agent tests
│ ├── editor/ # Editor integration tests
│ └── zelda3/ # Game-specific integration tests (ROM-dependent)
├── e2e/ # End-to-end user workflow tests (GUI-driven)
│ └── zelda3/ # ROM-dependent integration tests
├── e2e/ # End-to-end GUI workflow tests
│ ├── rom_dependent/ # E2E tests requiring a ROM
│ └── zscustomoverworld/ # ZSCustomOverworld upgrade E2E tests
│ └── zscustomoverworld/ # ZSCustomOverworld upgrade tests
├── benchmarks/ # Performance benchmarks
├── mocks/ # Mock objects for isolating tests
└── assets/ # Test assets (patches, data)
├── mocks/ # Mock objects
└── assets/ # Test data and patches
```
## 2. Test Categories
---
Based on the directory structure, tests fall into the following categories:
## Test Categories
### Unit Tests (`unit/`)
- **Purpose**: To test individual classes or functions in isolation.
- **Characteristics**:
- Fast, self-contained, and reliable.
- No external dependencies (e.g., ROM files, running GUI).
- Form the core of the CI/CD validation pipeline.
| Category | Purpose | Dependencies | Speed |
|----------|---------|--------------|-------|
| **Unit** | Test individual classes/functions | None | Fast |
| **Integration** | Test component interactions | May require ROM | Medium |
| **E2E** | Simulate user workflows | GUI + ROM | Slow |
| **Benchmarks** | Measure performance | None | Variable |
### Integration Tests (`integration/`)
- **Purpose**: To verify that different components of the application work together correctly.
- **Characteristics**:
- May require a real ROM file (especially those in `integration/zelda3/`). These are considered "ROM-dependent".
- Test interactions between modules, such as the `asar` wrapper and the `Rom` class, or AI services with the GUI controller.
- Slower than unit tests but crucial for catching bugs at module boundaries.
### Unit Tests
### End-to-End (E2E) Tests (`e2e/`)
- **Purpose**: To simulate a full user workflow from start to finish.
- **Characteristics**:
- Driven by the **ImGui Test Engine**.
- Almost always require a running GUI and often a real ROM.
- The slowest but most comprehensive tests, validating the user experience.
- Includes smoke tests, canvas interactions, and complex workflows like ZSCustomOverworld upgrades.
Fast, isolated tests with no external dependencies. Run in CI on every commit.
### Benchmarks (`benchmarks/`)
- **Purpose**: To measure and track the performance of critical code paths, particularly in the graphics system.
- **Characteristics**:
- Not focused on correctness but on speed and efficiency.
- Run manually or in specialized CI jobs to prevent performance regressions.
### Integration Tests
## 3. Running Tests
Test module interactions (e.g., `asar` wrapper with `Rom` class). Some require a ROM file.
> 💡 Need a refresher on presets/commands? See the [Build & Test Quick Reference](../build/quick-reference.md)
> for the canonical `cmake`, `ctest`, and helper script usage before running the commands below.
### E2E Tests
### Using the Enhanced Test Runner (`yaze_test`)
GUI-driven tests using ImGui Test Engine. Validate complete user workflows.
The most flexible way to run tests is by using the `yaze_test` executable directly. It provides flags to filter tests by category, which is ideal for development and AI agent workflows.
### Benchmarks
Performance measurement for critical paths. Run manually or in specialized CI jobs.
---
## Running Tests
> See the [Build and Test Quick Reference](../build/quick-reference.md) for full command reference.
### Using yaze_test Executable
```bash
# First, build the test executable
cmake --build build_ai --target yaze_test
# Build
cmake --build build --target yaze_test
# Run all tests
./build_ai/bin/yaze_test
./build/bin/yaze_test
# Run only unit tests
./build_ai/bin/yaze_test --unit
# Run by category
./build/bin/yaze_test --unit
./build/bin/yaze_test --integration
./build/bin/yaze_test --e2e --show-gui
# Run only integration tests
./build_ai/bin/yaze_test --integration
# Run ROM-dependent tests
./build/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
# Run E2E tests (requires a GUI)
./build_ai/bin/yaze_test --e2e --show-gui
# Run by pattern
./build/bin/yaze_test "*Asar*"
# Run ROM-dependent tests with a specific ROM
./build_ai/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
# Run tests matching a specific pattern (e.g., all Asar tests)
./build_ai/bin/yaze_test "*Asar*"
# Get a full list of options
./build_ai/bin/yaze_test --help
# Show all options
./build/bin/yaze_test --help
```
### Using CTest and CMake Presets
For CI/CD or a more traditional workflow, you can use `ctest` with CMake presets.
### Using CTest
```bash
# Configure a development build (enables ROM-dependent tests)
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/your/zelda3.sfc
# Configure with ROM tests
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Build the tests
# Build
cmake --build --preset mac-dev --target yaze_test
# Run stable tests (fast, primarily unit tests)
ctest --preset dev
# Run all tests, including ROM-dependent and E2E
ctest --preset all
# Run tests
ctest --preset dev # Stable tests
ctest --preset all # All tests
```
## 4. Writing Tests
---
When adding new tests, place them in the appropriate directory based on their purpose and dependencies.
## Writing Tests
- **New class `MyClass`?** Add `test/unit/my_class_test.cc`.
- **Testing `MyClass` with a real ROM?** Add `test/integration/my_class_rom_test.cc`.
- **Testing a full UI workflow involving `MyClass`?** Add `test/e2e/my_class_workflow_test.cc`.
Place tests based on their purpose:
## 5. E2E GUI Testing Framework
| Test Type | File Location |
|-----------|---------------|
| Unit test for `MyClass` | `test/unit/my_class_test.cc` |
| Integration with ROM | `test/integration/my_class_rom_test.cc` |
| UI workflow test | `test/e2e/my_class_workflow_test.cc` |
The E2E framework uses `ImGuiTestEngine` to automate UI interactions.
---
### Architecture
## E2E GUI Testing
- **`test/yaze_test.cc`**: The main test runner that can initialize a GUI for E2E tests.
- **`test/e2e/`**: Contains all E2E test files, such as:
- `framework_smoke_test.cc`: Basic infrastructure verification.
- `canvas_selection_test.cc`: Canvas interaction tests.
- `dungeon_editor_tests.cc`: UI tests for the dungeon editor.
- **`test/test_utils.h`**: Provides high-level helper functions for common actions like loading a ROM (`LoadRomInTest`) or opening an editor (`OpenEditorInTest`).
The E2E framework uses ImGui Test Engine for UI automation.
### Key Files
| File | Purpose |
|------|---------|
| `test/yaze_test.cc` | Main test runner with GUI initialization |
| `test/e2e/framework_smoke_test.cc` | Infrastructure verification |
| `test/e2e/canvas_selection_test.cc` | Canvas interaction tests |
| `test/e2e/dungeon_editor_tests.cc` | Dungeon editor UI tests |
| `test/test_utils.h` | Helper functions (LoadRomInTest, OpenEditorInTest) |
### Running GUI Tests
To run E2E tests and see the GUI interactions, use the `--show-gui` flag.
```bash
# Run all E2E tests with the GUI visible
./build_ai/bin/yaze_test --e2e --show-gui
# Run all E2E tests with visible GUI
./build/bin/yaze_test --e2e --show-gui
# Run a specific E2E test by name
./build_ai/bin/yaze_test --show-gui --gtest_filter="*DungeonEditorSmokeTest"
# Run specific test
./build/bin/yaze_test --show-gui --gtest_filter="*DungeonEditorSmokeTest"
```
### Widget Discovery and AI Integration
### AI Integration
The GUI testing framework is designed for AI agent automation. All major UI elements are registered with stable IDs, allowing an agent to "discover" and interact with them programmatically via the `z3ed` CLI.
UI elements are registered with stable IDs for programmatic access via `z3ed`:
- `z3ed gui discover` - List available widgets
- `z3ed gui click` - Interact with widgets
- `z3ed agent test replay` - Replay recorded tests
Refer to the `z3ed` agent guide for details on using commands like `z3ed gui discover`, `z3ed gui click`, and `z3ed agent test replay`.
See the [z3ed CLI Guide](../usage/z3ed-cli.md) for more details.

View File

@@ -300,7 +300,7 @@ After pushing, CI will run tests on all platforms (Linux, macOS, Windows):
scripts/agents/run-gh-workflow.sh ci.yml -f enable_http_api_tests=true
```
See [GH Actions Remote Guide](../../internal/agents/gh-actions-remote.md) for setup.
See [GH Actions Remote Guide](../../internal/agents/archive/utility-tools/gh-actions-remote.md) for setup.
## Advanced Topics

View File

@@ -151,8 +151,8 @@ jobs:
- name: Build z3ed
run: |
cmake -B build_test
cmake --build build_test --parallel
cmake -B build
cmake --build build --parallel
- name: Run Agent Tests (Mock ROM)
run: |

View File

@@ -6,10 +6,10 @@ doubt, link back to the relevant guide instead of duplicating long explanations.
## 1. Launching Common Editors
```bash
# Open YAZE directly in the Dungeon editor with room cards preset
# Open YAZE directly in the Dungeon editor with room panels preset
./build/bin/yaze --rom_file=zelda3.sfc \
--editor=Dungeon \
--cards="Rooms List,Room Graphics,Object Editor"
--open_panels="Rooms List,Room Graphics,Object Editor"
# Jump to an Overworld map from the CLI/TUI companion
./build/bin/z3ed overworld describe-map --map 0x80 --rom zelda3.sfc
@@ -34,9 +34,9 @@ cmake --preset mac-dbg
cmake --build --preset mac-dbg --target yaze yaze_test
./build/bin/yaze_test --unit
# AI-focused build in a dedicated directory (recommended for assistants)
cmake --preset mac-ai -B build_ai
cmake --build build_ai --target yaze z3ed
# AI-focused build
cmake --preset mac-ai
cmake --build --preset mac-ai --target yaze z3ed
```
## 4. Quick Verification

View File

@@ -0,0 +1,182 @@
// Example: How to use WasmPatchExport in the yaze editor
// This code would typically be integrated into the ROM file manager or editor menu
#include "app/platform/wasm/wasm_patch_export.h"
#include "app/rom.h"
#include "imgui.h"
namespace yaze {
namespace editor {
// Example function that could be added to RomFileManager or MenuOrchestrator
void ShowPatchExportDialog(Rom* rom) {
static bool show_export_dialog = false;
static int patch_format = 0; // 0 = BPS, 1 = IPS
static char filename[256] = "my_hack";
// Menu item to trigger export
if (ImGui::MenuItem("Export Patch...", nullptr, nullptr, rom->is_loaded())) {
show_export_dialog = true;
}
// Export dialog window
if (show_export_dialog) {
ImGui::OpenPopup("Export Patch");
}
if (ImGui::BeginPopupModal("Export Patch", &show_export_dialog)) {
// Get the original ROM data (assuming rom stores both original and modified)
const auto& original_data = rom->original_data(); // Would need to add this
const auto& modified_data = rom->data();
// Show patch preview information
auto patch_info = platform::WasmPatchExport::GetPatchPreview(
original_data, modified_data);
ImGui::Text("Patch Summary:");
ImGui::Separator();
ImGui::Text("Total changed bytes: %zu", patch_info.changed_bytes);
ImGui::Text("Number of regions: %zu", patch_info.num_regions);
if (patch_info.changed_bytes == 0) {
ImGui::TextColored(ImVec4(1, 1, 0, 1), "No changes detected!");
}
// Show changed regions (limit to first 10 for UI)
if (!patch_info.changed_regions.empty()) {
ImGui::Separator();
ImGui::Text("Changed Regions:");
int region_count = 0;
for (const auto& region : patch_info.changed_regions) {
if (region_count >= 10) {
ImGui::Text("... and %zu more regions",
patch_info.changed_regions.size() - 10);
break;
}
ImGui::Text(" Offset: 0x%06X, Size: %zu bytes",
static_cast<unsigned>(region.first), region.second);
region_count++;
}
}
ImGui::Separator();
// Format selection
ImGui::Text("Patch Format:");
ImGui::RadioButton("BPS (Beat)", &patch_format, 0);
ImGui::SameLine();
ImGui::RadioButton("IPS", &patch_format, 1);
// Filename input
ImGui::Text("Filename:");
ImGui::InputText("##filename", filename, sizeof(filename));
// Export buttons
ImGui::Separator();
if (ImGui::Button("Export", ImVec2(120, 0))) {
if (patch_info.changed_bytes > 0) {
absl::Status status;
std::string full_filename = std::string(filename);
if (patch_format == 0) {
// BPS format
if (full_filename.find(".bps") == std::string::npos) {
full_filename += ".bps";
}
status = platform::WasmPatchExport::ExportBPS(
original_data, modified_data, full_filename);
} else {
// IPS format
if (full_filename.find(".ips") == std::string::npos) {
full_filename += ".ips";
}
status = platform::WasmPatchExport::ExportIPS(
original_data, modified_data, full_filename);
}
if (status.ok()) {
ImGui::CloseCurrentPopup();
show_export_dialog = false;
// Could show success toast here
} else {
// Show error message
ImGui::OpenPopup("Export Error");
}
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
show_export_dialog = false;
}
// Error popup
if (ImGui::BeginPopupModal("Export Error")) {
ImGui::Text("Failed to export patch!");
if (ImGui::Button("OK", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::EndPopup();
}
}
// Alternative: Quick export functions for toolbar/menu
void QuickExportBPS(Rom* rom) {
#ifdef __EMSCRIPTEN__
if (!rom || !rom->is_loaded()) return;
const auto& original = rom->original_data(); // Would need to add this
const auto& modified = rom->data();
// Generate default filename based on ROM name
std::string filename = rom->filename();
size_t dot_pos = filename.find_last_of('.');
if (dot_pos != std::string::npos) {
filename = filename.substr(0, dot_pos);
}
filename += ".bps";
auto status = platform::WasmPatchExport::ExportBPS(
original, modified, filename);
if (!status.ok()) {
// Show error toast or log
}
#endif
}
void QuickExportIPS(Rom* rom) {
#ifdef __EMSCRIPTEN__
if (!rom || !rom->is_loaded()) return;
const auto& original = rom->original_data();
const auto& modified = rom->data();
// Check IPS size limit
if (modified.size() > 0xFFFFFF) {
// Show error: ROM too large for IPS format
return;
}
std::string filename = rom->filename();
size_t dot_pos = filename.find_last_of('.');
if (dot_pos != std::string::npos) {
filename = filename.substr(0, dot_pos);
}
filename += ".ips";
auto status = platform::WasmPatchExport::ExportIPS(
original, modified, filename);
if (!status.ok()) {
// Show error toast or log
}
#endif
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,241 @@
// Example: Using WasmMessageQueue with WasmCollaboration
// This example shows how the collaboration system can use the message queue
// for offline support and automatic replay when reconnecting.
#ifdef __EMSCRIPTEN__
#include "app/platform/wasm/wasm_collaboration.h"
#include "app/platform/wasm/wasm_message_queue.h"
#include <emscripten.h>
#include <memory>
namespace yaze {
namespace app {
namespace platform {
// Example integration class that combines collaboration with offline queue
class CollaborationWithOfflineSupport {
public:
CollaborationWithOfflineSupport()
: collaboration_(std::make_unique<WasmCollaboration>()),
message_queue_(std::make_unique<WasmMessageQueue>()) {
// Configure message queue
message_queue_->SetAutoPersist(true);
message_queue_->SetMaxQueueSize(500);
message_queue_->SetMessageExpiry(86400.0); // 24 hours
// Set up callbacks
SetupCallbacks();
// Load any previously queued messages
auto status = message_queue_->LoadFromStorage();
if (!status.ok()) {
emscripten_log(EM_LOG_WARN, "Failed to load offline queue: %s",
status.ToString().c_str());
}
}
// Send a change, queuing if offline
void SendChange(uint32_t offset, const std::vector<uint8_t>& old_data,
const std::vector<uint8_t>& new_data) {
if (collaboration_->IsConnected()) {
// Try to send directly
auto status = collaboration_->BroadcastChange(offset, old_data, new_data);
if (!status.ok()) {
// Failed to send, queue for later
QueueChange(offset, old_data, new_data);
}
} else {
// Not connected, queue for later
QueueChange(offset, old_data, new_data);
}
}
// Send cursor position, queuing if offline
void SendCursorPosition(const std::string& editor_type, int x, int y, int map_id) {
if (collaboration_->IsConnected()) {
auto status = collaboration_->SendCursorPosition(editor_type, x, y, map_id);
if (!status.ok()) {
QueueCursorPosition(editor_type, x, y, map_id);
}
} else {
QueueCursorPosition(editor_type, x, y, map_id);
}
}
// Called when connection is established
void OnConnectionEstablished() {
emscripten_log(EM_LOG_INFO, "Connection established, replaying queued messages...");
// Create sender function that uses the collaboration instance
auto sender = [this](const std::string& message_type, const std::string& payload) -> absl::Status {
// Parse the payload and send via collaboration
try {
nlohmann::json data = nlohmann::json::parse(payload);
if (message_type == "change") {
uint32_t offset = data["offset"];
std::vector<uint8_t> old_data = data["old_data"];
std::vector<uint8_t> new_data = data["new_data"];
return collaboration_->BroadcastChange(offset, old_data, new_data);
} else if (message_type == "cursor") {
std::string editor_type = data["editor_type"];
int x = data["x"];
int y = data["y"];
int map_id = data.value("map_id", -1);
return collaboration_->SendCursorPosition(editor_type, x, y, map_id);
}
return absl::InvalidArgumentError("Unknown message type: " + message_type);
} catch (const std::exception& e) {
return absl::InvalidArgumentError("Failed to parse payload: " + std::string(e.what()));
}
};
// Replay all queued messages
message_queue_->ReplayAll(sender, 3); // Max 3 retries per message
}
// Get queue status for UI display
WasmMessageQueue::QueueStatus GetQueueStatus() const {
return message_queue_->GetStatus();
}
// Clear all queued messages
void ClearQueue() {
message_queue_->Clear();
}
// Prune old messages
void PruneOldMessages() {
int removed = message_queue_->PruneExpiredMessages();
if (removed > 0) {
emscripten_log(EM_LOG_INFO, "Pruned %d expired messages", removed);
}
}
private:
void SetupCallbacks() {
// Set up replay complete callback
message_queue_->SetOnReplayComplete([](int replayed, int failed) {
emscripten_log(EM_LOG_INFO, "Replay complete: %d sent, %d failed", replayed, failed);
// Show notification to user
EM_ASM({
if (window.showNotification) {
const message = `Synced ${$0} changes` + ($1 > 0 ? `, ${$1} failed` : '');
window.showNotification(message, $1 > 0 ? 'warning' : 'success');
}
}, replayed, failed);
});
// Set up status change callback
message_queue_->SetOnStatusChange([](const WasmMessageQueue::QueueStatus& status) {
// Update UI with queue status
EM_ASM({
if (window.updateQueueStatus) {
window.updateQueueStatus({
pendingCount: $0,
failedCount: $1,
totalBytes: $2,
oldestMessageAge: $3,
isPersisted: $4
});
}
}, status.pending_count, status.failed_count, status.total_bytes,
status.oldest_message_age, status.is_persisted);
});
// Set up collaboration status callback
collaboration_->SetStatusCallback([this](bool connected, const std::string& message) {
if (connected) {
// Connection established, replay queued messages
OnConnectionEstablished();
} else {
// Connection lost
emscripten_log(EM_LOG_INFO, "Connection lost: %s", message.c_str());
}
});
}
void QueueChange(uint32_t offset, const std::vector<uint8_t>& old_data,
const std::vector<uint8_t>& new_data) {
nlohmann::json payload;
payload["offset"] = offset;
payload["old_data"] = old_data;
payload["new_data"] = new_data;
payload["timestamp"] = emscripten_get_now() / 1000.0;
std::string msg_id = message_queue_->Enqueue("change", payload.dump());
emscripten_log(EM_LOG_DEBUG, "Queued change message: %s", msg_id.c_str());
}
void QueueCursorPosition(const std::string& editor_type, int x, int y, int map_id) {
nlohmann::json payload;
payload["editor_type"] = editor_type;
payload["x"] = x;
payload["y"] = y;
if (map_id >= 0) {
payload["map_id"] = map_id;
}
payload["timestamp"] = emscripten_get_now() / 1000.0;
std::string msg_id = message_queue_->Enqueue("cursor", payload.dump());
emscripten_log(EM_LOG_DEBUG, "Queued cursor message: %s", msg_id.c_str());
}
std::unique_ptr<WasmCollaboration> collaboration_;
std::unique_ptr<WasmMessageQueue> message_queue_;
};
// JavaScript bindings for the enhanced collaboration
extern "C" {
// Create collaboration instance with offline support
EMSCRIPTEN_KEEPALIVE
void* create_collaboration_with_offline() {
return new CollaborationWithOfflineSupport();
}
// Send a change (with automatic queuing if offline)
EMSCRIPTEN_KEEPALIVE
void send_change_with_queue(void* instance, uint32_t offset,
uint8_t* old_data, int old_size,
uint8_t* new_data, int new_size) {
auto* collab = static_cast<CollaborationWithOfflineSupport*>(instance);
std::vector<uint8_t> old_vec(old_data, old_data + old_size);
std::vector<uint8_t> new_vec(new_data, new_data + new_size);
collab->SendChange(offset, old_vec, new_vec);
}
// Get queue status
EMSCRIPTEN_KEEPALIVE
int get_pending_message_count(void* instance) {
auto* collab = static_cast<CollaborationWithOfflineSupport*>(instance);
return collab->GetQueueStatus().pending_count;
}
// Clear offline queue
EMSCRIPTEN_KEEPALIVE
void clear_offline_queue(void* instance) {
auto* collab = static_cast<CollaborationWithOfflineSupport*>(instance);
collab->ClearQueue();
}
// Prune old messages
EMSCRIPTEN_KEEPALIVE
void prune_old_messages(void* instance) {
auto* collab = static_cast<CollaborationWithOfflineSupport*>(instance);
collab->PruneOldMessages();
}
} // extern "C"
} // namespace platform
} // namespace app
} // namespace yaze
#endif // __EMSCRIPTEN__

View File

@@ -0,0 +1,255 @@
# Web Terminal Integration for Z3ed
This document describes how to integrate the z3ed terminal functionality into the WASM web build.
## Overview
The `wasm_terminal_bridge.cc` file provides C++ functions that can be called from JavaScript to enable z3ed command processing in the browser. This allows users to interact with ROM data and use AI-powered features directly in the web interface.
## Exported Functions
### Core Functions
```javascript
// Process a z3ed command
const char* Z3edProcessCommand(const char* command);
// Get command completions for autocomplete
const char* Z3edGetCompletions(const char* partial);
// Set API key for AI services (Gemini)
void Z3edSetApiKey(const char* api_key);
// Check if terminal bridge is ready
int Z3edIsReady();
// Load ROM data from ArrayBuffer
int Z3edLoadRomData(const uint8_t* data, size_t size);
// Get current ROM information as JSON
const char* Z3edGetRomInfo();
// Execute resource queries
const char* Z3edQueryResource(const char* query);
```
## JavaScript Integration Example
```javascript
// Initialize the terminal when module is ready
Module.onRuntimeInitialized = function() {
// Check if terminal is ready
if (Module.ccall('Z3edIsReady', 'number', [], [])) {
console.log('Z3ed terminal ready');
}
// Set API key for AI features
const apiKey = localStorage.getItem('gemini_api_key');
if (apiKey) {
Module.ccall('Z3edSetApiKey', null, ['string'], [apiKey]);
}
// Create terminal interface
const terminal = new Terminal({
prompt: 'z3ed> ',
onCommand: (cmd) => {
const result = Module.ccall('Z3edProcessCommand', 'string', ['string'], [cmd]);
terminal.print(result);
},
onTab: (partial) => {
const completions = Module.ccall('Z3edGetCompletions', 'string', ['string'], [partial]);
return JSON.parse(completions);
}
});
// Expose terminal globally
window.z3edTerminal = terminal;
};
// Load ROM file
async function loadRomFile(file) {
const arrayBuffer = await file.arrayBuffer();
const data = new Uint8Array(arrayBuffer);
// Allocate memory in WASM heap
const ptr = Module._malloc(data.length);
Module.HEAPU8.set(data, ptr);
// Load ROM
const success = Module.ccall('Z3edLoadRomData', 'number',
['number', 'number'], [ptr, data.length]);
// Free memory
Module._free(ptr);
if (success) {
// Get ROM info
const info = Module.ccall('Z3edGetRomInfo', 'string', [], []);
console.log('ROM loaded:', JSON.parse(info));
}
}
```
## Terminal UI Component
```javascript
class Z3edTerminal {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.history = [];
this.historyIndex = 0;
this.setupUI();
}
setupUI() {
this.container.innerHTML = `
<div class="terminal-output"></div>
<div class="terminal-input">
<span class="prompt">z3ed> </span>
<input type="text" class="command-input" />
</div>
`;
this.output = this.container.querySelector('.terminal-output');
this.input = this.container.querySelector('.command-input');
this.input.addEventListener('keydown', (e) => this.handleKeydown(e));
}
handleKeydown(e) {
if (e.key === 'Enter') {
this.executeCommand(this.input.value);
this.history.push(this.input.value);
this.historyIndex = this.history.length;
this.input.value = '';
} else if (e.key === 'Tab') {
e.preventDefault();
this.handleAutocomplete();
} else if (e.key === 'ArrowUp') {
this.navigateHistory(-1);
} else if (e.key === 'ArrowDown') {
this.navigateHistory(1);
}
}
executeCommand(cmd) {
this.print(`z3ed> ${cmd}`, 'command');
if (!Module.ccall) {
this.printError('WASM module not loaded');
return;
}
try {
const result = Module.ccall('Z3edProcessCommand', 'string', ['string'], [cmd]);
this.print(result);
} catch (error) {
this.printError(`Error: ${error.message}`);
}
}
handleAutocomplete() {
const partial = this.input.value;
const completions = Module.ccall('Z3edGetCompletions', 'string', ['string'], [partial]);
const options = JSON.parse(completions);
if (options.length === 1) {
this.input.value = options[0];
} else if (options.length > 1) {
this.print(`Available commands: ${options.join(', ')}`);
}
}
navigateHistory(direction) {
this.historyIndex = Math.max(0, Math.min(this.history.length, this.historyIndex + direction));
this.input.value = this.history[this.historyIndex] || '';
}
print(text, className = 'output') {
const line = document.createElement('div');
line.className = className;
line.textContent = text;
this.output.appendChild(line);
this.output.scrollTop = this.output.scrollHeight;
}
printError(text) {
this.print(text, 'error');
}
clear() {
this.output.innerHTML = '';
}
}
```
## Available Commands
The WASM build includes a subset of z3ed commands that don't require native dependencies:
### Basic Commands
- `help` - Show available commands
- `help <category>` - Show commands in a category
- `clear` - Clear terminal output
- `version` - Show version information
### ROM Commands
- `rom load <file>` - Load ROM from file
- `rom info` - Display ROM information
- `rom validate` - Validate ROM structure
### Resource Queries
- `resource query dungeon.rooms` - List dungeon rooms
- `resource query overworld.maps` - List overworld maps
- `resource query graphics.sheets` - List graphics sheets
- `resource query palettes` - List palettes
### AI Commands (requires API key)
- `ai <prompt>` - Generate AI response
- `ai analyze <resource>` - Analyze ROM resource
- `ai suggest <context>` - Get suggestions
### Graphics Commands
- `gfx list` - List graphics resources
- `gfx export <id>` - Export graphics (returns base64)
## Build Configuration
The WASM terminal bridge is automatically included when building with Emscripten:
```bash
# Configure for WASM with AI support
cmake --preset wasm-ai
# Build
cmake --build build --target yaze
# The resulting files will be:
# - yaze.js (JavaScript loader)
# - yaze.wasm (WebAssembly module)
# - yaze.html (Example HTML page)
```
## Security Considerations
1. **API Keys**: Store API keys in sessionStorage or localStorage, never hardcode them
2. **ROM Data**: ROM data stays in browser memory, never sent to servers
3. **CORS**: AI API requests go through browser fetch, respecting CORS policies
4. **Sandboxing**: WASM runs in browser sandbox with limited filesystem access
## Troubleshooting
### Module not loading
- Ensure WASM files are served with correct MIME type: `application/wasm`
- Check browser console for CORS errors
- Verify SharedArrayBuffer support if using threads
### Commands not working
- Check if ROM is loaded: `Z3edGetRomInfo()`
- Verify terminal is ready: `Z3edIsReady()`
- Check browser console for error messages
### AI features not working
- Ensure API key is set: `Z3edSetApiKey()`
- Check network tab for API request failures
- Verify Gemini API quota and limits

View File

@@ -0,0 +1,874 @@
# Z3ED Workflow Examples
This guide demonstrates practical workflows using z3ed CLI for common ROM hacking tasks, test automation, and AI-assisted development.
## Table of Contents
1. [Basic ROM Editing Workflow](#basic-rom-editing-workflow)
2. [Automated Testing Pipeline](#automated-testing-pipeline)
3. [AI-Assisted Development](#ai-assisted-development)
4. [Multi-Agent Collaboration](#multi-agent-collaboration)
5. [CI/CD Integration](#cicd-integration)
6. [Advanced Automation Scripts](#advanced-automation-scripts)
## Basic ROM Editing Workflow
### Scenario: Adding a New Dungeon Room
This workflow demonstrates how to create and populate a new dungeon room using z3ed commands.
```bash
#!/bin/bash
# new_dungeon_room.sh - Create and populate a new dungeon room
# Load ROM and create snapshot for safety
z3ed rom snapshot --name "before_new_room" --compress
# Open dungeon editor for room 50
z3ed editor dungeon set-property --room 50 --property "layout" --value "2x2"
z3ed editor dungeon set-property --room 50 --property "floor1_graphics" --value 0x0A
# Place entrance and exit doors
z3ed editor dungeon place-object --room 50 --type 0x00 --x 7 --y 0 # North door
z3ed editor dungeon place-object --room 50 --type 0x00 --x 7 --y 15 # South door
# Add enemies
z3ed editor dungeon place-object --room 50 --type 0x08 --x 4 --y 5 # Soldier
z3ed editor dungeon place-object --room 50 --type 0x08 --x 10 --y 5 # Soldier
z3ed editor dungeon place-object --room 50 --type 0x0C --x 7 --y 8 # Knight
# Add treasure chest with key
z3ed editor dungeon place-object --room 50 --type 0x22 --x 7 --y 12
z3ed editor dungeon set-property --room 50 --property "chest_contents" --value "small_key"
# Validate the room
z3ed editor dungeon validate-room --room 50 --fix-issues
# Test in emulator
z3ed emulator run --warp-to-room 50
```
### Scenario: Batch Tile Replacement
Replace all instances of a tile across multiple overworld maps.
```bash
#!/bin/bash
# batch_tile_replace.sh - Replace tiles across overworld maps
# Define old and new tile IDs
OLD_TILE=0x142 # Old grass tile
NEW_TILE=0x143 # New grass variant
# Create snapshot
z3ed rom snapshot --name "before_tile_replacement"
# Create batch operation file
cat > tile_replacement.json << EOF
{
"operations": [
EOF
# Generate operations for all Light World maps (0x00-0x3F)
for map in {0..63}; do
# Find all occurrences of the old tile
positions=$(z3ed query find-tiles --map $map --tile $OLD_TILE --format json)
# Parse positions and add replacement operations
echo "$positions" | jq -r '.positions[] |
" {\"editor\": \"overworld\", \"action\": \"set-tile\", \"params\": {\"map\": '$map', \"x\": .x, \"y\": .y, \"tile\": '$NEW_TILE'}},"' >> tile_replacement.json
done
# Close JSON and execute
echo ' ]
}' >> tile_replacement.json
# Execute batch operation
z3ed editor batch --script tile_replacement.json --dry-run
read -p "Proceed with replacement? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
z3ed editor batch --script tile_replacement.json
fi
```
## Automated Testing Pipeline
### Scenario: Test-Driven Dungeon Development
Create tests before implementing dungeon features.
```bash
#!/bin/bash
# test_driven_dungeon.sh - TDD approach for dungeon creation
# Start test recording
z3ed test record --name "dungeon_puzzle_test" --start
# Define expected behavior
cat > expected_behavior.json << EOF
{
"room": 75,
"requirements": [
"Player must push block to activate switch",
"Door opens when switch is activated",
"Chest appears after door opens",
"Room must be completable in under 60 seconds"
]
}
EOF
# Record the intended solution path
z3ed editor dungeon place-object --room 75 --type "push_block" --x 5 --y 5
z3ed editor dungeon place-object --room 75 --type "floor_switch" --x 10 --y 10
z3ed editor dungeon place-object --room 75 --type "locked_door" --x 7 --y 0
z3ed editor dungeon set-property --room 75 --property "switch_target" --value "locked_door"
# Stop recording and generate test
z3ed test record --stop --save-as dungeon_puzzle_recording.json
z3ed test generate --from-recording dungeon_puzzle_recording.json \
--requirements expected_behavior.json \
--output test_dungeon_puzzle.cc
# Compile and run the test
z3ed test run --file test_dungeon_puzzle.cc
# Run continuously during development
watch -n 5 'z3ed test run --file test_dungeon_puzzle.cc --quiet'
```
### Scenario: Regression Test Suite
Automated regression testing for ROM modifications.
```bash
#!/bin/bash
# regression_test_suite.sh - Comprehensive regression testing
# Create baseline from stable version
z3ed test baseline --create --name "stable_v1.0"
# Define test suite
cat > regression_tests.yaml << EOF
tests:
- name: "Overworld Collision"
commands:
- "editor overworld validate-collision --map ALL"
expected: "no_errors"
- name: "Dungeon Room Connectivity"
commands:
- "query dungeon-graph --check-connectivity"
expected: "all_rooms_reachable"
- name: "Sprite Limits"
commands:
- "query sprite-count --per-screen"
expected: "max_sprites <= 16"
- name: "Memory Usage"
commands:
- "query memory-usage --runtime"
expected: "usage < 95%"
- name: "Save/Load Integrity"
commands:
- "test save-load --iterations 100"
expected: "no_corruption"
EOF
# Run regression suite
z3ed test run --suite regression_tests.yaml --parallel
# Compare against baseline
z3ed test baseline --compare --name "stable_v1.0" --threshold 98
# Generate report
z3ed test coverage --report html --output coverage_report.html
```
## AI-Assisted Development
### Scenario: AI-Powered Bug Fix
Use AI to identify and fix a bug in dungeon logic.
```bash
#!/bin/bash
# ai_bug_fix.sh - AI-assisted debugging
# Describe the bug to AI
BUG_DESCRIPTION="Player gets stuck when entering room 42 from the south"
# Ask AI to analyze
z3ed ai analyze --type bug \
--context "room=42" \
--description "$BUG_DESCRIPTION" \
--output bug_analysis.json
# Get AI suggestions for fix
z3ed ai suggest --task "fix dungeon room entry bug" \
--context bug_analysis.json \
--output suggested_fix.json
# Review suggestions
cat suggested_fix.json | jq '.suggestions[]'
# Apply AI-suggested fix (after review)
z3ed ai apply --fix suggested_fix.json --dry-run
read -p "Apply AI-suggested fix? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
z3ed ai apply --fix suggested_fix.json
# Validate the fix
z3ed editor dungeon validate-room --room 42
z3ed test run --filter "*Room42*"
fi
# Generate regression test for the bug
z3ed test generate --type regression \
--bug "$BUG_DESCRIPTION" \
--fix suggested_fix.json \
--output test_room42_entry.cc
```
### Scenario: AI Test Generation
Generate comprehensive tests using AI.
```bash
#!/bin/bash
# ai_test_generation.sh - AI-powered test creation
# Select component to test
COMPONENT="OverworldEditor"
# Analyze code and generate test specification
z3ed ai analyze --code src/app/editor/overworld_editor.cc \
--task "identify test cases" \
--output test_spec.json
# Generate comprehensive test suite
z3ed test generate --target $COMPONENT \
--spec test_spec.json \
--include-edge-cases \
--include-mocks \
--framework gtest \
--output ${COMPONENT}_test.cc
# AI review of generated tests
z3ed ai review --file ${COMPONENT}_test.cc \
--criteria "coverage,correctness,performance" \
--output test_review.json
# Apply AI improvements
z3ed ai improve --file ${COMPONENT}_test.cc \
--feedback test_review.json \
--output ${COMPONENT}_test_improved.cc
# Run and validate tests
z3ed test run --file ${COMPONENT}_test_improved.cc --coverage
```
## Multi-Agent Collaboration
### Scenario: Parallel ROM Development
Multiple AI agents working on different aspects simultaneously.
```python
#!/usr/bin/env python3
# multi_agent_development.py - Coordinate multiple AI agents
import asyncio
import json
from z3ed_client import Agent, Coordinator
async def main():
# Initialize coordinator
coordinator = Coordinator("localhost:8080")
# Define agents with specializations
agents = [
Agent("overworld_specialist", capabilities=["overworld", "sprites"]),
Agent("dungeon_specialist", capabilities=["dungeon", "objects"]),
Agent("graphics_specialist", capabilities=["graphics", "palettes"]),
Agent("testing_specialist", capabilities=["testing", "validation"])
]
# Connect all agents
for agent in agents:
await agent.connect()
# Define parallel tasks
tasks = [
{
"id": "task_1",
"type": "overworld",
"description": "Optimize Light World map connections",
"assigned_to": "overworld_specialist"
},
{
"id": "task_2",
"type": "dungeon",
"description": "Balance enemy placement in dungeons 1-3",
"assigned_to": "dungeon_specialist"
},
{
"id": "task_3",
"type": "graphics",
"description": "Create new palette variations for seasons",
"assigned_to": "graphics_specialist"
},
{
"id": "task_4",
"type": "testing",
"description": "Generate tests for all recent changes",
"assigned_to": "testing_specialist"
}
]
# Queue tasks
for task in tasks:
coordinator.queue_task(task)
# Monitor progress
while not coordinator.all_tasks_complete():
status = coordinator.get_status()
print(f"Progress: {status['completed']}/{status['total']} tasks")
# Handle conflicts if they arise
if status['conflicts']:
for conflict in status['conflicts']:
resolution = coordinator.resolve_conflict(
conflict,
strategy="merge" # or "last_write_wins", "manual"
)
print(f"Resolved conflict: {resolution}")
await asyncio.sleep(5)
# Collect results
results = coordinator.get_all_results()
# Generate combined report
report = {
"timestamp": datetime.now().isoformat(),
"agents": [agent.name for agent in agents],
"tasks_completed": len(results),
"results": results
}
with open("multi_agent_report.json", "w") as f:
json.dump(report, f, indent=2)
print("Multi-agent development complete!")
if __name__ == "__main__":
asyncio.run(main())
```
### Scenario: Agent Coordination Script
Bash script for coordinating multiple z3ed instances.
```bash
#!/bin/bash
# agent_coordination.sh - Coordinate multiple z3ed agents
# Start coordination server
z3ed server start --port 8080 --config server.yaml &
SERVER_PID=$!
# Function to run agent task
run_agent() {
local agent_name=$1
local task=$2
local log_file="${agent_name}.log"
echo "Starting $agent_name for task: $task"
z3ed agent run --name "$agent_name" \
--task "$task" \
--server localhost:8080 \
--log "$log_file" &
}
# Start multiple agents
run_agent "agent_overworld" "optimize overworld maps 0x00-0x3F"
run_agent "agent_dungeon" "validate and fix all dungeon rooms"
run_agent "agent_graphics" "compress unused graphics data"
run_agent "agent_testing" "generate missing unit tests"
# Monitor agent progress
while true; do
clear
echo "=== Agent Status ==="
z3ed agent status --server localhost:8080 --format table
# Check for completion
if z3ed agent status --server localhost:8080 --check-complete; then
echo "All agents completed!"
break
fi
sleep 10
done
# Collect and merge results
z3ed agent collect-results --server localhost:8080 --output results/
# Stop server
kill $SERVER_PID
```
## CI/CD Integration
### Scenario: GitHub Actions Integration
`.github/workflows/z3ed-testing.yml`:
```yaml
name: Z3ED Automated Testing
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
rom-validation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup z3ed
run: |
./scripts/install-z3ed.sh
z3ed --version
- name: Load ROM
run: |
z3ed rom load --file ${{ secrets.ROM_PATH }}
z3ed rom validate
- name: Run validation suite
run: |
z3ed test run --suite validation.yaml
z3ed query stats --type all --output stats.json
- name: Check for regressions
run: |
z3ed test baseline --compare --name stable --threshold 95
- name: Generate report
run: |
z3ed test coverage --report markdown --output REPORT.md
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('REPORT.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: report
});
ai-review:
runs-on: ubuntu-latest
needs: rom-validation
steps:
- name: AI Code Review
run: |
z3ed ai review --changes ${{ github.sha }} \
--model gemini-pro \
--output review.json
- name: Apply AI Suggestions
run: |
z3ed ai apply --suggestions review.json --auto-approve safe
```
### Scenario: Local CI Pipeline
```bash
#!/bin/bash
# local_ci.sh - Local CI/CD pipeline
# Configuration
ROM_FILE="zelda3.sfc"
BUILD_DIR="build"
TEST_RESULTS="test_results"
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Function to print colored output
print_status() {
local status=$1
local message=$2
case $status in
"SUCCESS") echo -e "${GREEN}${NC} $message" ;;
"FAILURE") echo -e "${RED}${NC} $message" ;;
"WARNING") echo -e "${YELLOW}${NC} $message" ;;
*) echo "$message" ;;
esac
}
# Step 1: Clean and build
print_status "INFO" "Starting CI pipeline..."
z3ed build clean --all
z3ed build --preset lin-dbg --parallel 8
if [ $? -eq 0 ]; then
print_status "SUCCESS" "Build completed"
else
print_status "FAILURE" "Build failed"
exit 1
fi
# Step 2: Run tests
mkdir -p $TEST_RESULTS
z3ed test run --category unit --output $TEST_RESULTS/unit.xml
UNIT_RESULT=$?
z3ed test run --category integration --rom $ROM_FILE --output $TEST_RESULTS/integration.xml
INTEGRATION_RESULT=$?
if [ $UNIT_RESULT -eq 0 ] && [ $INTEGRATION_RESULT -eq 0 ]; then
print_status "SUCCESS" "All tests passed"
else
print_status "FAILURE" "Some tests failed"
z3ed test report --dir $TEST_RESULTS --format console
fi
# Step 3: ROM validation
z3ed rom load --file $ROM_FILE
z3ed rom validate --checksums --headers --regions
if [ $? -eq 0 ]; then
print_status "SUCCESS" "ROM validation passed"
else
print_status "WARNING" "ROM validation warnings"
fi
# Step 4: Performance benchmarks
z3ed test benchmark --suite performance.yaml --output benchmarks.json
z3ed test benchmark --compare-baseline --threshold 110
# Step 5: Generate reports
z3ed test coverage --report html --output coverage/
z3ed test report --comprehensive --output CI_REPORT.md
print_status "SUCCESS" "CI pipeline completed!"
```
## Advanced Automation Scripts
### Scenario: Intelligent Room Generator
Generate dungeon rooms using AI and templates.
```python
#!/usr/bin/env python3
# intelligent_room_generator.py - AI-powered room generation
import json
import random
from z3ed_client import Z3edClient
class IntelligentRoomGenerator:
def __init__(self, client):
self.client = client
self.templates = self.load_templates()
def load_templates(self):
"""Load room templates from file"""
with open("room_templates.json", "r") as f:
return json.load(f)
def generate_room(self, room_id, difficulty="medium", theme="castle"):
"""Generate a room based on parameters"""
# Select appropriate template
template = self.select_template(difficulty, theme)
# Get AI suggestions for room layout
suggestions = self.client.ai_suggest(
task="generate dungeon room layout",
constraints={
"room_id": room_id,
"difficulty": difficulty,
"theme": theme,
"template": template["name"]
}
)
# Apply base template
self.apply_template(room_id, template)
# Apply AI suggestions
for suggestion in suggestions["modifications"]:
self.apply_modification(room_id, suggestion)
# Validate room
validation = self.client.validate_room(room_id)
if not validation["valid"]:
# Ask AI to fix issues
fixes = self.client.ai_suggest(
task="fix dungeon room issues",
context={
"room_id": room_id,
"issues": validation["issues"]
}
)
for fix in fixes["fixes"]:
self.apply_modification(room_id, fix)
return self.get_room_data(room_id)
def select_template(self, difficulty, theme):
"""Select best matching template"""
matching = [
t for t in self.templates
if t["difficulty"] == difficulty and t["theme"] == theme
]
return random.choice(matching) if matching else self.templates[0]
def apply_template(self, room_id, template):
"""Apply template to room"""
# Set room properties
for prop, value in template["properties"].items():
self.client.set_room_property(room_id, prop, value)
# Place template objects
for obj in template["objects"]:
self.client.place_object(
room_id,
obj["type"],
obj["x"],
obj["y"]
)
def apply_modification(self, room_id, modification):
"""Apply a single modification to room"""
action = modification["action"]
params = modification["params"]
if action == "place_object":
self.client.place_object(room_id, **params)
elif action == "set_property":
self.client.set_room_property(room_id, **params)
elif action == "remove_object":
self.client.remove_object(room_id, **params)
def generate_dungeon(self, start_room, num_rooms, difficulty_curve):
"""Generate entire dungeon"""
rooms = []
for i in range(num_rooms):
room_id = start_room + i
# Adjust difficulty based on curve
difficulty = self.calculate_difficulty(i, num_rooms, difficulty_curve)
# Generate room
room_data = self.generate_room(room_id, difficulty)
rooms.append(room_data)
# Connect to previous room
if i > 0:
self.connect_rooms(room_id - 1, room_id)
# Final validation
self.validate_dungeon(start_room, num_rooms)
return rooms
def calculate_difficulty(self, index, total, curve):
"""Calculate difficulty based on position and curve"""
if curve == "linear":
return ["easy", "medium", "hard"][min(index // (total // 3), 2)]
elif curve == "exponential":
return ["easy", "medium", "hard"][min(int((index / total) ** 2 * 3), 2)]
else:
return "medium"
# Main execution
if __name__ == "__main__":
client = Z3edClient("localhost:8080")
generator = IntelligentRoomGenerator(client)
# Generate a 10-room dungeon
dungeon = generator.generate_dungeon(
start_room=100,
num_rooms=10,
difficulty_curve="exponential"
)
# Save dungeon data
with open("generated_dungeon.json", "w") as f:
json.dump(dungeon, f, indent=2)
print(f"Generated {len(dungeon)} rooms successfully!")
```
### Scenario: Automated ROM Optimizer
Optimize ROM for size and performance.
```bash
#!/bin/bash
# rom_optimizer.sh - Automated ROM optimization
# Create backup
z3ed rom snapshot --name "pre-optimization" --compress
# Step 1: Identify unused space
echo "Analyzing ROM for optimization opportunities..."
z3ed query find-unused-space --min-size 256 --output unused_space.json
# Step 2: Compress graphics
echo "Compressing graphics data..."
for sheet in {0..223}; do
z3ed editor graphics compress-sheet --sheet $sheet --algorithm lz77
done
# Step 3: Optimize sprite data
echo "Optimizing sprite data..."
z3ed ai analyze --type optimization \
--target sprites \
--output sprite_optimization.json
z3ed ai apply --optimizations sprite_optimization.json
# Step 4: Remove duplicate data
echo "Removing duplicate data..."
z3ed query find-duplicates --min-size 16 --output duplicates.json
z3ed optimize remove-duplicates --input duplicates.json --safe-mode
# Step 5: Optimize room data
echo "Optimizing dungeon room data..."
for room in {0..295}; do
z3ed editor dungeon optimize-room --room $room \
--remove-unreachable \
--compress-objects
done
# Step 6: Pack data efficiently
echo "Repacking ROM data..."
z3ed optimize repack --strategy best-fit
# Step 7: Validate optimization
echo "Validating optimized ROM..."
z3ed rom validate --comprehensive
z3ed test run --suite optimization_validation.yaml
# Step 8: Generate report
original_size=$(z3ed rom info --snapshot "pre-optimization" | jq '.size')
optimized_size=$(z3ed rom info | jq '.size')
saved=$((original_size - optimized_size))
percent=$((saved * 100 / original_size))
cat > optimization_report.md << EOF
# ROM Optimization Report
## Summary
- Original Size: $original_size bytes
- Optimized Size: $optimized_size bytes
- Space Saved: $saved bytes ($percent%)
## Optimizations Applied
$(z3ed optimize list-applied --format markdown)
## Validation Results
$(z3ed test results --suite optimization_validation.yaml --format markdown)
EOF
echo "Optimization complete! Report saved to optimization_report.md"
```
## Best Practices
### 1. Always Create Snapshots
Before any major operation:
```bash
z3ed rom snapshot --name "descriptive_name" --compress
```
### 2. Use Dry-Run for Dangerous Operations
```bash
z3ed editor batch --script changes.json --dry-run
```
### 3. Validate After Changes
```bash
z3ed rom validate
z3ed test run --quick
```
### 4. Document Your Workflows
```bash
# Generate documentation from your scripts
z3ed docs generate --from-script my_workflow.sh --output workflow_docs.md
```
### 5. Use AI for Review
```bash
z3ed ai review --changes . --criteria "correctness,performance,style"
```
## Troubleshooting Common Issues
### Issue: Command Not Found
```bash
# Verify z3ed is in PATH
which z3ed
# Or use full path
/usr/local/bin/z3ed --version
```
### Issue: ROM Won't Load
```bash
# Check ROM validity
z3ed rom validate --file suspicious.sfc --verbose
# Try with different region
z3ed rom load --file rom.sfc --region USA
```
### Issue: Test Failures
```bash
# Run with verbose output
z3ed test run --verbose --filter failing_test
# Generate detailed report
z3ed test debug --test failing_test --output debug_report.json
```
### Issue: Network Connection Failed
```bash
# Test connection
z3ed network ping --host localhost --port 8080
# Use fallback mode
z3ed --offline --cache-only
```
## Next Steps
- Explore the [Z3ED Command Reference](z3ed-command-reference.md)
- Read the [API Documentation](../reference/api/)
- Join the [YAZE Discord](https://discord.gg/yaze) for support
- Contribute your workflows to the [Examples Repository](https://github.com/yaze/z3ed-examples)

View File

@@ -5,50 +5,52 @@
# YAZE Documentation
YAZE documentation now focuses on concise, Doxygen-friendly sections. Use the categories
below for human-readable guides and reference material. Internal planning, AI agent playbooks,
and research notes were moved to `docs/internal/` so the public docs stay focused.
## Overview
- [Getting Started](overview/getting-started.md)
## Build & Tooling
- [Build Quick Reference](build/quick-reference.md)
- [Build From Source](build/build-from-source.md)
- [Platform Compatibility](build/platform-compatibility.md)
- [CMake Presets](build/presets.md)
- [Build Troubleshooting](build/troubleshooting.md)
## Usage Guides
- [Dungeon Editor](usage/dungeon-editor.md)
- [Overworld Loading](usage/overworld-loading.md)
- [z3ed CLI](usage/z3ed-cli.md)
- [Examples & Recipes](examples/)
## Developer Guides
- [Architecture Overview](developer/architecture.md)
- [Dependency Architecture](developer/dependency-architecture.md)
- [Git Workflow](developer/git-workflow.md)
- [Networking Overview](developer/networking.md)
- [Testing Guide](developer/testing-guide.md)
- [Testing Without ROMs](developer/testing-without-roms.md)
- [Debugging Guide](developer/debugging-guide.md)
- [Debug Flags](developer/debug-flags.md)
- [Assembler Style Guide](developer/asm-style-guide.md)
- [API Reference](developer/api-reference.md)
- [Emulator Development Guide](developer/emulator-development-guide.md)
- [Canvas System](developer/canvas-system.md)
- [Palette System Overview](developer/palette-system-overview.md)
- [Tile16 Palette System](developer/tile16-palette-system.md)
- [Overworld Entity System](developer/overworld-entity-system.md)
- [GUI Consistency Guide](developer/gui-consistency-guide.md)
- [AI-Assisted Development](developer/ai-assisted-development.md)
## Reference
- [ROM Reference](reference/rom-reference.md)
- [Changelog](reference/changelog.md)
Welcome to the YAZE documentation. This site covers installation, usage, and development of the YAZE ROM editor for The Legend of Zelda: A Link to the Past.
---
Need editor playbooks, refactors, or AI workflows? Head over to [`docs/internal/`](../internal/README.md)
for internal documentation that stays out of the public Doxygen site.
## Quick Links
| Goal | Start Here |
|------|------------|
| **Get started** | [Getting Started](overview/getting-started.md) |
| **Build from source** | [Build Quick Reference](build/quick-reference.md) |
| **Understand the code** | [Architecture Overview](developer/architecture.md) |
| **Fix build issues** | [Build Troubleshooting](build/troubleshooting.md) |
| **Use the CLI** | [z3ed CLI Guide](usage/z3ed-cli.md) |
---
## Documentation Sections
### Getting Started
- [Getting Started](overview/getting-started.md)
### Build and Tooling
- [Build Quick Reference](build/quick-reference.md) - Essential build commands
- [Build from Source](build/build-from-source.md) - Full build guide
- [CMake Presets](build/presets.md) - Preset reference
- [Platform Compatibility](build/platform-compatibility.md) - OS-specific notes
- [Build Troubleshooting](build/troubleshooting.md) - Common issues
### Usage Guides
- [Dungeon Editor](usage/dungeon-editor.md) - Room editing guide
- [z3ed CLI](usage/z3ed-cli.md) - Command-line interface
- [Web App](usage/web-app.md) - Browser-based editor (preview)
- [Overworld Loading](usage/overworld-loading.md) - Technical reference
### Developer Guides
- [Architecture Overview](developer/architecture.md) - System design
- [Testing Guide](developer/testing-guide.md) - Test organization and execution
- [Debugging Guide](developer/debugging-guide.md) - Debugging workflows
- [Canvas System](developer/canvas-system.md) - UI canvas reference
- [Palette System](developer/palette-system-overview.md) - Color handling
### Reference
- [ROM Reference](reference/rom-reference.md) - ROM data structures
- [z3ed Command Reference](reference/z3ed-command-reference.md) - CLI commands
- [Changelog](reference/changelog.md) - Version history
---
**Internal Documentation:** Development plans, agent workflows, and architecture deep-dives are in [docs/internal/](../internal/README.md).

View File

@@ -1,68 +1,77 @@
# Getting Started
# Getting Started with YAZE
This software allows you to modify "The Legend of Zelda: A Link to the Past" (US or JP) ROMs. It is built for compatibility with ZScream projects and designed to be cross-platform.
YAZE is a ROM editor for "The Legend of Zelda: A Link to the Past" (US and JP versions). It provides a full-featured GUI editor, integrated SNES emulator, and AI-powered command-line tools.
---
## Quick Start
1. **Download** the latest release for your platform from the [releases page](https://github.com/scawful/yaze/releases).
2. **Load ROM** via `File > Open ROM`.
3. **Select an Editor** from the main toolbar (e.g., Overworld, Dungeon, Graphics).
4. **Make Changes** and save your project.
1. **Download** the latest release for your platform from the [GitHub Releases page](https://github.com/scawful/yaze/releases)
2. **Launch** the application and load your ROM via `File > Open ROM`
3. **Choose an Editor** from the toolbar (Overworld, Dungeon, Graphics, etc.)
4. **Edit** your ROM and save your changes
> Building from source or enabling AI tooling? Use the
> [Build & Test Quick Reference](../build/quick-reference.md) for the canonical commands and presets.
> **Building from source?** See the [Build and Test Quick Reference](../build/quick-reference.md).
## General Tips
---
- **Experiment Flags**: Enable or disable new features in `File > Options > Experiment Flags`.
- **Backup Files**: Enabled by default. Each save creates a timestamped backup of your ROM.
- **Extensions**: Load custom tools via the `Extensions` menu (C library and Python module support is planned).
## Tips
## Feature Status
- **Backups**: Automatic backups are enabled by default. Each save creates a timestamped backup.
- **Experiment Flags**: Try new features via `File > Options > Experiment Flags`.
- **Extensions**: Load custom tools from the `Extensions` menu (plugin system under development).
| Feature | State | Notes |
|---|---|---|
| Overworld Editor | Stable | Supports vanilla and ZSCustomOverworld v2/v3 projects. |
| Dungeon Editor | Experimental | Requires extensive manual testing before production use. |
| Tile16 Editor | Experimental | Palette and tile workflows are still being tuned. |
| Palette Editor | Stable | Reference implementation for palette utilities. |
| Graphics Editor | Experimental | Rendering pipeline under active refactor. |
| Sprite Editor | Experimental | Card/UI patterns mid-migration. |
| Message Editor | Stable | Re-test after recent palette fixes. |
| Hex Editor | Stable | Direct ROM editing utility. |
| Asar Patching | Stable | Wraps the bundled Asar assembler. |
---
## Command-Line Interface (`z3ed`)
## Editor Status
`z3ed` provides scripted access to the same ROM editors.
| Editor | Status | Notes |
|--------|--------|-------|
| Overworld | Stable | Full support for vanilla and ZSCustomOverworld v2/v3 |
| Dungeon | Stable | Room editing, objects, sprites, palettes |
| Palette | Stable | Reference implementation for palette utilities |
| Message | Stable | Text and dialogue editing |
| Hex | Stable | Direct ROM byte editing |
| Asar Patching | Stable | Integrated Asar assembler |
| Graphics | Stable | Tile and sprite graphics editing |
| Sprite | Stable | Vanilla and custom sprite editing |
| Music | Experimental | Tracker and instrument editing |
### AI Agent Chat
Chat with an AI to perform edits using natural language.
---
## Command-Line Interface (z3ed)
The `z3ed` CLI provides scriptable access to ROM editing capabilities.
### AI Chat
```bash
# Start an interactive chat session with the AI agent
z3ed agent chat --rom zelda3.sfc
```
> **Prompt:** "What sprites are in dungeon 2?"
Example prompt: "What sprites are in dungeon 2?"
### Resource Inspection
Directly query ROM data.
### ROM Inspection
```bash
# List all sprites in the Eastern Palace (dungeon 2)
# List sprites in Eastern Palace
z3ed dungeon list-sprites --rom zelda3.sfc --dungeon 2
# Get information about a specific overworld map area
# Describe overworld map
z3ed overworld describe-map --rom zelda3.sfc --map 80
```
### Patching
Apply assembly patches using the integrated Asar assembler.
```bash
# Apply an assembly patch to the ROM
z3ed asar patch.asm --rom zelda3.sfc
```
## Extending Functionality
For more details, see the [z3ed CLI Guide](../usage/z3ed-cli.md).
YAZE exports a C API that is still evolving. Treat it as experimental and expect breaking changes while the plugin system is built out.
---
## Next Steps
- **[Dungeon Editor Guide](../usage/dungeon-editor.md)** - Learn dungeon room editing
- **[z3ed CLI Guide](../usage/z3ed-cli.md)** - Master the command-line interface
- **[Architecture Overview](../developer/architecture.md)** - Understand the codebase

View File

@@ -0,0 +1,50 @@
# Save State Format (v2)
This documents the chunked save-state format introduced in `Snes::saveState/loadState` (state file version 2).
## Goals
- Forward/backward compatible: per-file header + per-chunk versioning.
- Corruption-resilient: CRC on each chunk; size caps to avoid runaway allocations.
- Explicit layout: avoid raw struct dumps to sidestep padding/endianness issues.
## File Structure
```
uint32 magic = 'YAZE' (0x59415A45)
uint32 version = 2
repeat until EOF:
Chunk {
uint32 tag // ASCII packed: 'SNES', 'CPU ', 'PPU ', 'APU '
uint32 version // per-chunk version; currently 1 for all
uint32 size // payload size in bytes (capped at 16 MiB)
uint32 crc32 // CRC-32 of payload
uint8 payload[size]
}
```
## Chunk Payloads (v1)
- `SNES`: Core machine state (WRAM, timers, IRQ/NMI flags, latched ports, timers, etc.).
- `CPU `: CPU registers/flags + breakpoint list (capped to 1024 entries).
- `PPU `: PPU registers, VRAM/CGRAM/OAM, layer/window/bg structs written field-by-field.
- `APU `: APU registers, ports, timers, and 64K ARAM (capped) plus DSP/SPC700 state.
## Compatibility
- Legacy v1 flat saves (no magic) are still loadable: the loader falls back if the magic/version header is missing. They do not carry CRCs and remain best-effort only.
- Host endianness: serialization assumes little-endian hosts; load/save will fail fast otherwise.
## Validation & Errors
- Size guard: any chunk `size > 16 MiB` is rejected.
- CRC guard: mismatched CRC rejects the load to avoid partial/dirty state.
- Missing required chunks (`SNES`, `CPU `, `PPU `, `APU `) rejects the load.
- Streams are checked for `fail()` after every read/write; callers receive `absl::Status`.
## Extending
- Add a new chunk tag and bump its per-chunk `version` only. Keep `file version` stable unless the top-level format changes.
- Keep payloads explicit (no raw struct dumps). Write scalars/arrays with defined width and order.
- If you add new fields to an existing chunk, prefer:
1. Extending the payload and bumping that chunks version.
2. Keeping old fields first so older loaders can short-circuit safely.
## Conventions
- Tags use little-endian ASCII packing: `'SNES'` -> `0x53454E53`.
- CRC uses `render::CalculateCRC32`.
- Max buffer cap mirrors the largest expected subsystem payload (WRAM/ARAM).

View File

@@ -0,0 +1,140 @@
# SNES Compression Format (ALttP)
Decompression algorithm used in A Link to the Past, documented from ZSpriteMaker.
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/Utils.cs`
## Command Format
Each compressed block starts with a command byte:
```
Normal Command (when upper 3 bits != 0b111):
┌─────────────────────────────────────┐
│ 7 6 5 │ 4 3 2 1 0 │
│ CMD │ LENGTH (0-31) │
└─────────────────────────────────────┘
Length = (byte & 0x1F) + 1
Expanded Command (when upper 3 bits == 0b111):
┌─────────────────────────────────────┬──────────────┐
│ 7 6 5 │ 4 3 2 │ 1 0 │ Byte 2 │
│ 0b111 │ CMD │ LENGTH_HI │ LENGTH_LO │
└─────────────────────────────────────┴──────────────┘
Length = ((byte & 0x03) << 8 | nextByte) + 1
```
## Commands
| CMD | Name | Description |
|-----|------|-------------|
| 0 | Direct Copy | Copy `length` bytes directly from ROM to output |
| 1 | Byte Fill | Repeat single byte `length` times |
| 2 | Word Fill | Repeat 2-byte word `length/2` times |
| 3 | Increasing Fill | Write byte, increment, repeat `length` times |
| 4 | Repeat | Copy `length` bytes from earlier in output buffer |
## Terminator
`0xFF` byte terminates decompression.
## C++ Implementation
```cpp
#include <vector>
#include <cstdint>
std::vector<uint8_t> decompress_alttp(const uint8_t* rom, int pos) {
std::vector<uint8_t> buffer(0x1000, 0);
int bufferPos = 0;
while (true) {
uint8_t databyte = rom[pos];
if (databyte == 0xFF) break; // End marker
uint8_t cmd;
int length;
if ((databyte & 0xE0) == 0xE0) {
// Expanded command
cmd = (databyte >> 2) & 0x07;
length = ((databyte & 0x03) << 8) | rom[pos + 1];
pos += 2;
} else {
// Normal command
cmd = (databyte >> 5) & 0x07;
length = databyte & 0x1F;
pos += 1;
}
length += 1; // Minimum length is 1
switch (cmd) {
case 0: // Direct Copy
for (int i = 0; i < length; i++) {
buffer[bufferPos++] = rom[pos++];
}
break;
case 1: // Byte Fill
for (int i = 0; i < length; i++) {
buffer[bufferPos++] = rom[pos];
}
pos += 1;
break;
case 2: // Word Fill
for (int i = 0; i < length; i += 2) {
buffer[bufferPos++] = rom[pos];
buffer[bufferPos++] = rom[pos + 1];
}
pos += 2;
break;
case 3: // Increasing Fill
{
uint8_t val = rom[pos];
for (int i = 0; i < length; i++) {
buffer[bufferPos++] = val++;
}
pos += 1;
}
break;
case 4: // Repeat from buffer
{
// Little-endian address
int addr = rom[pos] | (rom[pos + 1] << 8);
for (int i = 0; i < length; i++) {
buffer[bufferPos++] = buffer[addr++];
}
pos += 2;
}
break;
}
}
buffer.resize(bufferPos);
return buffer;
}
```
## Address Conversion
```cpp
// SNES LoROM to PC file offset
inline int snes_to_pc(int addr) {
return (addr & 0x7FFF) | ((addr & 0x7F0000) >> 1);
}
// PC file offset to SNES LoROM
inline int pc_to_snes(int addr) {
return (addr & 0x7FFF) | 0x8000 | ((addr & 0x7F8000) << 1);
}
```
## Notes
- Buffer size is 0x1000 (4KB) - typical for tile/map data
- Command 4 (Repeat) uses little-endian address within output buffer
- Expanded commands allow lengths up to 1024 bytes
- Used for: tile graphics, tilemaps, some sprite data

View File

@@ -0,0 +1,189 @@
# SNES Graphics Conversion
Tile format conversion routines for ALttP graphics, documented from ZSpriteMaker.
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/Utils.cs`
## SNES Tile Formats
### 3BPP (3 bits per pixel, 8 colors)
- Used for: Sprites, some backgrounds
- 24 bytes per 8x8 tile
- Planar format: 2 bytes per row (planes 0-1) + 1 byte per row (plane 2)
### 4BPP (4 bits per pixel, 16 colors)
- Used for: Most backgrounds, UI
- 32 bytes per 8x8 tile
- Planar format: 2 interleaved bitplanes per 16 bytes
## 3BPP Tile Layout
```
Bytes 0-15: Planes 0 and 1 (interleaved, 2 bytes per row)
Bytes 16-23: Plane 2 (1 byte per row)
Row 0: [Plane0_Row0][Plane1_Row0]
Row 1: [Plane0_Row1][Plane1_Row1]
...
Row 7: [Plane0_Row7][Plane1_Row7]
[Plane2_Row0][Plane2_Row1]...[Plane2_Row7]
```
## C++ Implementation: 3BPP to 8BPP
Converts a sheet of 64 tiles (16x4 arrangement, 128x32 pixels) from 3BPP to indexed 8BPP:
```cpp
#include <array>
#include <cstdint>
constexpr std::array<uint8_t, 8> bitmask = {
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};
// Input: 24 * 64 = 1536 bytes (64 tiles in 3BPP)
// Output: 128 * 32 = 4096 bytes (8BPP indexed)
std::array<uint8_t, 0x1000> snes_3bpp_to_8bpp(const uint8_t* data) {
std::array<uint8_t, 0x1000> sheet{};
int index = 0;
for (int tileRow = 0; tileRow < 4; tileRow++) { // 4 rows of tiles
for (int tileCol = 0; tileCol < 16; tileCol++) { // 16 tiles per row
int tileOffset = (tileCol + tileRow * 16) * 24; // 24 bytes per tile
for (int y = 0; y < 8; y++) { // 8 pixel rows
uint8_t plane0 = data[tileOffset + y * 2];
uint8_t plane1 = data[tileOffset + y * 2 + 1];
uint8_t plane2 = data[tileOffset + 16 + y];
for (int x = 0; x < 8; x++) { // 8 pixels per row
uint8_t mask = bitmask[x];
uint8_t pixel = 0;
if (plane0 & mask) pixel |= 1;
if (plane1 & mask) pixel |= 2;
if (plane2 & mask) pixel |= 4;
// Calculate output position in 128-wide sheet
int outX = tileCol * 8 + x;
int outY = tileRow * 8 + y;
sheet[outY * 128 + outX] = pixel;
}
}
}
}
return sheet;
}
```
## Alternative: Direct Index Calculation
```cpp
std::array<uint8_t, 0x1000> snes_3bpp_to_8bpp_v2(const uint8_t* data) {
std::array<uint8_t, 0x1000> sheet{};
int index = 0;
for (int j = 0; j < 4; j++) { // Tile row
for (int i = 0; i < 16; i++) { // Tile column
for (int y = 0; y < 8; y++) { // Pixel row
int base = y * 2 + i * 24 + j * 384;
uint8_t line0 = data[base];
uint8_t line1 = data[base + 1];
uint8_t line2 = data[base - y * 2 + y + 16];
for (uint8_t mask = 0x80; mask > 0; mask >>= 1) {
uint8_t pixel = 0;
if (line0 & mask) pixel |= 1;
if (line1 & mask) pixel |= 2;
if (line2 & mask) pixel |= 4;
sheet[index++] = pixel;
}
}
}
}
return sheet;
}
```
## Palette Reading
SNES uses 15-bit BGR color (5 bits per channel):
```cpp
#include <cstdint>
struct Color {
uint8_t r, g, b, a;
};
Color read_snes_color(const uint8_t* data, int offset) {
uint16_t color = data[offset] | (data[offset + 1] << 8);
return {
static_cast<uint8_t>((color & 0x001F) << 3), // R: bits 0-4
static_cast<uint8_t>(((color >> 5) & 0x1F) << 3), // G: bits 5-9
static_cast<uint8_t>(((color >> 10) & 0x1F) << 3), // B: bits 10-14
255 // A: opaque
};
}
// Read full 8-color palette (3BPP)
std::array<Color, 8> read_3bpp_palette(const uint8_t* data, int offset) {
std::array<Color, 8> palette;
for (int i = 0; i < 8; i++) {
palette[i] = read_snes_color(data, offset + i * 2);
}
return palette;
}
// Read full 16-color palette (4BPP)
std::array<Color, 16> read_4bpp_palette(const uint8_t* data, int offset) {
std::array<Color, 16> palette;
for (int i = 0; i < 16; i++) {
palette[i] = read_snes_color(data, offset + i * 2);
}
return palette;
}
```
## OAM Tile Positioning
From ZSpriteMaker's OamTile class - convert tile ID to sheet coordinates:
```cpp
// Tile ID to sprite sheet pixel position
// Assumes 16 tiles per row (128 pixels wide sheet)
inline int tile_to_sheet_x(uint16_t id) { return (id % 16) * 8; }
inline int tile_to_sheet_y(uint16_t id) { return (id / 16) * 8; }
// Packed OAM format (32-bit)
inline uint32_t pack_oam_tile(uint16_t id, uint8_t x, uint8_t y,
uint8_t palette, uint8_t priority,
bool mirrorX, bool mirrorY) {
return (id << 16) |
((mirrorY ? 0 : 1) << 31) |
((mirrorX ? 0 : 1) << 30) |
(priority << 28) |
(palette << 25) |
(x << 8) |
y;
}
```
## Sheet Dimensions
| Format | Tiles | Sheet Size | Bytes/Tile | Total Bytes |
|--------|-------|------------|------------|-------------|
| 3BPP 64-tile | 16x4 | 128x32 px | 24 | 1,536 |
| 4BPP 64-tile | 16x4 | 128x32 px | 32 | 2,048 |
| 3BPP 128-tile | 16x8 | 128x64 px | 24 | 3,072 |
## Integration Notes
- Color index 0 is typically transparent
- SNES sprites use 3BPP (8 colors per palette row)
- Background tiles often use 4BPP (16 colors)
- ALttP Link sprites: 3BPP, multiple sheets for different states

View File

@@ -0,0 +1,209 @@
# ALttP Symbol File Format
Documentation for importing disassembly symbol files into YAZE.
**Source:** `~/Code/alttp-gigaleak/DISASM/jpdasm/symbols_*.asm`
## Available Symbol Files
| File | Contents | Address Range |
|------|----------|---------------|
| `symbols_wram.asm` | Work RAM labels | $7E0000-$7FFFFF |
| `symbols_sram.asm` | Save RAM labels | $700000-$70FFFF |
| `symbols_apu.asm` | Audio processor | APU addresses |
| `registers.asm` | Hardware registers | $2100-$21FF, $4200-$43FF |
## File Format
Simple assembly-style symbol definitions:
```asm
; Comment lines start with semicolon
SYMBOL_NAME = $AABBCC ; Optional inline comment
; Multi-line comments for documentation
; LENGTH: 0x10
BLOCK_START = $7E0000
```
## Symbol Naming Conventions
### Suffixes
| Suffix | Meaning |
|--------|---------|
| `L` | Low byte of 16-bit value |
| `H` | High byte of 16-bit value |
| `U` | Unused high byte |
| `Q` | Queue (for NMI updates) |
### Prefixes
| Prefix | Category |
|--------|----------|
| `LINK_` | Player state |
| `SPR_` | Sprite/enemy |
| `NMI_` | NMI handler |
| `OAM_` | OAM buffer |
| `UNUSED_` | Free RAM |
### Bitfield Documentation
```asm
; a - found to the north west
; b - found to the north east
; c - found to the south west
; d - found to the south east
; Bitfield: abcd....
TILE_DIRECTION = $7E000A
```
## Key Symbols (Quick Reference)
### Game State ($7E0010-$7E001F)
```asm
MODE = $7E0010 ; Main game mode
SUBMODE = $7E0011 ; Sub-mode within mode
LAG = $7E0012 ; NMI sync flag
INIDISPQ = $7E0013 ; Display brightness queue
NMISTRIPES = $7E0014 ; Tilemap update flag
NMICGRAM = $7E0015 ; Palette update flag
```
### Player Position (typical)
```asm
LINK_X_LO = $7E0022 ; Link X position (low)
LINK_X_HI = $7E0023 ; Link X position (high)
LINK_Y_LO = $7E0020 ; Link Y position (low)
LINK_Y_HI = $7E0021 ; Link Y position (high)
LINK_LAYER = $7E00EE ; Current layer (0=BG1, 1=BG2)
```
### Room/Dungeon
```asm
ROOM_ID = $7E00A0 ; Current room number
DUNGEON_ID = $7E040C ; Current dungeon
```
## C++ Parser
```cpp
#include <string>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <regex>
struct Symbol {
std::string name;
uint32_t address;
std::string comment;
};
std::unordered_map<uint32_t, Symbol> parse_symbols(const std::string& path) {
std::unordered_map<uint32_t, Symbol> symbols;
std::ifstream file(path);
std::string line;
// Pattern: NAME = $ADDRESS ; comment
std::regex pattern(R"(^(\w+)\s*=\s*\$([0-9A-Fa-f]+)\s*(?:;\s*(.*))?$)");
while (std::getline(file, line)) {
// Skip pure comment lines
if (line.empty() || line[0] == ';') continue;
std::smatch match;
if (std::regex_search(line, match, pattern)) {
Symbol sym;
sym.name = match[1].str();
sym.address = std::stoul(match[2].str(), nullptr, 16);
sym.comment = match[3].str();
symbols[sym.address] = sym;
}
}
return symbols;
}
// Get symbol name for address (returns empty if not found)
std::string lookup_symbol(const std::unordered_map<uint32_t, Symbol>& syms,
uint32_t addr) {
auto it = syms.find(addr);
return (it != syms.end()) ? it->second.name : "";
}
```
## Integration Ideas
### Hex Editor Enhancement
```cpp
// Display symbol alongside address
void draw_hex_line(uint32_t addr, const uint8_t* data) {
std::string sym = lookup_symbol(symbols, addr);
if (!sym.empty()) {
printf("%06X %-20s ", addr, sym.c_str());
} else {
printf("%06X %-20s ", addr, "");
}
// ... draw hex bytes
}
```
### Disassembly View
```cpp
// Replace addresses with symbols in ASM output
std::string format_operand(uint32_t addr) {
std::string sym = lookup_symbol(symbols, addr);
if (!sym.empty()) {
return sym;
}
return "$" + to_hex(addr);
}
```
### Memory Watcher
```cpp
struct Watch {
std::string label;
uint32_t address;
uint8_t size; // 1, 2, or 3 bytes
};
// Auto-populate watches from symbol file
std::vector<Watch> create_watches_from_symbols() {
std::vector<Watch> watches;
// Key game state
watches.push_back({"Mode", 0x7E0010, 1});
watches.push_back({"Submode", 0x7E0011, 1});
watches.push_back({"Link X", 0x7E0022, 2});
watches.push_back({"Link Y", 0x7E0020, 2});
watches.push_back({"Room", 0x7E00A0, 2});
return watches;
}
```
## Free RAM Discovery
Symbol files mark unused RAM:
```asm
; FREE RAM: 0x20
UNUSED_7E0500 = $7E0500
UNUSED_7E0501 = $7E0501
; ...
; BIG FREE RAM
UNUSED_7E1000 = $7E1000 ; Large block available
```
Parse for `FREE RAM` comments to find available space for ROM hacks.
## File Locations
```
~/Code/alttp-gigaleak/DISASM/jpdasm/
├── symbols_wram.asm # Work RAM ($7E)
├── symbols_sram.asm # Save RAM ($70)
├── symbols_apu.asm # Audio
├── registers.asm # Hardware registers
└── registers_spc.asm # SPC700 registers
```

View File

@@ -0,0 +1,213 @@
# ZSM File Format Specification
ZSpriteMaker Project File format (`.zsm`) - used by ZSpriteMaker for ALttP custom sprites.
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/`
## Format Overview
Binary file format using .NET BinaryWriter/BinaryReader conventions:
- Strings: Length-prefixed (7-bit encoded length + UTF-8 bytes)
- Integers: Little-endian 32-bit
- Booleans: Single byte (0x00 = false, 0x01 = true)
## File Structure
```
┌─────────────────────────────────────────────────┐
│ ANIMATIONS SECTION │
├─────────────────────────────────────────────────┤
│ int32 animationCount │
│ for each animation: │
│ string name (length-prefixed) │
│ byte frameStart │
│ byte frameEnd │
│ byte frameSpeed │
├─────────────────────────────────────────────────┤
│ FRAMES SECTION │
├─────────────────────────────────────────────────┤
│ int32 frameCount │
│ for each frame: │
│ int32 tileCount │
│ for each tile: │
│ uint16 id (tile index in sheet) │
│ byte palette (0-7) │
│ bool mirrorX │
│ bool mirrorY │
│ byte priority (0-3, default 3) │
│ bool size (false=8x8, true=16x16) │
│ byte x (0-251) │
│ byte y (0-219) │
│ byte z (depth/layer) │
├─────────────────────────────────────────────────┤
│ SPRITE PROPERTIES (20 booleans) │
├─────────────────────────────────────────────────┤
│ bool blockable │
│ bool canFall │
│ bool collisionLayer │
│ bool customDeath │
│ bool damageSound │
│ bool deflectArrows │
│ bool deflectProjectiles │
│ bool fast │
│ bool harmless │
│ bool impervious │
│ bool imperviousArrow │
│ bool imperviousMelee │
│ bool interaction │
│ bool isBoss │
│ bool persist │
│ bool shadow │
│ bool smallShadow │
│ statis (stasis) │
│ bool statue │
│ bool waterSprite │
├─────────────────────────────────────────────────┤
│ SPRITE STATS (6 bytes) │
├─────────────────────────────────────────────────┤
│ byte prize (drop item ID) │
│ byte palette (sprite palette) │
│ byte oamNbr (OAM slot count) │
│ byte hitbox (collision box ID) │
│ byte health │
│ byte damage │
├─────────────────────────────────────────────────┤
│ USER ROUTINES SECTION (optional) │
├─────────────────────────────────────────────────┤
│ string spriteName (length-prefixed) │
│ int32 routineCount │
│ for each routine: │
│ string name (e.g., "Long Main") │
│ string code (ASM code) │
├─────────────────────────────────────────────────┤
│ SPRITE ID (optional) │
├─────────────────────────────────────────────────┤
│ string spriteId (hex string) │
└─────────────────────────────────────────────────┘
```
## Data Types
### OamTile
```cpp
struct OamTile {
uint16_t id; // Tile index in sprite sheet (0-511)
uint8_t palette; // Palette index (0-7)
bool mirrorX; // Horizontal flip
bool mirrorY; // Vertical flip
uint8_t priority; // BG priority (0-3)
bool size; // false=8x8, true=16x16
uint8_t x; // X position (0-251)
uint8_t y; // Y position (0-219)
uint8_t z; // Z depth for sorting
};
// Tile sheet position from ID:
// sheet_x = (id % 16) * 8
// sheet_y = (id / 16) * 8
```
### AnimationGroup
```cpp
struct AnimationGroup {
std::string name; // Animation name
uint8_t frameStart; // First frame index
uint8_t frameEnd; // Last frame index
uint8_t frameSpeed; // Frames per tick
};
```
### Frame
```cpp
struct Frame {
std::vector<OamTile> tiles;
};
```
## Sprite Properties Bitfield (Alternative)
The 20 boolean properties could be packed as bitfield:
```cpp
enum SpriteFlags : uint32_t {
BLOCKABLE = 1 << 0,
CAN_FALL = 1 << 1,
COLLISION_LAYER = 1 << 2,
CUSTOM_DEATH = 1 << 3,
DAMAGE_SOUND = 1 << 4,
DEFLECT_ARROWS = 1 << 5,
DEFLECT_PROJECTILES = 1 << 6,
FAST = 1 << 7,
HARMLESS = 1 << 8,
IMPERVIOUS = 1 << 9,
IMPERVIOUS_ARROW = 1 << 10,
IMPERVIOUS_MELEE = 1 << 11,
INTERACTION = 1 << 12,
IS_BOSS = 1 << 13,
PERSIST = 1 << 14,
SHADOW = 1 << 15,
SMALL_SHADOW = 1 << 16,
STASIS = 1 << 17,
STATUE = 1 << 18,
WATER_SPRITE = 1 << 19,
};
```
## Default User Routines
New projects include three template routines:
1. **Long Main** - Main sprite loop (`TemplateLongMain.asm`)
2. **Sprite Prep** - Initialization (`TemplatePrep.asm`)
3. **Sprite Draw** - Rendering (`TemplateDraw.asm`)
## Related Formats
### ZSPR (ALttP Randomizer Format)
Different format used by ALttP Randomizer for Link sprite replacements.
- Magic: `ZSPR` (4 bytes)
- Contains: sprite sheet, palette, glove colors, metadata
- Spec: https://github.com/Zarby89/ZScreamRandomizer
### ZSM vs ZSPR
| Feature | ZSM | ZSPR |
|---------|-----|------|
| Purpose | Custom enemy/NPC sprites | Link sprite replacement |
| Contains ASM | Yes (routines) | No |
| Animation data | Yes | No (uses ROM animations) |
| Properties | Sprite behavior flags | Metadata only |
| Editor | ZSpriteMaker | SpriteSomething, others |
## Implementation Notes
### Reading ZSM in C++
```cpp
// .NET BinaryReader string format:
std::string read_dotnet_string(std::istream& is) {
uint32_t length = 0;
uint8_t byte;
int shift = 0;
do {
is.read(reinterpret_cast<char*>(&byte), 1);
length |= (byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
std::string result(length, '\0');
is.read(&result[0], length);
return result;
}
```
### Validation
- Frame count typically 0-255 (byte range in UI)
- Tile positions clamped: x < 252, y < 220
- Palette 0-7
- Priority 0-3
## Source Files
From `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/`:
- `MainWindow.xaml.cs:323-419` - Save_Command (write format)
- `MainWindow.xaml.cs:209-319` - Open_Command (read format)
- `OamTile.cs` - Tile data structure
- `Frame.cs` - Frame container
- `AnimationGroup.cs` - Animation definition

View File

@@ -1,5 +1,48 @@
# Changelog
## 0.4.1 (December 2025)
### Overworld Editor Fixes
**Vanilla ROM Corruption Fix**:
- Fixed critical bug where save functions wrote to custom ASM address space (0x140000+) for ALL ROMs without version checking
- `SaveAreaSpecificBGColors()`, `SaveCustomOverworldASM()`, and `SaveDiggableTiles()` now properly check ROM version before writing
- Vanilla and v1 ROMs are no longer corrupted by writes to ZSCustomOverworld address space
- Added `OverworldVersionHelper` with `SupportsCustomBGColors()` and `SupportsAreaEnum()` methods
**Toolbar UI Improvements**:
- Increased button widths from 30px to 40px for comfortable touch targets
- Added version badge showing "Vanilla", "v2", or "v3" ROM version with color coding
- Added "Upgrade" button for applying ZSCustomOverworld ASM patch to vanilla ROMs
- Improved panel toggle button spacing and column layout
### Testing Infrastructure
**ROM Auto-Discovery**:
- Tests now automatically discover ROMs in common locations (roms/, ../roms/, etc.)
- Searches for common filenames: zelda3.sfc, alttp_vanilla.sfc, vanilla.sfc
- Environment variable `YAZE_TEST_ROM_PATH` still takes precedence if set
**Overworld Regression Tests**:
- Added 9 new regression tests for save function version checks
- Tests verify vanilla/v1/v2/v3 ROM handling for all version-gated save functions
- Version feature matrix validation tests added
### Logging & Diagnostics
- Added CLI controls for log level/categories and console mirroring (`--log_level`, `--log_categories`, `--log_to_console`); `--debug` now force-enables console logging at debug level.
- Startup logging now reports the resolved level, categories, and log file destination for easier reproducibility.
### Editor & Panel Launch Controls
- `--open_panels` matching is case-insensitive and accepts both display names and stable panel IDs (e.g., `dungeon.room_list`, `Room 105`, `welcome`, `dashboard`).
- New startup visibility overrides (`--startup_welcome`, `--startup_dashboard`, `--startup_sidebar`) let you force panels to show/hide on launch for automation or demos.
- Welcome and dashboard behavior is coordinated through the UI layer so CLI overrides and in-app toggles stay in sync.
### Documentation & Testing
- Debugging guides refreshed with the new logging filters and startup panel controls.
- Startup flag reference and dungeon editor guide now use panel terminology and up-to-date CLI examples for automation setups.
---
## 0.3.9 (November 2025)
### AI Agent Infrastructure

View File

@@ -257,5 +257,17 @@ Tile Data:
---
**Last Updated**: October 13, 2025
## Additional Format Documentation
For more detailed format specifications, see:
- [SNES Graphics Format](SNES_GRAPHICS.md) - Tile and sprite format specifications
- [SNES Compression](SNES_COMPRESSION.md) - Detailed LC-LZ2 compression algorithm
- [Symbol Format](SYMBOL_FORMAT.md) - Assembler symbol file format
- [ZSM Format](ZSM_FORMAT.md) - Music and sound effect format
- [Save State Format](SAVE_STATE_FORMAT.md) - Emulator save state specifications
---
**Last Updated**: November 27, 2025

View File

@@ -0,0 +1,659 @@
# Z3ED Command Reference
Complete command reference for the z3ed CLI tool, including all automation and AI-powered features.
## Table of Contents
1. [ROM Operations](#rom-operations)
2. [Editor Automation](#editor-automation)
3. [Testing Commands](#testing-commands)
4. [Build & CI/CD](#build--cicd)
5. [Query & Discovery](#query--discovery)
6. [Interactive REPL](#interactive-repl)
7. [Network & Collaboration](#network--collaboration)
8. [AI Integration](#ai-integration)
## ROM Operations
### `z3ed rom read`
Read bytes from ROM at specified address.
**Syntax:**
```bash
z3ed rom read --address <hex> [--length <bytes>] [--format hex|ascii|binary]
```
**Examples:**
```bash
# Read 16 bytes from address 0x1000
z3ed rom read --address 0x1000 --length 16
# Read 256 bytes and display as ASCII
z3ed rom read --address 0x20000 --length 256 --format ascii
# Read single byte
z3ed rom read --address 0xFFFF
```
**Output:**
```json
{
"address": "0x001000",
"data": "A9 00 85 2C A9 01 85 2D A9 02 85 2E A9 03 85 2F",
"ascii": ".........,......",
"length": 16
}
```
### `z3ed rom write`
Write bytes to ROM at specified address.
**Syntax:**
```bash
z3ed rom write --address <hex> --data <hex_string> [--verify]
```
**Examples:**
```bash
# Write 4 bytes
z3ed rom write --address 0x1000 --data "A9 00 85 2C"
# Write with verification
z3ed rom write --address 0x2000 --data "FF FE FD FC" --verify
# Write ASCII string (converted to hex)
z3ed rom write --address 0x3000 --data "YAZE" --format ascii
```
### `z3ed rom validate`
Validate ROM integrity and structure.
**Syntax:**
```bash
z3ed rom validate [--checksums] [--headers] [--regions] [--fix]
```
**Examples:**
```bash
# Full validation
z3ed rom validate
# Validate checksums only
z3ed rom validate --checksums
# Validate and attempt fixes
z3ed rom validate --fix
```
**Output:**
```json
{
"valid": true,
"checksums": {
"header": "OK",
"complement": "OK"
},
"headers": {
"title": "THE LEGEND OF ZELDA",
"version": "1.0",
"region": "USA"
},
"issues": [],
"warnings": ["Expanded ROM detected"]
}
```
### `z3ed rom snapshot`
Create a named snapshot of current ROM state.
**Syntax:**
```bash
z3ed rom snapshot --name <name> [--compress] [--metadata <json>]
```
**Examples:**
```bash
# Create snapshot
z3ed rom snapshot --name "before_dungeon_edit"
# Compressed snapshot with metadata
z3ed rom snapshot --name "v1.0_release" --compress --metadata '{"version": "1.0", "author": "user"}'
# List snapshots
z3ed rom list-snapshots
```
### `z3ed rom restore`
Restore ROM from a previous snapshot.
**Syntax:**
```bash
z3ed rom restore --snapshot <name> [--verify]
```
**Examples:**
```bash
# Restore snapshot
z3ed rom restore --snapshot "before_dungeon_edit"
# Restore with verification
z3ed rom restore --snapshot "last_stable" --verify
```
## Editor Automation
### `z3ed editor dungeon`
Automate dungeon editor operations.
**Subcommands:**
#### `place-object`
Place an object in a dungeon room.
```bash
z3ed editor dungeon place-object --room <id> --type <object_id> --x <x> --y <y> [--layer <0|1|2>]
# Example: Place a chest at position (10, 15) in room 0
z3ed editor dungeon place-object --room 0 --type 0x22 --x 10 --y 15
```
#### `set-property`
Modify room properties.
```bash
z3ed editor dungeon set-property --room <id> --property <name> --value <value>
# Example: Set room darkness
z3ed editor dungeon set-property --room 5 --property "dark" --value true
```
#### `list-objects`
List all objects in a room.
```bash
z3ed editor dungeon list-objects --room <id> [--filter-type <type>]
# Example: List all chests in room 10
z3ed editor dungeon list-objects --room 10 --filter-type 0x22
```
#### `validate-room`
Check room for issues.
```bash
z3ed editor dungeon validate-room --room <id> [--fix-issues]
# Example: Validate and fix room 0
z3ed editor dungeon validate-room --room 0 --fix-issues
```
### `z3ed editor overworld`
Automate overworld editor operations.
**Subcommands:**
#### `set-tile`
Modify a tile on the overworld map.
```bash
z3ed editor overworld set-tile --map <id> --x <x> --y <y> --tile <tile_id>
# Example: Set grass tile at position (100, 50) on Light World
z3ed editor overworld set-tile --map 0x00 --x 100 --y 50 --tile 0x002
```
#### `place-entrance`
Add an entrance to the overworld.
```bash
z3ed editor overworld place-entrance --map <id> --x <x> --y <y> --target <room_id> [--type <type>]
# Example: Place dungeon entrance
z3ed editor overworld place-entrance --map 0x00 --x 200 --y 150 --target 0x10 --type "dungeon"
```
#### `modify-sprite`
Modify sprite properties.
```bash
z3ed editor overworld modify-sprite --map <id> --sprite-index <idx> --property <prop> --value <val>
# Example: Change enemy sprite type
z3ed editor overworld modify-sprite --map 0x00 --sprite-index 0 --property "type" --value 0x08
```
### `z3ed editor batch`
Execute multiple editor operations from a script file.
**Syntax:**
```bash
z3ed editor batch --script <file> [--dry-run] [--parallel] [--continue-on-error]
```
**Script Format (JSON):**
```json
{
"operations": [
{
"editor": "dungeon",
"action": "place-object",
"params": {
"room": 1,
"type": 34,
"x": 10,
"y": 15
}
},
{
"editor": "overworld",
"action": "set-tile",
"params": {
"map": 0,
"x": 20,
"y": 30,
"tile": 322
}
}
],
"options": {
"stop_on_error": false,
"validate_after": true
}
}
```
## Testing Commands
### `z3ed test run`
Execute test suites.
**Syntax:**
```bash
z3ed test run [--category <unit|integration|e2e>] [--filter <pattern>] [--parallel]
```
**Examples:**
```bash
# Run all tests
z3ed test run
# Run unit tests only
z3ed test run --category unit
# Run specific test pattern
z3ed test run --filter "*Overworld*"
# Parallel execution
z3ed test run --parallel
```
### `z3ed test generate`
Generate test code for a class or component.
**Syntax:**
```bash
z3ed test generate --target <class|file> --output <file> [--framework <gtest|catch2>] [--include-mocks]
```
**Examples:**
```bash
# Generate tests for a class
z3ed test generate --target OverworldEditor --output overworld_test.cc
# Generate with mocks
z3ed test generate --target DungeonRoom --output dungeon_test.cc --include-mocks
# Generate integration tests
z3ed test generate --target src/app/rom.cc --output rom_integration_test.cc --framework catch2
```
### `z3ed test record`
Record interactions for test generation.
**Syntax:**
```bash
z3ed test record --name <test_name> --start
z3ed test record --stop [--save-as <file>]
z3ed test record --pause
z3ed test record --resume
```
**Examples:**
```bash
# Start recording
z3ed test record --name "dungeon_edit_test" --start
# Perform actions...
z3ed editor dungeon place-object --room 0 --type 0x22 --x 10 --y 10
# Stop and save
z3ed test record --stop --save-as dungeon_test.json
# Generate test from recording
z3ed test generate --from-recording dungeon_test.json --output dungeon_test.cc
```
### `z3ed test baseline`
Manage test baselines for regression testing.
**Syntax:**
```bash
z3ed test baseline --create --name <baseline>
z3ed test baseline --compare --name <baseline> [--threshold <percent>]
z3ed test baseline --update --name <baseline>
```
**Examples:**
```bash
# Create baseline
z3ed test baseline --create --name "v1.0_stable"
# Compare against baseline
z3ed test baseline --compare --name "v1.0_stable" --threshold 95
# Update baseline
z3ed test baseline --update --name "v1.0_stable"
```
## Build & CI/CD
### `z3ed build`
Build the project with specified configuration.
**Syntax:**
```bash
z3ed build --preset <preset> [--verbose] [--parallel <jobs>]
```
**Examples:**
```bash
# Debug build
z3ed build --preset lin-dbg
# Release build with 8 parallel jobs
z3ed build --preset lin-rel --parallel 8
# Verbose output
z3ed build --preset mac-dbg --verbose
```
### `z3ed ci status`
Check CI/CD pipeline status.
**Syntax:**
```bash
z3ed ci status [--workflow <name>] [--branch <branch>]
```
**Examples:**
```bash
# Check all workflows
z3ed ci status
# Check specific workflow
z3ed ci status --workflow "Build and Test"
# Check branch status
z3ed ci status --branch develop
```
## Query & Discovery
### `z3ed query rom-info`
Get comprehensive ROM information.
**Syntax:**
```bash
z3ed query rom-info [--detailed] [--format <json|yaml|text>]
```
**Output:**
```json
{
"title": "THE LEGEND OF ZELDA",
"size": 2097152,
"expanded": true,
"version": "1.0",
"region": "USA",
"checksum": "0xABCD",
"header": {
"mapper": "LoROM",
"rom_speed": "FastROM",
"rom_type": "ROM+SRAM+Battery"
}
}
```
### `z3ed query available-commands`
Discover available commands and their usage.
**Syntax:**
```bash
z3ed query available-commands [--category <category>] [--format tree|list|json]
```
**Examples:**
```bash
# List all commands as tree
z3ed query available-commands --format tree
# List editor commands
z3ed query available-commands --category editor
# JSON output for programmatic use
z3ed query available-commands --format json
```
### `z3ed query find-tiles`
Search for tile patterns in ROM.
**Syntax:**
```bash
z3ed query find-tiles --pattern <hex> [--context <bytes>] [--limit <count>]
```
**Examples:**
```bash
# Find specific tile pattern
z3ed query find-tiles --pattern "FF 00 FF 00"
# Find with context
z3ed query find-tiles --pattern "A9 00" --context 8
# Limit results
z3ed query find-tiles --pattern "00 00" --limit 10
```
## Interactive REPL
### Starting REPL Mode
```bash
# Start REPL with ROM
z3ed repl --rom zelda3.sfc
# Start with history file
z3ed repl --rom zelda3.sfc --history ~/.z3ed_history
```
### REPL Features
#### Variable Assignment
```
z3ed> $info = rom info
z3ed> echo $info.title
THE LEGEND OF ZELDA
```
#### Command Pipelines
```
z3ed> rom read --address 0x1000 --length 100 | find --pattern "A9"
z3ed> query find-tiles --pattern "FF" | head -10
```
#### Session Management
```
z3ed> session save my_session
z3ed> session load my_session
z3ed> history show
z3ed> history replay 5-10
```
## Network & Collaboration
### `z3ed network connect`
Connect to collaborative editing session.
**Syntax:**
```bash
z3ed network connect --host <host> --port <port> [--username <name>]
```
**Examples:**
```bash
# Connect to session
z3ed network connect --host localhost --port 8080 --username "agent1"
# Join specific room
z3ed network join --room "dungeon_editing" --password "secret"
```
### `z3ed network sync`
Synchronize changes with other collaborators.
**Syntax:**
```bash
z3ed network sync [--push] [--pull] [--merge-strategy <strategy>]
```
**Examples:**
```bash
# Push local changes
z3ed network sync --push
# Pull remote changes
z3ed network sync --pull
# Bidirectional sync with merge
z3ed network sync --merge-strategy last-write-wins
```
## AI Integration
### `z3ed ai chat`
Interactive AI assistant for ROM hacking.
**Syntax:**
```bash
z3ed ai chat [--model <model>] [--context <file>]
```
**Examples:**
```bash
# Start chat with default model
z3ed ai chat
# Use specific model
z3ed ai chat --model gemini-pro
# Provide context file
z3ed ai chat --context room_layout.json
```
### `z3ed ai suggest`
Get AI suggestions for ROM modifications.
**Syntax:**
```bash
z3ed ai suggest --task <task> [--model <model>] [--constraints <json>]
```
**Examples:**
```bash
# Suggest dungeon layout improvements
z3ed ai suggest --task "improve dungeon flow" --constraints '{"rooms": [1,2,3]}'
# Suggest sprite placements
z3ed ai suggest --task "balance enemy placement" --model ollama:llama2
```
### `z3ed ai analyze`
Analyze ROM for patterns and issues.
**Syntax:**
```bash
z3ed ai analyze --type <pattern|bug|optimization> [--report <file>]
```
**Examples:**
```bash
# Analyze for bugs
z3ed ai analyze --type bug --report bugs.json
# Find optimization opportunities
z3ed ai analyze --type optimization
# Pattern analysis
z3ed ai analyze --type pattern --report patterns.md
```
## Global Options
These options work with all commands:
- `--rom <file>` - Specify ROM file to use
- `--verbose` - Enable verbose output
- `--quiet` - Suppress non-error output
- `--format <json|yaml|text>` - Output format
- `--output <file>` - Write output to file
- `--no-color` - Disable colored output
- `--config <file>` - Use configuration file
- `--log-level <level>` - Set logging level
- `--help` - Show help for command
## Exit Codes
- `0` - Success
- `1` - General error
- `2` - Invalid arguments
- `3` - ROM not loaded
- `4` - Operation failed
- `5` - Network error
- `6` - Build error
- `7` - Test failure
- `127` - Command not found
## Configuration
Z3ed can be configured via `~/.z3edrc` or `z3ed.config.json`:
```json
{
"default_rom": "~/roms/zelda3.sfc",
"ai_model": "gemini-pro",
"output_format": "json",
"log_level": "info",
"network": {
"default_host": "localhost",
"default_port": 8080
},
"build": {
"default_preset": "lin-dbg",
"parallel_jobs": 8
},
"test": {
"framework": "gtest",
"coverage": true
}
}
```
## Environment Variables
- `Z3ED_ROM_PATH` - Default ROM file path
- `Z3ED_CONFIG` - Configuration file location
- `Z3ED_AI_MODEL` - Default AI model
- `Z3ED_LOG_LEVEL` - Logging verbosity
- `Z3ED_HISTORY_FILE` - REPL history file location
- `Z3ED_CACHE_DIR` - Cache directory for snapshots
- `Z3ED_API_KEY` - API key for AI services

View File

@@ -0,0 +1,33 @@
# Release Notes
## v0.5.0-alpha (In Development)
**Type:** Major Feature & Architecture Update
**Date:** TBD
### 🎵 New Music Editor
The highly anticipated SPC Music Editor is now available!
- **Tracker View:** Edit music patterns with a familiar tracker interface.
- **Piano Roll:** Visualize and edit notes with velocity and duration control.
- **Preview:** Real-time N-SPC audio preview with authentic ADSR envelopes.
- **Instruments:** Manage instruments and samples directly.
### 🕸️ Web App Improvements (WASM)
- **Experimental Web Port:** Run YAZE directly in your browser.
- **UI Refresh:** New panelized layout with VSCode-style terminal and problems view.
- **Usability:** Improved error handling for missing features (Emulator).
- **Security:** Hardened `SharedArrayBuffer` support with `coi-serviceworker`.
### 🏗️ Architecture & CI
- **Windows CI:** Fixed environment setup for `clang-cl` builds (MSVC dev cmd).
- **Code Quality:** Consolidated CI workflows.
- **SDL3 Prep:** Groundwork for the upcoming migration to SDL3.
---
## v0.4.0 - Music Editor & UI Polish
**Released:** November 2025
- Complete SPC music editing infrastructure.
- EditorManager refactoring for better multi-window support.
- AI Agent integration (experimental).

View File

@@ -1,116 +1,117 @@
# F2: Dungeon Editor v2 Guide
# Dungeon Editor Guide
**Scope**: DungeonEditorV2 (card-based UI), DungeonEditorSystem, dungeon canvases
**Related**: [Architecture Overview](../developer/architecture.md), [Canvas System](../developer/canvas-system.md)
The Dungeon Editor provides a multi-panel workspace for editing Zelda 3 dungeon rooms. Each room has isolated graphics, objects, and palette data, allowing you to work on multiple rooms simultaneously.
---
## 1. Overview
The Dungeon Editor ships with the multi-card workspace introduced in the 0.3.x releases.
Self-contained room buffers keep graphics, objects, and palettes isolated so you can switch between
rooms without invalidating the entire renderer.
## Overview
### Key Features
- 512×512 canvas per room with pan/zoom, grid, and collision overlays.
- Layer-specific visualization (BG1/BG2 toggles, colored object outlines, slot labels).
- Modular cards for rooms, objects, palettes, entrances, and toolsets.
- Undo/Redo shared across cards via `DungeonEditorSystem`.
- Tight overworld integration: double-click an entrance to open the linked dungeon room.
- **512x512 canvas** per room with pan, zoom, grid, and collision overlays
- **Layer visualization** with BG1/BG2 toggles and colored object outlines
- **Modular panels** for rooms, objects, palettes, and entrances
- **Undo/Redo** shared across all panels
- **Overworld integration** - double-click entrances to open linked rooms
**Related Documentation:**
- [Architecture Overview](../developer/architecture.md)
- [Canvas System](../developer/canvas-system.md)
---
## 2. Architecture Snapshot
## Architecture
```
DungeonEditorV2 (UI)
├─ Cards & docking
├─ Canvas presenter
└─ Menu + toolbar actions
The editor uses a three-layer architecture:
DungeonEditorSystem (Backend)
├─ Room/session state
├─ Undo/Redo stack
├─ Sprite/entrance/item helpers
└─ Persistence + ROM writes
| Layer | Components | Responsibility |
|-------|------------|----------------|
| **UI** | DungeonEditorV2 | Panels, canvas, menus, toolbar |
| **Backend** | DungeonEditorSystem | State management, undo/redo, persistence |
| **Data** | Room Model | Buffers, objects, palettes, blocksets |
Room Model (Data)
├─ bg1_buffer_, bg2_buffer_
├─ tile_objects_, door data, metadata
└─ Palette + blockset caches
```
### Rendering Pipeline
### Room Rendering Pipeline
1. **Load** `DungeonRoomLoader` reads the room header, blockset pointers, and door/entrance
metadata, producing a `Room` instance with immutable layout info.
2. **Decode** The requested blockset is converted into `current_gfx16_` bitmaps; objects are parsed
into `tile_objects_` grouped by layer and palette slot.
3. **Draw** `DungeonCanvasViewer` builds BG1/BG2 bitmaps, then overlays each object layer via
`ObjectDrawer`. Palette state comes from the rooms 90-color dungeon palette.
4. **Queue** The finished bitmaps are pushed into the graphics `Arena`, which uploads a bounded
number of textures per frame so UI latency stays flat.
5. **Present** When textures become available, the canvas displays the layers, draws interaction
widgets (selection rectangles, door gizmos, entity labels), and applies zoom/grid settings.
1. **Load** - Read room header, blockset pointers, and door/entrance metadata
2. **Decode** - Convert blockset into bitmaps; parse objects by layer
3. **Draw** - Build BG1/BG2 bitmaps with object overlays
4. **Queue** - Push bitmaps to texture queue for GPU upload
5. **Present** - Display layers with selection widgets and grid
Changing tiles, palettes, or objects invalidates the affected room cache so steps 25 rerun only for
that room.
Changes to tiles, palettes, or objects invalidate only the affected room's cache.
---
## 3. Editing Workflow
## Editing Workflow
### Opening Rooms
1. Launch `yaze` with a ROM (`./build/bin/yaze --rom_file=zelda3.sfc`).
2. Use the **Room Matrix** or **Rooms List** card to choose a room. The toolbar “+” button also opens
the selector.
3. Pin multiple rooms by opening them in separate cards; each card maintains its own canvas state.
### Working with Cards
1. Launch YAZE with a ROM: `./build/bin/yaze --rom_file=zelda3.sfc`
2. Select a room from **Room Matrix** or **Rooms List** panel
3. Open multiple rooms in separate panels for comparison
| Card | Purpose |
|------|---------|
| **Room Graphics** | Primary canvas, BG toggles, collision/grid switches. |
| **Object Editor** | Filter by type/layer, edit coordinates, duplicate/delete objects. |
| **Palette Editor** | Adjust per-room palette slots and preview results immediately. |
| **Entrances List** | Jump between overworld entrances and their mapped rooms. |
| **Room Matrix** | Visual grid of all rooms grouped per dungeon for quick navigation. |
### Available Panels
Cards can be docked, detached, or saved as workspace presets; use the sidebar to store favorite
layouts (e.g., Room Graphics + Object Editor + Palette).
| Panel | Purpose |
|-------|---------|
| **Room Graphics** | Main canvas with BG toggles and grid options |
| **Object Editor** | Edit objects by type, layer, and coordinates |
| **Palette Editor** | Adjust room palettes with live preview |
| **Entrances List** | Navigate between overworld entrances and rooms |
| **Room Matrix** | Visual dungeon room grid for quick navigation |
### Canvas Interactions
- Left-click to select an object; Shift-click to add to the selection.
- Drag handles to move objects or use the property grid for precise coordinates.
- Right-click to open the context menu, which includes quick inserts for common objects and a “jump
to entrance” helper.
- Hold Space to pan, use mouse wheel (or trackpad pinch) to zoom. The status footer shows current
zoom and cursor coordinates.
- Enable **Object Labels** from the toolbar to show layer-colored labels (e.g., `L1 Chest 0x23`).
Panels can be docked, detached, or saved as workspace presets.
### Saving & Undo
- The editor queues every change through `DungeonEditorSystem`. Use `Cmd/Ctrl+Z` and `Cmd/Ctrl+Shift+Z`
to undo/redo across cards.
- Saving writes back the room buffers, door metadata, and palettes for the active session. Keep
backups enabled (`File → Options → Experiment Flags`) for safety.
### Canvas Controls
| Action | Control |
|--------|---------|
| Select object | Left-click |
| Add to selection | Shift + Left-click |
| Move object | Drag handles |
| Pan canvas | Hold Space + drag |
| Zoom | Mouse wheel or trackpad pinch |
| Context menu | Right-click |
Enable **Object Labels** from the toolbar to display layer-colored labels.
### Saving
- **Undo/Redo**: `Cmd/Ctrl+Z` and `Cmd/Ctrl+Shift+Z`
- Changes are tracked across all panels
- Keep backups enabled in `File > Options > Experiment Flags`
---
## 4. Tips & Troubleshooting
## Troubleshooting
- **Layer sanity**: If objects appear on the wrong layer, check the BG toggles in Room Graphics and
the layer filter in Object Editor—they operate independently.
- **Palette issues**: Palettes are per room. After editing, ensure `Palette Editor` writes the new
values before switching rooms; the status footer confirms pending writes.
- **Door alignment**: Use the entrance/door inspector popup (right-click a door marker) to verify
leads-to IDs without leaving the canvas.
- **Performance**: Large ROMs with many rooms can accumulate textures. If the editor feels sluggish,
close unused room cards; each card releases its textures when closed.
| Issue | Solution |
|-------|----------|
| Objects on wrong layer | Check BG toggles in Room Graphics and layer filter in Object Editor |
| Palette not saving | Ensure Palette Editor writes values before switching rooms |
| Door alignment issues | Right-click door markers to verify leads-to IDs |
| Sluggish performance | Close unused room panels to release textures |
---
## 5. Related Docs
- [Developer Architecture Overview](../developer/architecture.md) patterns shared across editors.
- [Canvas System Guide](../developer/canvas-system.md) detailed explanation of canvas usage,
context menus, and popups.
- [Debugging Guide](../developer/debugging-guide.md) startup flags and logging tips (e.g.,
`--editor=Dungeon --cards="Room 0"` for focused debugging).
## Quick Launch Examples
```bash
# Open specific room for testing
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0"
# Compare multiple rooms
./yaze --rom_file=zelda3.sfc --editor=Dungeon --open_panels="Room 0,Room 1,Room 105"
# Full workspace with all tools
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--open_panels="Rooms List,Room Matrix,Object Editor,Palette Editor"
```
---
## Related Documentation
- [Architecture Overview](../developer/architecture.md) - Patterns shared across editors
- [Canvas System](../developer/canvas-system.md) - Canvas controls and context menus
- [Debugging Guide](../developer/debugging-guide.md) - Startup flags and logging

View File

@@ -495,6 +495,49 @@ class OverworldMap {
- Ensure proper sprite graphics table selection for v2 vs v3 ROMs
- Verify that special area maps use the correct graphics from referenced LW/DW maps
## Save Operations and Version Safety
### Version Checking for Save Functions
**CRITICAL**: All save functions that write to custom ASM address space (0x140000+) must check the ROM version before writing. Failing to do so will corrupt vanilla ROMs by overwriting game data with uninitialized values.
```cpp
// CORRECT: Check version before writing to custom address space
absl::Status Overworld::SaveAreaSpecificBGColors() {
auto version = OverworldVersionHelper::GetVersion(*rom_);
if (!OverworldVersionHelper::SupportsCustomBGColors(version)) {
return absl::OkStatus(); // Vanilla/v1 ROM - skip custom address writes
}
// Safe to write to 0x140000+ for v2+ ROMs
}
// INCORRECT: Writing without version check
absl::Status Overworld::SaveAreaSpecificBGColors() {
// BUG: This writes to 0x140000 even for vanilla ROMs!
for (int i = 0; i < 160; ++i) {
rom_->Write(OverworldCustomAreaSpecificBGPalette + i * 2, color);
}
}
```
### Version-Gated Save Functions
| Save Function | Required Version | Address Range |
|---------------|------------------|---------------|
| `SaveAreaSpecificBGColors()` | v2+ | 0x140000-0x140140 |
| `SaveCustomOverworldASM()` (v2 features) | v2+ | 0x140140-0x140180 |
| `SaveCustomOverworldASM()` (v3 features) | v3+ | 0x140200+ |
| `SaveDiggableTiles()` | v3+ | 0x140980+ |
| `SaveAreaSizes()` | v3+ | 0x1417F8+ |
### ROM Upgrade Path
To enable v2/v3 features on a vanilla ROM:
1. Use the toolbar version badge to identify current ROM version
2. Click "Upgrade" button to apply ZSCustomOverworld ASM patch
3. Editor automatically reinitializes custom tables with sensible defaults
4. New UI controls become visible after upgrade
## Best Practices
### 1. Version-Specific Code

View File

@@ -0,0 +1,278 @@
# Web App Guide (Preview)
YAZE is available as a **preview** web application that runs in your browser. The web port is under active development - expect bugs and incomplete features.
> **⚠️ Preview Status**: The web app is experimental. For production ROM hacking, use the [desktop build](../build/quick-reference.md).
## Quick Start
### Try it Now
Visit your deployed instance to access the web version. The application is served via GitHub Pages or custom deployment.
### Loading a ROM
1. Click **"Open ROM"** or drag and drop your Zelda 3 ROM file onto the page
2. The ROM is stored locally in your browser using IndexedDB - it never leaves your computer
3. Start editing using a subset of the desktop interface
### Saving Your Work
- **Auto-save**: Changes are automatically saved to browser storage (when working)
- **Download ROM**: Click the download button to save your modified ROM to disk
- **Backup recommended**: Download frequently; browser storage can be cleared
## Feature Status
The web version is in preview with varying editor support:
| Feature | Status | Notes |
|---------|--------|-------|
| ROM Loading | ✅ Working | Drag & drop, file picker |
| Overworld Editor | ⚡ Preview | Basic tile editing, incomplete features |
| Dungeon Editor | ⚡ Preview | Room viewing, editing incomplete |
| Palette Editor | ⚡ Preview | Basic palette viewing/editing |
| Graphics Editor | ⚡ Preview | Tile viewing, editing incomplete |
| Sprite Editor | ⚡ Preview | Limited functionality |
| Message Editor | ⚡ Preview | Text viewing, editing incomplete |
| Hex Editor | ✅ Working | Direct ROM editing |
| Asar Patching | ⚡ Preview | Basic assembly patching |
| Emulator | ❌ Not Available | Use desktop build |
| Real-time Collaboration | ⚡ Experimental | Requires server setup |
| Audio Playback | ⚡ Experimental | Limited SPC700 support |
**Legend**: ✅ Working | ⚡ Preview/Incomplete | ❌ Not Available
## Browser Requirements
### Recommended Browsers
- **Chrome/Edge** 90+ (best performance)
- **Firefox** 88+
- **Safari** 15.4+ (macOS/iOS)
### Required Features
The web app requires modern browser features:
- **WebAssembly**: Core application runtime
- **SharedArrayBuffer**: Multi-threading support (requires HTTPS or localhost)
- **IndexedDB**: Local ROM and project storage
- **File System Access API**: Better file handling (Chrome/Edge)
### Mobile Support
**Tablets**: Full support on iPad and Android tablets
⚠️ **Phones**: Limited - interface designed for larger screens
## Performance Tips
### For Best Performance
1. **Use Chrome or Edge** - Fastest WebAssembly implementation
2. **Close other tabs** - Free up browser memory
3. **Desktop/laptop recommended** - Better than mobile for complex editing
4. **Stable internet** - Only needed for initial load; works offline after
### Troubleshooting Slow Performance
- Clear browser cache and reload
- Disable browser extensions temporarily
- Check browser console for errors (F12)
- Use the debug build for better error messages (see Development section)
## Storage & Privacy
### What's Stored Locally
- ROM files (IndexedDB)
- Project settings and preferences
- Auto-save data
- Recent file history
### Privacy Guarantee
- **All data stays in your browser** - ROMs and edits never uploaded to servers
- **No tracking** - No analytics or user data collection
- **No login required** - No accounts or personal information needed
### Clearing Data
```javascript
// Open browser console (F12) and run:
localStorage.clear();
sessionStorage.clear();
// Then reload the page
```
Or use your browser's "Clear browsing data" feature.
## Keyboard Shortcuts
The web app supports desktop keyboard shortcuts:
| Shortcut | Action |
|----------|--------|
| **Ctrl/Cmd + O** | Open ROM |
| **Ctrl/Cmd + S** | Save/Download ROM |
| **Ctrl/Cmd + Z** | Undo |
| **Ctrl/Cmd + Y** | Redo |
| **`** (backtick) | Toggle terminal |
| **Esc** | Close panels/dialogs |
| **Tab** | Cycle through editors |
**Note**: Some shortcuts may conflict with browser defaults. Use the menu bar as an alternative.
## Advanced Features
### Terminal Access
Press **`** (backtick) to open the integrated terminal:
```bash
# List all dungeons
z3ed dungeon list --rom current
# Get overworld info
z3ed overworld info --map 0
# Apply a patch
z3ed asar my_patch.asm
```
### Collaboration (Experimental)
When enabled, multiple users can edit the same ROM simultaneously:
1. Click **"Start Collaboration"** in the toolbar
2. Share the session URL with collaborators
3. See real-time cursors and changes from other users
**Note**: Requires a collaboration server to be deployed. See deployment docs for setup.
### Developer Tools
Open browser console (F12) for debugging:
```javascript
// Check if WASM module is ready
window.yazeDebug.isReady()
// Get ROM status
window.yazeDebug.rom.getStatus()
// Get current editor state
window.yaze.editor.getActiveEditor()
// Read ROM data
window.yaze.data.getRoomTiles(roomId)
```
See [`docs/internal/wasm-yazeDebug-api-reference.md`](../../internal/wasm-yazeDebug-api-reference.md) for full API documentation.
## Known Limitations
### Not Yet Supported
- **Emulator**: No built-in emulator in web version (use desktop)
- **Custom plugins**: Plugin system requires desktop build
- **Large file export**: Browser limits on file size (typically 2GB+)
### Browser-Specific Issues
- **Safari**: Slower performance, some SharedArrayBuffer limitations
- **Firefox**: Clipboard access may require permissions
- **Mobile Chrome**: Touch controls under development
## Offline Usage
After first load, the web app works offline:
1. Visit the site once while online
2. Service worker caches the application
3. Disconnect from internet
4. App continues to function normally
**Note**: Initial load requires internet; updates require reconnection.
## Building from Source
To build and deploy your own instance:
```bash
# Build the web app
./scripts/build-wasm.sh
# Serve locally for testing
./scripts/serve-wasm.sh
# Deploy dist/ folder to your web server
# Artifacts are in build-wasm/dist/
```
See [`docs/internal/agents/wasm-development-guide.md`](../../internal/agents/wasm-development-guide.md) for detailed build instructions.
## Deployment
### GitHub Pages (Automated)
Pushes to `master` automatically build and deploy via GitHub Actions:
```yaml
# See .github/workflows/web-build.yml
```
### Custom Server
Requirements:
- Static file server (nginx, Apache, etc.)
- HTTPS enabled (required for SharedArrayBuffer)
- Proper CORS headers
Minimal nginx config:
```nginx
server {
listen 443 ssl http2;
server_name yaze.yourdomain.com;
root /var/www/yaze;
# Required for SharedArrayBuffer
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
# Cache WASM files
location ~* \.(wasm|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
```
## Getting Help
- **Discord**: [Oracle of Secrets](https://discord.gg/MBFkMTPEmk)
- **Issues**: [GitHub Issues](https://github.com/scawful/yaze/issues)
- **Docs**: [`docs/public/index.md`](../index.md)
## Comparison: Web vs Desktop
| Feature | Web (Preview) | Desktop (Stable) |
|---------|---------------|------------------|
| **Installation** | None required | Download & install |
| **Platforms** | Modern browsers | Windows, macOS, Linux |
| **Performance** | Moderate | Excellent |
| **Editor Completeness** | Preview/Incomplete | Full-featured |
| **Emulator** | ❌ | ✅ |
| **Plugins** | ❌ | ✅ |
| **Stability** | Experimental | Production-ready |
| **Updates** | Automatic | Manual download |
| **Offline** | After first load | Always |
| **Collaboration** | Experimental | Via server |
| **Mobile** | Tablets (limited) | No |
**Recommendation**:
- **For serious ROM hacking**: Use the desktop build
- **For quick previews or demos**: Web app is suitable
- **For learning/exploration**: Either works, but desktop is more complete

View File

@@ -1,107 +1,116 @@
# z3ed CLI Guide
_Last reviewed: November 2025. `z3ed` ships alongside the main editor in every `*-ai` preset and
runs on Windows, macOS, and Linux._
The `z3ed` command-line tool provides scriptable ROM editing, AI-assisted workflows, and resource inspection. It ships with all `*-ai` preset builds and runs on Windows, macOS, and Linux.
`z3ed` exposes the same ROM-editing capabilities as the GUI but in a scriptable form. Use it to
apply patches, inspect resources, run batch conversions, or drive the AI-assisted workflows that
feed the in-editor proposals.
---
## 1. Building & Configuration
## Building
```bash
# Enable the agent/CLI toolchain
# Build with AI features
cmake --preset mac-ai
cmake --build --preset mac-ai --target z3ed
# Run the text UI (FTXUI)
# Run the text UI
./build/bin/z3ed --tui
```
The AI features require at least one provider:
- **Ollama (local)** install via `brew install ollama`, run `ollama serve`, then set
`OLLAMA_MODEL=qwen2.5-coder:0.5b` (the lightweight default used in CI) or any other supported
model. Pass `--ai_model "$OLLAMA_MODEL"` on the CLI to override per-run.
- **Gemini (cloud)** export `GEMINI_API_KEY` before launching `z3ed`.
## AI Provider Configuration
If no provider is configured the CLI still works, but agent subcommands will fall back to manual
plans.
AI features require at least one provider:
## 2. Everyday Commands
| Provider | Setup |
|----------|-------|
| **Ollama** (local) | `brew install ollama && ollama serve` |
| **Gemini** (cloud) | `export GEMINI_API_KEY=your_key` |
| Task | Example |
| --- | --- |
| Apply an Asar patch | `z3ed asar patch.asm --rom zelda3.sfc` |
| Export all sprites from a dungeon | `z3ed dungeon list-sprites --dungeon 2 --rom zelda3.sfc --format json` |
| Inspect an overworld map | `z3ed overworld describe-map --map 80 --rom zelda3.sfc` |
| Dump palette data | `z3ed palette export --rom zelda3.sfc --output palettes.json` |
| Validate ROM headers | `z3ed rom info --rom zelda3.sfc` |
Set the model with `--ai_model` or `OLLAMA_MODEL` environment variable.
Pass `--help` after any command to see its flags. Most resource commands follow the
`<noun> <verb>` convention (`overworld set-tile`, `dungeon import-room`, etc.).
> Without a provider, z3ed still works but agent commands use manual plans.
## 3. Agent & Proposal Workflow
---
## Common Commands
| Task | Command |
|------|---------|
| Apply assembly patch | `z3ed asar patch.asm --rom zelda3.sfc` |
| List dungeon sprites | `z3ed dungeon list-sprites --dungeon 2 --rom zelda3.sfc` |
| Describe overworld map | `z3ed overworld describe-map --map 80 --rom zelda3.sfc` |
| Export palettes | `z3ed palette export --rom zelda3.sfc --output palettes.json` |
| Validate ROM | `z3ed rom info --rom zelda3.sfc` |
Commands follow `<noun> <verb>` convention. Use `--help` for flag details:
```bash
z3ed dungeon --help
z3ed dungeon list-sprites --help
```
---
## AI Agent Workflows
### Interactive Chat
### 3.1 Interactive Chat
```bash
z3ed agent chat --rom zelda3.sfc --theme overworld
```
- Maintains conversation history on disk so you can pause/resume.
- Supports tool-calling: the agent invokes subcommands (e.g., `overworld describe-map`) and
returns structured diffs.
### 3.2 Plans & Batches
Chat sessions maintain conversation history and can invoke ROM commands automatically.
### Plan and Apply
```bash
# Generate a proposal but do not apply it
z3ed agent plan --prompt "Move the eastern palace entrance 3 tiles east" --rom zelda3.sfc
# Create a plan without applying
z3ed agent plan --prompt "Move eastern palace entrance 3 tiles east" --rom zelda3.sfc
# List pending plans
z3ed agent list
# Apply a plan after review
# Apply after review
z3ed agent accept --proposal-id <id> --rom zelda3.sfc
```
Plans store the command transcript, diffs, and metadata inside
`$XDG_DATA_HOME/yaze/proposals/` (or `%APPDATA%\yaze\proposals\`). Review them before applying to
non-sandbox ROMs.
### 3.3 Non-interactive Scripts
Plans are stored in `$XDG_DATA_HOME/yaze/proposals/` (or `%APPDATA%\yaze\proposals\` on Windows).
### Scripted Prompts
```bash
# Run prompts from a file
z3ed agent simple-chat --file scripts/queries.txt --rom zelda3.sfc --stdout
# From file
z3ed agent simple-chat --file queries.txt --rom zelda3.sfc --stdout
# Feed stdin (useful in CI)
cat <<'PROMPTS' | z3ed agent simple-chat --rom zelda3.sfc --stdout
Describe tile 0x3A in map 0x80.
Suggest palette swaps for dungeon 2.
PROMPTS
# From stdin
echo "Describe tile 0x3A in map 0x80" | z3ed agent simple-chat --rom zelda3.sfc --stdout
```
## 4. Automation Tips
---
1. **Sandbox first** point the agent at a copy of your ROM (`--sandbox` flag) so you can review
patches safely.
2. **Log everything** `--log-file agent.log` captures the provider transcript for auditing.
3. **Structure output** most list/describe commands support `--format json` or `--format yaml`
for downstream tooling.
4. **Combine with `yaze_test`** run `./build_ai/bin/yaze_test --unit` after batch patches to
confirm nothing regressed.
5. **Use TUI filters** in `--tui`, press `:` to open the command palette, type part of a command,
hit Enter, and the tool auto-fills the available flags.
## Best Practices
## 5. Troubleshooting
| Tip | Description |
|-----|-------------|
| **Use sandbox mode** | `--sandbox` flag creates a copy for safe testing |
| **Log sessions** | `--log-file agent.log` captures transcripts |
| **Structured output** | `--format json` or `--format yaml` for scripting |
| **Run tests after patches** | `./build/bin/yaze_test --unit` |
| **TUI command palette** | Press `:` in TUI mode to search commands |
| Symptom | Fix |
| --- | --- |
| `agent chat` hangs after a prompt | Ensure `ollama serve` or the Gemini API key is configured. |
| `libgrpc` or `absl` missing | Re-run the `*-ai` preset; plain debug presets do not pull the agent stack. |
| CLI cannot find the ROM | Use absolute paths or set `YAZE_DEFAULT_ROM=/path/to/zelda3.sfc`. |
| Tool reports "command not found" | Run `z3ed --help` to refresh the command index; stale binaries from older builds lack new verbs. |
| Proposal diffs are empty | Provide `--rom` plus either `--sandbox` or `--workspace` so the agent knows where to stage files. |
---
## 6. Related Documentation
- `docs/public/developer/testing-without-roms.md` ROM-less fixtures for CI.
- `docs/public/developer/debugging-guide.md` logging and instrumentation tips shared between the
GUI and CLI.
- `docs/internal/agents/` deep dives into the agent architecture and refactor plans (internal
audience only).
## Troubleshooting
| Issue | Solution |
|-------|----------|
| `agent chat` hangs | Verify `ollama serve` is running or `GEMINI_API_KEY` is set |
| Missing `libgrpc` or `absl` | Rebuild with `*-ai` preset |
| ROM not found | Use absolute paths or set `YAZE_DEFAULT_ROM` |
| Command not found | Run `z3ed --help` to verify build is current |
| Empty proposal diffs | Include `--rom` with `--sandbox` or `--workspace` |
---
## Related Documentation
- [Testing Without ROMs](../developer/testing-without-roms.md) - CI fixtures
- [Debugging Guide](../developer/debugging-guide.md) - Logging and instrumentation
- [CLI Reference](../cli/README.md) - Complete command documentation