backend-infra-engineer: Release v0.3.3 snapshot

This commit is contained in:
scawful
2025-11-21 21:35:50 -05:00
parent 3d71417f62
commit 476dd1cd1c
818 changed files with 65706 additions and 35514 deletions

View File

@@ -0,0 +1,181 @@
# API Reference
This document provides a reference for the yaze C and C++ APIs, intended for developers creating extensions or contributing to the core application.
## C API (`incl/yaze.h`)
The C API provides a stable, language-agnostic interface for interacting with yaze's core functionalities.
### Core Library Functions
```c
/**
* @brief Initializes the yaze library. Must be called before any other API function.
* @return YAZE_OK on success, or an error code on failure.
*/
yaze_status yaze_library_init(void);
/**
* @brief Shuts down the yaze library and releases all resources.
*/
void yaze_library_shutdown(void);
/**
* @brief Gets the current version of the yaze library as a string.
* @return A constant string representing the version (e.g., "0.3.2").
*/
const char* yaze_get_version_string(void);
```
### ROM Operations
```c
/**
* @brief Loads a Zelda 3 ROM from a file.
* @param filename The path to the ROM file.
* @return A pointer to a zelda3_rom object, or NULL on failure.
*/
zelda3_rom* yaze_load_rom(const char* filename);
/**
* @brief Unloads a ROM and frees associated memory.
* @param rom A pointer to the zelda3_rom object to unload.
*/
void yaze_unload_rom(zelda3_rom* rom);
/**
* @brief Saves a ROM to a file.
* @param rom A pointer to the ROM to save.
* @param filename The path to save the file to.
* @return YAZE_OK on success.
*/
yaze_status yaze_save_rom(zelda3_rom* rom, const char* filename);
```
## C++ API
The C++ API offers a more powerful, object-oriented interface. The primary entry point for many operations is the `yaze::core::AsarWrapper` class.
### AsarWrapper (`src/core/asar_wrapper.h`)
This class provides a complete, cross-platform interface for applying assembly patches, extracting symbols, and validating assembly code using the Asar library.
#### CLI Examples (`z3ed`)
While the `AsarWrapper` can be used programmatically, the `z3ed` CLI is the most common way to interact with it.
```bash
# Apply an assembly patch to a ROM file.
z3ed asar my_patch.asm --rom=zelda3.sfc
# For more complex operations, use the AI agent.
z3ed agent chat --rom zelda3.sfc
```
> **Prompt:** "Apply the patch `mosaic_change.asm` to my ROM."
#### C++ API Example
```cpp
#include "core/asar_wrapper.h"
#include <vector>
#include <string>
// Assume rom_data is a std::vector<uint8_t> holding the ROM content.
yaze::core::AsarWrapper wrapper;
wrapper.Initialize();
// Apply a patch to the ROM data in memory.
auto result = wrapper.ApplyPatch("patch.asm", rom_data);
if (result.ok() && result->success) {
// On success, print the symbols generated by the patch.
for (const auto& symbol : result->symbols) {
std::cout << symbol.name << " @ $" << std::hex << symbol.address << std::endl;
}
}
```
#### Class Definition
```cpp
namespace yaze::core {
class AsarWrapper {
public:
/** @brief Initializes the Asar library. */
absl::Status Initialize();
/** @brief Shuts down the Asar library. */
void Shutdown();
/**
* @brief Applies an assembly patch to ROM data.
* @param patch_path Path to the main .asm file.
* @param rom_data A vector of bytes representing the ROM data.
* @param include_paths Optional paths for Asar to search for included files.
* @return A StatusOr containing the patch result, including success status and symbols.
*/
absl::StatusOr<AsarPatchResult> ApplyPatch(
const std::string& patch_path,
std::vector<uint8_t>& rom_data,
const std::vector<std::string>& include_paths = {});
/**
* @brief Extracts symbols from an assembly file without patching.
* @param asm_path Path to the .asm file.
* @return A StatusOr containing a vector of extracted symbols.
*/
absl::StatusOr<std::vector<AsarSymbol>> ExtractSymbols(
const std::string& asm_path,
const std::vector<std::string>& include_paths = {});
/**
* @brief Validates the syntax of an assembly file.
* @param asm_path Path to the .asm file.
* @return An OK status if syntax is valid, or an error status if not.
*/
absl::Status ValidateAssembly(const std::string& asm_path);
};
} // namespace yaze::core
```
## Data Structures
### `snes_color`
Represents a 15-bit SNES color, composed of 5 bits for each red, green, and blue component.
```c
typedef struct snes_color {
uint16_t raw; //!< Raw 15-bit BGR color value (0bbbbbgggggrrrrr).
uint8_t red; //!< Red component (0-31).
uint8_t green; //!< Green component (0-31).
uint8_t blue; //!< Blue component (0-31).
} snes_color;
```
### `zelda3_message`
Represents an in-game text message.
```c
typedef struct zelda3_message {
uint16_t id; //!< The message ID (0-65535).
uint32_t rom_address; //!< The address of the message data in the ROM.
uint16_t length; //!< The length of the raw message data in bytes.
uint8_t* raw_data; //!< A pointer to the raw, compressed message data.
char* parsed_text; //!< The decoded, human-readable UTF-8 text.
bool is_compressed; //!< Flag indicating if the message data is compressed.
}
zelda3_message;
```
## Error Handling
The C API uses an enum `yaze_status` for error handling, while the C++ API uses `absl::Status` and `absl::StatusOr`.
### C API Error Pattern
```c
yaze_status status = yaze_library_init();
if (status != YAZE_OK) {
fprintf(stderr, "Failed to initialize YAZE: %s\n", yaze_status_to_string(status));
return 1;
}
// ... operations ...
yaze_library_shutdown();
```

View File

@@ -0,0 +1,230 @@
# E2 - Development Guide
This guide summarizes the architecture and implementation standards used across the editor codebase.
## Editor Status (October 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. |
### Screen Editor Notes
- **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.
## 1. Core Architectural Patterns
These patterns, established during the Overworld Editor refactoring, should be applied to all new and existing editor components.
### Pattern 1: Modular Systems
**Principle**: Decompose large, monolithic editor classes into smaller, single-responsibility modules.
- **Rendering**: All drawing logic should be extracted into dedicated `*Renderer` classes (e.g., `OverworldEntityRenderer`). The main editor class should delegate drawing calls, not implement them.
- **UI Panels**: Complex UI panels should be managed by their own classes (e.g., `MapPropertiesSystem`), which then communicate with the parent editor via callbacks.
- **Interaction**: Canvas interaction logic (mouse handling, editing modes) should be separated from the main editor class to simplify state management.
**Benefit**: Smaller, focused modules are easier to test, debug, and maintain. The main editor class becomes a coordinator, which is a much cleaner architecture.
### Pattern 2: Callback-Based Communication
**Principle**: Use `std::function` callbacks for child-to-parent communication to avoid circular dependencies.
- **Implementation**: A parent editor provides its child components with callbacks (typically via a `SetCallbacks` method) during initialization. The child component invokes these callbacks to notify the parent of events or to request actions (like a refresh).
- **Example**: `MapPropertiesSystem` receives a `RefreshCallback` from `OverworldEditor`. When a property is changed in the UI, it calls the function, allowing the `OverworldEditor` to execute the refresh logic without the `MapPropertiesSystem` needing to know anything about the editor itself.
### Pattern 3: Centralized Progressive Loading via `gfx::Arena`
**Principle**: All expensive asset loading operations must be performed asynchronously to prevent UI freezes. The `gfx::Arena` singleton provides a centralized, priority-based system for this.
- **How it Works**:
1. **Queue**: Instead of loading a texture directly, queue it with the arena: `gfx::Arena::Get().QueueDeferredTexture(bitmap, priority);`
2. **Prioritize**: Assign a numerical priority. Lower numbers are higher priority. Use a high priority for assets the user is currently viewing and a low priority for assets that can be loaded in the background.
3. **Process**: In the main `Update()` loop of an editor, process a small batch of textures each frame: `auto batch = gfx::Arena::Get().GetNextDeferredTextureBatch(4, 2);` (e.g., 4 high-priority, 2 low-priority).
- **Benefit**: This provides a globally consistent, non-blocking loading mechanism that is available to all editors and ensures the UI remains responsive.
## 2. UI & Theming System
To ensure a consistent and polished look and feel, all new UI components must adhere to the established theme and helper function system.
### 2.1. The Theme System (`AgentUITheme`)
- **Principle**: **Never use hardcoded colors (`ImVec4`)**. All UI colors must be derived from the central theme.
- **Implementation**: The `AgentUITheme` system (`src/app/editor/agent/agent_ui_theme.h`) provides a struct of semantic color names (e.g., `panel_bg_color`, `status_success`, `provider_ollama`). These colors are automatically derived from the application's current `ThemeManager`.
- **Usage**: Fetch the theme at the beginning of a draw call and use the semantic colors:
```cpp
const auto& theme = AgentUI::GetTheme();
ImGui::PushStyleColor(ImGuiCol_ChildBg, theme.panel_bg_color);
```
### 2.2. Reusable UI Helper Functions
- **Principle**: Encapsulate common UI patterns into helper functions to reduce boilerplate and ensure consistency.
- **Available Helpers** (in `AgentUI` and `gui` namespaces):
- **Panels**: `AgentUI::PushPanelStyle()` / `PopPanelStyle()`
- **Headers**: `AgentUI::RenderSectionHeader(icon, label, color)`
- **Indicators**: `AgentUI::RenderStatusIndicator()` (status dot), `AgentUI::StatusBadge()` (colored text badge).
- **Buttons**: `AgentUI::StyledButton()`, `AgentUI::IconButton()`.
- **Layout**: `AgentUI::VerticalSpacing()`, `AgentUI::HorizontalSpacing()`.
- **Benefit**: Creates a consistent visual language and makes the UI code far more readable and maintainable.
### 2.3. Toolbar Implementation (`CompactToolbar`)
- **Stretching**: To prevent ImGui from stretching the last item in a toolbar, do not use `ImGui::BeginGroup()`. Instead, manage layout with `ImGui::SameLine()` and end the toolbar with `ImGui::NewLine()`.
- **Separators**: Use `ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical)` for vertical separators that do not affect item layout.
- **Property Inputs**: Use the `toolbar.AddProperty()` method for consistent spacing and sizing of input fields within the toolbar.
## 3. Key System Implementations & Gotchas
### 3.1. Graphics Refresh Logic
- **Immediate vs. Deferred**: When a visual property changes, the texture must be updated on the GPU immediately. Use `Renderer::Get().RenderBitmap()` for an immediate, blocking update. `UpdateBitmap()` is deferred and should not be used for changes the user expects to see instantly.
- **Call Order is Critical**: When a property affecting graphics is changed, the correct sequence of operations is crucial:
1. Update the property in the data model.
2. Call the relevant `Load*()` method (e.g., `map.LoadAreaGraphics()`) to load the new data from the ROM into memory.
3. Force a redraw/re-render of the bitmap.
### 3.2. Multi-Area Map Configuration
- **Use the Helper**: When changing a map's area size (e.g., from `Small` to `Large`), you **must** use the `zelda3::Overworld::ConfigureMultiAreaMap()` method. Do not set the `area_size` property directly.
- **Why**: This method correctly handles the complex logic of assigning parent IDs to all sibling maps and updating the necessary ROM data for persistence. Failure to use it will result in inconsistent state and refresh bugs.
### 3.3. Version-Specific Feature Gating
- **Principle**: The UI must adapt to the features supported by the loaded ROM. Do not show UI for features that are not available.
- **Implementation**: Check the ROM's `asm_version` byte before rendering a UI component. If the feature is not supported, display a helpful message (e.g., "This feature requires ZSCustomOverworld v3+") instead of the UI.
### 3.4. Entity Visibility for Visual Testing
- **Standard**: All overworld entity markers (entrances, exits, items, sprites) should be rendered with a high-contrast color and an alpha of `0.85f` to ensure they are clearly visible against any background.
- **Entrances**: Bright yellow-gold
- **Exits**: Cyan-white
- **Items**: Bright red
- **Sprites**: Bright magenta
## 4. Clang Tooling Configuration
The repository ships curated `.clangd` and `.clang-tidy` files that mirror our
Google-style C++23 guidelines while accommodating ROM hacking patterns.
- `.clangd` consumes `build/compile_commands.json`, enumerates `src/`, `incl/`,
`third_party/`, generated directories, and sets feature flags such as
`YAZE_WITH_GRPC`, `YAZE_WITH_JSON`, and `Z3ED_AI` so IntelliSense matches the
active preset.
- `.clang-tidy` enables the `clang-analyzer`, `performance`, `bugprone`,
`readability`, `modernize`, `google`, and `abseil` suites, but relaxes common
ROM hacking pain points (magic numbers, explicit integer sizing, C arrays,
carefully scoped narrowing conversions).
- The `gfx::SnesColor` utilities intentionally return ImVec4 values in 0255
space; rely on the helper converters instead of manual scaling to avoid
precision loss.
- Regenerate the compilation database whenever you reconfigure: `cmake --preset
mac-dbg` (or the platform equivalent) and ensure the file lives at
`build/compile_commands.json`.
- Spot-check tooling with `clang-tidy path/to/file.cc -p build --quiet` or a
batch run via presets before sending larger patches.
## 5. Debugging and Testing
### 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:
```bash
# Quick dungeon room testing
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
# Compare multiple rooms
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="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"
# Enable debug logging
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Dungeon
```
**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)
See [debugging-startup-flags.md](debugging-startup-flags.md) for complete documentation.
### 5.2. Testing Strategies
For a comprehensive overview of debugging tools and testing strategies, including how to use the logging framework, command-line test runners, and the GUI automation harness for AI agents, please refer to the [Debugging and Testing Guide](debugging-guide.md).
## 5. Command-Line Flag Standardization
**Decision**: All binaries in the yaze project (`yaze`, `z3ed`, `yaze_test`, etc.) will standardize on the **Abseil Flags** library for command-line argument parsing.
### Rationale
- **Consistency**: Provides a single, consistent flag system and user experience across all tools.
- **Industry Standard**: Abseil is a battle-tested and well-documented library used extensively by Google.
- **Features**: It provides robust features out-of-the-box, including automatic `--help` generation, type safety, validation, and `--flagfile` support.
- **Reduced Maintenance**: Eliminates the need to maintain multiple custom flag parsers, reducing the project's technical debt.
- **Existing Dependency**: Abseil is already included as a dependency via gRPC, so this adds no new third-party code.
### Migration Plan
The project will migrate away from the legacy `yaze::util::Flag` system and manual parsing in phases. All new flags should be implemented using `ABSL_FLAG`.
- **Phase 1 (Complete)**: `z3ed` and `yaze_emu_test` already use Abseil flags.
- **Phase 2 (In Progress)**: The main `yaze` application will be migrated from the custom `util::Flag` system to Abseil flags.
- **Phase 3 (Future)**: The `yaze_test` runner's manual argument parsing will be replaced with Abseil flags.
- **Phase 4 (Cleanup)**: The legacy `util::flag` source files will be removed from the project.
Developers should refer to the Abseil Flags Guide for documentation on defining, declaring, and accessing flags.
---
When working with bitmaps and textures, understand that two memory locations must stay synchronized:
1. **`data_` vector**: C++ std::vector<uint8_t> holding pixel data
2. **`surface_->pixels`**: SDL surface's raw pixel buffer (used for texture creation)
**Critical Rules**:
- Use `set_data()` for bulk data replacement (syncs both vector and surface)
- Use `WriteToPixel()` for single-pixel modifications
- Never assign directly to `mutable_data()` for replacements (only updates vector, not surface)
- Always call `ProcessTextureQueue()` every frame to process pending texture operations
**Example**:
```cpp
// WRONG - only updates vector
bitmap.mutable_data() = new_data;
// CORRECT - updates both vector and SDL surface
bitmap.set_data(new_data);
```
### 3.6. Graphics Sheet Management
Graphics sheets (223 total) are managed centrally by `gfx::Arena`. When modifying a sheet:
1. Modify the sheet: `auto& sheet = Arena::Get().mutable_gfx_sheet(index);`
2. Notify Arena: `Arena::Get().NotifySheetModified(index);`
3. Changes automatically propagate to all editors
Default palettes are applied during ROM loading based on sheet index:
- Sheets 0-112: Dungeon main palettes
- Sheets 113-127: Sprite palettes
- Sheets 128-222: HUD/menu palettes
### Naming Conventions
- Load: Reading data from ROM into memory
- Render: Processing graphics data into bitmaps/textures (CPU pixel operations)
- Draw: Displaying textures/shapes on canvas via ImGui (GPU rendering)
- Update: UI state changes, property updates, input handling

View File

@@ -0,0 +1,223 @@
# Asm Style Guide
65816 Assembly is the assembly language used by the Super Nintendo Entertainment System (SNES) and its Ricoh 5A22 processor. This style guide provides conventions and best practices for writing 65816 assembly code in the context of the yaze project. Following these guidelines will help maintain consistency and readability across the codebase.
This guide is based primarily on the [Oracle of Secrets](https://github.com/scawful/Oracle-of-Secrets) codebase and is meant for the [Asar](https://github.com/RPGHacker/asar) assembler and derives influence from the [Asar 1.9 Manual](https://rpghacker.github.io/asar/asar_19/manual/).
Custom assembly code applied to the game should be included through the `yaze.asm` file found in `assets/asm`. This file can be applied to the ROM by the editor using the Asar library or included into a projects codebase for use with the Asar assembler.
## Table of Contents
- [File Structure](#file-structure)
- [Labels and Symbols](#labels-and-symbols)
- [Comments](#comments)
- [Directives](#directives)
- [Instructions](#instructions)
- [Macros](#macros)
- [Loops and Branching](#loops-and-branching)
- [Data Structures](#data-structures)
- [Code Organization](#code-organization)
- [Custom Code](#custom-code)
## File Structure
- **File Extension**: Use `.asm` as the file extension for 65816 assembly files.
- **Header Comments**: Include a header comment at the beginning of each file describing its purpose and the author.
Example:
```asm
; =========================================================
; Purpose: [Brief description of the files functionality]
; Author: [Your Name]
; =========================================================
```
- **Section Headers**: Use clear and consistent section headers to divide code into logical blocks. Each major section (e.g., sprite properties, main logic, subroutines) should start with a delineated header.
Example:
```asm
; =========================================================
; Minecart Sprite Properties
; =========================================================
```
- **Macro Definitions and Includes**: Place macros and include directives at the beginning of the file to keep them organized and easily accessible.
## Labels and Symbols
- **Naming Conventions**:
- **Global Labels**: Use descriptive names in `PascalCase` for global labels (e.g., `Sprite_Minecart_Main`).
- **Local Labels**: Prefix local labels with a dot (`.`) to indicate their limited scope (e.g., `.check_direction`).
- **Constants and Flags**: Use `ALL_CAPS_WITH_UNDERSCORES` for constants and flags (e.g., `!MINECART_SPEED`, `!HARVESTING_FLAG`).
- **Variables**: Use `CamelCase` for variable names to maintain readability (e.g., `LinkInCart`, `SpriteDirection`).
- **Alignment**: Align labels to the left margin for better readability. Indent instructions and comments to separate them from labels.
Example:
```asm
Sprite_Minecart_Main:
{
JSR HandleTileDirections
JSR HandleDynamicSwitchTileDirections
RTS
}
```
## Comments
- **Purpose**: Comments should explain why the code exists and what it is intended to do, especially for complex logic.
- **Placement**:
- Comments can be placed above the code block they describe for longer explanations.
- Inline comments can be used for single lines of code where the purpose might not be immediately clear.
- **Clarity**: Avoid stating the obvious. Focus on explaining the logic rather than restating the code.
Example:
```asm
LDA $22 : SEC : SBC $3F : STA $31 ; Adjust X position for camera movement
```
## Instructions
- **Single Line Instructions**: Combine multiple instructions on a single line using colons (`:`) where appropriate for related operations.
- **Separation**: Use line breaks to separate distinct sections of code logically, improving readability.
- **Optimization**: Always consider the most efficient instruction for the task at hand, especially in performance-critical sections.
Example:
```asm
LDA #$01 : STA !LinkInCart ; Set Link in cart flag
```
## Macros
- **Naming**: Use `PascalCase` for macro names, with the first letter of each word capitalized (e.g., `InitMovement`, `MoveCart`).
- **Parameters**: Clearly define and document parameters within macros to ensure they are used correctly.
- **Reuse**: Encourage the reuse of macros to avoid code duplication and simplify maintenance.
Example:
```asm
%macro HandlePlayerCamera
LDA $22 : SEC : SBC $3F : STA $31
LDA $20 : SEC : SBC $3E : STA $30
JSL Link_HandleMovingAnimation_FullLongEntry
JSL HandleIndoorCameraAndDoors
RTS
endmacro
```
## Loops and Branching
- **Branch Labels**: Use meaningful names for branch labels, prefixed with a dot (`.`) for local branches.
- **Optimization**: Minimize the number of instructions within loops and branches to improve performance.
Example:
```asm
.loop_start
LDA $00 : CMP #$10 : BEQ .end_loop
INC $00
BRA .loop_start
.end_loop
RTS
```
## Data Structures
- **Alignment**: Align data tables and structures clearly, and use comments to describe the purpose and layout of each.
- **Access**: Ensure that data structures are accessed consistently, with clear boundaries between read and write operations.
Example:
```asm
.DirectionTileLookup
{
db $02, $00, $04, $00 ; North
db $00, $00, $03, $01 ; East
db $00, $02, $00, $04 ; South
db $03, $01, $00, $00 ; West
}
```
- **Structs**: Use structs to group related data together, improving readability and maintainability.
Example:
```asm
struct AncillaAdd_HookshotData $099AF8
.speed_y: skip 4
.speed_x: skip 4
.offset_y: skip 8
.offset_x: skip 8
endstruct
AncillaAdd_Hookshot:
; $099AF0
.speed_y
db -64 ; up
db 64 ; down
db 0 ; left
db 0 ; right
; $099AFC
.speed_x
db 0 ; up
db 0 ; down
db -64 ; left
db 64 ; right
; $099B00
.offset_y
dw 4 ; up
dw 20 ; down
dw 8 ; left
dw 8 ; right
; $099B08
.offset_x
dw 0 ; up
dw 0 ; down
dw -4 ; left
dw 11 ; right
```
## Code Organization
- **Logical Grouping**: Organize code into logical sections, with related routines and macros grouped together.
- **Separation of Concerns**: Ensure that each section of code is responsible for a specific task or set of related tasks, avoiding tightly coupled code.
- **Modularity**: Write code in a modular way, making it easier to reuse and maintain.
Example:
```asm
; =========================================================
; Minecart Sprite Logic
; =========================================================
Sprite_Minecart_Main:
{
PHX
JSR HandleMinecartMovement
PLX
REP #$20
LDA !SpriteDirection : STA $00
SEP #$20
RTS
}
```
## Custom Code
- **Integration**: Include custom assembly code in the `yaze.asm` file to ensure it is applied correctly to the ROM. The module should include a define and conditional statement to allow users to disable the module if needed.
Example:
```asm
!YAZE_CUSTOM_MOSAIC = 1
if !YAZE_CUSTOM_MOSAIC != 0
incsrc "mosaic_change.asm"
endif
```

View File

@@ -0,0 +1,171 @@
# Canvas System Guide
This guide provides a comprehensive overview of the `yaze` canvas system, its architecture, and best practices for integration. It reflects the state of the system after the October 2025 refactoring.
## 1. Architecture
The canvas system was refactored from a monolithic class into a modular, component-based architecture. The main `gui::Canvas` class now acts as a façade, coordinating a set of single-responsibility components and free functions.
### Core Principles
- **Modular Components**: Logic is broken down into smaller, testable units (e.g., state, rendering, interaction, menus).
- **Data-Oriented Design**: Plain-old-data (POD) structs like `CanvasState` and `CanvasConfig` hold state, which is operated on by free functions.
- **Backward Compatibility**: The refactor was designed to be 100% backward compatible. Legacy APIs still function, but new patterns are encouraged.
- **Editor Agnostic**: The core canvas system has no knowledge of `zelda3` specifics, making it reusable for any editor.
### Code Organization
The majority of the canvas code resides in `src/app/gui/canvas/`.
```
src/app/gui/canvas/
├── canvas.h/cc # Main Canvas class (facade)
├── canvas_state.h # POD state structs
├── canvas_config.h # Unified configuration struct
├── canvas_geometry.h/cc # Geometry calculation helpers
├── canvas_rendering.h/cc # Rendering free functions
├── canvas_events.h # Interaction event structs
├── canvas_interaction.h/cc # Interaction event handlers
├── canvas_menu.h/cc # Declarative menu structures
├── canvas_menu_builder.h/cc # Fluent API for building menus
├── canvas_popup.h/cc # PopupRegistry for persistent popups
└── canvas_utils.h/cc # General utility functions
```
## 2. Core Concepts
### Configuration (`CanvasConfig`)
- A single, unified `gui::CanvasConfig` struct (defined in `canvas_config.h`) holds all configuration for a canvas instance.
- This includes display settings (grid, labels), sizing, scaling, and usage mode.
- This replaces duplicated config structs from previous versions.
### State (`CanvasState`)
- A POD struct (`canvas_state.h`) that holds the dynamic state of the canvas, including geometry, zoom, and scroll.
- Editors can inspect this state for custom rendering and logic.
### Coordinate Systems
The canvas operates with three distinct coordinate spaces. Using the correct one is critical to avoid bugs.
1. **Screen Space**: Absolute pixel coordinates on the monitor (from `ImGui::GetIO().MousePos`). **Never use this for canvas logic.**
2. **Canvas/World Space**: Coordinates relative to the canvas's content, accounting for scrolling and panning. Use `Canvas::hover_mouse_pos()` to get this. This is the correct space for entity positioning and high-level calculations.
3. **Tile/Grid Space**: Coordinates in tile units. Use `Canvas::CanvasToTile()` to convert from world space.
A critical fix was made to ensure `Canvas::hover_mouse_pos()` is updated continuously whenever the canvas is hovered, decoupling it from specific actions like painting.
## 3. Interaction System
The canvas supports several interaction modes, managed via the `CanvasUsage` enum.
### Interaction Modes
- `kTilePainting`: For painting tiles onto a tilemap.
- `kTileSelection`: For selecting one or more tiles.
- `kRectangleSelection`: For drag-selecting a rectangular area.
- `kEntityManipulation`: For moving and interacting with entities.
- `kPaletteEditing`: For palette-related work.
- `kDiagnostics`: For performance and debug overlays.
Set the mode using `canvas.SetUsageMode(gui::CanvasUsage::kTilePainting)`. This ensures the context menu and interaction handlers behave correctly.
### Event-Driven Model
Interaction logic is moving towards an event-driven model. Instead of inspecting canvas state directly, editors should handle events returned by interaction functions.
**Example**:
```cpp
RectSelectionEvent event = HandleRectangleSelection(geometry, ...);
if (event.is_complete) {
// Process the selection event
}
```
## 4. Context Menu & Popups
The context menu system is now unified, data-driven, and supports persistent popups.
### Key Features
- **Unified Item Definition**: All menu items use the `gui::CanvasMenuItem` struct.
- **Priority-Based Ordering**: Menu sections are automatically sorted based on the `MenuSectionPriority` enum, ensuring a consistent layout:
1. `kEditorSpecific` (highest priority)
2. `kBitmapOperations`
3. `kCanvasProperties`
4. `kDebug` (lowest priority)
- **Automatic Popup Persistence**: Popups defined declaratively will remain open until explicitly closed by the user (ESC or close button), rather than closing on any click outside.
- **Fluent Builder API**: The `gui::CanvasMenuBuilder` provides a clean, chainable API for constructing complex menus.
### API Patterns
**Add a Simple Menu Item**:
```cpp
canvas.AddContextMenuItem(
gui::CanvasMenuItem("Label", ICON_MD_ICON, []() { /* Action */ })
);
```
**Add a Declarative Popup Item**:
This pattern automatically handles popup registration and persistence.
```cpp
auto item = gui::CanvasMenuItem::WithPopup(
"Properties",
"props_popup_id",
[]() {
// Render popup content here
ImGui::Text("My Properties");
}
);
canvas.AddContextMenuItem(item);
```
**Build a Complex Menu with the Builder**:
```cpp
gui::CanvasMenuBuilder builder;
canvas.editor_menu() = builder
.BeginSection("Editor Actions", gui::MenuSectionPriority::kEditorSpecific)
.AddItem("Cut", ICON_MD_CUT, []() { Cut(); })
.AddPopupItem("Settings", "settings_popup", []() { RenderSettings(); })
.EndSection()
.Build();
```
## 5. Entity System
A generic, Zelda-agnostic entity system allows editors to manage on-canvas objects.
- **Flat Functions**: Entity creation logic is handled by pure functions in `src/app/editor/overworld/operations/entity_operations.h`, such as `InsertEntrance`, `InsertSprite`, etc. These functions are designed for ZScream feature parity.
- **Delegation Pattern**: The `OverworldEditor` delegates to the `MapPropertiesSystem`, which in turn calls these flat functions to modify the ROM state.
- **Mode-Aware Menu**: The "Insert Entity" context submenu is only available when the canvas is in `kEntityManipulation` mode.
**Usage Flow**:
1. Set canvas mode to `kEntityManipulation`.
2. Right-click on the canvas to open the context menu.
3. Select "Insert Entity" and choose the entity type.
4. The appropriate callback is fired, which calls the corresponding `Insert...` function.
5. A popup appears to configure the new entity's properties.
## 6. Integration Guide for Editors
1. **Construct `Canvas`**: Instantiate `gui::Canvas`, providing a unique ID.
2. **Configure**: Set the desired `CanvasUsage` mode via `canvas.SetUsageMode()`. Configure available modes and other options in the `CanvasConfig` struct.
3. **Register Callbacks**: If using interaction modes like tile painting, register callbacks for events like `finish_paint`.
4. **Render Loop**:
- Call `canvas.Begin(size)`.
- Draw your editor-specific content (bitmaps, entities, overlays).
- Call `canvas.End()`. This handles rendering the grid, overlays, and the context menu.
5. **Provide Custom Menus**: Use `canvas.AddContextMenuItem()` or the `CanvasMenuBuilder` to add editor-specific actions to the context menu. Assign the `kEditorSpecific` priority to ensure they appear at the top.
6. **Handle State**: Respond to user interactions by inspecting the `CanvasState` or handling events returned from interaction helpers.
## 7. Debugging
If you encounter issues with the canvas, check the following:
- **Context Menu Doesn't Appear**:
- Is `config.enable_context_menu` true?
- Is the mouse button right-click?
- Is the canvas focused and not being dragged?
- **Popup Doesn't Persist**:
- Are you using the `CanvasMenuItem::WithPopup` pattern?
- Is `canvas.End()` being called every frame to allow the `PopupRegistry` to render?
- **Incorrect Coordinates**:
- Are you using `canvas.hover_mouse_pos()` for world coordinates instead of `ImGui::GetIO().MousePos`?
- Verify that you are correctly converting between world space and tile space.
- **Menu Items in Wrong Order**:
- Have you set the correct `MenuSectionPriority` for your custom menu sections?
## 8. Automation API
The `CanvasAutomationAPI` provides hooks for testing and automation. It allows for programmatic control of tile operations (`SetTileAt`, `SelectRect`), view controls (`ScrollToTile`, `SetZoom`), and entity manipulation. This API is exposed via the `z3ed` CLI and a gRPC service.

View File

@@ -0,0 +1,151 @@
# 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.
## Basic Usage
```bash
./yaze [flags]
```
## Available Flags
### `--rom_file`
Load a specific ROM file on startup.
```bash
./yaze --rom_file=/path/to/zelda3.sfc
```
### `--debug`
Enable debug logging with verbose output.
```bash
./yaze --debug --log_file=yaze_debug.log
```
### `--editor`
Open a specific editor on startup. This saves time by skipping manual navigation through the UI.
**Available editors:**
- `Assembly` - Assembly code editor
- `Dungeon` - Dungeon/underworld editor
- `Graphics` - Graphics and tile editor
- `Music` - Music and sound editor
- `Overworld` - Overworld map editor
- `Palette` - Palette editor
- `Screen` - Screen editor
- `Sprite` - Sprite editor
- `Message` - Message/text editor
- `Hex` - Hex/memory editor
- `Agent` - AI agent interface
- `Settings` - Settings editor
**Example:**
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon
```
### `--cards`
Open specific cards/panels within an editor. Most useful with the Dungeon editor.
**Dungeon Editor Cards:**
- `Rooms List` - Shows the list of all dungeon rooms
- `Room Matrix` - Shows the dungeon room layout matrix
- `Entrances List` - Shows dungeon entrance configurations
- `Room Graphics` - Shows room graphics settings
- `Object Editor` - Shows the object placement editor
- `Palette Editor` - Shows the palette editor
- `Room N` - Opens a specific room by ID (e.g., `Room 0`, `Room 105`)
**Example:**
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Rooms List,Room 0"
```
## Common Debugging Scenarios
### 1. Quick Dungeon Room Testing
Open a specific dungeon room for testing:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="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"
```
### 3. Full Dungeon Editor Workspace
Open all dungeon editor tools:
```bash
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Room Graphics,Object Editor,Palette Editor"
```
### 4. Debug Mode with Logging
Enable full debug output while working:
```bash
./yaze --rom_file=zelda3.sfc --debug --log_file=debug.log \
--editor=Dungeon --cards="Room 0"
```
### 5. Quick Overworld Editing
Jump straight to overworld editing:
```bash
./yaze --rom_file=zelda3.sfc --editor=Overworld
```
## gRPC Test Harness (Developer Feature)
If compiled with `YAZE_WITH_GRPC=ON`, you can enable automated GUI testing:
```bash
./yaze --enable_test_harness --test_harness_port=50051
```
This allows remote control via gRPC for automated testing and AI agent interaction.
## Combining Flags
All flags can be combined for powerful debugging setups:
```bash
# Full debugging setup for room 105
./yaze \
--rom_file=/path/to/zelda3.sfc \
--debug \
--log_file=room_105_debug.log \
--editor=Dungeon \
--cards="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
## Troubleshooting
**Editor doesn't open:**
- Check spelling (case-sensitive)
- Verify ROM loaded successfully
- Check log output with `--debug`
**Cards 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`

View File

@@ -0,0 +1,224 @@
# E5 - Debugging and Testing Guide
**Last Updated**: October 9, 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.
---
## 1. Standardized Logging for Print Debugging
For all print-based debugging, `yaze` uses a structured logging system defined in `util/log.h`. This is the **only** approved method for logging; direct use of `printf` or `std::cout` should be avoided and replaced with the appropriate `LOG_*` macro.
### Log Levels and Usage
- `LOG_DEBUG(category, "message", ...)`: For verbose, development-only information.
- `LOG_INFO(category, "message", ...)`: For general, informational messages.
- `LOG_WARN(category, "message", ...)`: For potential issues that don't break functionality.
- `LOG_ERROR(category, "message", ...)`: For errors that cause a specific operation to fail.
### Log Categories
Categories allow you to filter logs to focus on a specific subsystem. Common categories include:
- `"Main"`
- `"TestManager"`
- `"EditorManager"`
- `"APU"`, `"CPU"`, `"SNES"` (for the emulator)
### Enabling and Configuring Logs via CLI
You can control logging behavior using command-line flags when launching `yaze` or `yaze_test`.
- **Enable Verbose Debug Logging**:
```bash
./build/bin/yaze --debug
```
- **Log to a File**:
```bash
./build/bin/yaze --log_file=yaze_debug.log
```
- **Filter by Category**:
```bash
# Only show logs from the APU and CPU emulator components
./build/bin/yaze_emu --emu_debug_apu=true --emu_debug_cpu=true
```
**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.
---
## 2. Command-Line Workflows for Testing
The `yaze` ecosystem provides several executables and flags to streamline testing and debugging.
### Launching the GUI for Specific Tasks
- **Load a ROM on Startup**: To immediately test a specific ROM, use the `--rom_file` flag. This bypasses the welcome screen.
```bash
./build/bin/yaze --rom_file /path/to/your/zelda3.sfc
```
- **Enable the GUI Test Harness**: To allow the `z3ed` CLI to automate the GUI, you must start `yaze` with the gRPC server enabled.
```bash
./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.
```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)
```
**Quick Examples**:
```bash
# Fast dungeon room testing
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
# Compare multiple rooms side-by-side
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="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"
# 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).
### Running Automated C++ Tests
The `yaze_test` executable is used to run the project's suite of unit, integration, and E2E tests.
- **Run All Tests**:
```bash
./build_ai/bin/yaze_test
```
- **Run Specific Categories**:
```bash
# Run only fast, dependency-free unit tests
./build_ai/bin/yaze_test --unit
# Run tests that require a ROM file
./build_ai/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
```
### Inspecting ROMs with `z3ed`
The `z3ed` CLI is a powerful tool for inspecting ROM data without launching the full GUI. This is ideal for quick checks and scripting.
- **Get ROM Information**:
```bash
z3ed rom info --rom zelda3.sfc
```
- **Inspect Dungeon Sprites**:
```bash
z3ed dungeon list-sprites --rom zelda3.sfc --dungeon 2
```
---
## 3. GUI Automation for AI Agents
The primary way for an AI agent to test its changes and interact with `yaze` is through the GUI automation framework. This system consists of the `yaze` gRPC server (Test Harness) and the `z3ed` CLI client.
### Architecture Overview
1. **`yaze` (Server)**: When launched with `--enable_test_harness`, it starts a gRPC server that exposes the UI for automation.
2. **`z3ed` (Client)**: The `z3ed agent test` commands connect to the gRPC server to send commands and receive information.
3. **AI Agent**: The agent generates `z3ed` commands to drive the UI and verify its actions.
### Step-by-Step Workflow for AI
#### Step 1: Launch `yaze` with the Test Harness
The AI must first ensure the `yaze` GUI is running and ready for automation.
```bash
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --test_harness_port 50051
```
#### Step 2: Discover UI Elements
Before interacting with the UI, the agent needs to know the stable IDs of the widgets.
```bash
# Discover all widgets in the Dungeon editor window
z3ed agent test discover --window "Dungeon" --grpc localhost:50051
```
This will return a list of widget IDs (e.g., `Dungeon/Canvas/Map`) that can be used in scripts.
**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"
```
#### Step 3: Record or Write a Test Script
An agent can either generate a test script from scratch or use a pre-recorded one.
- **Recording a human interaction**:
```bash
z3ed agent test record --suite my_test.jsonl
```
- **A generated script might look like this**:
```json
// my_test.jsonl
{"action": "click", "target": "Dungeon/Toolbar/Open Room"}
{"action": "wait", "duration_ms": 500}
{"action": "type", "target": "Room Selector/Filter", "text": "Room 105"}
{"action": "click", "target": "Room Selector/List/Room 105"}
{"action": "assert_visible", "target": "Room Card 105"}
```
- **Or use startup flags to prepare the environment**:
```bash
# Start yaze with the room already open
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness \
--editor=Dungeon --cards="Room 105"
# Then your test script just needs to validate the state
{"action": "assert_visible", "target": "Room Card 105"}
{"action": "assert_visible", "target": "Dungeon/Canvas"}
```
#### Step 4: Replay the Test and Verify
The agent executes the script to perform the actions and validate the outcome.
```bash
z3ed agent test replay my_test.jsonl --watch
```
The `--watch` flag streams results back to the CLI in real-time. The agent can parse this output to confirm its actions were successful.
---
## 4. Advanced Debugging Tools
For more complex issues, especially within the emulator, `yaze` provides several advanced debugging windows. These are covered in detail in the [Emulator Development Guide](emulator-development-guide.md).
- **Disassembly Viewer**: A live, interactive view of the 65816 and SPC700 CPU execution.
- **Breakpoint Manager**: Set breakpoints on code execution, memory reads, or memory writes.
- **Memory Viewer**: Inspect WRAM, SRAM, VRAM, and ROM.
- **APU Inspector**: A dedicated debugger for the audio subsystem.
- **Event Viewer**: A timeline of all hardware events (NMI, IRQ, DMA).
These tools are accessible from the **Debug** menu in the main application.

View File

@@ -0,0 +1,106 @@
# Dependency & Build Overview
_Last reviewed: November 2025. All information in this document is derived from the current
`src/CMakeLists.txt` tree and shipped presets._
This guide explains how the major YAZE libraries fit together, which build switches control
them, and when a code change actually forces a full rebuild. It is intentionally concise so you
can treat it as a quick reference while editing.
## Build Switches & Presets
| CMake option | Default | Effect |
| --- | --- | --- |
| `YAZE_BUILD_APP` | `ON` | Build the main GUI editor (`yaze`). Disable when you only need CLI/tests. |
| `YAZE_BUILD_Z3ED` | `ON` | Build the `z3ed` automation tool and supporting agent libraries. |
| `YAZE_BUILD_EMU` | `OFF` | Build the standalone emulator binary. Always enabled inside the GUI build. |
| `YAZE_BUILD_TESTS` | `ON` in `*-dbg` presets | Compiles test helpers plus `yaze_test`. Required for GUI test dashboard. |
| `YAZE_ENABLE_GRPC` | `OFF` | Pulls in gRPC/protobuf for automation and remote control features. |
| `YAZE_MINIMAL_BUILD` | `OFF` | Skips optional editors/assets. Useful for CI smoke builds. |
| `YAZE_BUILD_LIB` | `OFF` | Produces the `yaze_core` INTERFACE target used by external tooling. |
| `YAZE_BUILD_AGENT_UI` | `ON` when `YAZE_BUILD_GUI` is `ON` | Compiles ImGui chat widgets. Disable for lighter GUI builds. |
| `YAZE_ENABLE_REMOTE_AUTOMATION` | `OFF` in `win-*` core presets | Builds gRPC servers/clients plus proto generation. |
| `YAZE_ENABLE_AI_RUNTIME` | `OFF` in `win-*` core presets | Enables Gemini/Ollama transports, proposal planning, and advanced routing code. |
| `YAZE_ENABLE_AGENT_CLI` | `ON` when `YAZE_BUILD_CLI` is `ON` | Compiles the conversational agent stack used by `z3ed`. |
Use the canned presets from `CMakePresets.json` so these options stay consistent across
platforms: `mac-dbg`, `mac-ai`, `lin-dbg`, `win-dbg`, etc. The `*-ai` presets enable both
`YAZE_BUILD_Z3ED` and `YAZE_ENABLE_GRPC` so the CLI and agent features match what ships.
## Library Layers
### 1. Foundation (`src/util`, `incl/`)
- **`yaze_common`**: cross-platform macros, generated headers, and lightweight helpers shared by
every other target.
- **`yaze_util`**: logging, file I/O, BPS patch helpers, and the legacy flag system. Only depends
on `yaze_common` plus optional gRPC/Abseil symbols.
- **Third-party**: SDL2, ImGui, Abseil, yaml-cpp, FTXUI, Asar. They are configured under
`cmake/dependencies/*.cmake` and linked where needed.
Touching headers in this layer effectively invalidates most of the build. Keep common utilities
stable and prefer editor-specific helpers instead of bloating `yaze_util`.
### 2. Graphics & UI (`src/app/gfx`, `src/app/gui`)
- **`yaze_gfx`**: bitmap containers, palette math, deferred texture arena, canvas abstractions.
Depends on SDL2 + `yaze_util`.
- **`yaze_gui`**: shared ImGui widgets, docking layout utilities, and theme plumbing. Depends on
ImGui + `yaze_gfx`.
Changes here rebuild all editors but do not touch the lower-level Zelda 3 logic. Use the graphics
layer for rendering and asset streaming primitives; keep domain logic in the Zelda 3 library.
### 3. Game Domain (`src/zelda3`, `src/app/editor`)
- **`yaze_zelda3`**: map/room/sprite models, parsers, and ROM serialization. It links
`yaze_gfx`, `yaze_util`, and Abseil.
- **`yaze_editor`**: ImGui editors (overworld, dungeon, palette, etc.). Depends on
`yaze_gui`, `yaze_zelda3`, `yaze_gfx`, and optional agent/test hooks.
- **`yaze_emulator`**: CPU, PPU, and APU subsystems plus the debugger UIs (`src/app/emu`). The GUI
app links this to surface emulator panels.
Touching Zelda 3 headers triggers rebuilds of the editor and CLI but leaves renderer-only changes
alone. Touching editor UI code does **not** require rebuilding `yaze_emulator`.
### 4. Tooling & Export Targets
- **`yaze_agent`** (`src/cli/agent`): shared logic behind the CLI and AI workflows. Built whenever
`YAZE_ENABLE_AGENT_CLI` is enabled (automatically true when `YAZE_BUILD_Z3ED=ON`). When both the CLI and the agent UI are disabled, CMake now emits a lightweight stub target so GUI-only builds don't drag in unnecessary dependencies.
- **`z3ed` binary** (`src/cli/z3ed.cmake`): links `yaze_agent`, `yaze_zelda3`, `yaze_gfx`, and
Abseil/FTXUI.
- **`yaze_core_lib`** (`src/core`): static library that exposes project management helpers and the
Asar integration. When `YAZE_BUILD_LIB=ON` it can be consumed by external tools.
- **`yaze_test_support`** (`src/app/test`): harness for the in-editor dashboard and `yaze_test`.
- **`yaze_grpc_support`**: server-only aggregation of gRPC/protobuf code, gated by `YAZE_ENABLE_REMOTE_AUTOMATION`. CLI clients (`cli/service/gui/**`, `cli/service/planning/**`) now live solely in `yaze_agent` so GUI builds can opt out entirely.
### 5. Final Binaries
- **`yaze`**: GUI editor. Links every layer plus `yaze_test_support` when tests are enabled.
- **`yaze_test`**: GoogleTest runner (unit, integration, e2e). Built from `test/CMakeLists.txt`.
- **`z3ed`**: CLI + TUI automation tool. Built when `YAZE_BUILD_Z3ED=ON`.
- **`yaze_emu`**: optional standalone emulator for fast boot regression tests.
## Rebuild Cheatsheet
| Change | Targets Affected |
| --- | --- |
| `src/util/*.h` or `incl/yaze/*.h` | Everything (foundation dependency) |
| `src/app/gfx/**` | `yaze_gfx`, `yaze_gui`, editors, CLI. Emulator core unaffected. |
| `src/zelda3/**` | All editors, CLI, tests. Rebuild does **not** touch renderer-only changes. |
| `src/app/editor/**` | GUI editor + CLI (shared panels). Emulator/test support untouched. |
| `src/app/emu/**` | Emulator panels + GUI app. CLI and Zelda 3 libraries unaffected. |
| `src/cli/**` | `yaze_agent`, `z3ed`. No impact on GUI/editor builds. |
| `src/app/test/**` | `yaze_test_support`, `yaze_test`, GUI app (only when tests enabled). |
Use this table when deciding whether to invalidate remote build caches or to schedule longer CI
runs. Whenever possible, localize changes to the upper layers to avoid rebuilding the entire
stack.
## Tips for Faster Iteration
1. **Leverage presets** `cmake --build --preset mac-ai --target yaze` automatically enables
precompiled headers and shared dependency trees.
2. **Split work by layer** renderer bugs usually live in `yaze_gfx`; leave Zelda 3 logic alone
unless you need ROM serialization tweaks.
3. **Turn off unused targets** set `YAZE_BUILD_Z3ED=OFF` when working purely on GUI features to
shave a few hundred object files.
4. **Test without ROMs** `docs/public/developer/testing-without-roms.md` documents the mock ROM
harness so you do not need to rebuild assets between iterations.
5. **See also** for deep dives into refactors or planned changes, read the internal blueprints in
`docs/internal/blueprints/` instead of bloating the public docs.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,566 @@
# Git Workflow and Branching Strategy
**Last Updated:** October 10, 2025
**Status:** Active
**Current Phase:** Pre-1.0 (Relaxed Rules)
## Warning: Pre-1.0 Workflow (Current)
**TLDR for now:** Since yaze is pre-1.0 and actively evolving, we use a **simplified workflow**:
- **Documentation changes**: Commit directly to `master` or `develop`
- **Small bug fixes**: Can go direct to `develop`, no PR required
- **Solo work**: Push directly when you're the only one working
- Warning: **Breaking changes**: Use feature branches and document in changelog
- Warning: **Major refactors**: Use feature branches for safety (can always revert)
- **Always keep local backups**: copy ROMs/assets before editing; never risk the only copy.
- **Before rebasing/rewriting history**, stash or copy work elsewhere to prevent accidental loss.
**Why relaxed?**
- Small team / solo development
- Pre-1.0 means breaking changes are expected
- Documentation needs to be public quickly
- Overhead of PRs/reviews slows down experimentation
**When to transition to strict workflow:**
- Multiple active contributors
- Stable API (post-1.0)
- Large user base depending on stability
- Critical bugs need rapid hotfixes
---
## Pre-1.0 Release Strategy: Best Effort Releases
For all versions prior to 1.0.0, yaze follows a **"best effort"** release strategy. This prioritizes getting working builds to users quickly, even if not all platforms build successfully on the first try.
### Core Principles
1. **Release Can Proceed with Failed Platforms**: The `release` CI/CD workflow will create a GitHub Release even if one or more platform-specific build jobs (e.g., Windows, Linux, macOS) fail.
2. **Missing Platforms Can Be Added Later**: A failed job for a specific platform can be re-run from the GitHub Actions UI. If it succeeds, the binary artifact will be **automatically added to the existing GitHub Release**.
3. **Transparency is Key**: The release notes will automatically generate a "Platform Availability" report, clearly indicating which platforms succeeded () and which failed (❌), so users know the current status.
### How It Works in Practice
- The `build-and-package` jobs in the `release.yml` workflow have `continue-on-error: true`.
- The final `create-github-release` job has `if: always()` and uses `softprops/action-gh-release@v2`, which intelligently updates an existing release if the tag already exists.
- If a platform build fails, a developer can investigate the issue and simply re-run the failed job. Upon success, the new binary is uploaded and attached to the release that was already created.
This strategy provides flexibility and avoids blocking a release for all users due to a transient issue on a single platform. Once the project reaches v1.0.0, this policy will be retired in favor of a stricter approach where all platforms must pass for a release to proceed.
---
## 📚 Full Workflow Reference (Future/Formal)
The sections below document the **formal Git Flow model** that yaze will adopt post-1.0 or when the team grows. For now, treat this as aspirational best practices.
## Branch Structure
### Main Branches
#### `master`
- **Purpose**: Production-ready release branch
- **Protection**: Protected, requires PR approval
- **Versioning**: Tagged with semantic versions (e.g., `v0.3.2`, `v0.4.0`)
- **Updates**: Only via approved PRs from `develop` or hotfix branches
#### `develop`
- **Purpose**: Main development branch, integration point for all features
- **Protection**: Protected, requires PR approval
- **State**: Should always build and pass tests
- **Updates**: Merges from feature branches, releases merge back after tagging
### Supporting Branches
#### Feature Branches
**Naming Convention:** `feature/<short-description>`
**Examples:**
- `feature/overworld-editor-improvements`
- `feature/dungeon-room-painter`
- `feature/add-sprite-animations`
**Rules:**
- Branch from: `develop`
- Merge back to: `develop`
- Lifetime: Delete after merge
- Naming: Use kebab-case, be descriptive but concise
**Workflow:**
```bash
# Create feature branch
git checkout develop
git pull origin develop
git checkout -b feature/my-feature
# Work on feature
git add .
git commit -m "feat: add new feature"
# Keep up to date with develop
git fetch origin
git rebase origin/develop
# Push and create PR
git push -u origin feature/my-feature
```
#### Bugfix Branches
**Naming Convention:** `bugfix/<issue-number>-<short-description>`
**Examples:**
- `bugfix/234-canvas-scroll-regression`
- `bugfix/fix-dungeon-crash`
**Rules:**
- Branch from: `develop`
- Merge back to: `develop`
- Lifetime: Delete after merge
- Reference issue number when applicable
#### Hotfix Branches
**Naming Convention:** `hotfix/<version>-<description>`
**Examples:**
- `hotfix/v0.3.3-memory-leak`
- `hotfix/v0.3.2-crash-on-startup`
**Rules:**
- Branch from: `master`
- Merge to: BOTH `master` AND `develop`
- Creates new patch version
- Used for critical production bugs only
**Workflow:**
```bash
# Create hotfix from master
git checkout master
git pull origin master
git checkout -b hotfix/v0.3.3-critical-fix
# Fix the issue
git add .
git commit -m "fix: critical production bug"
# Merge to master
git checkout master
git merge --no-ff hotfix/v0.3.3-critical-fix
git tag -a v0.3.3 -m "Hotfix: critical bug"
git push origin master --tags
# Merge to develop
git checkout develop
git merge --no-ff hotfix/v0.3.3-critical-fix
git push origin develop
# Delete hotfix branch
git branch -d hotfix/v0.3.3-critical-fix
```
#### Release Branches
**Naming Convention:** `release/<version>`
**Examples:**
- `release/v0.4.0`
- `release/v0.3.2`
**Rules:**
- Branch from: `develop`
- Merge to: `master` AND `develop`
- Used for release preparation (docs, version bumps, final testing)
- Only bugfixes allowed, no new features
**Workflow:**
```bash
# Create release branch
git checkout develop
git pull origin develop
git checkout -b release/v0.4.0
# Prepare release (update version, docs, changelog)
# ... make changes ...
git commit -m "chore: prepare v0.4.0 release"
# Merge to master and tag
git checkout master
git merge --no-ff release/v0.4.0
git tag -a v0.4.0 -m "Release v0.4.0"
git push origin master --tags
# Merge back to develop
git checkout develop
git merge --no-ff release/v0.4.0
git push origin develop
# Delete release branch
git branch -d release/v0.4.0
```
#### Experimental Branches
**Naming Convention:** `experiment/<description>`
**Examples:**
- `experiment/vulkan-renderer`
- `experiment/wasm-build`
## Git Safety Crash Course
- Run `git status` often and avoid staging ROMs or build artifacts; add ignore rules when necessary.
- Never force-push shared branches (`develop`, `master`). PRs and feature branches are safer places
for rewrites.
- Keep backups of any tools that mutate large files (scripts, automation) so you can revert quickly.
- Before deleting branches that touched ROMs/assets, confirm those files were merged and backed up.
**Rules:**
- Branch from: `develop` or `master`
- May never merge (prototypes, research)
- Document findings in docs/experiments/
- Delete when concluded or merge insights into features
## Commit Message Conventions
Follow **Conventional Commits** specification:
### Format
```
<type>(<scope>): <subject>
<body>
<footer>
```
### Types
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation only
- `style`: Code style (formatting, semicolons, etc.)
- `refactor`: Code refactoring
- `perf`: Performance improvements
- `test`: Adding or updating tests
- `build`: Build system changes (CMake, dependencies)
- `ci`: CI/CD configuration changes
- `chore`: Maintenance tasks
### Scopes (optional)
- `overworld`: Overworld editor
- `dungeon`: Dungeon editor
- `graphics`: Graphics editor
- `emulator`: Emulator core
- `canvas`: Canvas system
- `gui`: GUI/ImGui components
### Examples
```bash
# Good commit messages
feat(overworld): add tile16 quick-select palette
fix(canvas): resolve scroll regression after refactoring
docs: update build instructions for SDL3
refactor(emulator): extract APU timing to cycle-accurate model
perf(dungeon): optimize room rendering with batched draw calls
# With body and footer
feat(overworld): add multi-tile selection tool
Allows users to select and copy/paste rectangular regions
of tiles in the overworld editor. Supports undo/redo.
Closes #123
```
## Pull Request Guidelines
### PR Title
Follow commit message convention:
```
feat(overworld): add new feature
fix(dungeon): resolve crash
```
### PR Description Template
```markdown
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix (non-breaking change)
- [ ] New feature (non-breaking change)
- [ ] Breaking change (fix or feature that breaks existing functionality)
- [ ] Documentation update
## Testing
- [ ] All tests pass
- [ ] Added new tests for new features
- [ ] Manual testing completed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] No new warnings
- [ ] Dependent changes merged
```
### PR Review Process
1. **Author**: Create PR, fill out template, request reviewers
2. **CI**: Automatic build and test on all platforms
3. **Reviewers**: Code review, suggest changes
4. **Author**: Address feedback, push updates
5. **Approval**: At least 1 approval required for merge
6. **Merge**: Squash or merge commit (case-by-case)
## Version Numbering
Follow **Semantic Versioning (SemVer)**: `MAJOR.MINOR.PATCH`
### MAJOR (e.g., 1.0.0)
- Breaking API changes
- Major architectural changes
- First stable release
### MINOR (e.g., 0.4.0)
- New features (backward compatible)
- Significant improvements
- Major dependency updates (SDL3)
### PATCH (e.g., 0.3.2)
- Bug fixes
- Minor improvements
- Documentation updates
### Pre-release Tags
- `v0.4.0-alpha.1` - Early testing
- `v0.4.0-beta.1` - Feature complete
- `v0.4.0-rc.1` - Release candidate
## Release Process
### For Minor/Major Releases (0.x.0, x.0.0)
1. **Create release branch**
```bash
git checkout -b release/v0.4.0
```
2. **Update version numbers**
- `CMakeLists.txt`
- `../reference/changelog.md`
- `README.md`
3. **Update documentation**
- Review all docs for accuracy
- Update migration guides if breaking changes
- Finalize changelog
4. **Create release commit**
```bash
git commit -m "chore: prepare v0.4.0 release"
```
5. **Merge and tag**
```bash
git checkout master
git merge --no-ff release/v0.4.0
git tag -a v0.4.0 -m "Release v0.4.0"
git push origin master --tags
```
6. **Merge back to develop**
```bash
git checkout develop
git merge --no-ff release/v0.4.0
git push origin develop
```
7. **Create GitHub Release**
- Draft release notes
- Attach build artifacts (CI generates these)
- Publish release
### For Patch Releases (0.3.x)
1. **Collect fixes on develop**
- Merge all bugfix PRs to develop
- Ensure tests pass
2. **Create release branch**
```bash
git checkout -b release/v0.3.2
```
3. **Follow steps 2-7 above**
## Long-Running Feature Branches
For large features (e.g., v0.4.0 modernization), use a **feature branch with sub-branches**:
```
develop
└── feature/v0.4.0-modernization (long-running)
├── feature/v0.4.0-sdl3-core
├── feature/v0.4.0-sdl3-graphics
└── feature/v0.4.0-sdl3-audio
```
**Rules:**
- Long-running branch stays alive during development
- Sub-branches merge to long-running branch
- Long-running branch periodically rebases on `develop`
- Final merge to `develop` when complete
## Tagging Strategy
### Release Tags
- Format: `v<MAJOR>.<MINOR>.<PATCH>[-prerelease]`
- Examples: `v0.3.2`, `v0.4.0-rc.1`, `v1.0.0`
- Annotated tags with release notes
### Internal Milestones
- Format: `milestone/<name>`
- Examples: `milestone/canvas-refactor-complete`
- Used for tracking major internal achievements
## Best Practices
### DO
- Keep commits atomic and focused
- Write descriptive commit messages
- Rebase feature branches on develop regularly
- Run tests before pushing
- Update documentation with code changes
- Delete branches after merging
### DON'T ❌
- Commit directly to master or develop
- Force push to shared branches
- Mix unrelated changes in one commit
- Merge without PR review
- Leave stale branches
## Quick Reference
```bash
# Start new feature
git checkout develop
git pull
git checkout -b feature/my-feature
# Update feature branch with latest develop
git fetch origin
git rebase origin/develop
# Finish feature
git push -u origin feature/my-feature
# Create PR on GitHub → Merge → Delete branch
# Start hotfix
git checkout master
git pull
git checkout -b hotfix/v0.3.3-fix
# ... fix, commit, merge to master and develop ...
# Create release
git checkout develop
git pull
git checkout -b release/v0.4.0
# ... prepare, merge to master, tag, merge back to develop ...
```
## Emergency Procedures
### If master is broken
1. Create hotfix branch immediately
2. Fix critical issue
3. Fast-track PR review
4. Hotfix deploy ASAP
### If develop is broken
1. Identify breaking commit
2. Revert if needed
3. Fix in new branch
4. Merge fix with priority
### If release needs to be rolled back
1. Tag current state as `v0.x.y-broken`
2. Revert master to previous tag
3. Create hotfix branch
4. Fix and release as patch version
---
## Current Simplified Workflow (Pre-1.0)
### Daily Development Pattern
```bash
# For documentation or small changes
git checkout master # or develop, your choice
git pull
# ... make changes ...
git add docs/
git commit -m "docs: update workflow guide"
git push origin master
# For experimental features
git checkout -b feature/my-experiment
# ... experiment ...
git push -u origin feature/my-experiment
# If it works: merge to develop
# If it doesn't: delete branch, no harm done
```
### When to Use Branches (Pre-1.0)
**Use a branch for:**
- Large refactors that might break things
- Experimenting with new ideas
- Features that take multiple days
- SDL3 migration or other big changes
**Don't bother with branches for:**
- Documentation updates
- Small bug fixes
- Typo corrections
- README updates
- Adding comments or tests
### Current Branch Usage
For now, treat `master` and `develop` interchangeably:
- `master`: Latest stable-ish code + docs
- `develop`: Optional staging area for integration
When you want docs public, just push to `master`. The GitHub Pages / docs site will update automatically.
### Commit Message (Simplified)
Still try to follow the convention, but don't stress:
```bash
# Good enough
git commit -m "docs: reorganize documentation structure"
git commit -m "fix: dungeon editor crash on load"
git commit -m "feat: add overworld sprite editor"
# Also fine for now
git commit -m "update docs"
git commit -m "fix crash"
```
### Releases (Pre-1.0)
Just tag and push:
```bash
# When you're ready for v0.3.2
git tag -a v0.3.2 -m "Release v0.3.2"
git push origin v0.3.2
# GitHub Actions builds it automatically
```
No need for release branches or complex merging until you have multiple contributors.
---
**References:**
- [Git Flow](https://nvie.com/posts/a-successful-git-branching-model/)
- [Conventional Commits](https://www.conventionalcommits.org/)
- [Semantic Versioning](https://semver.org/)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
# B5 - Architecture and Networking
This document provides a comprehensive overview of the yaze application's architecture, focusing on its service-oriented design, gRPC integration, and real-time collaboration features. For build/preset instructions when enabling gRPC/automation presets, refer to the [Build & Test Quick Reference](../build/quick-reference.md).
## 1. High-Level Architecture
The yaze ecosystem is split into two main components: the **YAZE GUI Application** and the **`z3ed` CLI Tool**.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ YAZE GUI Application │
│ (Runs on local machine) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ UnifiedGRPCServer (Port 50051) │ │
│ │ ════════════════════════════════════════════════════════ │ │
│ │ Hosts 3 gRPC Services on a SINGLE PORT: │ │
│ │ │ │
│ │ 1. ImGuiTestHarness Service │ │
│ │ • GUI automation (click, type, wait, assert) │ │
│ │ │ │
│ │ 2. RomService │ │
│ │ • Read/write ROM bytes │ │
│ │ • Proposal system for collaborative editing │ │
│ │ │ │
│ │ 3. CanvasAutomation Service │ │
│ │ • High-level canvas operations (tile ops, selection) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↑ │
│ │ gRPC Connection │
└────────────────────────────────────┼──────────────────────────────────────┘
┌────────────────────────────────────┼──────────────────────────────────────┐
│ z3ed CLI Tool │
│ (Command-line interface) │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ CLI Services (Business Logic - NOT gRPC servers) │ │
│ │ ══════════════════════════════════════════════════ │ │
│ │ - AI Services (Gemini, Ollama) │ │
│ │ - Agent Services (Chat, Tool Dispatcher) │ │
│ │ - Network Clients (gRPC and WebSocket clients) │ │
│ └──────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘
```
## 2. Service Taxonomy
It's important to distinguish between the two types of "services" in the yaze project.
### APP Services (gRPC Servers)
- **Location**: `src/app/core/service/`, `src/app/net/`
- **Runs In**: YAZE GUI application
- **Purpose**: Expose application functionality to remote clients (like the `z3ed` CLI).
- **Type**: gRPC **SERVER** implementations.
### CLI Services (Business Logic)
- **Location**: `src/cli/service/`
- **Runs In**: `z3ed` CLI tool
- **Purpose**: Implement the business logic for the CLI commands.
- **Type**: These are helper classes, **NOT** gRPC servers. They may include gRPC **CLIENTS** to connect to the APP Services.
## 3. gRPC Services
yaze exposes its core functionality through a `UnifiedGRPCServer` that hosts multiple services on a single port (typically 50051).
### ImGuiTestHarness Service
- **Proto**: `imgui_test_harness.proto`
- **Purpose**: GUI automation.
- **Features**: Click, type, screenshots, widget discovery.
### RomService
- **Proto**: `rom_service.proto`
- **Purpose**: Low-level ROM manipulation.
- **Features**: Read/write bytes, proposal system for collaborative editing, snapshots for version management.
### CanvasAutomation Service
- **Proto**: `canvas_automation.proto`
- **Purpose**: High-level, abstracted control over canvas-based editors.
- **Features**: Tile operations (get/set), selection management, view control (pan/zoom).
## 4. Real-Time Collaboration
Real-time collaboration is enabled through a WebSocket-based protocol managed by the `yaze-server`, a separate Node.js application.
### Architecture
```
┌─────────────┐ WebSocket ┌──────────────┐
│ yaze app │◄────────────────────────────►│ yaze-server │
│ (GUI) │ │ (Node.js) │
└─────────────┘ └──────────────┘
▲ ▲
│ gRPC │ WebSocket
└─────────────┐ │
┌──────▼──────┐ │
│ z3ed CLI │◄──────────────────────┘
└─────────────┘
```
### Core Components
- **ROM Version Manager**: Protects the ROM from corruption with snapshots and safe points.
- **Proposal Approval Manager**: Manages a collaborative voting system for applying changes.
- **Collaboration Panel**: A UI within the YAZE editor for managing collaboration.
### WebSocket Protocol
The WebSocket protocol handles real-time messaging for:
- Hosting and joining sessions.
- Broadcasting ROM diffs (`rom_sync`).
- Sharing and voting on proposals (`proposal_share`, `proposal_vote`).
- Sharing snapshots.
## 5. Data Flow Example: AI Agent Edits a Tile
1. **User** runs `z3ed agent --prompt "Paint grass at 10,10"`.
2. The **GeminiAIService** (a CLI Service) parses the prompt and returns a tool call to `overworld-set-tile`.
3. The **ToolDispatcher** (a CLI Service) routes this to the appropriate handler.
4. The **Tile16ProposalGenerator** (a CLI Service) creates a proposal.
5. The **GuiAutomationClient** (a CLI Service acting as a gRPC client) calls the `CanvasAutomation.SetTile()` RPC method on the YAZE application's `UnifiedGRPCServer`.
6. The **CanvasAutomationService** in the YAZE app receives the request and uses the `CanvasAutomationAPI` to paint the tile.
7. If collaboration is active, the change is submitted through the **ProposalApprovalManager**, which communicates with the `yaze-server` via WebSocket to manage voting.
8. Once approved, the change is applied to the ROM and synced with all collaborators.

View File

@@ -0,0 +1,122 @@
# Overworld Entity System
This document provides a technical overview of the overworld entity system, including critical bug fixes that enable its functionality and the ongoing plan to refactor it for modularity and ZScream feature parity.
## 1. System Overview
The overworld entity system manages all interactive objects on the overworld map, such as entrances, exits, items, and sprites. The system is undergoing a refactor to move from a monolithic architecture within the `Overworld` class to a modular design where each entity's save/load logic is handled in dedicated files.
**Key Goals of the Refactor**:
- **Modularity**: Isolate entity logic into testable, self-contained units.
- **ZScream Parity**: Achieve feature compatibility with ZScream's entity management, including support for expanded ROM formats.
- **Maintainability**: Simplify the `Overworld` class by delegating I/O responsibilities.
## 2. Core Components & Bug Fixes
Several critical bugs were fixed to make the entity system functional. Understanding these fixes is key to understanding the system's design.
### 2.1. Entity Interaction and Hover Detection
**File**: `src/app/editor/overworld/overworld_entity_renderer.cc`
- **Problem**: Exit entities were not responding to mouse interactions because the hover state was being improperly reset.
- **Fix**: The hover state (`hovered_entity_`) is now reset only once at the beginning of the entity rendering cycle, specifically in `DrawExits()`, which is the first rendering function called. Subsequent functions (`DrawEntrances()`, `DrawItems()`, etc.) can set the hover state without it being cleared, preserving the correct hover priority (last-drawn entity wins).
```cpp
// In DrawExits(), which is called first:
hovered_entity_ = nullptr; // Reset hover state at the start of the cycle.
// In DrawEntrances() and other subsequent renderers:
// The reset is removed to allow hover state to persist.
```
### 2.2. Entity Property Popup Save/Cancel Logic
**File**: `src/app/editor/overworld/entity.cc`
- **Problem**: The "Done" and "Cancel" buttons in entity property popups had inverted logic, causing changes to be saved on "Cancel" and discarded on "Done".
- **Fix**: The `set_done` flag, which controls the popup's return value, is now correctly managed. The "Done" and "Delete" buttons set `set_done = true` to signal a save action, while the "Cancel" button does not, correctly discarding changes.
```cpp
// Corrected logic for the "Done" button in popups
if (ImGui::Button(ICON_MD_DONE)) {
set_done = true; // Save changes
ImGui::CloseCurrentPopup();
}
// Corrected logic for the "Cancel" button
if (ImGui::Button(ICON_MD_CANCEL)) {
// Discard changes (do not set set_done)
ImGui::CloseCurrentPopup();
}
```
### 2.3. Exit Entity Coordinate System
**File**: `src/zelda3/overworld/overworld_exit.h`
- **Problem**: Saving a vanilla ROM would corrupt exit positions, causing them to load at (0,0). This was because the `OverworldExit` class used `uint8_t` for player coordinates, truncating 16-bit values.
- **Fix**: The coordinate-related members of `OverworldExit` were changed to `uint16_t` to match the full 0-4088 coordinate range, achieving parity with ZScream's data structures.
```cpp
// In OverworldExit class definition:
class OverworldExit : public GameEntity {
public:
// ...
uint16_t y_player_; // Changed from uint8_t
uint16_t x_player_; // Changed from uint8_t
uint16_t y_camera_; // Changed from uint8_t
uint16_t x_camera_; // Changed from uint8_t
// ...
};
```
### 2.4. Coordinate Synchronization on Drag
**File**: `src/zelda3/overworld/overworld_exit.h`
- **Problem**: When dragging an exit, the visual position (`x_`, `y_`) would update, but the underlying data used for saving (`x_player_`, `y_player_`) would not, leading to a data desync and incorrect saves.
- **Fix**: The `UpdateMapProperties` method now explicitly syncs the base entity coordinates to the player coordinates before recalculating scroll and camera values. This ensures that drag operations correctly persist.
```cpp
// In OverworldExit::UpdateMapProperties()
void UpdateMapProperties(uint16_t map_id) override {
// Sync player position from the base entity coordinates updated by the drag system.
x_player_ = static_cast<uint16_t>(x_);
y_player_ = static_cast<uint16_t>(y_);
// Proceed with auto-calculation using the now-correct player coordinates.
// ...
}
```
## 3. Entity I/O Refactoring Plan
The next phase of development is to extract all entity save and load logic from the monolithic `overworld.cc` into dedicated files.
### 3.1. File Structure
New files will be created to handle I/O for each entity type:
- `src/zelda3/overworld/overworld_entrance.cc`
- `src/zelda3/overworld/overworld_exit.cc`
- `src/zelda3/overworld/overworld_item.cc`
- `src/zelda3/overworld/overworld_transport.cc` (for new transport/whirlpool support)
### 3.2. Core Functions
Each new file will implement a standard set of flat functions:
- `LoadAll...()`: Reads all entities of a given type from the ROM.
- `SaveAll...()`: Writes all entities of a given type to the ROM.
- Helper functions for coordinate calculation and data manipulation, mirroring ZScream's logic.
### 3.3. ZScream Parity Goals
The refactor aims to implement key ZScream features:
- **Expanded ROM Support**: Correctly read/write from vanilla or expanded ROM addresses for entrances and items.
- **Pointer Deduplication**: When saving items, reuse pointers for identical item lists on different maps to conserve space.
- **Automatic Coordinate Calculation**: For exits and transports, automatically calculate camera and scroll values based on player position, matching the `UpdateMapStuff` logic in ZScream.
- **Transport Entity**: Add full support for transport entities (whirlpools, birds).
### 3.4. `Overworld` Class Role
After the refactor, the `Overworld` class will act as a coordinator, delegating all entity I/O to the new, modular functions. Its responsibility will be to hold the entity vectors and orchestrate the calls to the `LoadAll...` and `SaveAll...` functions.

View File

@@ -0,0 +1,353 @@
# SNES Palette System Overview
## Understanding SNES Color and Palette Organization
### Core Concepts
#### 1. SNES Color Format (15-bit BGR555)
- **Storage**: 2 bytes per color (16 bits total, 15 bits used)
- **Format**: `0BBB BBGG GGGR RRRR`
- Bits 0-4: Red (5 bits, 0-31)
- Bits 5-9: Green (5 bits, 0-31)
- Bits 10-14: Blue (5 bits, 0-31)
- Bit 15: Unused (always 0)
- **Range**: Each channel has 32 levels (0-31)
- **Total Colors**: 32,768 possible colors (2^15)
#### 2. Palette Groups in Zelda 3
Zelda 3 organizes palettes into logical groups for different game areas and entities:
```cpp
struct PaletteGroupMap {
PaletteGroup overworld_main; // Main overworld graphics (35 colors each)
PaletteGroup overworld_aux; // Auxiliary overworld (21 colors each)
PaletteGroup overworld_animated; // Animated colors (7 colors each)
PaletteGroup hud; // HUD graphics (32 colors each)
PaletteGroup global_sprites; // Sprite palettes (60 colors each)
PaletteGroup armors; // Armor colors (15 colors each)
PaletteGroup swords; // Sword colors (3 colors each)
PaletteGroup shields; // Shield colors (4 colors each)
PaletteGroup sprites_aux1; // Auxiliary sprite palette 1 (7 colors each)
PaletteGroup sprites_aux2; // Auxiliary sprite palette 2 (7 colors each)
PaletteGroup sprites_aux3; // Auxiliary sprite palette 3 (7 colors each)
PaletteGroup dungeon_main; // Dungeon palettes (90 colors each)
PaletteGroup grass; // Grass colors (special handling)
PaletteGroup object_3d; // 3D object palettes (8 colors each)
PaletteGroup overworld_mini_map; // Mini-map palettes (128 colors each)
};
```
#### 3. Color Representations in Code
- **SNES 15-bit (`uint16_t`)**: On-disk format `0bbbbbgggggrrrrr`; store raw ROM
words or write back with `ConvertRgbToSnes`.
- **`gfx::snes_color` struct**: Expands each channel to 0-255 for arithmetic
without floating point; use in converters and palette math.
- **`gfx::SnesColor` class**: High-level wrapper retaining the original SNES
value, a `snes_color`, and an ImVec4. Its `rgb()` accessor purposely returns
0-255 components—run the helper converters (e.g., `ConvertSnesColorToImVec4`)
before handing colors to ImGui widgets that expect 0.0-1.0 floats.
### Dungeon Palette System
#### 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)
#### Usage
```cpp
// Loading a dungeon palette
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)
// IMPORTANT: Use operator[] not palette() method!
auto palette = dungeon_pal_group[palette_id]; // Returns reference
// NOT: auto palette = dungeon_pal_group.palette(palette_id); // Returns copy!
```
#### Color Distribution (90 colors)
The 90 colors are typically distributed as:
- **BG1 Palette** (Background Layer 1): First 8-16 subpalettes
- **BG2 Palette** (Background Layer 2): Next 8-16 subpalettes
- **Sprite Palettes**: Remaining colors (handled separately)
Each "subpalette" is 16 colors (one SNES palette unit).
### Overworld Palette System
#### Structure
- **Main Overworld**: 35 colors per palette
- **Auxiliary**: 21 colors per palette
- **Animated**: 7 colors per palette (for water, lava effects)
#### 3BPP Graphics and Left/Right Palettes
Overworld graphics use 3BPP (3 bits per pixel) format:
- **8 colors per tile** (2^3 = 8)
- **Left Side**: Uses palette 0-7
- **Right Side**: Uses palette 8-15
When decompressing 3BPP graphics:
```cpp
// Palette assignment for 3BPP overworld tiles
if (tile_position < half_screen_width) {
// Left side of screen
tile_palette_offset = 0; // Use colors 0-7
} else {
// Right side of screen
tile_palette_offset = 8; // Use colors 8-15
}
```
### Common Issues and Solutions
#### Issue 1: Empty Palette
**Symptom**: "Palette size: 0 colors"
**Cause**: Using `palette()` method instead of `operator[]`
**Solution**:
```cpp
// WRONG:
auto palette = group.palette(id); // Returns copy, may be empty
// CORRECT:
auto palette = group[id]; // Returns reference
```
#### Issue 2: Bitmap Corruption
**Symptom**: Graphics render only in top portion of image
**Cause**: Wrong depth parameter in `CreateAndRenderBitmap`
**Solution**:
```cpp
// WRONG:
CreateAndRenderBitmap(0x200, 0x200, 0x200, data, bitmap, palette);
// depth ^^^^ should be 8!
// CORRECT:
CreateAndRenderBitmap(0x200, 0x200, 8, data, bitmap, palette);
// width, height, depth=8 bits
```
### Transparency and Conversion Best Practices
- Preserve ROM palette words exactly as read; hardware enforces transparency on
index0 so we no longer call `set_transparent(true)` while loading.
- Apply transparency only at render time via `SetPaletteWithTransparent()` for
3BPP sub-palettes or `SetPalette()` for full 256-color assets.
- `SnesColor::rgb()` yields components in 0-255 space; convert to ImGuis
expected 0.0-1.0 floats with the helper functions instead of manual divides.
- Use the provided conversion helpers (`ConvertSnesToRgb`, `ImVec4ToSnesColor`,
`SnesTo8bppColor`) to prevent rounding mistakes and alpha bugs.
```cpp
ImVec4 rgb_255 = snes_color.rgb();
ImVec4 display = ConvertSnesColorToImVec4(snes_color);
ImGui::ColorButton("color", display);
```
#### Issue 3: ROM Not Loaded in Preview
**Symptom**: "ROM not loaded" error in emulator preview
**Cause**: Initializing before ROM is set
**Solution**:
```cpp
// Initialize emulator preview AFTER ROM is loaded and set
void Load() {
// ... load ROM data ...
// ... set up other components ...
// NOW initialize emulator preview with loaded ROM
object_emulator_preview_.Initialize(rom_);
}
```
### Palette Editor Integration
#### Key Functions for UI
```cpp
// Reading a color from ROM
absl::StatusOr<uint16_t> ReadColorFromRom(uint32_t address, const uint8_t* rom);
// Converting SNES color to RGB
SnesColor color(snes_value); // snes_value is uint16_t
uint8_t r = color.red(); // 0-255 (converted from 0-31)
uint8_t g = color.green(); // 0-255
uint8_t b = color.blue(); // 0-255
// Writing color back to ROM
uint16_t snes_value = color.snes(); // Get 15-bit BGR555 value
rom->WriteByte(address, snes_value & 0xFF); // Low byte
rom->WriteByte(address + 1, (snes_value >> 8) & 0xFF); // High byte
```
#### Palette Widget Requirements
1. **Display**: Show colors in organized grids (16 colors per row for SNES standard)
2. **Selection**: Allow clicking to select a color
3. **Editing**: Provide RGB sliders (0-255) or color picker
4. **Conversion**: Auto-convert RGB (0-255) ↔ SNES (0-31) values
5. **Preview**: Show before/after comparison
6. **Save**: Write modified palette back to ROM
#### Palette UI Helpers
- `InlinePaletteSelector` renders a lightweight selection strip (no editing)
ideal for 8- or 16-color sub-palettes.
- `InlinePaletteEditor` supplies the full editing experience with ImGui color
pickers, context menus, and optional live preview toggles.
- `PopupPaletteEditor` fits in context menus or modals; it caps at 64 colors to
keep popups manageable.
- Legacy helpers such as `DisplayPalette()` remain for backward compatibility
but inherit the 32-color limit—prefer the new helpers for new UI.
-### Metadata-Driven Palette Application
`gfx::BitmapMetadata` tracks the source BPP, palette format, type string, and
expected color count. Set it immediately after creating a bitmap so later code
can make the right choice automatically:
```cpp
bitmap.metadata() = BitmapMetadata{/*source_bpp=*/3,
/*palette_format=*/1, // 0=full, 1=sub-palette
/*source_type=*/"graphics_sheet",
/*palette_colors=*/8};
bitmap.ApplyPaletteByMetadata(palette);
```
- `palette_format == 0` routes to `SetPalette()` and preserves every color
(Mode7, HUD assets, etc.).
- `palette_format == 1` routes to `SetPaletteWithTransparent()` and injects the
transparent color 0 for 3BPP workflows.
- Validation hooks help catch mismatched palette sizes before they hit SDL.
### Graphics Manager Integration
#### Sheet Palette Assignment
```cpp
// Assigning palette to graphics sheet
if (sheet_id > 115) {
// Sprite sheets use sprite palette
graphics_sheet.SetPaletteWithTransparent(
rom.palette_group().global_sprites[0], 0);
} else {
// Dungeon sheets use dungeon palette
graphics_sheet.SetPaletteWithTransparent(
rom.palette_group().dungeon_main[0], 0);
}
```
### Texture Synchronization and Regression Notes
- Call `bitmap.UpdateSurfacePixels()` after mutating `bitmap.mutable_data()` to
copy rendered bytes into the SDL surface before queuing texture creation or
updates.
- `Bitmap::ApplyStoredPalette()` now rebuilds an `SDL_Color` array sized to the
actual palette instead of forcing 256 entries—this fixes regressions where
8- or 16-color palettes were padded with opaque black.
- When updating SDL palette data yourself, mirror that pattern:
```cpp
std::vector<SDL_Color> colors(palette.size());
for (size_t i = 0; i < palette.size(); ++i) {
const auto& c = palette[i];
const ImVec4 rgb = c.rgb(); // 0-255 components
colors[i] = SDL_Color{static_cast<Uint8>(rgb.x),
static_cast<Uint8>(rgb.y),
static_cast<Uint8>(rgb.z),
c.is_transparent() ? 0 : 255};
}
SDL_SetPaletteColors(surface->format->palette, colors.data(), 0,
static_cast<int>(colors.size()));
```
### Best Practices
1. **Always use `operator[]` for palette access** - returns reference, not copy
2. **Validate palette IDs** before accessing:
```cpp
if (palette_id >= 0 && palette_id < group.size()) {
auto palette = group[palette_id];
}
```
3. **Use correct depth parameter** when creating bitmaps (usually 8 for indexed color)
4. **Initialize ROM-dependent components** only after ROM is fully loaded
5. **Cache palettes** when repeatedly accessing the same palette
6. **Update textures** after changing palettes (textures don't auto-update)
### User Workflow Tips
- Choose the widget that matches the task: selectors for choosing colors,
editors for full control, popups for contextual tweaks.
- The live preview toggle trades responsiveness for performance; disable it
while batch-editing large (64+ color) palettes.
- Right-click any swatch in the editor to copy the color as SNES hex, RGB
tuples, or HTML hex—useful when coordinating with external art tools.
- Remember hardware rules: palette index0 is always transparent and will not
display even if the stored value is non-zero.
- Keep ROM backups when performing large palette sweeps; palette groups are
shared across screens so a change can have multiple downstream effects.
### ROM Addresses (for reference)
```cpp
// From snes_palette.cc
constexpr uint32_t kOverworldPaletteMain = 0xDE6C8;
constexpr uint32_t kOverworldPaletteAux = 0xDE86C;
constexpr uint32_t kOverworldPaletteAnimated = 0xDE604;
constexpr uint32_t kHudPalettes = 0xDD218;
constexpr uint32_t kGlobalSpritesLW = 0xDD308;
constexpr uint32_t kArmorPalettes = 0xDD630;
constexpr uint32_t kSwordPalettes = 0xDD630;
constexpr uint32_t kShieldPalettes = 0xDD648;
constexpr uint32_t kSpritesPalettesAux1 = 0xDD39E;
constexpr uint32_t kSpritesPalettesAux2 = 0xDD446;
constexpr uint32_t kSpritesPalettesAux3 = 0xDD4E0;
constexpr uint32_t kDungeonMainPalettes = 0xDD734;
constexpr uint32_t kHardcodedGrassLW = 0x5FEA9;
constexpr uint32_t kTriforcePalette = 0xF4CD0;
constexpr uint32_t kOverworldMiniMapPalettes = 0x55B27;
```
## Graphics Sheet Palette Application
### Default Palette Assignment
Graphics sheets receive default palettes during ROM loading based on their index:
```cpp
// In LoadAllGraphicsData() - rom.cc
if (i < 113) {
// Sheets 0-112: Overworld/Dungeon graphics
graphics_sheets[i].SetPalette(rom.palette_group().dungeon_main[0]);
} else if (i < 128) {
// Sheets 113-127: Sprite graphics
graphics_sheets[i].SetPalette(rom.palette_group().sprites_aux1[0]);
} else {
// Sheets 128-222: Auxiliary/HUD graphics
graphics_sheets[i].SetPalette(rom.palette_group().hud.palette(0));
}
```
This ensures graphics are visible immediately after loading rather than appearing white.
### Palette Update Workflow
When changing a palette in any editor:
1. Apply the palette: `bitmap.SetPalette(new_palette)`
2. Notify Arena: `gfx::Arena::Get().NotifySheetModified(sheet_index)`
3. Changes propagate to all editors automatically
### Common Pitfalls
**Wrong Palette Access**:
```cpp
// WRONG - Returns copy, may be empty
auto palette = group.palette(id);
// CORRECT - Returns reference
auto palette = group[id];
```
**Missing Surface Update**:
```cpp
// WRONG - Only updates vector, not SDL surface
bitmap.mutable_data() = new_data;
// CORRECT - Updates both vector and surface
bitmap.set_data(new_data);
```

View File

@@ -0,0 +1,153 @@
# A1 - 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.
## 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/
├── unit/ # Unit tests for individual components
│ ├── core/ # Core functionality (asar, hex utils)
│ ├── cli/ # Command-line interface tests
│ ├── emu/ # Emulator component tests
│ ├── gfx/ # Graphics system (tiles, palettes)
│ ├── 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
│ ├── editor/ # Editor integration tests
│ └── zelda3/ # Game-specific integration tests (ROM-dependent)
├── e2e/ # End-to-end user workflow tests (GUI-driven)
│ ├── rom_dependent/ # E2E tests requiring a ROM
│ └── zscustomoverworld/ # ZSCustomOverworld upgrade E2E tests
├── benchmarks/ # Performance benchmarks
├── mocks/ # Mock objects for isolating tests
└── assets/ # Test assets (patches, data)
```
## 2. Test Categories
Based on the directory structure, tests fall into the following 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.
### 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.
### 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.
### 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.
## 3. Running Tests
> 💡 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.
### Using the Enhanced Test Runner (`yaze_test`)
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.
```bash
# First, build the test executable
cmake --build build_ai --target yaze_test
# Run all tests
./build_ai/bin/yaze_test
# Run only unit tests
./build_ai/bin/yaze_test --unit
# Run only integration tests
./build_ai/bin/yaze_test --integration
# Run E2E tests (requires a GUI)
./build_ai/bin/yaze_test --e2e --show-gui
# 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
```
### Using CTest and CMake Presets
For CI/CD or a more traditional workflow, you can use `ctest` with CMake presets.
```bash
# Configure a development build (enables ROM-dependent tests)
cmake --preset mac-dev -DYAZE_TEST_ROM_PATH=/path/to/your/zelda3.sfc
# Build the tests
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
```
## 4. Writing Tests
When adding new tests, place them in the appropriate directory based on their purpose and dependencies.
- **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`.
## 5. E2E GUI Testing Framework
The E2E framework uses `ImGuiTestEngine` to automate UI interactions.
### Architecture
- **`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`).
### 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 a specific E2E test by name
./build_ai/bin/yaze_test --show-gui --gtest_filter="*DungeonEditorSmokeTest"
```
### Widget Discovery and 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.
Refer to the `z3ed` agent guide for details on using commands like `z3ed gui discover`, `z3ed gui click`, and `z3ed agent test replay`.

View File

@@ -0,0 +1,372 @@
# Testing Quick Start - Before You Push
**Target Audience**: Developers contributing to yaze
**Goal**: Ensure your changes pass tests before pushing to remote
## The 5-Minute Pre-Push Checklist
Before pushing changes to the repository, run these commands to catch issues early:
### 1. Build Tests (30 seconds)
```bash
# Build the test executable
cmake --build build --target yaze_test
```
### 2. Run Fast Tests (<2 minutes)
```bash
# Run unit tests only (fastest)
./build/bin/yaze_test --unit
# Or run all stable tests (unit + non-ROM integration)
./build/bin/yaze_test
```
### 3. Platform-Specific Quick Check
**macOS**:
```bash
scripts/agents/smoke-build.sh mac-dbg yaze
```
**Linux**:
```bash
scripts/agents/smoke-build.sh lin-dbg yaze
```
**Windows (PowerShell)**:
```powershell
pwsh -File scripts/agents/windows-smoke-build.ps1 -Preset win-dbg -Target yaze
```
### 4. Check for Format Issues (optional but recommended)
```bash
# Check if code is formatted correctly
cmake --build build --target format-check
# Auto-fix formatting issues
cmake --build build --target format
```
## When to Run Full Test Suite
Run the **complete test suite** before pushing if:
- You modified core systems (ROM, graphics, editor base classes)
- You changed CMake configuration or build system
- You're preparing a pull request
- CI previously failed on your branch
### Full Test Suite Commands
```bash
# Run all tests (may take 5+ minutes)
./build/bin/yaze_test
# Include ROM-dependent tests (requires zelda3.sfc)
./build/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
# Run E2E GUI tests (headless)
./build/bin/yaze_test --e2e
# Run E2E with visible GUI (for debugging)
./build/bin/yaze_test --e2e --show-gui
```
## Common Test Failures and Fixes
### 1. Compilation Errors
**Symptom**: `cmake --build build --target yaze_test` fails
**Fix**:
```bash
# Clean and reconfigure
rm -rf build
cmake --preset mac-dbg # or lin-dbg, win-dbg
cmake --build build --target yaze_test
```
### 2. Unit Test Failures
**Symptom**: `./build/bin/yaze_test --unit` shows failures
**Fix**:
- Read the error message carefully
- Check if you broke contracts in modified code
- Verify test expectations match your changes
- Update tests if behavior change was intentional
### 3. ROM-Dependent Test Failures
**Symptom**: Tests fail with "ROM file not found"
**Fix**:
```bash
# Set environment variable
export YAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Or pass directly to test runner
./build/bin/yaze_test --rom-path /path/to/zelda3.sfc
```
### 4. E2E/GUI Test Failures
**Symptom**: E2E tests fail or hang
**Fix**:
- Check if SDL is initialized properly
- Run with `--show-gui` to see what's happening visually
- Verify ImGui Test Engine is enabled in build
- Check test logs for specific assertion failures
### 5. Platform-Specific Failures
**Symptom**: Tests pass locally but fail in CI
**Solution**:
1. Check which platform failed in CI logs
2. If Windows: ensure you're using the `win-*` preset
3. If Linux: check for case-sensitive path issues
4. If macOS: verify you're testing on compatible macOS version
## Test Categories Explained
| Category | What It Tests | When to Run | Duration |
|----------|---------------|-------------|----------|
| **Unit** | Individual functions/classes | Before every commit | <10s |
| **Integration** | Component interactions | Before every push | <30s |
| **E2E** | Full user workflows | Before PRs | 1-5min |
| **ROM-Dependent** | ROM data loading/saving | Before ROM changes | Variable |
## Recommended Workflows
### For Small Changes (typos, docs, minor fixes)
```bash
# Just build to verify no compile errors
cmake --build build --target yaze
```
### For Code Changes (new features, bug fixes)
```bash
# Build and run unit tests
cmake --build build --target yaze_test
./build/bin/yaze_test --unit
# If tests pass, push
git push
```
### For Core System Changes (ROM, graphics, editors)
```bash
# Run full test suite
cmake --build build --target yaze_test
./build/bin/yaze_test
# If all tests pass, push
git push
```
### For Pull Requests
```bash
# Run everything including ROM tests and E2E
./build/bin/yaze_test --rom-dependent --rom-path zelda3.sfc
./build/bin/yaze_test --e2e
# Check code formatting
cmake --build build --target format-check
# If all pass, create PR
git push origin feature-branch
```
## IDE Integration
### Visual Studio Code
Add this to `.vscode/tasks.json`:
```json
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Tests",
"type": "shell",
"command": "cmake --build build --target yaze_test",
"group": "build"
},
{
"label": "Run Unit Tests",
"type": "shell",
"command": "./build/bin/yaze_test --unit",
"group": "test",
"dependsOn": "Build Tests"
},
{
"label": "Run All Tests",
"type": "shell",
"command": "./build/bin/yaze_test",
"group": "test",
"dependsOn": "Build Tests"
}
]
}
```
Then use `Cmd/Ctrl+Shift+B` to build tests or `Cmd/Ctrl+Shift+P` → "Run Test Task" to run them.
### CLion / Visual Studio
Both IDEs auto-detect CTest and provide built-in test runners:
- **CLion**: Tests appear in "Test Explorer" panel
- **Visual Studio**: Use "Test Explorer" window
Configure test presets in `CMakePresets.json` (already configured in this project).
## Environment Variables
Customize test behavior with these environment variables:
```bash
# Path to test ROM file
export YAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Skip ROM-dependent tests entirely
export YAZE_SKIP_ROM_TESTS=1
# Enable UI tests (E2E)
export YAZE_ENABLE_UI_TESTS=1
# Verbose test output
export YAZE_TEST_VERBOSE=1
```
## Getting Test Output
### Verbose Test Output
```bash
# Show all test output (even passing tests)
./build/bin/yaze_test --gtest_output=verbose
# Show only failed test output
./build/bin/yaze_test --gtest_output=on_failure
```
### Specific Test Patterns
```bash
# Run only tests matching pattern
./build/bin/yaze_test --gtest_filter="*AsarWrapper*"
# Run tests in specific suite
./build/bin/yaze_test --gtest_filter="RomTest.*"
# Exclude specific tests
./build/bin/yaze_test --gtest_filter="-*SlowTest*"
```
### Repeat Tests for Flakiness
```bash
# Run tests 10 times to catch flakiness
./build/bin/yaze_test --gtest_repeat=10
# Stop on first failure
./build/bin/yaze_test --gtest_repeat=10 --gtest_break_on_failure
```
## CI/CD Testing
After pushing, CI will run tests on all platforms (Linux, macOS, Windows):
1. **Check CI status**: Look for green checkmark in GitHub
2. **If CI fails**: Click "Details" to see which platform/test failed
3. **Fix and push again**: CI re-runs automatically
**Pro tip**: Use remote workflow triggers to test in CI before pushing:
```bash
# Trigger CI remotely (requires gh CLI)
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.
## Advanced Topics
### Running Tests with CTest
```bash
# Run all stable tests via ctest
ctest --preset dev
# Run specific test suite
ctest -L unit
# Run with verbose output
ctest --preset dev --output-on-failure
# Run tests in parallel
ctest --preset dev -j8
```
### Debugging Failed Tests
```bash
# Run test under debugger (macOS/Linux)
lldb ./build/bin/yaze_test -- --gtest_filter="*FailingTest*"
# Run test under debugger (Windows)
devenv /debugexe ./build/bin/yaze_test.exe --gtest_filter="*FailingTest*"
```
### Writing New Tests
See [Testing Guide](testing-guide.md) for comprehensive guide on writing tests.
Quick template:
```cpp
#include <gtest/gtest.h>
#include "my_class.h"
namespace yaze {
namespace test {
TEST(MyClassTest, BasicFunctionality) {
MyClass obj;
EXPECT_TRUE(obj.DoSomething());
}
} // namespace test
} // namespace yaze
```
Add your test file to `test/CMakeLists.txt` in the appropriate suite.
## Help and Resources
- **Detailed Testing Guide**: [docs/public/developer/testing-guide.md](testing-guide.md)
- **Build Commands**: [docs/public/build/quick-reference.md](../build/quick-reference.md)
- **Testing Infrastructure**: [docs/internal/testing/README.md](../../internal/testing/README.md)
- **Troubleshooting**: [docs/public/build/troubleshooting.md](../build/troubleshooting.md)
## Questions?
1. Check [Testing Guide](testing-guide.md) for detailed explanations
2. Search existing issues: https://github.com/scawful/yaze/issues
3. Ask in discussions: https://github.com/scawful/yaze/discussions
---
**Remember**: Running tests before pushing saves time for everyone. A few minutes of local testing prevents hours of CI debugging.

View File

@@ -0,0 +1,287 @@
# Testing z3ed Without ROM Files
**Last Updated:** October 10, 2025
**Status:** Active
## Overview
The `z3ed` AI agent now supports **mock ROM mode** for testing without requiring actual ROM files. This is essential for:
- **CI/CD pipelines** - No ROM files can be committed to GitHub
- **Development testing** - Quick iterations without ROM dependencies
- **Contributors** - Test the agent without needing to provide ROMs
- **Automated testing** - Consistent, reproducible test environments
## How Mock ROM Mode Works
Mock ROM mode creates a minimal but valid ROM structure with:
- Proper SNES header (LoROM mapping, 1MB size)
- Zelda3 embedded labels (rooms, sprites, entrances, items, music, etc.)
- Resource label manager initialization
- No actual ROM data (tiles, graphics, maps remain empty)
This allows the AI agent to:
- Answer questions about room names, sprite IDs, entrance numbers
- Lookup labels and constants
- Test function calling and tool dispatch
- Validate agent logic without game data
## Usage
### Command Line Flag
Add `--mock-rom` to any `z3ed agent` command:
```bash
# Simple chat with mock ROM
z3ed agent simple-chat "What is room 5?" --mock-rom
# Test conversation with mock ROM
z3ed agent test-conversation --mock-rom
# AI provider testing
z3ed agent simple-chat "List all dungeons" --mock-rom --ai_provider=ollama
```
### Test Suite
The `agent_test_suite.sh` script now defaults to mock ROM mode:
```bash
# Run tests with mock ROM (default)
./scripts/agent_test_suite.sh ollama
# Or with Gemini
./scripts/agent_test_suite.sh gemini
# Override the Ollama model (CI uses qwen2.5-coder:0.5b)
OLLAMA_MODEL=qwen2.5-coder:0.5b ./scripts/agent_test_suite.sh ollama
```
To use a real ROM instead, edit the script:
```bash
USE_MOCK_ROM=false # At the top of agent_test_suite.sh
```
## What Works with Mock ROM
### Fully Supported
**Label Queries:**
- "What is room 5?" → "Tower of Hera - Moldorm Boss"
- "What sprites are in the game?" → Lists all 256 sprite names
- "What is entrance 0?" → "Link's House Main"
- "List all items" → Bow, Boomerang, Hookshot, etc.
**Resource Lookups:**
- Room names (296 rooms)
- Entrance names (133 entrances)
- Sprite names (256 sprites)
- Overlord names (14 overlords)
- Overworld map names (160 maps)
- Item names
- Music track names
- Graphics sheet names
**AI Testing:**
- Function calling / tool dispatch
- Natural language understanding
- Error handling
- Tool output parsing
- Multi-turn conversations
### Limited Support
**Queries Requiring Data:**
- "What tiles are used in room 5?" → No tile data in mock ROM
- "Show me the palette for map 0" → No palette data
- "What's at coordinate X,Y?" → No map data
- "Export graphics from dungeon 1" → No graphics data
These queries will either return empty results or errors indicating no ROM data is available.
### Not Supported
**Operations That Modify ROM:**
- Editing tiles
- Changing palettes
- Modifying sprites
- Patching ROM data
## Testing Strategy
### For Agent Logic
Use **mock ROM** for testing:
- Function calling mechanisms
- Tool dispatch and routing
- Natural language understanding
- Error handling
- Label resolution
- Resource lookups
### For ROM Operations
Use **real ROM** for testing:
- Tile editing
- Graphics manipulation
- Palette modifications
- Data extraction
- ROM patching
## CI/CD Integration
### GitHub Actions Example
```yaml
name: Test z3ed Agent
on: [push, pull_request]
jobs:
test-agent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Dependencies
run: |
# Install ollama if testing local models
curl -fsSL https://ollama.ai/install.sh | sh
ollama pull qwen2.5-coder
- name: Build z3ed
run: |
cmake -B build_test
cmake --build build_test --parallel
- name: Run Agent Tests (Mock ROM)
run: |
./scripts/agent_test_suite.sh ollama
env:
# Or use Gemini with API key
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
```
## Embedded Labels Reference
Mock ROM includes all these labels from `zelda3::Zelda3Labels`:
| Resource Type | Count | Example |
|--------------|-------|---------|
| Rooms | 296 | "Sewer - Throne Room" |
| Entrances | 133 | "Link's House Main" |
| Sprites | 256 | "Moldorm (Boss)" |
| Overlords | 14 | "Overlord - Agahnim's Barrier" |
| Overworld Maps | 160 | "Light World - Hyrule Castle" |
| Items | 64+ | "Bow", "Boomerang", "Hookshot" |
| Music Tracks | 64+ | "Title Theme", "Overworld", "Dark World" |
| Graphics Sheets | 128+ | "Link Sprites", "Enemy Pack 1" |
See `src/zelda3/zelda3_labels.h` for the complete list.
## Troubleshooting
### "No ROM loaded" error
Make sure you're using the `--mock-rom` flag:
```bash
# Wrong
z3ed agent simple-chat "test"
# Correct
z3ed agent simple-chat "test" --mock-rom
```
### Mock ROM fails to initialize
Check the error message. Common issues:
- Build system didn't include `mock_rom.cc`
- Missing `zelda3_labels.cc` in build
- Linker errors with resource labels
### Agent returns empty/wrong results
Remember: Mock ROM has **labels only**, no actual game data.
Queries like "What tiles are in room 5?" won't work because there's no tile data.
Use queries about labels and IDs instead: "What is the name of room 5?"
## Development
### Adding New Labels
To add new label types to mock ROM:
1. **Add to `zelda3_labels.h`:**
```cpp
static const std::vector<std::string>& GetNewResourceNames();
```
2. **Implement in `zelda3_labels.cc`:**
```cpp
const std::vector<std::string>& Zelda3Labels::GetNewResourceNames() {
static std::vector<std::string> names = {"Item1", "Item2", ...};
return names;
}
```
3. **Add to `ToResourceLabels()`:**
```cpp
const auto& new_resources = GetNewResourceNames();
for (size_t i = 0; i < new_resources.size(); ++i) {
labels["new_resource"][std::to_string(i)] = new_resources[i];
}
```
4. **Rebuild:**
```bash
cmake --build build --parallel
```
### Testing Mock ROM Directly
```cpp
#include "cli/handlers/mock_rom.h"
Rom rom;
auto status = InitializeMockRom(rom);
if (status.ok()) {
// ROM is ready with all labels
auto* label_mgr = rom.resource_label();
std::string room_name = label_mgr->GetLabel("room", "5");
// room_name == "Tower of Hera - Moldorm Boss"
}
```
## Best Practices
### DO
- Use mock ROM for CI/CD and automated tests
- Use mock ROM for agent logic development
- Use mock ROM when contributing (no ROM files needed)
- Test with real ROM before releasing features
- Document which features require real ROM data
### DON'T ❌
- Commit ROM files to Git (legal issues)
- Assume mock ROM has actual game data
- Use mock ROM for testing data extraction
- Skip real ROM testing entirely
## Related Documentation
- [z3ed CLI Guide](../usage/z3ed-cli.md) - Main agent and CLI documentation
- [Testing Guide](testing-guide.md) - General testing strategy
- [API Reference](api-reference.md) - ROM API documentation
---
**Implementation Status:** Complete
**Since Version:** v0.3.3
**Files:**
- `src/cli/handlers/mock_rom.h`
- `src/cli/handlers/mock_rom.cc`
- `src/cli/flags.cc` (--mock-rom flag)
- `scripts/agent_test_suite.sh` (updated)

View File

@@ -0,0 +1,362 @@
# Tile16 Editor Palette System
**Status: Work in Progress** - Documentation for the ongoing Tile16 Editor palette system redesign, implementation, and refactoring improvements.
## Executive Summary
The tile16 editor palette system is undergoing a complete redesign to resolve critical crashes and ensure proper color alignment between the tile16 editor and overworld display. Significant progress has been made addressing fundamental architectural issues with palette mapping, implementing crash fixes, and creating an improved three-column layout. However, palette handling for the tile8 source canvas and palette button functionality remain works in progress.
## Problem Analysis
### Critical Issues Identified
1. **SIGBUS Crash in SnesColor::rgb()**
- **Root Cause**: `SetPaletteWithTransparent()` method used incorrect `index * 7` calculation
- **Impact**: Crashes when selecting palette buttons 4-7 due to out-of-bounds memory access
- **Location**: `bitmap.cc:371` - `start_index = index * 7`
2. **Fundamental Architecture Misunderstanding**
- **Root Cause**: Attempting to use `SetPaletteWithTransparent()` instead of `SetPalette()`
- **Impact**: Broke the 256-color palette system that the overworld relies on
- **Reality**: Overworld system uses complete 256-color palettes with `SetPalette()`
3. **Color Mapping Misunderstanding**
- **Root Cause**: Confusion about how color mapping works in the graphics system
- **Impact**: Incorrect palette handling in tile16 editor
- **Reality**: Pixel data already contains correct color indices for the 256-color palette
## Solution Architecture
### Core Design Principles
1. **Use Same Palette System as Overworld**: Use `SetPalette()` with complete 256-color palette
2. **Direct Color Mapping**: The pixel data in graphics already contains correct color indices that map to the 256-color palette
3. **Proper Bounds Checking**: Prevent out-of-bounds memory access in palette operations
4. **Consistent Architecture**: Match overworld editor's palette handling exactly
### 256-Color Overworld Palette Structure
Based on analysis of `SetColorsPalette()` in `overworld_map.cc`:
```
Row 0-1: HUD palette (32 colors) - Slots 0-31
Row 2-6: MAIN palette (35 colors) - Slots 32-87 (rows 2-6, cols 1-7)
Row 7: ANIMATED palette (7 colors) - Slots 112-118 (row 7, cols 1-7)
Row 8: Sprite AUX1 + AUX3 (14 colors) - Slots 128-141
Row 9-12: Global sprites (60 colors) - Slots 144-203
Row 13: Sprite palette 1 (7 colors) - Slots 208-214
Row 14: Sprite palette 2 (7 colors) - Slots 224-230
Row 15: Armor palettes (15 colors) - Slots 240-254
Right side (cols 9-15):
Row 2-4: AUX1 palette (21 colors) - Slots 136-151
Row 5-7: AUX2 palette (21 colors) - Slots 152-167
```
### Sheet-to-Palette Mapping
```
Sheet 0,3,4: → AUX1 region (slots 136-151)
Sheet 5,6: → AUX2 region (slots 152-167)
Sheet 1,2: → MAIN region (slots 32-87)
Sheet 7: → ANIMATED region (slots 112-118)
```
### Palette Button Mapping
| Button | Sheet 0,3,4 (AUX1) | Sheet 1,2 (MAIN) | Sheet 5,6 (AUX2) | Sheet 7 (ANIMATED) |
|--------|---------------------|-------------------|-------------------|---------------------|
| 0 | 136 | 32 | 152 | 112 |
| 1 | 138 | 39 | 154 | 113 |
| 2 | 140 | 46 | 156 | 114 |
| 3 | 142 | 53 | 158 | 115 |
| 4 | 144 | 60 | 160 | 116 |
| 5 | 146 | 67 | 162 | 117 |
| 6 | 148 | 74 | 164 | 118 |
| 7 | 150 | 81 | 166 | 119 |
## Implementation Details
### 1. Fixed SetPaletteWithTransparent Method
**File**: `src/app/gfx/bitmap.cc`
**Before**:
```cpp
auto start_index = index * 7; // WRONG: Creates invalid indices for buttons 4-7
```
**After**:
```cpp
// Extract 8-color sub-palette starting at the specified index
// Always start with transparent color (index 0)
colors.push_back(ImVec4(0, 0, 0, 0));
// Extract up to 7 colors from the palette starting at index
for (size_t i = 0; i < 7 && (index + i) < palette.size(); ++i) {
auto &pal_color = palette[index + i];
colors.push_back(pal_color.rgb());
}
```
### 2. Corrected Tile16 Editor Palette System
**File**: `src/app/editor/overworld/tile16_editor.cc`
**Before**:
```cpp
// WRONG: Trying to extract 8-color sub-palettes
current_gfx_individual_[i].SetPaletteWithTransparent(display_palette, base_palette_slot, 8);
```
**After**:
```cpp
// CORRECT: Use complete 256-color palette (same as overworld system)
// The pixel data already contains correct color indices for the 256-color palette
current_gfx_individual_[i].SetPalette(display_palette);
```
### 3. Palette Coordination Flow
```
Overworld System:
ProcessGraphicsBuffer() → adds 0x88 offset to sheets 0,3,4,5
BuildTiles16Gfx() → combines with palette info
current_gfx_bmp_ → contains properly offset pixel data
Tile16 Editor:
Initialize() → receives current_gfx_bmp_ with correct data
LoadTile8() → extracts tiles with existing pixel values
SetPalette() → applies complete 256-color palette
Display → correct colors shown automatically
```
## UI/UX Refactoring
### New Three-Column Layout
The tile16 editor layout was completely restructured into a unified 3-column table for better space utilization:
**Column 1: Tile16 Blockset** (35% width)
- Complete 512-tile blockset display
- Integrated zoom slider (0.5x - 4.0x)
- Zoom in/out buttons for quick adjustments
- Click to select tiles for editing
- Full vertical scrolling support
**Column 2: Tile8 Source Tileset** (35% width)
- All 8x8 source tiles
- Palette group selector (OW Main, OW Aux, etc.)
- Integrated zoom slider (1.0x - 8.0x)
- Click to select tiles for painting
- Full vertical scrolling support
**Column 3: Editor & Controls** (30% width)
- Tile16 editor canvas with dynamic zoom (2.0x - 8.0x)
- Canvas size adjusts automatically with zoom level
- All controls in compact vertical layout:
- Tile8 preview and ID
- Flip controls (X, Y, Priority)
- Palette selector (8 buttons)
- Action buttons (Clear, Copy, Paste)
- Save/Discard changes
- Undo button
- Advanced controls (collapsible)
- Debug information (collapsible)
**Benefits**:
- Better space utilization - all three main components visible simultaneously
- Improved workflow - blockset, source tiles, and editor all in one view
- Resizable columns allow users to adjust layout to preference
### Canvas Context Menu Fixes
**Problem**: The "Advanced Canvas Properties" and "Scaling Controls" popup dialogs were not appearing when selected from the context menu.
**Solution**:
- Changed popup windows from modal popups to regular windows
- Added boolean flags `show_advanced_properties_` and `show_scaling_controls_` to track window state
- Windows now appear as floating windows instead of modal dialogs
- Added public accessor methods:
- `OpenAdvancedProperties()`
- `OpenScalingControls()`
- `CloseAdvancedProperties()`
- `CloseScalingControls()`
**Files Modified**:
- `src/app/gui/canvas.cc` - Updated popup implementation
- `src/app/gui/canvas.h` - Added boolean flags and accessor methods
### Dynamic Zoom Controls
Each canvas now has independent zoom control with real-time feedback:
**Tile16 Blockset Zoom**:
- Range: 0.5x to 4.0x
- Slider control with +/- buttons
- Properly scales mouse coordinate calculations
**Tile8 Source Zoom**:
- Range: 1.0x to 8.0x
- Essential for viewing small pixel details
- Correctly accounts for zoom in tile selection
**Tile16 Editor Zoom**:
- Range: 2.0x to 8.0x
- Canvas size dynamically adjusts with zoom
- Grid scales proportionally
- Mouse coordinates properly transformed
**Implementation Details**:
```cpp
// Mouse coordinate calculations account for dynamic zoom
int tile_x = static_cast<int>(mouse_pos.x / (8 * tile8_zoom));
// Grid drawing scaled with zoom
tile16_edit_canvas_.DrawGrid(8.0F * tile16_zoom / 4.0F);
// Canvas display size calculated dynamically
float canvas_display_size = 16 * tile16_zoom + 4;
```
## Testing Protocol
### Crash Prevention Testing
1. **Load ROM** and open tile16 editor
2. **Test all palette buttons 0-7** - should not crash
3. **Verify color display** - should match overworld appearance
4. **Test different graphics sheets** - should use appropriate palette regions
### Color Alignment Testing
1. **Select tile16 in overworld** - note colors displayed
2. **Open tile16 editor** - load same tile
3. **Test palette buttons 0-7** - colors should match overworld
4. **Verify sheet-specific behavior** - different sheets should show different colors
### UI/UX Testing
1. **Canvas Popups**: Right-click on each canvas → verify "Advanced Properties" and "Scaling Controls" open correctly
2. **Zoom Controls**: Test each zoom slider and button for all three canvases
3. **Tile Selection**: Verify clicking works at various zoom levels for blockset, tile8 source, and tile16 editor
4. **Layout Responsiveness**: Resize window and columns to verify proper behavior
5. **Workflow**: Test complete tile editing workflow from selection to save
## Error Handling
### Bounds Checking
- **Palette Index Validation**: Ensure palette indices don't exceed palette size
- **Sheet Index Validation**: Ensure sheet indices are within valid range (0-7)
- **Surface Validation**: Ensure SDL surface exists before palette operations
### Fallback Mechanisms
- **Default Palette**: Use MAIN region if sheet detection fails
- **Safe Indices**: Clamp palette indices to valid ranges
- **Error Logging**: Comprehensive logging for debugging
## Debug Information Display
The debug panel (collapsible by default) shows:
1. **Current State**: Tile16 ID, Tile8 ID, selected palette button
2. **Mapping Info**: Sheet index, actual palette slot
3. **Reference Table**: Complete button-to-slot mapping for all sheets
4. **Color Preview**: Visual display of actual colors being used
## Known Issues and Ongoing Work
### Completed Items
- **No Crashes**: Fixed SIGBUS errors - palette buttons 0-7 work without crashing
- **Three-Column Layout**: Unified layout with blockset, tile8 source, and editor
- **Dynamic Zoom Controls**: Independent zoom for all three canvases
- **Canvas Popup Fixes**: Advanced properties and scaling controls now working
- **Stable Memory**: No memory leaks or corruption
- **Code Architecture**: Proper bounds checking and error handling
### Active Issues Warning:
**1. Tile8 Source Canvas Palette Issues**
- **Problem**: The tile8 source canvas (displaying current area graphics) does not show correct colors
- **Impact**: Source tiles appear with incorrect palette, making it difficult to preview how tiles will look
- **Root Cause**: Area graphics not receiving proper palette application
- **Status**: Under investigation - may be related to graphics buffer processing
**2. Palette Button Functionality**
- **Problem**: Palette buttons (0-7) do not properly update the displayed colors
- **Impact**: Cannot switch between different palette groups as intended
- **Expected Behavior**: Clicking palette buttons should change the active palette for tile8 graphics
- **Actual Behavior**: Button clicks do not trigger palette updates correctly
- **Status**: Needs implementation of proper palette switching logic
**3. Color Alignment Between Canvases**
- **Problem**: Colors in tile8 source canvas don't match tile16 editor canvas
- **Impact**: Inconsistent visual feedback during tile editing
- **Related To**: Issues #1 and #2 above
- **Status**: Blocked by palette button functionality
### Current Status Summary
| Component | Status | Notes |
|-----------|--------|-------|
| Crash Prevention | Complete | No SIGBUS errors |
| Three-Column Layout | Complete | Fully functional |
| Zoom Controls | Complete | All canvases working |
| Tile16 Editor Palette | Complete | Shows correct colors |
| Tile8 Source Palette | Warning: In Progress | Incorrect colors displayed |
| Palette Button Updates | Warning: In Progress | Not updating palettes |
| Sheet-Aware Logic | Warning: Partial | Foundation in place, needs fixes |
| Overall Color System | Warning: In Progress | Ongoing development |
### Future Enhancements
1. **Zoom Presets**: Add preset buttons (1x, 2x, 4x) for quick zoom levels
2. **Zoom Sync**: Option to sync zoom levels across related canvases
3. **Canvas Layout Persistence**: Save user's preferred column widths and zoom levels
4. **Keyboard Shortcuts**: Add +/- keys for zoom control
5. **Mouse Wheel Zoom**: Ctrl+Wheel to zoom canvases
6. **Performance Optimization**: Cache palette calculations for frequently used combinations
7. **Extended Debug Tools**: Add pixel-level color comparison tools
8. **Palette Preview**: Real-time preview of palette changes before applying
9. **Batch Operations**: Apply palette changes to multiple tiles simultaneously
## Maintenance Notes
1. **Palette Structure Changes**: Update `GetActualPaletteSlot()` if overworld palette structure changes
2. **Sheet Detection**: Verify `GetSheetIndexForTile8()` logic if graphics organization changes
3. **ProcessGraphicsBuffer**: Monitor for changes in pixel data offset logic
4. **Static Zoom Variables**: User preferences preserved across sessions
---
## Next Steps
### Immediate Priorities
1. **Fix Tile8 Source Canvas Palette Application**
- Investigate graphics buffer processing for area graphics
- Ensure consistent palette application across all graphics sources
- Test with different area graphics combinations
2. **Implement Palette Button Update Logic**
- Add proper event handling for palette button clicks
- Trigger palette refresh for tile8 source canvas
- Update visual feedback to show active palette
3. **Verify Color Consistency**
- Ensure tile8 source colors match tile16 editor
- Test sheet-specific palette regions
- Validate color mapping for all graphics sheets
### Investigation Areas
- Review `current_gfx_individual_` palette application
- Check palette group management for tile8 source
- Verify graphics buffer offset logic for area graphics
- Test palette switching across different overworld areas
---
*Development Status: January 2025*
*Status: Partial implementation - UI complete, palette system in progress*
*Not yet ready for production use - active development ongoing*