62 Commits

Author SHA1 Message Date
scawful
4944066fa7 backend-infra-engineer: fix release build failures 2025-12-29 07:41:51 -06:00
scawful
cfed3c1c5a backend-infra-engineer: log release status 2025-12-29 01:32:32 -06:00
scawful
4a517a384c backend-infra-engineer: fix release build errors 2025-12-29 01:31:39 -06:00
scawful
675d7bb4f9 backend-infra-engineer: log grpc include fix 2025-12-28 23:50:25 -06:00
scawful
1b167061a1 backend-infra-engineer: fix grpc proto includes 2025-12-28 23:49:16 -06:00
scawful
d4b2ce50a5 backend-infra-engineer: log release rerun 2025-12-28 19:02:33 -06:00
scawful
9b3597e981 backend-infra-engineer: fix grpc link + layout preset alias 2025-12-28 19:00:53 -06:00
scawful
43d622ee08 backend-infra-engineer: log formatting CI rerun 2025-12-28 18:40:39 -06:00
scawful
607f6aa1e8 backend-infra-engineer: format welcome screen 2025-12-28 18:37:54 -06:00
scawful
f6a92669da backend-infra-engineer: log CI status 2025-12-28 18:30:39 -06:00
scawful
d9a6de9c43 backend-infra-engineer: log CI rerun 2025-12-28 18:27:44 -06:00
scawful
b45d2ba97a backend-infra-engineer: log usdasm submodule fix 2025-12-28 18:26:35 -06:00
scawful
b354e6f3d7 backend-infra-engineer: drop usdasm submodule 2025-12-28 18:25:07 -06:00
scawful
b0906baa6d backend-infra-engineer: record release failure 2025-12-28 18:20:45 -06:00
scawful
63a36ea295 backend-infra-engineer: log release run status 2025-12-28 18:18:53 -06:00
scawful
26b35642b4 backend-infra-engineer: finalize v0.5.0 branding 2025-12-28 18:16:27 -06:00
scawful
8b45b16431 backend-infra-engineer: update release rollout notes 2025-12-28 18:04:53 -06:00
scawful
583ec35475 backend-infra-engineer: log history compaction rollout 2025-12-28 18:02:51 -06:00
scawful
c3fa3f6074 imgui-frontend-engineer: ignore Xcode user data 2025-12-28 17:42:35 -06:00
scawful
eed51b8d17 imgui-frontend-engineer: refine editor card text rendering 2025-12-28 17:42:09 -06:00
scawful
7f28b377dd imgui-frontend-engineer: stabilize editor card layout 2025-12-28 10:59:18 -06:00
scawful
51aac88868 imgui-frontend-engineer: gate ROM tests on availability 2025-12-28 10:58:42 -06:00
scawful
79bfcf8306 imgui-frontend-engineer: harden C API room loader 2025-12-28 10:58:32 -06:00
scawful
65c4fe13cd imgui-frontend-engineer: simplify nightly workflow 2025-12-28 10:58:24 -06:00
scawful
636229c097 imgui-frontend-engineer: add build helper scripts 2025-12-28 10:58:16 -06:00
scawful
c9df4372d7 imgui-frontend-engineer: add iOS platform scaffolding 2025-12-28 10:57:53 -06:00
scawful
954c612f69 imgui-frontend-engineer: refactor gRPC agent services 2025-12-28 10:52:15 -06:00
scawful
a65fc1d975 imgui-frontend-engineer: refine agent editor wiring 2025-12-28 10:52:01 -06:00
scawful
14a3084c7f imgui-frontend-engineer: refresh editor UI layout 2025-12-28 10:51:47 -06:00
scawful
8d011bf094 imgui-frontend-engineer: update docs and plans 2025-12-28 10:51:31 -06:00
scawful
403590585d imgui-frontend-engineer: fix palette slicing for graphics sheets 2025-12-28 10:18:56 -06:00
scawful
b78e167105 imgui-frontend-engineer: move layout designer into lab target 2025-12-28 09:56:41 -06:00
scawful
d002f5556d imgui-frontend-engineer: fix palette conversion and tile16 tint 2025-12-28 09:51:00 -06:00
scawful
a256b589ac imgui-frontend-engineer: fix merge blockers in graphics/dungeon map 2025-12-28 09:50:46 -06:00
scawful
07291a0eac docs(internal): update coordination board notes 2025-12-22 14:56:47 -05:00
scawful
1d297d5228 test(overworld): align integration expectations 2025-12-22 14:56:31 -05:00
scawful
326594f3e6 fix(dungeon-map): rewrite save layout 2025-12-22 14:56:17 -05:00
scawful
d5e06e943f fix(dungeon): align object drawing and tests 2025-12-22 14:55:59 -05:00
scawful
26ce12cd6f fix(palette): align ROM backing and tests 2025-12-22 14:55:31 -05:00
scawful
c8ec329f14 fix(message): guard parsing and command args 2025-12-22 14:55:14 -05:00
scawful
2fd740e529 fix(api): return room count in yaze_load_all_rooms 2025-12-22 14:53:47 -05:00
scawful
572bfe5df7 test: stabilize rendering and benchmarks 2025-12-22 14:53:35 -05:00
scawful
2c72709003 fix(asar): improve CLI patch handling 2025-12-22 14:52:45 -05:00
scawful
face88a940 cli: harden tool output and tests 2025-12-22 14:52:33 -05:00
scawful
76512daba2 chore: add usdasm fetch helper 2025-12-22 14:51:38 -05:00
scawful
319e903f24 docs: update ROM test paths and envs 2025-12-22 14:50:57 -05:00
scawful
42ae359abc test(rom): add role-based ROM selection 2025-12-22 14:49:04 -05:00
scawful
df866b3f7f build: refresh toolchain and dependency wiring 2025-12-22 14:45:31 -05:00
scawful
7b72b2e3d4 test(zsco): relax upgrade expectations 2025-12-22 14:30:09 -05:00
scawful
3c9c3455af test(screen): avoid boss room in save coverage 2025-12-22 14:30:01 -05:00
scawful
e733bc78d6 fix(overworld): stabilize palette and tilemap saves 2025-12-22 14:29:43 -05:00
scawful
ffc3ddd854 fix(gfx): convert indexed sheets to SNES planar 2025-12-22 14:29:26 -05:00
scawful
ea4e1873de build: standardize build dir policy 2025-12-22 14:28:55 -05:00
scawful
9333498538 test(asar): fix ROM patch fixtures 2025-12-22 14:28:42 -05:00
scawful
344ef39d66 fix(emu): load GameData for render service 2025-12-22 14:28:35 -05:00
scawful
62734f3727 deps: move json/httplib to CPM 2025-12-22 14:28:24 -05:00
scawful
ca71140a88 backend-infra-engineer: Fix Homebrew LLVM build plumbing 2025-12-21 19:41:41 -05:00
scawful
e674fc3214 backend-infra-engineer: Log pre-0.2.2 snapshotting 2025-12-21 19:21:33 -05:00
scawful
5c4cd57ff8 backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup) 2025-12-22 00:20:49 +00:00
scawful
2934c82b75 backend-infra-engineer: Release v0.3.9-hotfix7 snapshot 2025-11-23 13:37:10 -05:00
scawful
c8289bffda backend-infra-engineer: Release v0.3.8 snapshot 2025-11-22 02:10:38 -05:00
scawful
476dd1cd1c backend-infra-engineer: Release v0.3.3 snapshot 2025-11-21 21:35:50 -05:00
1651 changed files with 324223 additions and 59635 deletions

58
.clangd
View File

@@ -1,8 +1,34 @@
# YAZE ROM Editor - clangd configuration
# Optimized for C++23, Google style, Abseil/gRPC, and ROM hacking workflows
CompileFlags: CompileFlags:
CompilationDatabase: build CompilationDatabase: build
Add:
# Additional include paths for better IntelliSense
- -I/Users/scawful/Code/yaze/src
- -I/Users/scawful/Code/yaze/src/lib
- -I/Users/scawful/Code/yaze/src/lib/imgui
- -I/Users/scawful/Code/yaze/src/lib/imgui/backends
- -I/Users/scawful/Code/yaze/src/lib/SDL/include
- -I/Users/scawful/Code/yaze/incl
- -I/Users/scawful/Code/yaze/third_party
- -I/Users/scawful/Code/yaze/third_party/json/include
- -I/Users/scawful/Code/yaze/third_party/httplib
- -I/Users/scawful/Code/yaze/build
- -I/Users/scawful/Code/yaze/build/src/lib/SDL/include
- -I/Users/scawful/Code/yaze/build/src/lib/SDL/include-config-Debug
# Feature flags
- -DYAZE_WITH_GRPC
- -DYAZE_WITH_JSON
- -DZ3ED_AI
# Standard library
- -std=c++23
# Platform detection
- -DMACOS
Remove: Remove:
- -mllvm - -mllvm
- -xclang - -xclang
- -w # Remove warning suppression for better diagnostics
Index: Index:
Background: Build Background: Build
@@ -18,20 +44,44 @@ Hover:
Diagnostics: Diagnostics:
MissingIncludes: Strict MissingIncludes: Strict
UnusedIncludes: Strict
ClangTidy: ClangTidy:
Add: Add:
# Core checks for ROM hacking software
- performance-* - performance-*
- bugprone-* - bugprone-*
- readability-* - readability-*
- modernize-* - modernize-*
- misc-*
- clang-analyzer-*
# Abseil-specific checks
- abseil-*
# Google C++ style enforcement
- google-*
Remove: Remove:
# - readability-* # Disable overly strict checks for ROM hacking workflow
# - modernize-*
- modernize-use-trailing-return-type - modernize-use-trailing-return-type
- readability-braces-around-statements - readability-braces-around-statements
- readability-magic-numbers - readability-magic-numbers # ROM hacking uses many magic numbers
- readability-implicit-bool-conversion - readability-implicit-bool-conversion
- readability-identifier-naming - readability-identifier-naming # Allow ROM-specific naming
- readability-function-cognitive-complexity - readability-function-cognitive-complexity
- readability-function-size - readability-function-size
- readability-uppercase-literal-suffix - readability-uppercase-literal-suffix
# Disable checks that conflict with ROM data structures
- modernize-use-auto # ROM hacking needs explicit types
- modernize-avoid-c-arrays # ROM data often uses C arrays
- bugprone-easily-swappable-parameters
- bugprone-exception-escape
- bugprone-narrowing-conversions # ROM data often requires narrowing
- bugprone-implicit-widening-of-multiplication-result
- misc-no-recursion
- misc-non-private-member-variables-in-classes
- misc-const-correctness
Completion:
AllScopes: Yes
SemanticTokens:
DisabledKinds: []
DisabledModifiers: []

126
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,126 @@
#!/bin/bash
# Pre-commit Hook - Quick symbol conflict detection
#
# This hook runs on staged changes and warns if duplicate symbol definitions
# are detected in affected object files.
#
# Bypass with: git commit --no-verify
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
echo -e "${BLUE}[Pre-Commit]${NC} Checking for symbol conflicts..."
# Get the repository root
REPO_ROOT="$(git rev-parse --show-toplevel)"
SCRIPTS_DIR="${REPO_ROOT}/scripts"
BUILD_DIR="${REPO_ROOT}/build"
# Check if build directory exists
if [[ ! -d "${BUILD_DIR}" ]]; then
echo -e "${YELLOW}[Pre-Commit]${NC} Build directory not found - skipping symbol check"
exit 0
fi
# Get list of changed .cc and .h files
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cc|h)$' || echo "")
if [[ -z "${CHANGED_FILES}" ]]; then
echo -e "${YELLOW}[Pre-Commit]${NC} No C++ source changes detected"
exit 0
fi
echo -e "${CYAN}Changed files:${NC}"
echo "${CHANGED_FILES}" | sed 's/^/ /'
echo ""
# Quick symbol database check (only on changed files)
# Find object files that might be affected by the changes
AFFECTED_OBJ_FILES=""
for file in ${CHANGED_FILES}; do
# Convert source file path to likely object file names
# e.g., src/cli/flags.cc -> *flags.cc.o
filename=$(basename "${file}" | sed 's/\.[ch]$//')
# Find matching object files in build directory
matching=$(find "${BUILD_DIR}" -name "*${filename}*.o" -o -name "*${filename}*.obj" 2>/dev/null | head -5)
if [[ -n "${matching}" ]]; then
AFFECTED_OBJ_FILES+=$'\n'"${matching}"
fi
done
if [[ -z "${AFFECTED_OBJ_FILES}" ]]; then
echo -e "${YELLOW}[Pre-Commit]${NC} No compiled objects found for changed files (might not be built yet)"
exit 0
fi
echo -e "${CYAN}Affected object files:${NC}"
echo "${AFFECTED_OBJ_FILES}" | grep -v '^$' | sed 's/^/ /' || echo " (none found)"
echo ""
# Extract symbols from affected files
echo -e "${CYAN}Analyzing symbols...${NC}"
TEMP_SYMBOLS="/tmp/yaze_precommit_symbols_$$.txt"
trap "rm -f ${TEMP_SYMBOLS}" EXIT
: > "${TEMP_SYMBOLS}"
# Platform detection
UNAME_S=$(uname -s)
SYMBOL_CONFLICTS=0
while IFS= read -r obj_file; do
[[ -z "${obj_file}" ]] && continue
[[ ! -f "${obj_file}" ]] && continue
# Extract symbols using nm (Unix/macOS)
if [[ "${UNAME_S}" == "Darwin"* ]] || [[ "${UNAME_S}" == "Linux"* ]]; then
nm -P "${obj_file}" 2>/dev/null | while read -r sym rest; do
[[ -z "${sym}" ]] && continue
# Skip undefined symbols (contain 'U')
[[ "${rest}" == *"U"* ]] && continue
echo "${sym}|${obj_file##*/}"
done >> "${TEMP_SYMBOLS}" || true
fi
done < <(echo "${AFFECTED_OBJ_FILES}" | grep -v '^$')
# Check for duplicates
if [[ -f "${TEMP_SYMBOLS}" ]]; then
SYMBOL_CONFLICTS=$(cut -d'|' -f1 "${TEMP_SYMBOLS}" | sort | uniq -d | wc -l)
fi
# Report results
if [[ ${SYMBOL_CONFLICTS} -gt 0 ]]; then
echo -e "${RED}WARNING: Symbol conflicts detected!${NC}\n"
echo "Duplicate symbols in affected files:"
cut -d'|' -f1 "${TEMP_SYMBOLS}" | sort | uniq -d | while read -r symbol; do
echo -e " ${CYAN}${symbol}${NC}"
grep "^${symbol}|" "${TEMP_SYMBOLS}" | cut -d'|' -f2 | sort | uniq | sed 's/^/ - /'
done
echo ""
echo -e "${YELLOW}You can:${NC}"
echo " 1. Fix the conflicts before committing"
echo " 2. Skip this check: git commit --no-verify"
echo " 3. Run full analysis: ./scripts/extract-symbols.sh && ./scripts/check-duplicate-symbols.sh"
echo ""
echo -e "${YELLOW}Common fixes:${NC}"
echo " - Add 'static' keyword to make it internal linkage"
echo " - Use anonymous namespace in .cc files"
echo " - Use 'inline' keyword for function/variable definitions"
echo ""
exit 1
else
echo -e "${GREEN}No symbol conflicts in changed files${NC}"
exit 0
fi

84
.github/actions/README.md vendored Normal file
View File

@@ -0,0 +1,84 @@
# GitHub Actions - Composite Actions
This directory contains reusable composite actions for the YAZE CI/CD pipeline.
## Available Actions
### 1. `setup-build`
Sets up the build environment with dependencies and caching.
**Inputs:**
- `platform` (required): Target platform (linux, macos, windows)
- `preset` (required): CMake preset to use
- `cache-key` (optional): Cache key for dependencies
**What it does:**
- Configures CPM cache
- Installs platform-specific build dependencies
- Sets up sccache/ccache for faster builds
### 2. `build-project`
Builds the project with CMake and caching.
**Inputs:**
- `platform` (required): Target platform (linux, macos, windows)
- `preset` (required): CMake preset to use
- `build-type` (optional): Build type (Debug, Release, RelWithDebInfo)
**What it does:**
- Caches build artifacts
- Configures the project with CMake
- Builds the project with optimal parallel settings
- Shows build artifacts for verification
### 3. `run-tests`
Runs the test suite with appropriate filtering.
**Inputs:**
- `test-type` (required): Type of tests to run (stable, unit, integration, all)
- `preset` (optional): CMake preset to use (default: ci)
**What it does:**
- Runs the specified test suite(s)
- Generates JUnit XML test results
- Uploads test results as artifacts
## Usage
These composite actions are used in the main CI workflow (`.github/workflows/ci.yml`). They must be called after checking out the repository:
```yaml
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: linux
preset: ci
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
```
## Important Notes
1. **Repository checkout required**: The repository must be checked out before calling any of these composite actions. They do not include a checkout step themselves.
2. **Platform-specific behavior**: Each action adapts to the target platform (Linux, macOS, Windows) and runs appropriate commands for that environment.
3. **Caching**: The actions use GitHub Actions caching to speed up builds by caching:
- CPM dependencies (~/.cpm-cache)
- Build artifacts (build/)
- Compiler cache (sccache/ccache)
4. **Dependencies**: The Linux CI packages are listed in `.github/workflows/scripts/linux-ci-packages.txt`.
## Maintenance
When updating these actions:
- Test on all three platforms (Linux, macOS, Windows)
- Ensure shell compatibility (bash for Linux/macOS, pwsh for Windows)
- Update this README if inputs or behavior changes

View File

@@ -0,0 +1,58 @@
name: 'Build Project'
description: 'Build the project with CMake and caching'
inputs:
platform:
description: 'Target platform (linux, macos, windows)'
required: true
preset:
description: 'CMake preset to use'
required: true
build-type:
description: 'Build type (Debug, Release, RelWithDebInfo)'
required: false
default: 'RelWithDebInfo'
runs:
using: 'composite'
steps:
- name: Cache build artifacts
uses: actions/cache@v4
with:
path: build
key: build-${{ inputs.platform }}-${{ github.sha }}
restore-keys: |
build-${{ inputs.platform }}-
- name: Configure project
shell: bash
run: |
cmake --preset "${{ inputs.preset }}"
- name: Build project (Linux/macOS)
if: inputs.platform != 'windows'
shell: bash
run: |
if command -v nproc >/dev/null 2>&1; then
CORES=$(nproc)
elif command -v sysctl >/dev/null 2>&1; then
CORES=$(sysctl -n hw.ncpu)
else
CORES=2
fi
echo "Using $CORES parallel jobs"
cmake --build build --config "${{ inputs.build-type }}" --parallel "$CORES"
- name: Build project (Windows)
if: inputs.platform == 'windows'
shell: pwsh
run: |
$JOBS = ${env:CMAKE_BUILD_PARALLEL_LEVEL:-4}
echo "Using $JOBS parallel jobs"
cmake --build build --config "${{ inputs.build-type }}" --parallel "$JOBS"
- name: Show build artifacts
shell: bash
run: |
echo "Build artifacts:"
find build -name "*.exe" -o -name "yaze" -o -name "*.app" | head -10

61
.github/actions/run-tests/action.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: 'Run Tests'
description: 'Run test suite with appropriate filtering'
inputs:
test-type:
description: 'Type of tests to run (stable, unit, integration, all)'
required: true
preset:
description: 'CMake preset to use (for reference, tests use minimal preset)'
required: false
default: 'minimal'
runs:
using: 'composite'
steps:
- name: Select test preset suffix
shell: bash
run: |
if [ "${{ inputs.preset }}" = "ci-windows-ai" ]; then
echo "CTEST_SUFFIX=-ai" >> $GITHUB_ENV
else
echo "CTEST_SUFFIX=" >> $GITHUB_ENV
fi
- name: Run stable tests
if: inputs.test-type == 'stable' || inputs.test-type == 'all'
shell: bash
run: |
cd build
ctest --preset stable${CTEST_SUFFIX} \
--output-on-failure \
--timeout 300 \
--output-junit stable_test_results.xml || true
- name: Run unit tests
if: inputs.test-type == 'unit' || inputs.test-type == 'all'
shell: bash
run: |
cd build
ctest --preset unit${CTEST_SUFFIX} \
--output-on-failure \
--timeout 300 \
--output-junit unit_test_results.xml || true
- name: Run integration tests
if: inputs.test-type == 'integration' || inputs.test-type == 'all'
shell: bash
run: |
cd build
ctest --preset integration${CTEST_SUFFIX} \
--output-on-failure \
--timeout 300 \
--output-junit integration_test_results.xml || true
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results-${{ inputs.test-type }}
path: build/*test_results.xml
if-no-files-found: ignore
retention-days: 7

83
.github/actions/setup-build/action.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: 'Setup Build Environment'
description: 'Setup build environment with dependencies and caching'
inputs:
platform:
description: 'Target platform (linux, macos, windows)'
required: true
preset:
description: 'CMake preset to use'
required: true
cache-key:
description: 'Cache key for dependencies'
required: false
default: ''
runs:
using: 'composite'
steps:
- name: Setup CPM cache
if: inputs.cache-key != ''
uses: actions/cache@v4
with:
path: ~/.cpm-cache
key: cpm-${{ inputs.platform }}-${{ inputs.cache-key }}
restore-keys: |
cpm-${{ inputs.platform }}-
- name: Setup build environment (Linux)
if: inputs.platform == 'linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y $(tr '\n' ' ' < .github/workflows/scripts/linux-ci-packages.txt) gcc-12 g++-12
sudo apt-get clean
- name: Setup build environment (macOS)
if: inputs.platform == 'macos'
shell: bash
run: |
brew install ninja pkg-config ccache
if ! command -v cmake &> /dev/null; then
brew install cmake
fi
- name: Setup build environment (Windows)
if: inputs.platform == 'windows'
shell: pwsh
run: |
choco install --no-progress -y nasm ccache
if ($env:ChocolateyInstall) {
$profilePath = Join-Path $env:ChocolateyInstall "helpers\chocolateyProfile.psm1"
if (Test-Path $profilePath) {
Import-Module $profilePath
refreshenv
}
}
# Ensure Git can handle long paths and cached directories when cloning gRPC dependencies
git config --global core.longpaths true
git config --global --add safe.directory '*'
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ inputs.platform }}-${{ github.sha }}
restore-keys: |
${{ inputs.platform }}-
max-size: 500M
variant: sccache
- name: Setup MSVC Dev Cmd (Windows)
if: inputs.platform == 'windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Configure compiler for Windows
if: inputs.platform == 'windows'
shell: pwsh
run: |
echo "CC=clang-cl" >> $env:GITHUB_ENV
echo "CXX=clang-cl" >> $env:GITHUB_ENV
echo "CMAKE_CXX_COMPILER_LAUNCHER=sccache" >> $env:GITHUB_ENV
echo "CMAKE_C_COMPILER_LAUNCHER=sccache" >> $env:GITHUB_ENV

84
.github/scripts/validate-actions.sh vendored Executable file
View File

@@ -0,0 +1,84 @@
#!/bin/bash
# Validate GitHub Actions composite action structure
set -e
echo "Validating GitHub Actions composite actions..."
ACTIONS_DIR=".github/actions"
REQUIRED_FIELDS=("name" "description" "runs")
validate_action() {
local action_file="$1"
local action_name=$(basename $(dirname "$action_file"))
echo "Checking $action_name..."
# Check if file exists
if [ ! -f "$action_file" ]; then
echo " ✗ action.yml not found"
return 1
fi
# Check required fields
for field in "${REQUIRED_FIELDS[@]}"; do
if ! grep -q "^${field}:" "$action_file"; then
echo " ✗ Missing required field: $field"
return 1
fi
done
# Check for 'using: composite'
if ! grep -q "using: 'composite'" "$action_file"; then
echo " ✗ Not marked as composite action"
return 1
fi
echo " ✓ Valid composite action"
return 0
}
# Validate all actions
all_valid=true
for action_yml in "$ACTIONS_DIR"/*/action.yml; do
if ! validate_action "$action_yml"; then
all_valid=false
fi
done
# Check that CI workflow references actions correctly
echo ""
echo "Checking CI workflow..."
CI_FILE=".github/workflows/ci.yml"
if [ ! -f "$CI_FILE" ]; then
echo " ✗ CI workflow not found"
all_valid=false
else
# Check for checkout before action usage
if grep -q "uses: actions/checkout@v4" "$CI_FILE"; then
echo " ✓ Repository checkout step present"
else
echo " ✗ Missing checkout step"
all_valid=false
fi
# Check for composite action references
action_refs=$(grep -c "uses: ./.github/actions/" "$CI_FILE" || echo "0")
if [ "$action_refs" -gt 0 ]; then
echo " ✓ Found $action_refs composite action references"
else
echo " ✗ No composite action references found"
all_valid=false
fi
fi
echo ""
if [ "$all_valid" = true ]; then
echo "✓ All validations passed!"
exit 0
else
echo "✗ Some validations failed"
exit 1
fi

View File

@@ -1,5 +1,19 @@
name: CI/CD Pipeline name: CI/CD Pipeline
# Test Strategy:
# - PR/Push: Run lean test set (stable tests + smoke tests) for fast feedback
# - Nightly: Run comprehensive suite including ROM-dependent, experimental, benchmarks
# - See .github/workflows/nightly.yml for extended test coverage
#
# Test Labels:
# - stable: Core functionality tests that should always pass
# - unit: Fast isolated component tests (subset of stable)
# - integration: Multi-component tests (subset of stable)
# - rom_dependent: Tests requiring Zelda3 ROM file (nightly only)
# - experimental: AI and experimental feature tests (nightly only)
# - gui: GUI/E2E tests with ImGuiTestEngine (nightly only)
# - benchmark: Performance benchmarks (nightly only)
on: on:
push: push:
branches: [ "master", "develop" ] branches: [ "master", "develop" ]
@@ -38,13 +52,18 @@ on:
required: false required: false
default: false default: false
type: boolean type: boolean
enable_http_api_tests:
description: 'Enable HTTP API tests'
required: false
default: false
type: boolean
env: env:
BUILD_TYPE: ${{ github.event.inputs.build_type || 'RelWithDebInfo' }} BUILD_TYPE: ${{ github.event.inputs.build_type || 'RelWithDebInfo' }}
jobs: jobs:
build-and-test: build:
name: "${{ matrix.name }}" name: "Build - ${{ matrix.name }}"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -52,653 +71,170 @@ jobs:
include: include:
- name: "Ubuntu 22.04 (GCC-12)" - name: "Ubuntu 22.04 (GCC-12)"
os: ubuntu-22.04 os: ubuntu-22.04
cc: gcc-12 platform: linux
cxx: g++-12 preset: ci-linux
- name: "macOS 14 (Clang)" - name: "macOS 14 (Clang)"
os: macos-14 os: macos-14
cc: clang platform: macos
cxx: clang++ preset: ci-macos
- name: "Windows 2022 (Clang-CL)" - name: "Windows 2022 (Core)"
os: windows-2022 os: windows-2022
cc: clang-cl platform: windows
cxx: clang-cl preset: ci-windows
- name: "Windows 2022 (MSVC)"
os: windows-2022
cc: cl
cxx: cl
steps: steps:
- name: Checkout - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up vcpkg (Windows) - name: Setup build environment
if: runner.os == 'Windows' uses: ./.github/actions/setup-build
uses: lukka/run-vcpkg@v11 with:
id: vcpkg platform: ${{ matrix.platform }}
continue-on-error: true preset: ${{ matrix.preset }}
env: cache-key: ${{ github.sha }}
VCPKG_DEFAULT_TRIPLET: x64-windows-static
VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'
with:
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
vcpkgGitCommitId: 'b2c74683ecfd6a8e7d27ffb0df077f66a9339509' # 2025.01.20 release
runVcpkgInstall: true # Pre-install SDL2, yaml-cpp (fast packages only)
- name: Retry vcpkg setup (Windows) - name: Build project
if: runner.os == 'Windows' && steps.vcpkg.outcome == 'failure' uses: ./.github/actions/build-project
uses: lukka/run-vcpkg@v11 with:
id: vcpkg_retry platform: ${{ matrix.platform }}
env: preset: ${{ matrix.preset }}
VCPKG_DEFAULT_TRIPLET: x64-windows-static build-type: ${{ env.BUILD_TYPE }}
VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'
with:
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
vcpkgGitCommitId: 'b2c74683ecfd6a8e7d27ffb0df077f66a9339509'
runVcpkgInstall: true
- name: Resolve vcpkg toolchain (Windows) - name: Upload build artifacts (Windows)
if: runner.os == 'Windows' if: matrix.platform == 'windows' && (github.event.inputs.upload_artifacts == 'true' || github.event_name == 'push')
shell: pwsh uses: actions/upload-artifact@v4
run: | with:
# Try to get vcpkg root from either initial setup or retry name: yaze-windows-ci-${{ github.run_number }}
$vcpkgRoot = "${{ steps.vcpkg.outputs.vcpkgRoot }}" path: |
if (-not $vcpkgRoot) { build/bin/*.exe
$vcpkgRoot = "${{ steps.vcpkg_retry.outputs.vcpkgRoot }}" build/bin/*.dll
} build/bin/${{ env.BUILD_TYPE }}/*.exe
if (-not $vcpkgRoot) { build/bin/${{ env.BUILD_TYPE }}/*.dll
$vcpkgRoot = Join-Path "${{ github.workspace }}" "vcpkg" if-no-files-found: warn
} retention-days: 3
Write-Host "Checking vcpkg root: $vcpkgRoot" test:
if (-not (Test-Path $vcpkgRoot)) { name: "Test - ${{ matrix.name }}"
Write-Host "::error::vcpkg root not found at $vcpkgRoot" runs-on: ${{ matrix.os }}
Write-Host "vcpkg setup status: ${{ steps.vcpkg.outcome }}" strategy:
Write-Host "vcpkg retry status: ${{ steps.vcpkg_retry.outcome }}" fail-fast: false
exit 1 matrix:
} include:
- name: "Ubuntu 22.04"
os: ubuntu-22.04
platform: linux
preset: ci-linux
- name: "macOS 14"
os: macos-14
platform: macos
preset: ci-macos
- name: "Windows 2022 (Core)"
os: windows-2022
platform: windows
preset: ci-windows
$toolchain = Join-Path $vcpkgRoot "scripts/buildsystems/vcpkg.cmake" steps:
if (-not (Test-Path $toolchain)) { - name: Checkout code
Write-Host "::error::vcpkg toolchain file missing at $toolchain" uses: actions/checkout@v4
exit 1 with:
} submodules: recursive
$normalizedRoot = $vcpkgRoot -replace '\\', '/' - name: Setup build environment
$normalizedToolchain = $toolchain -replace '\\', '/' uses: ./.github/actions/setup-build
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
cache-key: ${{ github.sha }}
Write-Host "✓ vcpkg root: $normalizedRoot" - name: Build project
Write-Host "✓ Toolchain: $normalizedToolchain" uses: ./.github/actions/build-project
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
build-type: ${{ env.BUILD_TYPE }}
"VCPKG_ROOT=$normalizedRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Run stable tests only
"CMAKE_TOOLCHAIN_FILE=$normalizedToolchain" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append uses: ./.github/actions/run-tests
with:
test-type: stable
preset: ${{ matrix.preset }}
- name: Install Windows build tools - name: Run smoke tests (GUI framework validation)
if: runner.os == 'Windows' if: matrix.platform == 'linux'
shell: pwsh run: |
run: | cd build
choco install --no-progress -y nasm ccache # Run just the smoke tests to validate GUI framework is working
if ($env:ChocolateyInstall) { ./bin/yaze_test_gui -nogui --gtest_filter="*Smoke*" || true
$profilePath = Join-Path $env:ChocolateyInstall "helpers\chocolateyProfile.psm1" continue-on-error: true
if (Test-Path $profilePath) {
Import-Module $profilePath
refreshenv
}
}
if (Test-Path "C:\Program Files\NASM") {
"C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
}
- name: Ensure MSVC Dev Cmd (Windows) - name: Run HTTP API tests
if: runner.os == 'Windows' if: github.event.inputs.enable_http_api_tests == 'true'
uses: ilammy/msvc-dev-cmd@v1 run: scripts/agents/test-http-api.sh
with:
arch: x64
- name: Diagnose vcpkg (Windows) windows-agent:
if: runner.os == 'Windows' name: "Windows Agent (Full Stack)"
shell: pwsh runs-on: windows-2022
run: | needs: [build, test]
Write-Host "=== vcpkg Diagnostics ===" -ForegroundColor Cyan if: github.event_name != 'pull_request'
Write-Host "Initial setup: ${{ steps.vcpkg.outcome }}"
Write-Host "Retry setup: ${{ steps.vcpkg_retry.outcome }}"
Write-Host "vcpkg directory: ${{ github.workspace }}/vcpkg"
if (Test-Path "${{ github.workspace }}/vcpkg/vcpkg.exe") { steps:
Write-Host "✅ vcpkg.exe found" -ForegroundColor Green - name: Checkout code
& "${{ github.workspace }}/vcpkg/vcpkg.exe" version uses: actions/checkout@v4
Write-Host "`nvcpkg installed packages:" -ForegroundColor Cyan with:
& "${{ github.workspace }}/vcpkg/vcpkg.exe" list | Select-Object -First 20 submodules: recursive
} else {
Write-Host "❌ vcpkg.exe not found" -ForegroundColor Red
}
Write-Host "`nEnvironment:" -ForegroundColor Cyan - name: Setup build environment
Write-Host "CMAKE_TOOLCHAIN_FILE: $env:CMAKE_TOOLCHAIN_FILE" uses: ./.github/actions/setup-build
Write-Host "VCPKG_DEFAULT_TRIPLET: $env:VCPKG_DEFAULT_TRIPLET" with:
Write-Host "VCPKG_ROOT: $env:VCPKG_ROOT" platform: windows
Write-Host "Workspace: ${{ github.workspace }}" preset: ci-windows-ai
cache-key: ${{ github.sha }}
Write-Host "`nManifest files:" -ForegroundColor Cyan - name: Build project
if (Test-Path "vcpkg.json") { uses: ./.github/actions/build-project
Write-Host "✅ vcpkg.json found" with:
Get-Content "vcpkg.json" | Write-Host platform: windows
} preset: ci-windows-ai
if (Test-Path "vcpkg-configuration.json") { build-type: ${{ env.BUILD_TYPE }}
Write-Host "✅ vcpkg-configuration.json found"
}
- name: Setup sccache - name: Run stable tests only (agent stack)
uses: hendrikmuhs/ccache-action@v1.2 uses: ./.github/actions/run-tests
with: with:
key: ${{ runner.os }}-${{ matrix.cc }}-${{ github.sha }} test-type: stable
restore-keys: | preset: ci-windows-ai
${{ runner.os }}-${{ matrix.cc }}-
max-size: 500M
variant: sccache
- name: Configure sccache for clang-cl wasm-smoke-test:
if: runner.os == 'Windows' && matrix.cc == 'clang-cl' name: "WASM Build Smoke Test"
shell: pwsh runs-on: ubuntu-22.04
run: | if: github.event_name == 'pull_request'
echo "CC=sccache clang-cl" >> $env:GITHUB_ENV
echo "CXX=sccache clang-cl" >> $env:GITHUB_ENV
- name: Restore vcpkg packages cache steps:
uses: actions/cache@v4 - name: Checkout code
with: uses: actions/checkout@v4
path: | with:
build/vcpkg_installed submodules: recursive
${{ github.workspace }}/vcpkg/packages
${{ github.workspace }}/vcpkg/buildtrees
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}
restore-keys: |
vcpkg-${{ runner.os }}-
- name: Restore FetchContent dependencies (gRPC) - name: Setup Emscripten
uses: actions/cache@v4 uses: mymindstorm/setup-emsdk@v14
with: with:
path: | version: 3.1.51
build/_deps actions-cache-folder: 'emsdk-cache'
key: fetchcontent-${{ runner.os }}-${{ matrix.cc }}-${{ hashFiles('cmake/grpc*.cmake') }}-v2
restore-keys: |
fetchcontent-${{ runner.os }}-${{ matrix.cc }}-
- name: Monitor build progress (Windows) - name: Setup Ninja
if: runner.os == 'Windows' uses: seanmiddleditch/gha-setup-ninja@v4
shell: pwsh
run: |
Write-Host "=== Pre-Build Status ===" -ForegroundColor Cyan
# Check if gRPC is cached - name: Quick WASM build test
if (Test-Path "build/_deps/grpc-subbuild") { run: |
Write-Host "✅ gRPC FetchContent cache found" -ForegroundColor Green emcmake cmake --preset wasm-debug
} else { cmake --build build-wasm-debug --target yaze --parallel
Write-Host "⚠️ gRPC will be built from source (~10-15 min first time)" -ForegroundColor Yellow # Verify output exists
} test -f build-wasm-debug/bin/yaze.wasm
echo "WASM build successful!"
# Check vcpkg packages
if (Test-Path "build/vcpkg_installed") {
Write-Host "✅ vcpkg packages cache found" -ForegroundColor Green
if (Test-Path "${{ github.workspace }}/vcpkg/vcpkg.exe") {
& "${{ github.workspace }}/vcpkg/vcpkg.exe" list
}
} else {
Write-Host "⚠️ vcpkg packages will be installed (~2-3 min)" -ForegroundColor Yellow
}
- name: Install Dependencies (Unix)
id: deps
shell: bash
continue-on-error: true
run: |
if [[ "${{ runner.os }}" == "Linux" ]]; then
sudo apt-get update
sudo apt-get install -y \
build-essential ninja-build pkg-config ccache \
libglew-dev libxext-dev libwavpack-dev libboost-all-dev \
libpng-dev python3-dev libpython3-dev \
libasound2-dev libpulse-dev libaudio-dev \
libx11-dev libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev \
libxss-dev libxxf86vm-dev libxkbcommon-dev libwayland-dev libdecor-0-dev \
libgtk-3-dev libdbus-1-dev \
${{ matrix.cc }} ${{ matrix.cxx }}
# Note: libabsl-dev removed - gRPC uses bundled Abseil via FetchContent when enabled
elif [[ "${{ runner.os }}" == "macOS" ]]; then
brew install ninja pkg-config ccache
fi
- name: Retry Dependencies (Unix)
if: steps.deps.outcome == 'failure'
shell: bash
run: |
echo "::warning::First dependency install failed, retrying..."
if [[ "${{ runner.os }}" == "Linux" ]]; then
sudo apt-get clean
sudo apt-get update --fix-missing
sudo apt-get install -y \
build-essential ninja-build pkg-config ccache \
libglew-dev libxext-dev libwavpack-dev libboost-all-dev \
libpng-dev python3-dev libpython3-dev \
libasound2-dev libpulse-dev libaudio-dev \
libx11-dev libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev \
libxss-dev libxxf86vm-dev libxkbcommon-dev libwayland-dev libdecor-0-dev \
libgtk-3-dev libdbus-1-dev \
${{ matrix.cc }} ${{ matrix.cxx }}
elif [[ "${{ runner.os }}" == "macOS" ]]; then
brew update
brew install ninja pkg-config ccache
fi
- name: Free Disk Space (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
echo "=== Freeing Disk Space ==="
df -h
echo ""
echo "Removing unnecessary software..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo apt-get clean
echo ""
echo "Disk space after cleanup:"
df -h
- name: Pre-configure Diagnostics (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
Write-Host "=== Pre-configure Diagnostics ===" -ForegroundColor Cyan
Write-Host "Build Type: ${{ env.BUILD_TYPE }}"
Write-Host "Workspace: ${{ github.workspace }}"
# Check Visual Studio installation
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vsWhere) {
Write-Host "`nVisual Studio Installation:" -ForegroundColor Cyan
& $vsWhere -latest -property displayName
& $vsWhere -latest -property installationVersion
}
# Check CMake
Write-Host "`nCMake Version:" -ForegroundColor Cyan
cmake --version
# Verify vcpkg toolchain
$toolchain = "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake"
if (Test-Path $toolchain) {
Write-Host "✅ vcpkg toolchain found at: $toolchain" -ForegroundColor Green
} else {
Write-Host "⚠️ vcpkg toolchain not found at: $toolchain" -ForegroundColor Yellow
}
# Show vcpkg manifest
if (Test-Path "vcpkg.json") {
Write-Host "`nvcpkg.json contents:" -ForegroundColor Cyan
Get-Content "vcpkg.json" | Write-Host
}
# Show available disk space
Write-Host "`nDisk Space:" -ForegroundColor Cyan
Get-PSDrive C | Select-Object Used,Free | Format-Table -AutoSize
- name: Configure (Windows)
if: runner.os == 'Windows'
id: configure_windows
shell: pwsh
run: |
Write-Host "::group::CMake Configuration (Windows)" -ForegroundColor Cyan
if (Get-Command ccache -ErrorAction SilentlyContinue) {
$env:CCACHE_BASEDIR = "${{ github.workspace }}"
$env:CCACHE_DIR = Join-Path $env:USERPROFILE ".ccache"
ccache --zero-stats
}
$toolchain = "${env:CMAKE_TOOLCHAIN_FILE}"
if (-not $toolchain -or -not (Test-Path $toolchain)) {
Write-Host "::error::CMAKE_TOOLCHAIN_FILE is missing or invalid: '$toolchain'"
exit 1
}
$cmakeArgs = @(
"-S", ".",
"-B", "build",
"-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}",
"-DCMAKE_TOOLCHAIN_FILE=$toolchain",
"-DVCPKG_TARGET_TRIPLET=x64-windows-static",
"-DVCPKG_MANIFEST_MODE=ON",
"-DYAZE_BUILD_TESTS=ON",
"-DYAZE_BUILD_EMU=ON",
"-DYAZE_BUILD_Z3ED=ON",
"-DYAZE_BUILD_TOOLS=ON",
"-DYAZE_ENABLE_ROM_TESTS=OFF"
)
cmake @cmakeArgs 2>&1 | Tee-Object -FilePath cmake_config.log
$exit = $LASTEXITCODE
Write-Host "::endgroup::"
if ($exit -ne 0) {
exit $exit
}
if (Get-Command ccache -ErrorAction SilentlyContinue) {
ccache --show-stats
}
- name: Configure (Unix)
if: runner.os != 'Windows'
id: configure_unix
shell: bash
run: |
set -e
echo "::group::CMake Configuration"
if command -v ccache >/dev/null 2>&1; then
export CCACHE_BASEDIR=${GITHUB_WORKSPACE}
export CCACHE_DIR=${HOME}/.ccache
ccache --zero-stats
fi
if [[ "${{ runner.os }}" == "Linux" ]]; then
# Linux: Use portal backend for file dialogs (more reliable in CI)
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_C_COMPILER=${{ matrix.cc }} \
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
-DYAZE_BUILD_TESTS=ON \
-DYAZE_BUILD_EMU=ON \
-DYAZE_ENABLE_ROM_TESTS=OFF \
-DYAZE_BUILD_Z3ED=ON \
-DYAZE_BUILD_TOOLS=ON \
-DNFD_PORTAL=ON 2>&1 | tee cmake_config.log
else
# macOS: Use default GTK backend
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_C_COMPILER=${{ matrix.cc }} \
-DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
-DYAZE_BUILD_TESTS=ON \
-DYAZE_BUILD_EMU=ON \
-DYAZE_ENABLE_ROM_TESTS=OFF \
-DYAZE_BUILD_Z3ED=ON \
-DYAZE_BUILD_TOOLS=ON 2>&1 | tee cmake_config.log
fi
echo "::endgroup::"
if command -v ccache >/dev/null 2>&1; then
ccache --show-stats
fi
# Note: Full-featured build to match release configuration
# Note: YAZE_BUILD_EMU=OFF disables standalone emulator executable
# but yaze_emulator library is still built for main app/tests
# Note: NFD_PORTAL=ON uses D-Bus portal instead of GTK on Linux (more reliable in CI)
- name: Report Configure Failure
if: always() && (steps.configure_windows.outcome == 'failure' || steps.configure_unix.outcome == 'failure')
shell: bash
run: |
echo "::error::CMake configuration failed. Check cmake_config.log for details."
if [ -f cmake_config.log ]; then
echo "::group::CMake Configuration Log (last 50 lines)"
tail -50 cmake_config.log
echo "::endgroup::"
fi
if [ -f build/CMakeFiles/CMakeError.log ]; then
echo "::group::CMake Error Log"
cat build/CMakeFiles/CMakeError.log
echo "::endgroup::"
fi
- name: Build
id: build
shell: bash
run: |
BUILD_TYPE=${BUILD_TYPE:-${{ env.BUILD_TYPE }}}
echo "Building with ${BUILD_TYPE} configuration..."
if [[ "${{ runner.os }}" == "Windows" ]]; then
JOBS=${CMAKE_BUILD_PARALLEL_LEVEL:-4}
echo "Using $JOBS parallel jobs"
cmake --build build --config "${BUILD_TYPE}" --parallel "${JOBS}" 2>&1 | tee build.log
else
# Determine number of parallel jobs based on platform
if command -v nproc >/dev/null 2>&1; then
CORES=$(nproc)
elif command -v sysctl >/dev/null 2>&1; then
CORES=$(sysctl -n hw.ncpu)
else
CORES=2
fi
echo "Using $CORES parallel jobs"
cmake --build build --parallel $CORES 2>&1 | tee build.log
fi
if command -v ccache >/dev/null 2>&1; then
ccache --show-stats
fi
- name: Report Build Failure
if: always() && steps.build.outcome == 'failure'
shell: bash
run: |
echo "::error::Build failed. Check build.log for details."
if [ -f build.log ]; then
echo "::group::Build Log (last 100 lines)"
tail -100 build.log
echo "::endgroup::"
# Extract and highlight actual errors
echo "::group::Build Errors"
grep -i "error" build.log | head -20 || true
echo "::endgroup::"
fi
- name: Windows Build Diagnostics
if: always() && runner.os == 'Windows' && steps.build.outcome == 'failure'
shell: pwsh
run: |
Write-Host "=== Windows Build Diagnostics ===" -ForegroundColor Red
# Check for vcpkg-related errors
if (Select-String -Path "build.log" -Pattern "vcpkg" -Quiet) {
Write-Host "`nvcpkg-related errors found:" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "vcpkg.*error" -CaseSensitive:$false | Select-Object -First 10
}
# Check for linker errors
if (Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" -Quiet) {
Write-Host "`nLinker errors found:" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" | Select-Object -First 10
}
# Check for missing dependencies
if (Select-String -Path "build.log" -Pattern "fatal error.*No such file" -Quiet) {
Write-Host "`nMissing file errors found:" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "fatal error.*No such file" | Select-Object -First 10
}
# List vcpkg installed packages if available
$vcpkgExe = "${{ github.workspace }}/vcpkg/vcpkg.exe"
if (Test-Path $vcpkgExe) {
Write-Host "`nInstalled vcpkg packages:" -ForegroundColor Cyan
& $vcpkgExe list
}
- name: Post-Build Diagnostics (Windows)
if: always() && runner.os == 'Windows' && steps.build.outcome == 'success'
shell: pwsh
run: |
Write-Host "=== Post-Build Diagnostics ===" -ForegroundColor Green
$binCandidates = @("build/bin", "build/bin/${{ env.BUILD_TYPE }}")
$found = $false
foreach ($candidate in $binCandidates) {
if (-not (Test-Path $candidate)) { continue }
$found = $true
Write-Host "`nArtifacts under $candidate:" -ForegroundColor Cyan
Get-ChildItem -Path $candidate -Include *.exe,*.dll -Recurse | ForEach-Object {
$size = [math]::Round($_.Length / 1MB, 2)
Write-Host " $($_.FullName.Replace($PWD.Path + '\', '')) - ${size} MB"
}
}
if (-not $found) {
Write-Host "⚠️ Build output directories not found." -ForegroundColor Yellow
} else {
$yazeExe = Get-ChildItem -Path build -Filter yaze.exe -Recurse | Select-Object -First 1
if ($yazeExe) {
Write-Host "`n✅ yaze.exe located at $($yazeExe.FullName)" -ForegroundColor Green
$yazeSize = [math]::Round($yazeExe.Length / 1MB, 2)
Write-Host " Size: ${yazeSize} MB"
} else {
Write-Host "`n⚠ yaze.exe not found in build output" -ForegroundColor Yellow
}
}
- name: Upload Build Artifacts (Windows)
if: |
runner.os == 'Windows' &&
steps.build.outcome == 'success' &&
(github.event.inputs.upload_artifacts == 'true' || github.event_name == 'push')
uses: actions/upload-artifact@v4
with:
name: yaze-windows-ci-${{ github.run_number }}
path: |
build/bin/*.exe
build/bin/*.dll
build/bin/${{ env.BUILD_TYPE }}/*.exe
build/bin/${{ env.BUILD_TYPE }}/*.dll
if-no-files-found: warn
retention-days: 3
- name: Test (Stable)
id: test_stable
working-directory: build
shell: bash
run: |
BUILD_TYPE=${BUILD_TYPE:-${{ env.BUILD_TYPE }}}
echo "Running stable test suite..."
ctest --output-on-failure -C "$BUILD_TYPE" -j1 \
-L "stable" \
--output-junit stable_test_results.xml 2>&1 | tee ../stable_test.log || true
- name: Test (Experimental - Informational)
id: test_experimental
working-directory: build
continue-on-error: true
shell: bash
run: |
BUILD_TYPE=${BUILD_TYPE:-${{ env.BUILD_TYPE }}}
echo "Running experimental test suite (informational only)..."
ctest --output-on-failure -C "$BUILD_TYPE" --parallel \
-L "experimental" \
--output-junit experimental_test_results.xml 2>&1 | tee ../experimental_test.log
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.name }}
path: |
build/*test_results.xml
stable_test.log
experimental_test.log
retention-days: 7
if-no-files-found: ignore
- name: Upload Build Logs on Failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: build-logs-${{ matrix.name }}
path: |
cmake_config.log
build.log
build/CMakeFiles/CMakeError.log
build/CMakeFiles/CMakeOutput.log
if-no-files-found: ignore
retention-days: 7
- name: Generate Job Summary
if: always()
shell: bash
run: |
echo "## Build Summary - ${{ matrix.name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Workflow trigger info
echo "### Workflow Information" >> $GITHUB_STEP_SUMMARY
echo "- **Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "- **Manual Build Type**: ${{ github.event.inputs.build_type }}" >> $GITHUB_STEP_SUMMARY
echo "- **Upload Artifacts**: ${{ github.event.inputs.upload_artifacts }}" >> $GITHUB_STEP_SUMMARY
echo "- **Run Sanitizers**: ${{ github.event.inputs.run_sanitizers }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Configuration info
echo "### Configuration" >> $GITHUB_STEP_SUMMARY
echo "- **Platform**: ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY
echo "- **Compiler**: ${{ matrix.cc }}/${{ matrix.cxx }}" >> $GITHUB_STEP_SUMMARY
echo "- **Build Type**: ${{ env.BUILD_TYPE }}" >> $GITHUB_STEP_SUMMARY
echo "- **Build Mode**: Full (matches release)" >> $GITHUB_STEP_SUMMARY
echo "- **Features**: gRPC, JSON, AI, ImGui Test Engine" >> $GITHUB_STEP_SUMMARY
if [[ "${{ runner.os }}" == "Windows" ]]; then
echo "- **vcpkg Triplet**: x64-windows-static" >> $GITHUB_STEP_SUMMARY
if [[ "${{ steps.vcpkg.outcome }}" != "" ]]; then
echo "- **vcpkg Setup**: ${{ steps.vcpkg.outcome }}" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Build status
echo "### Build Status" >> $GITHUB_STEP_SUMMARY
CONFIGURE_OUTCOME="${{ steps.configure_windows.outcome || steps.configure_unix.outcome }}"
if [[ "$CONFIGURE_OUTCOME" == "success" ]]; then
echo "- ✅ Configure: Success" >> $GITHUB_STEP_SUMMARY
else
echo "- ❌ Configure: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ steps.build.outcome }}" == "success" ]]; then
echo "- ✅ Build: Success" >> $GITHUB_STEP_SUMMARY
else
echo "- ❌ Build: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ steps.test_stable.outcome }}" == "success" ]]; then
echo "- ✅ Stable Tests: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "- ❌ Stable Tests: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ steps.test_experimental.outcome }}" == "success" ]]; then
echo "- ✅ Experimental Tests: Passed" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ steps.test_experimental.outcome }}" == "failure" ]]; then
echo "- ⚠️ Experimental Tests: Failed (informational)" >> $GITHUB_STEP_SUMMARY
else
echo "- ⏭️ Experimental Tests: Skipped" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Artifacts info
if [[ "${{ runner.os }}" == "Windows" && "${{ steps.build.outcome }}" == "success" ]]; then
if [[ "${{ github.event.inputs.upload_artifacts }}" == "true" || "${{ github.event_name }}" == "push" ]]; then
echo "### Artifacts" >> $GITHUB_STEP_SUMMARY
echo "- 📦 Windows build artifacts uploaded: yaze-windows-ci-${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
fi
# Test results
if [ -f build/stable_test_results.xml ]; then
echo "### Test Results" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
grep -E "tests=|failures=|errors=" build/stable_test_results.xml | head -1 || echo "Test summary not available"
echo '```' >> $GITHUB_STEP_SUMMARY
fi
code-quality: code-quality:
name: "Code Quality" name: "Code Quality"
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
continue-on-error: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }} continue-on-error: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') }}
@@ -724,7 +260,7 @@ jobs:
xargs clang-tidy-14 --header-filter='src/.*\.(h|hpp)$' xargs clang-tidy-14 --header-filter='src/.*\.(h|hpp)$'
memory-sanitizer: memory-sanitizer:
name: "🔬 Memory Sanitizer" name: "Memory Sanitizer"
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
if: | if: |
github.event_name == 'pull_request' || github.event_name == 'pull_request' ||
@@ -761,8 +297,10 @@ jobs:
run: ctest --output-on-failure run: ctest --output-on-failure
z3ed-agent-test: z3ed-agent-test:
name: "🤖 z3ed Agent" name: "z3ed Agent"
runs-on: macos-14 runs-on: macos-14
# Only run on master/develop push, not on PRs (moved to nightly for PRs)
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -777,16 +315,23 @@ jobs:
cmake -B build_test -G Ninja \ cmake -B build_test -G Ninja \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DZ3ED_AI=ON \ -DZ3ED_AI=ON \
-DYAZE_BUILD_Z3ED=ON -DYAZE_BUILD_Z3ED=ON \
-DYAZE_ENABLE_AI_RUNTIME=ON \
-DYAZE_ENABLE_REMOTE_AUTOMATION=ON \
-DYAZE_BUILD_AGENT_UI=ON
cmake --build build_test --target z3ed cmake --build build_test --target z3ed
- name: Start Ollama - name: Start Ollama
env:
OLLAMA_MODEL: qwen2.5-coder:0.5b
run: | run: |
ollama serve & ollama serve &
sleep 10 sleep 10
ollama pull qwen2.5-coder:7b ollama pull "$OLLAMA_MODEL"
- name: Run Test Suite - name: Run Test Suite
env:
OLLAMA_MODEL: qwen2.5-coder:0.5b
run: | run: |
chmod +x ./scripts/agent_test_suite.sh chmod +x ./scripts/agent_test_suite.sh
./scripts/agent_test_suite.sh ollama ./scripts/agent_test_suite.sh ollama

View File

@@ -1,16 +1,7 @@
name: Doxygen Documentation name: Doxygen Documentation Check
# Only run when documentation-related files are modified # Only run when documentation-related files are modified
on: on:
push:
branches: [ master ]
paths:
- 'src/**/*.h'
- 'src/**/*.cc'
- 'src/**/*.cpp'
- 'docs/**'
- 'Doxyfile'
- '.github/workflows/doxy.yml'
pull_request: pull_request:
branches: [ master ] branches: [ master ]
paths: paths:
@@ -21,12 +12,10 @@ on:
- 'Doxyfile' - 'Doxyfile'
- '.github/workflows/doxy.yml' - '.github/workflows/doxy.yml'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel # A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs: jobs:
generate-docs: check-docs:
name: Generate Documentation name: Check Documentation Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -35,47 +24,26 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install dependencies - name: Install Graphviz
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y doxygen graphviz sudo apt-get install -y graphviz
- name: Check if documentation build is needed - name: Install Doxygen 1.10.0
id: changes uses: ssciwr/doxygen-install@v1
run: | with:
# Check if this is the first commit or if docs-related files changed version: "1.10.0"
if git show --name-only HEAD | grep -E '\.(h|cc|cpp|md)$|Doxyfile'; then
echo "docs_changed=true" >> $GITHUB_OUTPUT
echo "📝 Documentation-related files have changed"
else
echo "docs_changed=false" >> $GITHUB_OUTPUT
echo " No documentation changes detected"
fi
- name: Clean previous build
if: steps.changes.outputs.docs_changed == 'true'
run: rm -rf html
- name: Generate Doxygen documentation - name: Generate Doxygen documentation
if: steps.changes.outputs.docs_changed == 'true'
uses: mattnotmitt/doxygen-action@v1.9.8
with:
doxyfile-path: "./Doxyfile"
working-directory: "."
- name: Deploy to GitHub Pages
if: steps.changes.outputs.docs_changed == 'true'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./html
commit_message: 'docs: update API documentation'
- name: Summary
run: | run: |
if [[ "${{ steps.changes.outputs.docs_changed }}" == "true" ]]; then mkdir -p build/docs
echo "✅ Documentation generated and deployed successfully" doxygen Doxyfile
echo "📖 View at: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}"
- name: Verify Generation
run: |
if [ -d "build/docs/html" ]; then
echo "✅ Documentation generated successfully"
else else
echo "⏭️ Documentation build skipped - no relevant changes detected" echo " Documentation generation failed"
exit 1
fi fi

352
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,352 @@
name: Nightly Test Suite
on:
# Disabled scheduled runs until test infrastructure matures
# schedule:
# - cron: '0 3 * * *'
workflow_dispatch:
inputs:
test_suites:
description: 'Test suites to run'
required: false
default: 'all'
type: choice
options:
- all
- experimental
- benchmarks
- gui_e2e
- extended_integration
env:
BUILD_TYPE: RelWithDebInfo
jobs:
experimental-ai-tests:
name: "Experimental AI Tests - ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
if: |
github.event_name == 'schedule' ||
github.event.inputs.test_suites == 'all' ||
github.event.inputs.test_suites == 'experimental'
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-14, windows-2022]
include:
- os: ubuntu-22.04
platform: linux
preset: ci-linux
- os: macos-14
platform: macos
preset: ci-macos
- os: windows-2022
platform: windows
preset: ci-windows-ai
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
- name: Configure with AI runtime enabled
run: |
cmake --preset ${{ matrix.preset }} \
-B build_nightly \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
-DYAZE_ENABLE_AI_RUNTIME=ON \
-DYAZE_ENABLE_GRPC=ON \
-DYAZE_BUILD_AGENT_UI=ON
- name: Build project
run: |
cmake --build build_nightly \
--config ${{ env.BUILD_TYPE }} \
--target yaze_test_experimental \
--parallel
- name: Setup Ollama (Linux/macOS)
if: runner.os != 'Windows'
run: |
if [ "${{ runner.os }}" = "macOS" ]; then
brew install ollama || true
else
curl -fsSL https://ollama.com/install.sh | sh || true
fi
ollama serve &
sleep 10
ollama pull qwen2.5-coder:0.5b || true
continue-on-error: true
- name: Run experimental AI tests
run: |
cd build_nightly
ctest -L experimental \
--output-on-failure \
--timeout 600 \
--output-junit experimental_results.xml
continue-on-error: true
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: experimental-test-results-${{ matrix.platform }}
path: build_nightly/experimental_results.xml
retention-days: 30
gui-e2e-tests:
name: "GUI E2E Tests - ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
if: |
github.event_name == 'schedule' ||
github.event.inputs.test_suites == 'all' ||
github.event.inputs.test_suites == 'gui_e2e'
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-14] # Windows GUI tests are flaky in CI
include:
- os: ubuntu-22.04
platform: linux
preset: ci-linux
- os: macos-14
platform: macos
preset: ci-macos
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
- name: Install GUI dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y xvfb libgl1-mesa-dev libglu1-mesa-dev
- name: Build project
uses: ./.github/actions/build-project
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
build-type: ${{ env.BUILD_TYPE }}
- name: Run GUI E2E tests (Linux with Xvfb)
if: runner.os == 'Linux'
run: |
xvfb-run -a ./build/bin/yaze_test_gui \
--e2e \
--nogui \
--output-junit gui_e2e_results.xml
continue-on-error: true
- name: Run GUI E2E tests (macOS)
if: runner.os == 'macOS'
run: |
./build/bin/yaze_test_gui \
--e2e \
--nogui \
--output-junit gui_e2e_results.xml
continue-on-error: true
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: gui-e2e-results-${{ matrix.platform }}
path: gui_e2e_results.xml
retention-days: 30
benchmark-tests:
name: "Performance Benchmarks"
runs-on: ubuntu-22.04
if: |
github.event_name == 'schedule' ||
github.event.inputs.test_suites == 'all' ||
github.event.inputs.test_suites == 'benchmarks'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: linux
preset: ci-linux
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
- name: Build benchmarks
run: |
cmake --preset ci-linux \
-B build_bench \
-DCMAKE_BUILD_TYPE=Release \
-DYAZE_BUILD_TESTS=ON
cmake --build build_bench \
--config Release \
--target yaze_test_benchmark \
--parallel
- name: Run benchmarks
run: |
./build_bench/bin/yaze_test_benchmark \
--benchmark_format=json \
--benchmark_out=benchmark_results.json
continue-on-error: true
- name: Upload benchmark results
if: always()
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: benchmark_results.json
retention-days: 90
- name: Compare with baseline (if exists)
if: ${{ hashFiles('benchmark_baseline.json') != '' }}
run: |
# Compare current results with baseline
# This would use a tool like google/benchmark's compare.py
echo "Benchmark comparison would happen here"
continue-on-error: true
extended-integration-tests:
name: "Extended Integration Tests"
runs-on: ubuntu-22.04
if: |
github.event_name == 'schedule' ||
github.event.inputs.test_suites == 'all' ||
github.event.inputs.test_suites == 'extended_integration'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: linux
preset: ci-linux
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
- name: Build with full features
run: |
cmake --preset ci-linux \
-B build_extended \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
-DYAZE_ENABLE_GRPC=ON \
-DYAZE_ENABLE_JSON=ON \
-DYAZE_ENABLE_HTTP_API=ON \
-DYAZE_BUILD_AGENT_UI=ON
cmake --build build_extended \
--config ${{ env.BUILD_TYPE }} \
--parallel
- name: Run extended integration tests
run: |
cd build_extended
# Run all integration tests with extended timeout
ctest -L integration \
--output-on-failure \
--timeout 1200 \
--output-junit extended_integration_results.xml
- name: Run HTTP API tests
if: ${{ hashFiles('scripts/agents/test-http-api.sh') != '' }}
run: |
chmod +x scripts/agents/test-http-api.sh
scripts/agents/test-http-api.sh
continue-on-error: true
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: extended-integration-results
path: build_extended/extended_integration_results.xml
retention-days: 30
nightly-summary:
name: "Nightly Test Summary"
runs-on: ubuntu-latest
if: always()
needs: [
experimental-ai-tests,
gui-e2e-tests,
benchmark-tests,
extended-integration-tests
]
steps:
- name: Generate summary
run: |
echo "# Nightly Test Results - $(date +'%Y-%m-%d')" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Experimental AI Tests
if [ "${{ needs.experimental-ai-tests.result }}" == "success" ]; then
echo "✅ **Experimental AI Tests:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.experimental-ai-tests.result }}" == "skipped" ]; then
echo "⏭️ **Experimental AI Tests:** Skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Experimental AI Tests:** Failed" >> $GITHUB_STEP_SUMMARY
fi
# GUI E2E Tests
if [ "${{ needs.gui-e2e-tests.result }}" == "success" ]; then
echo "✅ **GUI E2E Tests:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.gui-e2e-tests.result }}" == "skipped" ]; then
echo "⏭️ **GUI E2E Tests:** Skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **GUI E2E Tests:** Failed" >> $GITHUB_STEP_SUMMARY
fi
# Benchmark Tests
if [ "${{ needs.benchmark-tests.result }}" == "success" ]; then
echo "✅ **Performance Benchmarks:** Completed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.benchmark-tests.result }}" == "skipped" ]; then
echo "⏭️ **Performance Benchmarks:** Skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Performance Benchmarks:** Failed" >> $GITHUB_STEP_SUMMARY
fi
# Extended Integration Tests
if [ "${{ needs.extended-integration-tests.result }}" == "success" ]; then
echo "✅ **Extended Integration Tests:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.extended-integration-tests.result }}" == "skipped" ]; then
echo "⏭️ **Extended Integration Tests:** Skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Extended Integration Tests:** Failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "*Nightly tests include comprehensive suites not run during PR/push CI.*" >> $GITHUB_STEP_SUMMARY
- name: Send notification (if configured)
if: ${{ failure() && vars.SLACK_WEBHOOK_URL != '' }}
run: |
# Send notification about nightly test failures
echo "Notification would be sent here"
continue-on-error: true

View File

@@ -6,399 +6,335 @@ on:
- 'v*' - 'v*'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
tag: version:
description: 'Release tag (e.g., v0.3.2)' description: 'Version to release (e.g., v1.0.0)'
required: true required: true
type: string type: string
permissions:
contents: write
env: env:
BUILD_TYPE: Release VERSION: ${{ github.event.inputs.version || github.ref_name }}
jobs: jobs:
build-windows: build:
name: Windows x64 name: "Build Release - ${{ matrix.name }}"
runs-on: windows-2022 runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: "Ubuntu 22.04"
os: ubuntu-22.04
platform: linux
preset: release
- name: "macOS 14"
os: macos-14
platform: macos
preset: release
- name: "Windows 2022"
os: windows-2022
platform: windows
preset: release
steps: steps:
- uses: actions/checkout@v4 - name: Free up disk space (Linux)
with: if: matrix.platform == 'linux'
submodules: recursive
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
vcpkgGitCommitId: 'b2c74683ecfd6a8e7d27ffb0df077f66a9339509'
runVcpkgInstall: true
env:
VCPKG_DEFAULT_TRIPLET: x64-windows-static
VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite'
- name: Install build tools
shell: pwsh
run: |
choco install --no-progress -y nasm
"C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Append
- name: Setup MSVC environment for clang-cl
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Configure clang-cl
shell: pwsh
run: |
Write-Host "Setting up clang-cl compiler"
echo "CC=clang-cl" >> $env:GITHUB_ENV
echo "CXX=clang-cl" >> $env:GITHUB_ENV
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: windows-x64-release-${{ github.run_id }}
restore-keys: |
windows-x64-release-
max-size: 500M
variant: sccache
- name: Restore vcpkg packages cache
uses: actions/cache@v4
with:
path: |
build/vcpkg_installed
${{ github.workspace }}/vcpkg/packages
key: vcpkg-release-${{ hashFiles('vcpkg.json') }}
restore-keys: |
vcpkg-release-
- name: Restore FetchContent dependencies
uses: actions/cache@v4
with:
path: |
build/_deps
key: fetchcontent-release-${{ hashFiles('cmake/grpc*.cmake') }}-v2
restore-keys: |
fetchcontent-release-
- name: Configure sccache
shell: pwsh
run: |
echo "CC=sccache clang-cl" >> $env:GITHUB_ENV
echo "CXX=sccache clang-cl" >> $env:GITHUB_ENV
- name: Configure
id: configure
shell: pwsh
run: |
Write-Host "=== Build Configuration ===" -ForegroundColor Cyan
Write-Host "Compiler: clang-cl"
Write-Host "Build Type: Release"
cmake --version
clang-cl --version
$toolchain = "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake"
cmake -S . -B build `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_C_COMPILER=clang-cl `
-DCMAKE_CXX_COMPILER=clang-cl `
-DCMAKE_TOOLCHAIN_FILE=$toolchain `
-DVCPKG_TARGET_TRIPLET=x64-windows-static `
-DVCPKG_MANIFEST_MODE=ON `
-DYAZE_BUILD_TESTS=OFF `
-DYAZE_BUILD_EMU=ON `
-DYAZE_BUILD_Z3ED=ON `
-DYAZE_BUILD_TOOLS=ON 2>&1 | Tee-Object -FilePath cmake_config.log
- name: Report Configure Failure
if: always() && steps.configure.outcome == 'failure'
shell: pwsh
run: |
Write-Host "::error::CMake configuration failed. Check cmake_config.log for details." -ForegroundColor Red
if (Test-Path cmake_config.log) {
Write-Host "::group::CMake Configuration Log (last 50 lines)"
Get-Content cmake_config.log -Tail 50
Write-Host "::endgroup::"
}
exit 1
- name: Build
id: build
shell: pwsh
run: cmake --build build --config Release --parallel 4 -- /p:CL_MPcount=4 2>&1 | Tee-Object -FilePath build.log
- name: Report Build Failure
if: always() && steps.build.outcome == 'failure'
shell: pwsh
run: |
Write-Host "::error::Build failed. Check build.log for details." -ForegroundColor Red
if (Test-Path build.log) {
Write-Host "::group::Build Log (last 100 lines)"
Get-Content build.log -Tail 100
Write-Host "::endgroup::"
# Check for specific error patterns
if (Select-String -Path "build.log" -Pattern "vcpkg" -Quiet) {
Write-Host "`n::group::vcpkg-related errors" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "vcpkg.*error" -CaseSensitive:$false | Select-Object -First 10
Write-Host "::endgroup::"
}
if (Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" -Quiet) {
Write-Host "`n::group::Linker errors" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "LNK[0-9]{4}" | Select-Object -First 10
Write-Host "::endgroup::"
}
if (Select-String -Path "build.log" -Pattern "fatal error" -Quiet) {
Write-Host "`n::group::Fatal errors" -ForegroundColor Yellow
Select-String -Path "build.log" -Pattern "fatal error" | Select-Object -First 10
Write-Host "::endgroup::"
}
}
# List vcpkg installed packages
$vcpkgExe = "${{ github.workspace }}/vcpkg/vcpkg.exe"
if (Test-Path $vcpkgExe) {
Write-Host "`n::group::Installed vcpkg packages"
& $vcpkgExe list
Write-Host "::endgroup::"
}
exit 1
- name: Package
shell: pwsh
run: |
New-Item -ItemType Directory -Path release
Copy-Item -Path build/bin/Release/* -Destination release/ -Recurse
Copy-Item -Path assets -Destination release/ -Recurse
Copy-Item LICENSE, README.md -Destination release/
Compress-Archive -Path release/* -DestinationPath yaze-windows-x64.zip
- name: Upload Build Logs on Failure (Windows)
if: always() && (steps.configure.outcome == 'failure' || steps.build.outcome == 'failure')
uses: actions/upload-artifact@v4
with:
name: build-logs-windows
path: |
cmake_config.log
build.log
if-no-files-found: ignore
retention-days: 7
- uses: actions/upload-artifact@v4
if: steps.build.outcome == 'success'
with:
name: yaze-windows-x64
path: yaze-windows-x64.zip
build-macos:
name: macOS Universal
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: brew install ninja cmake
- name: Configure arm64
id: configure_arm64
run: |
cmake -S . -B build-arm64 -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DYAZE_BUILD_TESTS=OFF \
-DYAZE_BUILD_EMU=ON \
-DYAZE_BUILD_Z3ED=ON \
-DYAZE_BUILD_TOOLS=ON 2>&1 | tee cmake_config_arm64.log
- name: Build arm64
id: build_arm64
run: cmake --build build-arm64 --config Release 2>&1 | tee build_arm64.log
- name: Configure x86_64
id: configure_x86_64
run: |
cmake -S . -B build-x86_64 -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
-DYAZE_BUILD_TESTS=OFF \
-DYAZE_BUILD_EMU=ON \
-DYAZE_BUILD_Z3ED=ON \
-DYAZE_BUILD_TOOLS=ON 2>&1 | tee cmake_config_x86_64.log
- name: Build x86_64
id: build_x86_64
run: cmake --build build-x86_64 --config Release 2>&1 | tee build_x86_64.log
- name: Create Universal Binary
run: |
cp -R build-arm64/bin/yaze.app yaze.app
lipo -create \
build-arm64/bin/yaze.app/Contents/MacOS/yaze \
build-x86_64/bin/yaze.app/Contents/MacOS/yaze \
-output yaze.app/Contents/MacOS/yaze
lipo -info yaze.app/Contents/MacOS/yaze
- name: Create DMG
run: |
hdiutil create -fs HFS+ -srcfolder yaze.app \
-volname "yaze" yaze-macos-universal.dmg
- name: Upload Build Logs on Failure (macOS)
if: always() && (steps.configure_arm64.outcome == 'failure' || steps.build_arm64.outcome == 'failure' || steps.configure_x86_64.outcome == 'failure' || steps.build_x86_64.outcome == 'failure')
uses: actions/upload-artifact@v4
with:
name: build-logs-macos
path: |
cmake_config_arm64.log
build_arm64.log
cmake_config_x86_64.log
build_x86_64.log
if-no-files-found: ignore
retention-days: 7
- uses: actions/upload-artifact@v4
if: steps.build_arm64.outcome == 'success' && steps.build_x86_64.outcome == 'success'
with:
name: yaze-macos-universal
path: yaze-macos-universal.dmg
build-linux:
name: Linux x64
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Free disk space
run: | run: |
echo "=== Disk space before cleanup ==="
df -h
sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc sudo rm -rf /opt/ghc
sudo apt-get clean sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
echo "=== Disk space after cleanup ==="
df -h
- name: Install dependencies - name: Checkout code
run: | uses: actions/checkout@v4
sudo apt-get update
sudo apt-get install -y \
build-essential ninja-build pkg-config \
libglew-dev libxext-dev libwavpack-dev libboost-all-dev \
libpng-dev python3-dev \
libasound2-dev libpulse-dev \
libx11-dev libxrandr-dev libxcursor-dev libxinerama-dev libxi-dev \
libxss-dev libxxf86vm-dev libxkbcommon-dev libwayland-dev libdecor-0-dev \
libgtk-3-dev libdbus-1-dev
- name: Configure
id: configure
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DYAZE_BUILD_TESTS=OFF \
-DYAZE_BUILD_EMU=ON \
-DYAZE_BUILD_Z3ED=ON \
-DYAZE_BUILD_TOOLS=ON \
-DNFD_PORTAL=ON 2>&1 | tee cmake_config.log
- name: Build
id: build
run: cmake --build build --config Release 2>&1 | tee build.log
- name: Package
run: |
mkdir -p release
cp build/bin/yaze release/
cp -r assets release/
cp LICENSE README.md release/
tar -czf yaze-linux-x64.tar.gz -C release .
- name: Upload Build Logs on Failure (Linux)
if: always() && (steps.configure.outcome == 'failure' || steps.build.outcome == 'failure')
uses: actions/upload-artifact@v4
with: with:
name: build-logs-linux submodules: recursive
path: |
cmake_config.log
build.log
if-no-files-found: ignore
retention-days: 7
- uses: actions/upload-artifact@v4 - name: Compute dependency lock hash
if: steps.build.outcome == 'success' id: deps-hash
with: shell: bash
name: yaze-linux-x64
path: yaze-linux-x64.tar.gz
create-release:
name: Create Release
needs: [build-windows, build-macos, build-linux]
runs-on: ubuntu-latest
if: always() && (needs.build-windows.result == 'success' || needs.build-macos.result == 'success' || needs.build-linux.result == 'success')
steps:
- uses: actions/checkout@v4
- name: Determine tag
id: tag
run: | run: |
if [ "${{ github.event_name }}" = "push" ]; then python_cmd="$(command -v python3 || command -v python || true)"
echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT if [ -z "$python_cmd" ]; then
else echo "hash=none" >> "$GITHUB_OUTPUT"
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT exit 0
fi fi
hash=$("$python_cmd" - <<'PY'
import hashlib
import pathlib
path = pathlib.Path("cmake/dependencies.lock")
if path.is_file():
print(hashlib.sha256(path.read_bytes()).hexdigest())
else:
print("none")
PY
)
echo "hash=$hash" >> "$GITHUB_OUTPUT"
- name: Download artifacts - name: Setup build environment
uses: actions/download-artifact@v4 uses: ./.github/actions/setup-build
with: with:
path: artifacts platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
cache-key: ${{ steps.deps-hash.outputs.hash }}
- name: Display structure - name: Build project
run: ls -R artifacts uses: ./.github/actions/build-project
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
build-type: Release
- name: Create release notes - name: Patch cmake_install.cmake (Unix)
id: notes if: matrix.platform == 'linux' || matrix.platform == 'macos'
shell: bash
run: | run: |
TAG="${{ steps.tag.outputs.tag }}" cd build
VERSION="${TAG#v}" # Create a Python script to patch cmake_install.cmake
python3 << 'EOF'
cat > release_notes.md << 'EOF' import re
## yaze ${{ steps.tag.outputs.tag }} with open('cmake_install.cmake', 'r') as f:
content = f.read()
### Downloads # Wrap include() statements with if(EXISTS)
- **Windows**: `yaze-windows-x64.zip` pattern = r'(\s*)include\("(.*)/_deps/([^"]+)/cmake_install\.cmake"\)'
- **macOS**: `yaze-macos-universal.dmg` (Universal Binary) replacement = r'\1if(EXISTS "\2/_deps/\3/cmake_install.cmake")\n\1 include("\2/_deps/\3/cmake_install.cmake")\n\1endif()'
- **Linux**: `yaze-linux-x64.tar.gz` content = re.sub(pattern, replacement, content)
with open('cmake_install.cmake', 'w') as f:
### Installation f.write(content)
print("Patched cmake_install.cmake to handle missing dependency install scripts")
**Windows**: Extract the ZIP file and run `yaze.exe`
**macOS**: Open the DMG and drag yaze.app to Applications
**Linux**: Extract the tarball and run `./yaze`
### Changes
See the [changelog](https://github.com/${{ github.repository }}/blob/develop/docs/H1-changelog.md) for details.
EOF EOF
cat release_notes.md - name: Clean old packages before CPack
shell: bash
run: |
echo "Cleaning old package files to ensure fresh generation"
rm -rf build/packages build/_CPack_Packages
rm -f build/*.deb build/*.tar.gz build/*.dmg build/*.zip build/*.exe
mkdir -p build/packages
- name: Package artifacts (Linux)
if: matrix.platform == 'linux'
run: |
cd build
cpack -G DEB -G TGZ
echo "=== Contents of packages directory ==="
ls -la packages/ 2>/dev/null || echo "No packages directory"
echo "=== Package files created ==="
ls -la packages/*.deb packages/*.tar.gz 2>/dev/null || echo "No packages found"
- name: Package artifacts (macOS)
if: matrix.platform == 'macos'
run: |
cd build
cpack -G DragNDrop
echo "=== Contents of packages directory ==="
ls -la packages/ 2>/dev/null || echo "No packages directory"
echo "=== Package files created ==="
ls -la packages/*.dmg 2>/dev/null || echo "No packages found"
- name: Create notarized bundle (macOS)
if: matrix.platform == 'macos'
shell: bash
run: |
chmod +x ./scripts/create-macos-bundle.sh
./scripts/create-macos-bundle.sh ${{ env.VERSION }} yaze-${{ env.VERSION }}-bundle || true
if [ -f "yaze-${{ env.VERSION }}-bundle.dmg" ]; then
mkdir -p build/packages
mv yaze-${{ env.VERSION }}-bundle.dmg build/packages/
fi
- name: Patch cmake_install.cmake (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
cd build
# Wrap include() statements with if(EXISTS) to handle missing dependency install scripts
$nl = [Environment]::NewLine
$content = Get-Content cmake_install.cmake -Raw
$content = $content -replace '(\s+)include\("(.*)/_deps/([^"]+)/cmake_install\.cmake"\)', "`$1if(EXISTS `"`$2/_deps/`$3/cmake_install.cmake`")$nl`$1 include(`"`$2/_deps/`$3/cmake_install.cmake`")$nl`$1endif()"
Set-Content cmake_install.cmake $content
Write-Host "Patched cmake_install.cmake to handle missing dependency install scripts"
- name: Package artifacts (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
cd build
cpack -G NSIS -G ZIP
Write-Host "=== Contents of packages directory ==="
Get-ChildItem packages -ErrorAction SilentlyContinue
Write-Host "=== Package files created ==="
Get-ChildItem packages/*.exe, packages/*.zip -ErrorAction SilentlyContinue
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: yaze-${{ matrix.platform }}-${{ env.VERSION }}
path: |
build/packages/*.deb
build/packages/*.tar.gz
build/packages/*.dmg
build/packages/*.exe
build/packages/*.zip
if-no-files-found: warn
retention-days: 30
test:
name: "Test Release - ${{ matrix.name }}"
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: "Ubuntu 22.04"
os: ubuntu-22.04
platform: linux
preset: release
- name: "macOS 14"
os: macos-14
platform: macos
preset: release
- name: "Windows 2022"
os: windows-2022
platform: windows
preset: release
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Compute dependency lock hash
id: deps-hash
shell: bash
run: |
python_cmd="$(command -v python3 || command -v python || true)"
if [ -z "$python_cmd" ]; then
echo "hash=none" >> "$GITHUB_OUTPUT"
exit 0
fi
hash=$("$python_cmd" - <<'PY'
import hashlib
import pathlib
path = pathlib.Path("cmake/dependencies.lock")
if path.is_file():
print(hashlib.sha256(path.read_bytes()).hexdigest())
else:
print("none")
PY
)
echo "hash=$hash" >> "$GITHUB_OUTPUT"
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
cache-key: ${{ steps.deps-hash.outputs.hash }}
- name: Build project
uses: ./.github/actions/build-project
with:
platform: ${{ matrix.platform }}
preset: ${{ matrix.preset }}
build-type: Release
- name: Run tests
uses: ./.github/actions/run-tests
with:
test-type: stable
preset: ${{ matrix.preset }}
create-release:
name: "Create Release"
needs: [build] # Tests are informational only in pre-1.0
runs-on: ubuntu-22.04
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Display downloaded artifacts
run: |
echo "=== Downloaded artifacts structure ==="
ls -laR artifacts/
echo "=== Package files found ==="
find artifacts -type f \( -name "*.zip" -o -name "*.exe" -o -name "*.deb" -o -name "*.tar.gz" -o -name "*.dmg" \)
- name: Reorganize artifacts
run: |
# Flatten the artifact directory structure
mkdir -p release-files
find artifacts -type f \( -name "*.zip" -o -name "*.exe" -o -name "*.deb" -o -name "*.tar.gz" -o -name "*.dmg" \) -exec cp {} release-files/ \;
echo "=== Files in release-files ==="
ls -la release-files/
- name: Generate release checksums
run: |
cd release-files
sha256sum * > checksums.txt
cat checksums.txt
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
tag_name: ${{ steps.tag.outputs.tag }} tag_name: ${{ env.VERSION }}
name: yaze ${{ steps.tag.outputs.tag }} name: "YAZE ${{ env.VERSION }}"
body_path: release_notes.md body: |
draft: false ## What's Changed
prerelease: ${{ contains(steps.tag.outputs.tag, '-') }}
This release includes:
- Cross-platform builds for Linux, macOS, and Windows
- Improved dependency management with CPM.cmake
- Enhanced CI/CD pipeline with parallel execution
- Automated packaging and release creation
## Downloads
### Linux
- **DEB Package**: `yaze-*.deb` (Ubuntu/Debian)
- **Tarball**: `yaze-*.tar.gz` (Generic Linux)
### macOS
- **DMG**: `yaze-*.dmg` (macOS 11.0+)
### Windows
- **Installer**: `yaze-*.exe` (Windows 10/11)
- **ZIP**: `yaze-*.zip` (Portable)
## Installation
### Linux (DEB)
```bash
sudo dpkg -i yaze-*.deb
```
### macOS
```bash
# Mount the DMG and drag to Applications
open yaze-*.dmg
```
### Windows
```bash
# Run the installer
yaze-*.exe
```
files: | files: |
artifacts/yaze-windows-x64/* release-files/*
artifacts/yaze-macos-universal/* draft: false
artifacts/yaze-linux-x64/* prerelease: ${{ contains(env.VERSION, 'alpha') || contains(env.VERSION, 'beta') || contains(env.VERSION, 'rc') }}
fail_on_unmatched_files: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,26 @@
build-essential
ninja-build
pkg-config
ccache
libglew-dev
libxext-dev
libwavpack-dev
libboost-all-dev
libpng-dev
python3-dev
libasound2-dev
libpulse-dev
libaudio-dev
libx11-dev
libxrandr-dev
libxcursor-dev
libxinerama-dev
libxi-dev
libxss-dev
libxxf86vm-dev
libxkbcommon-dev
libwayland-dev
libdecor-0-dev
libgtk-3-dev
libdbus-1-dev
libgtk-3-0

88
.github/workflows/security.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Security Scanning
on:
push:
branches: [ "master", "develop" ]
pull_request:
branches: [ "master", "develop" ]
schedule:
- cron: '0 2 * * 1' # Weekly on Monday at 2 AM
workflow_dispatch:
jobs:
codeql:
name: "CodeQL Analysis"
runs-on: ubuntu-22.04
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality
- name: Setup build environment
uses: ./.github/actions/setup-build
with:
platform: linux
preset: ci
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
- name: Build project
uses: ./.github/actions/build-project
with:
platform: linux
preset: ci
build-type: RelWithDebInfo
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
dependency-scan:
name: "Dependency Scan"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
dependabot:
name: "Dependabot"
runs-on: ubuntu-22.04
if: github.event_name == 'schedule'
steps:
- name: Dependabot metadata
run: |
echo "Dependabot is configured via .github/dependabot.yml"
echo "This job runs weekly to ensure dependencies are up to date"

175
.github/workflows/wasm-dev.yml vendored Normal file
View File

@@ -0,0 +1,175 @@
name: WASM Development Build
on:
pull_request:
paths:
- 'src/**'
- 'CMakeLists.txt'
- 'CMakePresets.json'
- 'scripts/build-wasm.sh'
- '.github/workflows/wasm-dev.yml'
workflow_dispatch:
inputs:
debug_build:
description: 'Build debug version'
required: false
default: true
type: boolean
run_tests:
description: 'Run WASM tests (experimental)'
required: false
default: false
type: boolean
jobs:
wasm-build:
name: "WASM Build (Debug)"
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.51
actions-cache-folder: 'emsdk-cache'
- name: Setup Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Setup CMake
uses: jwlawson/actions-setup-cmake@v1.14
with:
cmake-version: '3.27.x'
- name: Setup ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: wasm-dev-ccache
max-size: 1G
- name: Cache CPM packages
uses: actions/cache@v4
with:
path: ~/.cache/CPM
key: cpm-wasm-debug-${{ hashFiles('**/CMakeLists.txt') }}
restore-keys: |
cpm-wasm-debug-
cpm-wasm-
- name: Verify Emscripten setup
run: |
echo "=== Emscripten Configuration ==="
emcc --version
emcmake --version
echo "=== Node.js Version ==="
node --version
echo "=== Environment ==="
env | grep -i em || true
- name: Build WASM (Debug)
if: github.event.inputs.debug_build != 'false'
run: |
export PATH="/usr/lib/ccache:$PATH"
echo "Building WASM debug version..."
chmod +x scripts/build-wasm.sh
./scripts/build-wasm.sh debug
- name: Build WASM (Release)
if: github.event.inputs.debug_build == 'false'
run: |
export PATH="/usr/lib/ccache:$PATH"
echo "Building WASM release version..."
chmod +x scripts/build-wasm.sh
./scripts/build-wasm.sh release
- name: Verify build output
run: |
BUILD_DIR="build-wasm-debug"
if [ "${{ github.event.inputs.debug_build }}" == "false" ]; then
BUILD_DIR="build-wasm"
fi
echo "=== Build Output Verification ==="
ls -lh "$BUILD_DIR/bin/" || true
# Check for critical files
if [ ! -f "$BUILD_DIR/bin/yaze.wasm" ]; then
echo "ERROR: yaze.wasm not found!"
exit 1
fi
if [ ! -f "$BUILD_DIR/bin/yaze.js" ]; then
echo "ERROR: yaze.js not found!"
exit 1
fi
if [ ! -f "$BUILD_DIR/bin/yaze.html" ]; then
echo "ERROR: yaze.html not found!"
exit 1
fi
# Report file sizes
echo ""
echo "=== File Sizes ==="
du -h "$BUILD_DIR/bin/yaze.wasm"
du -h "$BUILD_DIR/bin/yaze.js"
du -h "$BUILD_DIR/bin/yaze.data" 2>/dev/null || echo "No .data file (lazy loading enabled?)"
# Check for SharedArrayBuffer support
if grep -q "SharedArrayBuffer" "$BUILD_DIR/bin/yaze.js"; then
echo "✓ SharedArrayBuffer support detected"
else
echo "⚠ SharedArrayBuffer not detected - threading may not work"
fi
- name: Run WASM tests (experimental)
if: github.event.inputs.run_tests == 'true'
run: |
echo "WASM tests are experimental and not yet implemented"
# TODO: Add Node.js-based WASM tests here
- name: Upload WASM artifacts
uses: actions/upload-artifact@v4
with:
name: wasm-debug-${{ github.sha }}
path: |
build-wasm-debug/bin/*.wasm
build-wasm-debug/bin/*.js
build-wasm-debug/bin/*.html
build-wasm-debug/bin/*.data
build-wasm/bin/*.wasm
build-wasm/bin/*.js
build-wasm/bin/*.html
build-wasm/bin/*.data
if-no-files-found: warn
retention-days: 7
- name: Build summary
if: always()
run: |
echo "## WASM Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
BUILD_DIR="build-wasm-debug"
BUILD_TYPE="Debug"
if [ "${{ github.event.inputs.debug_build }}" == "false" ]; then
BUILD_DIR="build-wasm"
BUILD_TYPE="Release"
fi
echo "**Build Type:** $BUILD_TYPE" >> $GITHUB_STEP_SUMMARY
echo "**Emscripten Version:** 3.1.51" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f "$BUILD_DIR/bin/yaze.wasm" ]; then
WASM_SIZE=$(du -h "$BUILD_DIR/bin/yaze.wasm" | cut -f1)
echo "**WASM Size:** $WASM_SIZE" >> $GITHUB_STEP_SUMMARY
echo "✓ Build successful" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Build failed - WASM file not generated" >> $GITHUB_STEP_SUMMARY
fi

209
.github/workflows/web-build.yml vendored Normal file
View File

@@ -0,0 +1,209 @@
name: Web Build & Deploy
on:
workflow_dispatch:
push:
branches: [ master, main ]
paths:
- 'src/**'
- 'docs/**'
- 'CMakeLists.txt'
- 'CMakePresets.json'
- 'Doxyfile'
- 'scripts/build-wasm.sh'
jobs:
build-deploy:
runs-on: ubuntu-22.04
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 2
# --- Detect what changed ---
- name: Check for web-only changes
id: changes
run: |
set -e
echo "Event: ${GITHUB_EVENT_NAME}"
NEEDS_BUILD=false
CHANGED=""
if git rev-parse HEAD~1 >/dev/null 2>&1; then
CHANGED=$(git diff --name-only HEAD~1 HEAD || echo "")
else
echo "No previous commit to diff against; forcing WASM build."
NEEDS_BUILD=true
fi
echo "Changed files:"
echo "${CHANGED}"
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
echo "Manual dispatch - force WASM build."
NEEDS_BUILD=true
fi
if echo "${CHANGED}" | grep -qE '\.(cc|cpp|h|hpp|c)$|CMakeLists\.txt|CMakePresets\.json|build-wasm\.sh'; then
echo "C++ or build files changed - full WASM build required"
NEEDS_BUILD=true
fi
echo "needs_wasm_build=${NEEDS_BUILD}" >> "${GITHUB_OUTPUT}"
# --- Cache CPM dependencies ---
- name: Cache CPM packages
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
uses: actions/cache@v4
with:
path: ~/.cache/CPM
key: cpm-wasm-${{ hashFiles('**/CMakeLists.txt') }}
restore-keys: |
cpm-wasm-
# --- Cache WASM build artifacts ---
- name: Cache WASM build
uses: actions/cache@v4
with:
path: |
build_wasm_ai/bin
build_wasm_ai/CMakeCache.txt
build_wasm_ai/CMakeFiles
key: wasm-build-${{ hashFiles('src/**/*.cc', 'src/**/*.h', 'CMakeLists.txt', 'CMakePresets.json') }}
restore-keys: |
wasm-build-
- name: Check cached WASM artifacts
if: steps.changes.outputs.needs_wasm_build == 'false'
id: wasm_cache
run: |
if [ -f build_wasm_ai/bin/yaze.js ] && [ -f build_wasm_ai/bin/yaze.wasm ]; then
echo "Cached WASM found."
echo "has_cached_wasm=true" >> "${GITHUB_OUTPUT}"
else
echo "No cached WASM artifacts; will trigger a build."
echo "has_cached_wasm=false" >> "${GITHUB_OUTPUT}"
fi
# --- Setup Tools (only if WASM build needed or cache missing) ---
- name: Setup Emscripten
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.51
actions-cache-folder: 'emsdk-cache'
- name: Setup Ninja
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Setup CMake
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
uses: jwlawson/actions-setup-cmake@v1.14
with:
cmake-version: '3.27.x'
- name: Setup ccache
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
uses: hendrikmuhs/ccache-action@v1.2
with:
key: wasm-ccache
max-size: 1G
- name: Install Graphviz
run: |
sudo apt-get update
sudo apt-get install -y graphviz
- name: Install Doxygen 1.10.0
uses: ssciwr/doxygen-install@v1
with:
version: "1.10.0"
# --- Build Web App (full build or use cache) ---
- name: Build WASM App
if: steps.changes.outputs.needs_wasm_build == 'true' || steps.wasm_cache.outputs.has_cached_wasm == 'false'
run: |
export PATH="/usr/lib/ccache:$PATH"
chmod +x scripts/build-wasm.sh
./scripts/build-wasm.sh ai
- name: Use cached WASM + update web assets
if: steps.changes.outputs.needs_wasm_build == 'false' && steps.wasm_cache.outputs.has_cached_wasm == 'true'
run: |
echo "Using cached WASM build, updating web assets only..."
mkdir -p build_wasm_ai/dist
# Check if we have cached WASM files
if [ -f build_wasm_ai/bin/yaze.wasm ]; then
cp build_wasm_ai/bin/yaze.html build_wasm_ai/dist/index.html
cp build_wasm_ai/bin/yaze.js build_wasm_ai/dist/
cp build_wasm_ai/bin/yaze.wasm build_wasm_ai/dist/
cp build_wasm_ai/bin/yaze.data build_wasm_ai/dist/ 2>/dev/null || true
# CRITICAL: Copy pthread worker script for multi-threading
cp build_wasm_ai/bin/yaze.worker.js build_wasm_ai/dist/ 2>/dev/null || true
else
echo "ERROR: No cached WASM build found. Triggering full build..."
exit 1
fi
# Copy updated web assets (new directory structure)
# Root level files
cp src/web/app.js build_wasm_ai/dist/
# Copy subdirectories
cp -r src/web/styles build_wasm_ai/dist/ 2>/dev/null || true
cp -r src/web/components build_wasm_ai/dist/ 2>/dev/null || true
cp -r src/web/core build_wasm_ai/dist/ 2>/dev/null || true
cp -r src/web/pwa build_wasm_ai/dist/ 2>/dev/null || true
cp -r src/web/icons build_wasm_ai/dist/ 2>/dev/null || true
cp -r src/web/debug build_wasm_ai/dist/ 2>/dev/null || true
# CRITICAL: coi-serviceworker.js must be at root for proper service worker scope
if [ -f src/web/pwa/coi-serviceworker.js ]; then
cp src/web/pwa/coi-serviceworker.js build_wasm_ai/dist/
echo "coi-serviceworker.js copied to root (required for SharedArrayBuffer)"
else
echo "WARNING: coi-serviceworker.js not found!"
fi
# Copy yaze icon
if [ -f assets/yaze.png ]; then
mkdir -p build_wasm_ai/dist/assets
cp assets/yaze.png build_wasm_ai/dist/assets/
fi
echo "Web assets updated!"
# --- Build Documentation ---
- name: Build Documentation
run: |
mkdir -p build/docs
doxygen Doxyfile
# Move docs into dist/docs
mkdir -p build_wasm_ai/dist/docs
mv build/docs/html/* build_wasm_ai/dist/docs/
# --- Deploy ---
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: build_wasm_ai/dist/
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

54
.gitignore vendored
View File

@@ -1,8 +1,8 @@
# Build directories - organized by platform # Build directories - organized by platform (root level only)
build/ /build/
build-*/ /build-*/
out/ /build_*/
build*/ /out/
docs/html/ docs/html/
docs/latex/ docs/latex/
@@ -14,7 +14,7 @@ compile_commands.json
CPackConfig.cmake CPackConfig.cmake
CPackSourceConfig.cmake CPackSourceConfig.cmake
CTestTestfile.cmake CTestTestfile.cmake
Testing/ CMakeUserPresets.json
# Build artifacts # Build artifacts
*.o *.o
@@ -33,16 +33,15 @@ Testing/
*.swo *.swo
*~ *~
.DS_Store .DS_Store
src/ios/yaze_ios.xcodeproj/
src/ios/yaze.xcodeproj/xcuserdata/
src/ios/yaze.xcodeproj/project.xcworkspace/xcuserdata/
# Test outputs # Test outputs
test_screenshots/ test_screenshots/
test_temp_rom.sfc test_temp_rom.sfc
zelda3_v3_test.sfc zelda3_v3_test.sfc
# Logs
yaze_log.txt
*.log
# vcpkg # vcpkg
vcpkg_installed/ vcpkg_installed/
@@ -79,6 +78,7 @@ src/lib/cmake/
src/lib/GL/ src/lib/GL/
src/lib/libpng/ src/lib/libpng/
src/lib/zlib/ src/lib/zlib/
assets/asm/usdasm/
src/app/gui/modules/component.h src/app/gui/modules/component.h
src/ios/macOS/Info-macOS.plist src/ios/macOS/Info-macOS.plist
src/ios/macOS/MainMenu.storyboard src/ios/macOS/MainMenu.storyboard
@@ -93,3 +93,37 @@ recent_files.txt
.gitignore .gitignore
.genkit .genkit
.claude .claude
scripts/__pycache__/
build_gemini
scripts/ai/results/*.json
scripts/ai/results/*.md
scripts/ai/results/*.txt
logs/breakthrough_test.log
logs/cmake_config 2.log
logs/cmake_config 3.log
logs/cmake_config.log
logs/dungeon_debug.log
logs/final_rendering.log
logs/flag_fix_enhanced_test.log
logs/flag_fix_test.log
logs/layout_rendering.log
logs/layout_tiles_test.log
logs/scale_test.log
logs/test_fixed.log
logs/test_output.log
logs/tile_data_test.log
logs/timing_analysis.log
logs/visual_test.log
logs/windows_ci_linker_error.log
logs/yaze_emu_trace.log
logs/yaze_release.wasm
logs/yaze_release.wat
logs/build-logs-Windows 2022 (Clang)/build.log
logs/build-logs-Windows 2022 (Clang)/cmake_config.log
logs/build-logs-Windows 2022 (Clang-CL)/build.log
logs/build-logs-Windows 2022 (Clang-CL)/cmake_config.log
vanilla.sfc
zelda3.sfc
yaze_manual.log
yaze_startup.log
roms/

32
.gitmodules vendored
View File

@@ -1,27 +1,15 @@
[submodule "src/lib/imgui"] [submodule "ext/imgui"]
path = src/lib/imgui path = ext/imgui
url = https://github.com/ocornut/imgui.git url = https://github.com/ocornut/imgui.git
[submodule "assets/asm/alttp-hacker-workspace"] [submodule "ext/SDL"]
path = assets/asm/alttp-hacker-workspace path = ext/SDL
url = https://github.com/scawful/alttp-hacker-workspace.git
[submodule "src/lib/SDL"]
path = src/lib/SDL
url = https://github.com/libsdl-org/SDL.git url = https://github.com/libsdl-org/SDL.git
[submodule "src/lib/asar"] [submodule "ext/asar"]
path = src/lib/asar path = ext/asar
url = https://github.com/RPGHacker/asar.git url = https://github.com/RPGHacker/asar.git
[submodule "src/lib/imgui_test_engine"] [submodule "ext/imgui_test_engine"]
path = src/lib/imgui_test_engine path = ext/imgui_test_engine
url = https://github.com/ocornut/imgui_test_engine.git url = https://github.com/ocornut/imgui_test_engine.git
[submodule "src/lib/nativefiledialog-extended"] [submodule "ext/nativefiledialog-extended"]
path = src/lib/nativefiledialog-extended path = ext/nativefiledialog-extended
url = https://github.com/btzy/nativefiledialog-extended.git url = https://github.com/btzy/nativefiledialog-extended.git
[submodule "assets/asm/usdasm"]
path = assets/asm/usdasm
url = https://github.com/spannerisms/usdasm.git
[submodule "third_party/json"]
path = third_party/json
url = https://github.com/nlohmann/json.git
[submodule "third_party/httplib"]
path = third_party/httplib
url = https://github.com/yhirose/cpp-httplib.git

81
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,81 @@
# Pre-commit hooks for YAZE
# Install with: pip install pre-commit && pre-commit install
repos:
# Clang format
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v18.1.0
hooks:
- id: clang-format
args: [--style=Google, -i]
files: \.(cc|cpp|h|hpp)$
exclude: ^(src/lib/|third_party/)
# Clang tidy
- repo: local
hooks:
- id: clang-tidy
name: clang-tidy
entry: clang-tidy
language: system
args: [--header-filter=src/.*\.(h|hpp)$]
files: \.(cc|cpp)$
exclude: ^(src/lib/|third_party/)
# Cppcheck
- repo: local
hooks:
- id: cppcheck
name: cppcheck
entry: cppcheck
language: system
args: [--enable=warning,style,performance, --inconclusive, --error-exitcode=0]
files: \.(cc|cpp|h|hpp)$
exclude: ^(src/lib/|third_party/)
# General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
args: [--maxkb=1000]
- id: check-case-conflict
- id: check-merge-conflict
- id: mixed-line-ending
args: [--fix=lf]
# CMake formatting
- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13
hooks:
- id: cmake-format
args: [--config-files=cmake-format.yaml]
files: CMakeLists\.txt$|\.cmake$
# Shell script linting
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.9.0.6
hooks:
- id: shellcheck
files: \.(sh|bash)$
# Python linting (for scripts)
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
files: \.py$
exclude: ^(third_party/|build/)
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
files: \.py$
exclude: ^(third_party/|build/)

60
AGENTS.md Normal file
View File

@@ -0,0 +1,60 @@
# Agent Protocol
_Extends: ~/AGENTS.md_
Project-specific operating procedures for AI agents contributing to `yaze`.
## 1. Persona Adoption
**Rule:** You must adopt a specific persona for every session.
* **Source of Truth:** [docs/internal/agents/personas.md](docs/internal/agents/personas.md)
* **Requirement:** Use the exact `Agent ID` from that list in all logs, commits, and board updates.
* **Legacy IDs:** Do not use `CLAUDE_CORE`, `CLAUDE_AIINF`, etc. Use the role-based IDs (e.g., `ai-infra-architect`).
* **System Prompts:** Load the matching persona prompt from `.claude/agents/<agent-id>.md` (accessible to all agents) before starting work.
## 2. Workflows & Coordination
### Quick Tasks (< 30 min)
* **Board:** No update required.
* **Tools:** Use `z3ed agent todo` to track your own sub-steps if helpful.
* **Commit:** Commit directly with a clear message.
### Substantial Work (> 30 min / Multi-file)
1. **Check Context:**
* Read [docs/internal/agents/coordination-board.md](docs/internal/agents/coordination-board.md) for `REQUEST` or `BLOCKER` tags.
* Run `git status` and `git diff` to understand the current state.
2. **Declare Intent:**
* If your work overlaps with an active task on the board, post a note or Request for Comments (RFC) there first.
* Otherwise, log a new entry on the **Coordination Board**.
3. **Execute:**
* Use `z3ed agent todo` to break down the complex task.
* Use `z3ed agent handoff` if you cannot finish in one session.
### Multi-Day Initiatives
* Create a dedicated document using [docs/internal/agents/initiative-template.md](docs/internal/agents/initiative-template.md).
* Link to this document from the Coordination Board.
### Specs & Docs
* Keep one canonical spec per initiative (link it from the board entry and back).
* Add a header with Status/Owner/Created/Last Reviewed/Next Review (≤14 days) and validation/exit criteria.
* Use existing templates (`initiative-template.md`, `release-checklist-template.md`) instead of creating ad-hoc files.
* Archive idle or completed specs to `docs/internal/agents/archive/` with the date; do not open duplicate status pages.
## 3. The Coordination Board
**Location:** `docs/internal/agents/coordination-board.md`
* **Hygiene:** Keep entries concise (≤ 5 lines).
* **Status:** Update your entry status to `COMPLETE` or `ARCHIVED` when done.
* **Maintenance:** Archive completed work weekly to `docs/internal/agents/archive/`.
## 4. Helper Scripts
Located in `scripts/agents/`:
* `run-gh-workflow.sh`: Trigger CI manually.
* `smoke-build.sh`: Fast verification build.
* `test-http-api.sh`: Validate the agent API.
**Log results:** When running these scripts for significant validation, paste the run ID or result summary to the Board.
## 5. Documentation Hygiene
- Follow [docs/internal/agents/doc-hygiene.md](docs/internal/agents/doc-hygiene.md) to avoid doc sprawl.
- Keep specs short, template-driven, and linked to the coordination board; prefer edits over new files.
- Archive completed/idle docs (>=14 days) under `docs/internal/agents/archive/` with dates to keep the root clean.

64
CLAUDE.md Normal file
View File

@@ -0,0 +1,64 @@
# CLAUDE.md
_Extends: ~/AGENTS.md, ~/CLAUDE.md_
C++23 ROM editor for Zelda: A Link to the Past. GUI editor + SNES emulator + AI CLI (`z3ed`).
## Build & Test
```bash
cmake --preset mac-dbg && cmake --build build -j8 # Build
ctest --test-dir build -L stable -j4 # Test
./yaze --rom_file=zelda3.sfc --editor=Dungeon # Run
```
Presets: `mac-dbg`/`lin-dbg`/`win-dbg`, `mac-ai`/`win-ai`, `*-rel`. See `docs/public/build/quick-reference.md`.
## Architecture
| Component | Location | Purpose |
|-----------|----------|---------|
| Rom | `src/app/rom.h` | ROM data access, transactions |
| Editors | `src/app/editor/` | Overworld, Dungeon, Graphics, Palette |
| Graphics | `src/app/gfx/` | Bitmap, Arena (async loading), Tiles |
| Zelda3 | `src/zelda3/` | Overworld (160 maps), Dungeon (296 rooms) |
| Canvas | `src/app/gui/canvas.h` | ImGui canvas with pan/zoom |
| CLI | `src/cli/z3ed.cc` | AI-powered ROM hacking tool |
## Key Patterns
**Graphics refresh**: Update model → `Load*()``Renderer::Get().RenderBitmap()`
**Async loading**: `Arena::Get().QueueDeferredTexture(bitmap, priority)` + process in `Update()`
**Bitmap sync**: Use `set_data()` for bulk updates (syncs `data_` and `surface_->pixels`)
**Theming**: Always use `AgentUI::GetTheme()`, never hardcoded colors
**Multi-area maps**: Always use `Overworld::ConfigureMultiAreaMap()`, never set `area_size` directly
## Naming
- **Load**: ROM → memory
- **Render**: Data → bitmap (CPU)
- **Draw**: Bitmap → screen (GPU)
## Pitfalls
1. Use `set_data()` not `mutable_data()` assignment for bitmap bulk updates
2. Call `ProcessTextureQueue()` every frame
3. Pass `0x800` to `DecompressV2`, never `0`
4. SMC header: `size % 1MB == 512`, not `size % 32KB`
5. Check `rom_->is_loaded()` before ROM operations
## Code Style
Google C++ Style. Use `absl::Status`/`StatusOr<T>` with `RETURN_IF_ERROR()`/`ASSIGN_OR_RETURN()`.
Format: `cmake --build build --target format`
## Docs
- Architecture: `docs/internal/architecture/`
- Build issues: `docs/BUILD-TROUBLESHOOTING.md`
- Tests: `test/README.md`

View File

@@ -8,35 +8,62 @@ set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "Minimum policy version for su
# Set policies for compatibility # Set policies for compatibility
cmake_policy(SET CMP0091 NEW) cmake_policy(SET CMP0091 NEW)
# CMP0091 allows CMAKE_MSVC_RUNTIME_LIBRARY to be set by presets
# Ensure we consistently use the static MSVC runtime (/MT, /MTd) to match vcpkg static triplets # Windows presets specify dynamic CRT (/MD) to avoid linking issues
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
cmake_policy(SET CMP0048 NEW) cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0077 NEW)
# Enable Objective-C only on macOS where it's actually used # Enable Objective-C only on macOS where it's actually used
if(CMAKE_SYSTEM_NAME MATCHES "Darwin") if(DEFINED ENV{YAZE_VERSION_OVERRIDE})
project(yaze VERSION 0.3.2 set(YAZE_VERSION $ENV{YAZE_VERSION_OVERRIDE})
elseif(DEFINED YAZE_VERSION_OVERRIDE)
set(YAZE_VERSION ${YAZE_VERSION_OVERRIDE})
else()
set(YAZE_VERSION "0.5.0")
set(YAZE_VERSION_SUFFIX "")
endif()
if(APPLE)
project(yaze VERSION ${YAZE_VERSION}
DESCRIPTION "Yet Another Zelda3 Editor" DESCRIPTION "Yet Another Zelda3 Editor"
LANGUAGES CXX C OBJC OBJCXX) LANGUAGES CXX C OBJC OBJCXX)
else() else()
project(yaze VERSION 0.3.2 project(yaze VERSION ${YAZE_VERSION}
DESCRIPTION "Yet Another Zelda3 Editor" DESCRIPTION "Yet Another Zelda3 Editor"
LANGUAGES CXX C) LANGUAGES CXX C)
endif() endif()
# Enable ccache for faster rebuilds if available # Set language standards early so dependencies (e.g., Abseil) build with them.
find_program(CCACHE_FOUND ccache) set(CMAKE_CXX_STANDARD 23)
if(CCACHE_FOUND) set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "✓ ccache found, enabling for faster builds") set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache) set(CMAKE_C_STANDARD 99)
set(CMAKE_C_COMPILER_LAUNCHER ccache) set(CMAKE_C_STANDARD_REQUIRED ON)
# Include build options first
include(cmake/options.cmake)
# Enable sccache/ccache for faster rebuilds if available
find_program(SCCACHE_FOUND sccache)
if(SCCACHE_FOUND)
message(STATUS "✓ sccache found, enabling for faster builds")
set(CMAKE_CXX_COMPILER_LAUNCHER sccache)
set(CMAKE_C_COMPILER_LAUNCHER sccache)
else()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
message(STATUS "✓ ccache found, enabling for faster builds")
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
set(CMAKE_C_COMPILER_LAUNCHER ccache)
endif()
endif() endif()
# Set project metadata # Version is defined in project() above - use those variables
set(YAZE_VERSION_MAJOR 0) # CMake automatically sets: yaze_VERSION, yaze_VERSION_MAJOR, yaze_VERSION_MINOR, yaze_VERSION_PATCH
set(YAZE_VERSION_MINOR 3) # These YAZE_VERSION_* aliases are for compatibility with existing code
set(YAZE_VERSION_PATCH 2) set(YAZE_VERSION_MAJOR ${yaze_VERSION_MAJOR})
set(YAZE_VERSION_MINOR ${yaze_VERSION_MINOR})
set(YAZE_VERSION_PATCH ${yaze_VERSION_PATCH})
# Suppress deprecation warnings from submodules # Suppress deprecation warnings from submodules
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Suppress deprecation warnings") set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Suppress deprecation warnings")
@@ -44,64 +71,37 @@ set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Suppress deprecation warnings")
# Add cmake directory to module path # Add cmake directory to module path
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
# Include utility functions
include(cmake/utils.cmake)
# Build Flags
set(YAZE_BUILD_APP ON)
set(YAZE_BUILD_LIB ON)
set(YAZE_BUILD_EMU ON)
set(YAZE_BUILD_Z3ED ON)
set(YAZE_BUILD_TESTS ON CACHE BOOL "Build test suite")
set(YAZE_INSTALL_LIB OFF)
# Testing and CI Configuration
option(YAZE_ENABLE_ROM_TESTS "Enable tests that require ROM files" OFF)
option(YAZE_MINIMAL_BUILD "Minimal build for CI (disable optional features)" OFF)
option(YAZE_UNITY_BUILD "Enable Unity (Jumbo) builds" OFF)
# Feature Flags - Simplified: Always enabled by default (use wrapper classes to hide complexity)
# JSON is header-only with minimal overhead
# gRPC is only used in agent/cli tools, not in core editor runtime
set(YAZE_WITH_JSON ON)
set(YAZE_WITH_GRPC ON)
set(Z3ED_AI ON)
# Minimal build override - disable only the most expensive features
if(YAZE_MINIMAL_BUILD)
set(YAZE_WITH_GRPC OFF)
set(Z3ED_AI OFF)
message(STATUS "✓ Minimal build: gRPC and AI disabled")
else()
message(STATUS "✓ Full build: All features enabled (JSON, gRPC, AI)")
endif()
# Define preprocessor macros for feature flags (so #ifdef works in source code)
if(YAZE_WITH_GRPC)
add_compile_definitions(YAZE_WITH_GRPC)
endif()
if(YAZE_WITH_JSON)
add_compile_definitions(YAZE_WITH_JSON)
endif()
if(Z3ED_AI)
add_compile_definitions(Z3ED_AI)
endif()
option(YAZE_SUPPRESS_WARNINGS "Suppress compiler warnings (use -v preset suffix for verbose)" ON)
set(YAZE_TEST_ROM_PATH "${CMAKE_BINARY_DIR}/bin/zelda3.sfc" CACHE STRING "Path to test ROM file")
# Export compile commands for clangd/LSP
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Platform detection # Platform detection
if(CMAKE_SYSTEM_NAME MATCHES "Darwin") if(APPLE)
set(YAZE_PLATFORM_MACOS ON) if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(YAZE_PLATFORM_IOS ON)
else()
set(YAZE_PLATFORM_MACOS ON)
endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(YAZE_PLATFORM_LINUX ON) set(YAZE_PLATFORM_LINUX ON)
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(YAZE_PLATFORM_WINDOWS ON) set(YAZE_PLATFORM_WINDOWS ON)
endif() endif()
# Include utility functions
include(cmake/utils.cmake)
# Set up dependencies using CPM.cmake
include(cmake/dependencies.cmake)
# Additional configuration options
option(YAZE_SUPPRESS_WARNINGS "Suppress compiler warnings (use -v preset suffix for verbose)" ON)
set(YAZE_TEST_ROM_VANILLA_PATH "" CACHE STRING "Path to vanilla test ROM file")
set(YAZE_TEST_ROM_US_PATH "" CACHE STRING "Path to US test ROM file")
set(YAZE_TEST_ROM_JP_PATH "" CACHE STRING "Path to JP test ROM file")
set(YAZE_TEST_ROM_EU_PATH "" CACHE STRING "Path to EU test ROM file")
set(YAZE_TEST_ROM_EXPANDED_PATH "" CACHE STRING "Path to expanded test ROM file")
set(YAZE_TEST_ROM_PATH "" CACHE STRING "Legacy path to test ROM file (vanilla)")
# Export compile commands for clangd/LSP
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Setup compiler flags and common interface target # Setup compiler flags and common interface target
yaze_add_compiler_flags() yaze_add_compiler_flags()
@@ -121,13 +121,11 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
# Handle dependencies # Handle dependencies
include(cmake/dependencies.cmake)
# Project Files # Project Files
add_subdirectory(src) add_subdirectory(src)
# Tools # Tools
option(YAZE_BUILD_TOOLS "Build development utility tools" OFF)
if(YAZE_BUILD_TOOLS) if(YAZE_BUILD_TOOLS)
message(STATUS "Building development tools") message(STATUS "Building development tools")
add_subdirectory(tools) add_subdirectory(tools)
@@ -139,7 +137,27 @@ if(YAZE_BUILD_TESTS)
endif() endif()
# Code quality targets # Code quality targets
find_program(CLANG_FORMAT NAMES clang-format clang-format-14 clang-format-15 clang-format-16 clang-format-17 clang-format-18) if(YAZE_ENABLE_CLANG_TIDY)
if(NOT YAZE_CLANG_TIDY_EXE)
find_program(YAZE_CLANG_TIDY_EXE NAMES clang-tidy clang-tidy-18 clang-tidy-17 clang-tidy-16)
endif()
if(YAZE_CLANG_TIDY_EXE)
message(STATUS "✓ clang-tidy enabled: ${YAZE_CLANG_TIDY_EXE}")
set(CMAKE_CXX_CLANG_TIDY "${YAZE_CLANG_TIDY_EXE}")
else()
message(WARNING "clang-tidy requested but not found")
endif()
endif()
find_program(CLANG_FORMAT
NAMES clang-format-18 clang-format
HINTS "${HOMEBREW_LLVM_PREFIX}/bin" # Prefer clang-format from Homebrew LLVM
NO_DEFAULT_PATH
)
if(NOT CLANG_FORMAT) # Fallback to generic search if not found in Homebrew prefix
find_program(CLANG_FORMAT NAMES clang-format clang-format-17 clang-format-16 clang-format-15 clang-format-14)
endif()
if(CLANG_FORMAT) if(CLANG_FORMAT)
file(GLOB_RECURSE ALL_SOURCE_FILES file(GLOB_RECURSE ALL_SOURCE_FILES
"${CMAKE_SOURCE_DIR}/src/*.cc" "${CMAKE_SOURCE_DIR}/src/*.cc"
@@ -147,19 +165,24 @@ if(CLANG_FORMAT)
"${CMAKE_SOURCE_DIR}/test/*.cc" "${CMAKE_SOURCE_DIR}/test/*.cc"
"${CMAKE_SOURCE_DIR}/test/*.h") "${CMAKE_SOURCE_DIR}/test/*.h")
add_custom_target(format # Exclude third-party library directories from formatting
COMMAND ${CLANG_FORMAT} -i --style=Google ${ALL_SOURCE_FILES} list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/lib/.*")
add_custom_target(yaze-format
COMMAND ${CLANG_FORMAT} -i --style=file ${ALL_SOURCE_FILES}
COMMENT "Running clang-format on source files" COMMENT "Running clang-format on source files"
) )
add_custom_target(format-check add_custom_target(yaze-format-check
COMMAND ${CLANG_FORMAT} --dry-run --Werror --style=Google ${ALL_SOURCE_FILES} COMMAND ${CLANG_FORMAT} --dry-run --Werror --style=file ${ALL_SOURCE_FILES}
COMMENT "Checking code format" COMMENT "Checking code format"
) )
endif() endif()
# Packaging configuration # Packaging configuration
include(cmake/packaging.cmake) if(NOT YAZE_PLATFORM_IOS)
include(cmake/packaging/cpack.cmake)
endif()
add_custom_target(build_cleaner add_custom_target(build_cleaner
COMMAND ${CMAKE_COMMAND} -E echo "Running scripts/build_cleaner.py --dry-run" COMMAND ${CMAKE_COMMAND} -E echo "Running scripts/build_cleaner.py --dry-run"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
{
"version": 6,
"configurePresets": [
{
"name": "dev-local",
"inherits": "dev",
"binaryDir": "$env{YAZE_BUILD_ROOT}/build",
"environment": {
"CPM_SOURCE_CACHE": "$env{HOME}/.cpm-cache",
"VCPKG_DOWNLOADS": "$env{HOME}/.cache/vcpkg/downloads",
"VCPKG_BINARY_SOURCES": "clear;files,$env{HOME}/.cache/vcpkg/bincache,readwrite"
}
},
{
"name": "wasm-debug-local",
"inherits": "wasm-debug",
"binaryDir": "$env{YAZE_BUILD_ROOT}/build-wasm",
"environment": {
"CPM_SOURCE_CACHE": "$env{HOME}/.cpm-cache",
"VCPKG_DOWNLOADS": "$env{HOME}/.cache/vcpkg/downloads",
"VCPKG_BINARY_SOURCES": "clear;files,$env{HOME}/.cache/vcpkg/bincache,readwrite"
}
},
{
"name": "wasm-release-local",
"inherits": "wasm-release",
"binaryDir": "$env{YAZE_BUILD_ROOT}/build-wasm",
"environment": {
"CPM_SOURCE_CACHE": "$env{HOME}/.cpm-cache",
"VCPKG_DOWNLOADS": "$env{HOME}/.cache/vcpkg/downloads",
"VCPKG_BINARY_SOURCES": "clear;files,$env{HOME}/.cache/vcpkg/bincache,readwrite"
}
}
],
"buildPresets": [
{
"name": "dev-local",
"configurePreset": "dev-local"
},
{
"name": "wasm-debug-local",
"configurePreset": "wasm-debug-local"
},
{
"name": "wasm-release-local",
"configurePreset": "wasm-release-local"
}
]
}

View File

@@ -74,7 +74,7 @@ PROJECT_ICON = "assets/yaze.ico"
# entered, it will be relative to the location where doxygen was started. If # entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used. # left blank the current directory will be used.
OUTPUT_DIRECTORY = OUTPUT_DIRECTORY = build/docs
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format # sub-directories (in 2 levels) under the output directory of each output format
@@ -949,7 +949,9 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = INPUT = docs/public \
src \
incl
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1055,10 +1057,12 @@ RECURSIVE = YES
# run. # run.
EXCLUDE = assets/ \ EXCLUDE = assets/ \
build/ \ build/ \
cmake/ \ cmake/ \
docs/archive/ \ docs/html/ \
src/lib/ \ docs/latex/ \
docs/internal/ \
src/lib/
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded # directories that are symbolic links (a Unix file system feature) are excluded
@@ -1169,7 +1173,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output. # and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = getting-started.md USE_MDFILE_AS_MAINPAGE = docs/public/index.md
# The Fortran standard specifies that for fixed formatted Fortran code all # The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common # characters from position 72 are to be considered as comment. A common

354
GEMINI.md Normal file
View File

@@ -0,0 +1,354 @@
# GEMINI.md - YAZE Build Instructions
_Extends: ~/AGENTS.md, ~/GEMINI.md_
Build and test instructions for YAZE project. Follow commands exactly.
## Critical Rules
1. **Use `build_ai/` directory** - Never use `build/` (reserved for user)
2. **Use `*-ai` presets** - Never use `*-dbg` presets
3. **Load persona** - Check `.claude/agents/<agent-id>.md` for system prompt
4. **Use helper script:**
```bash
./scripts/agent_build.sh [target]
```
*Example:* `./scripts/agent_build.sh yaze` or `./scripts/agent_build.sh yaze_test`
---
## Quick Reference: Build Times
**First Build (Cold Start)**:
- **Fast Mode (Recommended)**: 2-4 minutes (uses system gRPC/sccache)
- Standard Mode: 10-20 minutes (compiles gRPC from source)
**Incremental Builds (After Changes)**:
- Typically 10-60 seconds depending on what changed
- **sccache/ccache**: Automatically detected and used if installed (highly recommended)
## Platform-Specific Build Commands
### macOS
```bash
# Step 1: Configure (First time only, or when CMakeLists.txt changes)
cmake --preset mac-ai
# Step 2: Build the entire project
cmake --build build_ai --preset mac-ai
# Step 3: Build specific targets (faster for incremental work)
cmake --build build_ai --target yaze # GUI application only
cmake --build build_ai --target yaze_test # Test suite only
cmake --build build_ai --target ylib # Core library only
```
**Available macOS Presets**:
- `mac-ai` - **Preferred for Agents**. Configured to use system gRPC/protobuf if available (brew installed) and defaults to `build_ai`.
- `mac-dbg` - User's debug build (DO NOT USE).
### Linux
```bash
# Step 1: Configure
cmake --preset lin-ai
# Step 2: Build
cmake --build build_ai --preset lin-ai
```
**Available Linux Presets**:
- `lin-ai` - **Preferred for Agents**. Uses `build_ai` and system libraries.
### Windows
```bash
# Step 1: Configure (PowerShell or CMD)
cmake --preset win-ai
# Step 2: Build
cmake --build build_ai --preset win-ai
```
**Available Windows Presets**:
- `win-ai` - **Preferred for Agents**. Uses `build_ai`.
## Testing
### Running All Tests
```bash
# Build tests first
cmake --build build_ai --target yaze_test
# Run all tests
./build_ai/bin/yaze_test
```
### Running Specific Test Categories
```bash
# Unit tests only (fast, ~5-10 seconds)
./build/bin/yaze_test --unit
# Integration tests (requires ROM file)
./build/bin/yaze_test --integration --rom-path /path/to/zelda3.sfc
# End-to-end GUI tests
./build/bin/yaze_test --e2e --show-gui
# Run specific test by name pattern
./build/bin/yaze_test "*Asar*" # All tests with "Asar" in name
./build/bin/yaze_test "*Dungeon*" # All dungeon-related tests
```
### Test Output Modes
```bash
# Minimal output (default)
./build/bin/yaze_test
# Verbose output (shows all test names)
./build/bin/yaze_test -v
# Very verbose (shows detailed test execution)
./build/bin/yaze_test -vv
# List all available tests without running
./build/bin/yaze_test --list-tests
```
## Common Build Issues and Solutions
### Issue 1: "No preset found"
**Error**: `CMake Error: No such preset in CMakePresets.json`
**Solution**: Check the exact preset name. Use tab-completion or check `CMakePresets.json`.
```bash
# List available presets
cmake --list-presets
# Common mistake: Using wrong platform prefix
cmake --preset dbg # ❌ WRONG
cmake --preset mac-dbg # ✅ CORRECT (macOS)
cmake --preset lin-dbg # ✅ CORRECT (Linux)
cmake --preset win-dbg # ✅ CORRECT (Windows)
```
### Issue 2: "Build directory exists but is outdated"
**Error**: CMake complains about existing build directory
**Solution**: Clean and reconfigure
```bash
# Remove old build directory
rm -rf build
# Reconfigure from scratch
cmake --preset mac-dbg # or lin-dbg / win-dbg
```
### Issue 3: "Tests fail with 'ROM not found'"
**Error**: Integration tests fail with ROM-related errors
**Solution**: Some tests require a Zelda3 ROM file
```bash
# Skip ROM-dependent tests
./build/bin/yaze_test --unit
# Or provide ROM path
./build/bin/yaze_test --integration --rom-path zelda3.sfc
```
### Issue 4: Long build times on first run
**Not an Error**: This is normal!
**Explanation**:
- CPM.cmake downloads all dependencies (~3-5 minutes)
- gRPC compilation (Windows only, ~15-20 minutes)
- ImGui compilation (~2-3 minutes)
- SDL2, Abseil, PNG libraries (~3-5 minutes)
**Solution**: Be patient on first build. Subsequent builds use ccache/sccache and are MUCH faster (10-60 seconds).
```bash
# Monitor build progress with verbose output
cmake --build build --preset mac-dbg -v | tee build.log
# Check build log for specific step taking long
grep "Linking" build.log
```
### Issue 5: Incremental builds seem slow
**Solution**: Only rebuild what changed
```bash
# Instead of rebuilding everything:
cmake --build build --preset mac-dbg # ❌ Rebuilds all targets
# Build only what you need:
cmake --build build --target yaze # ✅ Just the GUI app
cmake --build build --target ylib # ✅ Just the core library
cmake --build build --target object_editor_card # ✅ Just one component
```
## Development Workflow
### Typical Development Session
```bash
# 1. Configure once (first time only)
cmake --preset mac-dbg
# 2. Make code changes to src/app/editor/dungeon/object_editor_card.cc
# 3. Rebuild only the affected target (fast!)
cmake --build build --target yaze
# 4. Run the application to test manually
./build/bin/yaze --rom_file zelda3.sfc --editor Dungeon
# 5. Run automated tests to verify
./build/bin/yaze_test --unit
# 6. If tests pass, commit
git add src/app/editor/dungeon/object_editor_card.cc
git commit -m "feat(dungeon): add feature X"
```
### Testing Dungeon Editor Changes
```bash
# 1. Build just the GUI app (includes dungeon editor)
cmake --build build --target yaze
# 2. Launch directly to dungeon editor with ROM
./build/bin/yaze --rom_file zelda3.sfc --editor Dungeon
# 3. To test keyboard shortcuts specifically:
# - Open Object Editor card
# - Try Ctrl+A (select all)
# - Try Delete key (delete selected)
# - Try Ctrl+D (duplicate)
# - Try Arrow keys (nudge objects)
# - Try Tab (cycle selection)
```
### Before Committing Changes
```bash
# 1. Run unit tests (fast check)
./build/bin/yaze_test --unit
# 2. Run format check (ensure code style)
cmake --build build --target format-check
# 3. If format check fails, auto-format
cmake --build build --target format
# 4. Build in release mode to catch optimization warnings
cmake --preset mac-rel
cmake --build build --preset mac-rel
# 5. If all passes, you're ready to commit!
```
## Preset Comparison Matrix
| Preset | Platform | Build Type | AI Features | gRPC | Agent UI | Use Case |
|------------|----------|------------|-------------|------|----------|----------|
| mac-dbg | macOS | Debug | No | No | No | Daily development |
| mac-rel | macOS | Release | No | No | No | Performance testing |
| mac-ai | macOS | Debug | Yes | Yes | Yes | z3ed development |
| lin-dbg | Linux | Debug | No | No | No | Daily development |
| lin-rel | Linux | Release | No | No | No | Performance testing |
| lin-ai | Linux | Debug | Yes | Yes | Yes | z3ed development |
| win-dbg | Windows | Debug | No | No | No | Daily development |
| win-rel | Windows | Release | No | No | No | Performance testing |
| win-ai | Windows | Debug | Yes | Yes | Yes | z3ed development |
## CI/CD Build Times (For Reference)
GitHub Actions runners typically see these build times:
- **Ubuntu 22.04**: 6-8 minutes (with caching)
- **macOS 14**: 8-10 minutes (with caching)
- **Windows 2022**: 12-18 minutes (gRPC adds time)
Your local builds may be faster or slower depending on:
- CPU cores (more = faster parallel builds)
- SSD speed (affects linking time)
- Available RAM (swap = slower builds)
- ccache/sccache hit rate (warm cache = much faster)
## Target Dependencies Reference
Understanding what rebuilds when you change files:
```
yaze (GUI app)
├── ylib (core library)
│ ├── zelda3_dungeon (dungeon module)
│ │ └── object_editor_card.cc ← Your changes here
│ ├── zelda3_overworld
│ ├── gfx (graphics system)
│ └── core (compression, ROM I/O)
├── imgui (UI framework)
└── SDL2 (windowing/graphics)
yaze_test (test suite)
├── ylib (same as above)
├── gtest (Google Test framework)
└── test/*.cc files
```
**When you change**:
- `object_editor_card.cc` → Rebuilds: ylib, yaze (30-60 seconds)
- `object_editor_card.h` → Rebuilds: ylib, yaze, any test including header (1-2 minutes)
- `rom.cc` → Rebuilds: Most of ylib, yaze, yaze_test (3-5 minutes)
- `CMakeLists.txt` → Reconfigure + full rebuild (5-10 minutes)
## Quick Command Cheat Sheet
```bash
# === Configuration ===
cmake --list-presets # Show available presets
cmake --preset mac-dbg # Configure for macOS debug
# === Building ===
cmake --build build --target yaze # Build GUI app
cmake --build build --target yaze_test # Build test suite
cmake --build build --target format # Format all code
cmake --build build -v # Verbose build output
# === Testing ===
./build/bin/yaze_test # Run all tests
./build/bin/yaze_test --unit # Unit tests only
./build/bin/yaze_test "*Asar*" # Specific test pattern
./build/bin/yaze_test --list-tests # List available tests
# === Running ===
./build/bin/yaze # Launch GUI
./build/bin/yaze --rom_file zelda3.sfc # Load ROM
./build/bin/yaze --editor Dungeon # Open editor
./build/bin/yaze --rom_file zelda3.sfc --editor Dungeon # Combined
# === Cleaning ===
cmake --build build --target clean # Clean build artifacts
rm -rf build # Full clean (reconfigure needed)
```
## Key Reminders
- Use full preset names: `mac-ai` not just `ai`
- First builds: 10-20 min (normal), incremental: 10-60 sec
- Build specific targets: `--target yaze` faster than full build
- Some tests require ROM file to pass

185
README.md
View File

@@ -1,139 +1,100 @@
# yaze - Yet Another Zelda3 Editor # YAZE - Yet Another Zelda3 Editor
A modern, cross-platform editor for The Legend of Zelda: A Link to the Past ROM hacking, built with C++23 and featuring complete Asar 65816 assembler integration. [![CI](https://github.com/scawful/yaze/workflows/CI%2FCD%20Pipeline/badge.svg)](https://github.com/scawful/yaze/actions)
[![Code Quality](https://github.com/scawful/yaze/workflows/Code%20Quality/badge.svg)](https://github.com/scawful/yaze/actions)
[![Security](https://github.com/scawful/yaze/workflows/Security%20Scanning/badge.svg)](https://github.com/scawful/yaze/actions)
[![Release](https://github.com/scawful/yaze/workflows/Release/badge.svg)](https://github.com/scawful/yaze/actions)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)
[![Build Status](https://github.com/scawful/yaze/workflows/CI/badge.svg)](https://github.com/scawful/yaze/actions) A cross-platform Zelda 3 ROM editor with a modern C++ GUI, Asar 65816 assembler integration, and an automation-friendly CLI (`z3ed`). YAZE bundles its toolchain, offers AI-assisted editing flows, and targets reproducible builds on Windows, macOS, and Linux. A preview web version is also available for browser-based editing.
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
## Version 0.3.2 - Release ## Highlights
- **All-in-one editing**: Overworld, dungeon, sprite, palette, and messaging tools with live previews.
- **Assembler-first workflow**: Built-in Asar integration, symbol extraction, and patch validation.
- **Automation & AI**: `z3ed` exposes CLI/TUI automation, proposal workflows, and optional AI agents.
- **Web preview**: Experimental browser-based editor (WASM) - see [Web App Guide](docs/public/usage/web-app.md).
- **Testing & CI hooks**: CMake presets, ROM-less test fixtures, and gRPC-based GUI automation support.
- **Cross-platform toolchains**: Single source tree targeting MSVC, Clang, and GCC with identical presets.
- **Modular AI stack**: Toggle agent UI (`YAZE_BUILD_AGENT_UI`), remote automation/gRPC (`YAZE_ENABLE_REMOTE_AUTOMATION`), and AI runtimes (`YAZE_ENABLE_AI_RUNTIME`) per preset.
#### z3ed agent - AI-powered CLI assistant ## Project Status
- **AI-assisted ROM hacking** with ollama and Gemini support v0.5.0 is the current release. New to YAZE? Start with a clean, legally obtained ALttP (USA) ROM and the Quick Start steps below. See [`docs/public/release-notes.md`](docs/public/release-notes.md) for details.
- **Natural language commands** for editing and querying ROM data
- **Tool calling** for structured data extraction and modification
- **Interactive chat** with conversation history and context
#### ZSCustomOverworld v3
- **Enhanced overworld editing** capabilities
- **Advanced map properties** and metadata support
- **Custom graphics support** and tile management
- **Improved compatibility** with existing projects
#### Asar 65816 Assembler Integration
- **Cross-platform ROM patching** with assembly code support
- **Symbol extraction** with addresses and opcodes from assembly files
- **Assembly validation** with comprehensive error reporting
- **Modern C++ API** with safe memory management
#### Advanced Features
- **Theme Management**: Complete theme system with 5+ built-in themes and custom theme editor
- **Multi-Session Support**: Work with multiple ROMs simultaneously in docked workspace
- **Enhanced Welcome Screen**: Themed interface with quick access to all editors
- **Message Editing**: Enhanced text editing interface with real-time preview
- **GUI Docking**: Flexible workspace management with customizable layouts
- **Modern CLI**: Enhanced z3ed tool with interactive TUI and subcommands
- **Cross-Platform**: Full support for Windows, macOS, and Linux
## Quick Start ## Quick Start
### Build ### Clone & Bootstrap
```bash ```bash
# Clone with submodules
git clone --recursive https://github.com/scawful/yaze.git git clone --recursive https://github.com/scawful/yaze.git
cd yaze cd yaze
# Build with CMake
cmake --preset debug # macOS
cmake -B build && cmake --build build # Linux/Windows
# Windows-specific
scripts\verify-build-environment.ps1 # Verify your setup
cmake --preset windows-debug # Basic build
cmake --preset windows-ai-debug # With AI features
cmake --build build --config Debug # Build
``` ```
### Applications Run the environment verifier once per machine:
- **yaze**: Complete GUI editor for Zelda 3 ROM hacking
- **z3ed**: Command-line tool with interactive interface
- **yaze_test**: Comprehensive test suite for development
## Usage
### GUI Editor
Launch the main application to edit Zelda 3 ROMs:
- Load ROM files using native file dialogs
- Edit overworld maps, dungeons, sprites, and graphics
- Apply assembly patches with integrated Asar support
- Export modifications as patches or modified ROMs
### Command Line Tool
```bash ```bash
# Apply assembly patch # macOS / Linux
z3ed asar patch.asm --rom=zelda3.sfc ./scripts/verify-build-environment.sh --fix
# Extract symbols from assembly # Windows (PowerShell)
z3ed extract patch.asm .\scripts\verify-build-environment.ps1 -FixIssues
# Interactive mode
z3ed --tui
``` ```
### C++ API ### Configure & Build
```cpp - Use the CMake preset that matches your platform (`mac-dbg`, `lin-dbg`, `win-dbg`, etc.).
#include "yaze.h" - Build with `cmake --build --preset <name> [--target …]`.
- See [`docs/public/build/quick-reference.md`](docs/public/build/quick-reference.md) for the canonical
list of presets, AI build policy, and testing commands.
// Load ROM and apply patch ### Agent Feature Flags
yaze_project_t* project = yaze_load_project("zelda3.sfc");
yaze_apply_asar_patch(project, "patch.asm"); | Option | Default | Effect |
yaze_save_project(project, "modified.sfc"); | --- | --- | --- |
| `YAZE_BUILD_AGENT_UI` | `ON` when GUI builds are enabled | Compiles the chat/dialog widgets so the editor can host agent sessions. Turn this `OFF` when you want a lean GUI-only build. |
| `YAZE_ENABLE_REMOTE_AUTOMATION` | `ON` for `*-ai` presets | Builds the gRPC servers/clients and protobufs that power GUI automation. |
| `YAZE_ENABLE_AI_RUNTIME` | `ON` for `*-ai` presets | Enables Gemini/Ollama transports, proposal planning, and advanced routing logic. |
| `YAZE_ENABLE_AGENT_CLI` | `ON` when CLI builds are enabled | Compiles the conversational agent stack consumed by `z3ed`. Disable to skip the CLI entirely. |
Windows `win-*` presets keep every switch `OFF` by default (`win-dbg`, `win-rel`, `ci-windows`) so MSVC builds stay fast. Use `win-ai`, `win-vs-ai`, or the new `ci-windows-ai` preset whenever you need remote automation or AI runtime features.
All bundled third-party code (SDL, ImGui, ImGui Test Engine, Asar, nlohmann/json, cpp-httplib, nativefiledialog-extended) now lives under `ext/` for easier vendoring and cleaner include paths.
## Applications & Workflows
- **`./build/bin/yaze`** full GUI editor with multi-session dockspace, theming, and ROM patching.
- **Web App (Preview)** browser-based editor at your deployed instance; see [`docs/public/usage/web-app.md`](docs/public/usage/web-app.md) for details and limitations.
- **`./build/bin/z3ed --tui`** CLI/TUI companion for scripting, AI-assisted edits, and Asar workflows.
- **`ctest --test-dir build -L unit|integration|e2e`** structured test runner for quick regression checks.
- **`z3ed` + macOS automation** pair the CLI with sketchybar/yabai/skhd or Emacs/Spacemacs to drive ROM workflows without opening the GUI.
Typical commands:
```bash
# Launch GUI with a ROM
./build/bin/yaze roms/alttp_vanilla.sfc
# Apply a patch via CLI
./build/bin/z3ed asar patch.asm --rom roms/alttp_vanilla.sfc
# Run focused tests
cmake --build --preset mac-ai --target yaze_test
ctest --test-dir build -L unit
``` ```
## Testing
- `ctest --test-dir build -L unit` for fast checks; add `-L integration` or `-L e2e --output-on-failure` for broader coverage.
- `ctest --preset dev` mirrors CIs stable set; `ctest --preset all` runs the full matrix.
- Set `YAZE_TEST_ROM_VANILLA` or pass `--rom-vanilla` when a test needs a real ROM image (legacy `--rom-path` still works).
## Documentation ## Documentation
- Human-readable docs live under `docs/public/` with an entry point at [`docs/public/index.md`](docs/public/index.md).
- Run `doxygen Doxyfile` to generate API + guide pages (`build/docs/html` and `build/docs/latex`).
- Agent playbooks, architecture notes, and testing recipes now live in [`docs/internal/`](docs/internal/README.md).
- [Getting Started](docs/01-getting-started.md) - Setup and basic usage ## Contributing & Community
- [Build Instructions](docs/02-build-instructions.md) - Building from source - Review [`CONTRIBUTING.md`](CONTRIBUTING.md) and the build/test guides in `docs/public/`.
- [API Reference](docs/04-api-reference.md) - Programming interface - Conventional commit messages (`feat:`, `fix:`, etc.) keep history clean; use topic branches for larger work.
- [Contributing](docs/B1-contributing.md) - Development guidelines - Chat with the team on [Oracle of Secrets Discord](https://discord.gg/MBFkMTPEmk).
**[Complete Documentation](docs/index.md)**
## Supported Platforms
- **Windows** (MSVC 2019+, MinGW)
- **macOS** (Intel and Apple Silicon)
- **Linux** (GCC 13+, Clang 16+)
## ROM Compatibility
- Original Zelda 3 ROMs (US/Japan versions)
- ZSCustomOverworld v2/v3 enhanced overworld features
- Community ROM hacks and modifications
## Contributing
See [Contributing Guide](docs/B1-contributing.md) for development guidelines.
**Community**: [Oracle of Secrets Discord](https://discord.gg/MBFkMTPEmk)
## License ## License
YAZE is licensed under the GNU GPL v3. See [`LICENSE`](LICENSE) for details and third-party notices.
GNU GPL v3 - See [LICENSE](LICENSE) for details. ## Screenshots
## 🙏 Acknowledgments
Takes inspiration from:
- [Hyrule Magic](https://www.romhacking.net/utilities/200/) - Original Zelda 3 editor
- [ZScream](https://github.com/Zarby89/ZScreamDungeon) - Dungeon editing capabilities
- [Asar](https://github.com/RPGHacker/asar) - 65816 assembler integration
## 📸 Screenshots
![YAZE GUI Editor](https://github.com/scawful/yaze/assets/47263509/8b62b142-1de4-4ca4-8c49-d50c08ba4c8e) ![YAZE GUI Editor](https://github.com/scawful/yaze/assets/47263509/8b62b142-1de4-4ca4-8c49-d50c08ba4c8e)
![Dungeon Editor](https://github.com/scawful/yaze/assets/47263509/d8f0039d-d2e4-47d7-b420-554b20ac626f) ![Dungeon Editor](https://github.com/scawful/yaze/assets/47263509/d8f0039d-d2e4-47d7-b420-554b20ac626f)
![Overworld Editor](https://github.com/scawful/yaze/assets/47263509/34b36666-cbea-420b-af90-626099470ae4) ![Overworld Editor](https://github.com/scawful/yaze/assets/47263509/34b36666-cbea-420b-af90-626099470ae4)
---
**Ready to hack Zelda 3? [Get started now!](docs/01-getting-started.md)**

View File

@@ -0,0 +1,12 @@
;#ENABLED=False
;#PATCH_NAME=File fairy skin color fix
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Fixes the file select fairy's skin color
;#ENDPATCH_DESCRIPTION
pushpc
org $1BF02A
db $10
pullpc

View File

@@ -0,0 +1,69 @@
;#ENABLED=True
;#PATCH_NAME=Misc Small Patches
;#PATCH_AUTHOR=Zarby89
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Lots of small patches to do various things
;No Zelda Telepathy is removing the timed message that tell you to rescue her every minute
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Titlescreen forever (no intro)
;#type=bool
!TitleScreenForever = $00
;#name=Skip Ending (before credits)
;#type=bool
!SkipEnding = $00
;#name=Prevent S+Q to Dark World
;#type=bool
!NoDwSpan = $00
;#name=Disable Dungeon Map
;#type=bool
!NoDungeonMap = $00
;#name=Disable Oveworld Map
;#type=bool
!NoOWnMap = $00
;#name=No Zelda Telepathy
;#type=bool
!NoZeldaFollower = $00
;#DEFINE_END
if !TitleScreenForever = 1
org $0CC2E3
db $80
endif
if !SkipEnding = 1
org $0E9889
LDA #$20 : STA $11
RTS
endif
if !NoDwSpan = 1
org $028192
LDA #$00 : STA $7EF3CA ; Clear the DW address so game doesn't think we are in DW
JML $0281BD ; To the lightworld !
endif
if !NoDungeonMap = 1
org $0288FD ; Replace a BEQ by a BRA (dungeon map removed)
db $80
endif
if !NoOWnMap = 1
org $02A55E ; Replace a BEQ by a BRA (overworld map removed)
db $80
endif
if !NoZeldaFollower = 1
org $05DEF8
LDA.b #$00
endif

View File

@@ -0,0 +1,12 @@
;#ENABLED=True
;#PATCH_NAME=No sword beams
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Disables sword beams
;#ENDPATCH_DESCRIPTION
pushpc
org $079C70
JMP.w $079CA0
pullpc

View File

@@ -0,0 +1,11 @@
;#ENABLED=True
;#PATCH_NAME=No grass cutting
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Grass no longer gets cut by the sword
;#ENDPATCH_DESCRIPTION
pushpc
org $1BBE26
BRA + : NOP #3 : +
pullpc

View File

@@ -0,0 +1,80 @@
;#ENABLED=True
;#PATCH_NAME=AST Boots
;#PATCH_AUTHOR=Conn, Zarby89
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Copies the boots mechanics from Ancient Stone Tablets.
;DPad changes boots directions, and transitions can be
;optionally prevented from halting the dash
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Keep running after transition
;#type=bool
!KeepRunningTransition = $00
;#DEFINE_END
pushpc
org $87911D
JML AstBoots
if !KeepRunningTransition != 00
org $828B13
db $80
endif
pullpc
AstBoots:
BIT.b $F2
BPL .continue
LDA.b $F0
AND.b #$0F
BNE .pressing_direction
JML $879138
.pressing_direction
CMP.b #$0A ; up left
BEQ +
CMP.b #$05 ; down right
BEQ +
CMP.b #$09 ; down left
BEQ +
CMP.b #$06 ; up right
BNE ++
+ AND.b #$0C
++ CMP.b $26
BNE +
JML $879138
+ STA.b $26
STA.b $67
STA.w $0340
JSL $87E6A6
JML $879138
.continue
LDA.b #$12
STA.b $5D
LDA.b $3A
AND.b #$7F
STA.b $3A
STZ.b $3C
STZ.b $3D
LDA.b #$11
STA.w $0374
JML $87915E

View File

@@ -0,0 +1,44 @@
;#ENABLED=True
;#PATCH_NAME=Big Bomb requirement
;#PATCH_AUTHOR=Zarby89,kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Modify the crystal and dwarf requirements for the big bomb
;If SmithRequirement is set to 20, you will need to save the Smith first
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Crystals Required
;#type=bitfield
;#bit0=Crystal 6
;#bit1=Crystal 1
;#bit2=Crystal 5
;#bit3=Crystal 7
;#bit4=Crystal 2
;#bit5=Crystal 4
;#bit6=Crystal 3
!CrystalRequirement =$02
;#name=Required smith saved?
;#type=bool
;#uncheckedvalue=$00
;#checkedvalue=$20
!SmithRequirement =$00
;#DEFINE_END
pushpc
org $1EE16A
LDA.l $7EF37A : AND.b #!CrystalRequirement : CMP.b #!CrystalRequirement
skip 2
LDA.l $7EF3C9 : AND.b #!SmithRequirement
pullpc

View File

@@ -0,0 +1,41 @@
;#ENABLED=True
;#PATCH_NAME=Hole Overlay Fix
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Allow the floor collision of the hole overlay to work on every floor types
;#ENDPATCH_DESCRIPTION
pushpc
org $01B83E : JSL FigureOutFloor1
; change comparisons to our dynamic values
org $01FE6C : CMP.w $0318
org $01FE71 : CMP.w $031A
pullpc
;===================================================================================================
; Find floor 1 index and save its tiles
FigureOutFloor1:
REP #$30
LDX.w $046A ; read floor 1 index
; this reuses some memory related to conveyors
; the memory is very temporary so it should be safe
; databank is 0, so we can use abs,X
LDA.w $009B52+0,X ; find top tile
AND.w #$03FE ; isolate tile name
STA.w $0318 ; save tile
LDA.w $009B52+8,X ; find bottom tile
AND.w #$03FE ; isolate tile name
STA.w $031A ; save tile
LDA.b $BA ; vanilla code and return
RTL

View File

@@ -0,0 +1,13 @@
;#ENABLED=true
;#PATCH_NAME=Intro skip
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Skip the intro sooner
;#ENDPATCH_DESCRIPTION
pushpc
org $0CC123
db 4
pullpc

View File

@@ -0,0 +1,39 @@
;#ENABLED=True
;#PATCH_NAME=1.0 Glitches
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Restore the JP 1.0 glitches
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Mirror block erase
;#type=bool
!MirrorEraseBlock = $00
;#name=Spin speed and Item dash
;#type=bool
!SpinSpeedItemDash = $00
;#name=Fake flippers
;#type=bool
!FakeFlippers = $00
;#DEFINE_END
pushpc
if !MirrorEraseBlock == 1
org $07A969
JMP.w $07A970
endif
if !SpinSpeedItemDash == 1
org $0781C0
BRA + : NOP #4 : +
endif
if !FakeFlippers == 1
org $079665
JMP.w $07966C
endif
pullpc

View File

@@ -0,0 +1,39 @@
;#ENABLED=True
;#PATCH_NAME=Link Bed Starting Position
;#PATCH_AUTHOR=Zarby89, Jared_Brian_, kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes where Link spawns during the opening bed cutscene
;Positions can be found by temporarily moving the Link's house entrance to
;the desired location
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Link Y Position
;#type=word
!LinkYPosition = $2182
;#name=Link Y Position
;#type=word
!LinkXPosition = $095B
;#DEFINE_END
; Link sleep position changes
org $079A31
LDA.w #!LinkYPosition : STA.b $20 ; Y link position in bed
LDA.w #!LinkXPosition : STA.b $22 ; X link position in bed
org $05DE52 ; These values should be the same as the ones above
LDA.b #!LinkXPosition : STA.w $0FC2 ; X link position in bed
LDA.b #!LinkXPosition>>8 : STA.w $0FC3 ; X link position in bed
LDA.b #!LinkYPosition : STA.w $0FC4 ; Y link position in bed
LDA.b #!LinkYPosition>>8 : STA.w $0FC5 ; Y link position in bed
org $0980B7
LDA.w #(!LinkYPosition+8) : STA.b $00 ; Y link sheet in bed
LDA.w #(!LinkXPosition-8) : STA.b $02 ; X link sheet in bed
org $05DE8C
LDA.b #(!LinkYPosition-3) : STA.b $20 ; Y link position in bed when awoken
LDA.b #(!LinkYPosition-3)>>8 : STA.b $21 ; Y link position in bed when awoken

View File

@@ -0,0 +1,28 @@
;#ENABLED=True
;#PATCH_NAME=No Hardcoded Rocks
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Removes the 2 hardcoded rocks that get placed in area 33 and 2F.
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Remove rock in area 33.
;#type=bool
!RemoveRock1 = $01
;#name=Remove rock in area 2F.
;#type=bool
!RemoveRock2 = $01
;#DEFINE_END
pushpc
if !RemoveRock1 == 1
org $02EF33
NOP #4
endif
if !RemoveRock2 == 1
org $02EF3C
NOP #4
endif
pullpc

View File

@@ -0,0 +1,51 @@
;#ENABLED=True
;#PATCH_NAME=Rainstate Skip
;#PATCH_AUTHOR=Zarby89
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Skips over gamestates 0 and 1 (rain) straight to 2 (Zelda rescued)
;Setting BedIntro to 00 will keep the opening bed sequence
;#ENDPATCH_DESCRIPTION
lorom
;#DEFINE_START
;#name=Remove bed intro
;#type=bool
!BedIntro = $00
;#DEFINE_END
if !BedIntro == 00
pushpc
org $828356
JSL NewIntroRain
pullpc
NewIntroRain:
LDA.l $7EF3C5 : BNE +
LDA.b #$02 : STA.l $7EF3C5 ; Set Game mode on 2
PHX
JSL $80FC62 ; Sprite_LoadGfxProperties.justLightWorld
PLX
LDA.b #$00 ; will take care of the bed intro wether we are in game rainstate or not
+ RTL
else
pushpc
org $0CD8F6
JSL NewIntroRain
pullpc
NewIntroRain:
PHA
LDA.w #$1002
STA.l $7003C5, X
PLA
STA.l $7003D9, X
RTL
endif

View File

@@ -0,0 +1,30 @@
;#ENABLED=True
;#PATCH_NAME=Torch Tags
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes the number of torches required to open doors and chests when using the "Light_Torches_to_Open" and "Light_Torches_to_get_Chest" tags.
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=torches required to make a chest appear.
;#type=word
;#range=$01,$08
!ChestTorches =$01
;#name=torches required to open a door.
;#type=word
;#range=$01,$08
!DoorTorches =$08
;#DEFINE_END
pushpc
; Changes the amount of torches required to make a chest appear.
org $01C8CA
dw !ChestTorches
; Changes the amount of torches required to open a door.
org $01C645
dw !DoorTorches
pullpc

View File

@@ -0,0 +1,153 @@
;#ENABLED=True
;=========================
;TODO FINISH ADDING DEFINES
;INCOMPLETE PATCH
;==========================
;#PATCH_NAME=Weathervane Position
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Modify the position of where you need to use the flute to destroy weathervane and spawn bird
;#ENDPATCH_DESCRIPTION
!AREAID = $34
; Weather vane explosion changes:
; Let the flute work in special areas.
org $07A40A
NOP : NOP
org $07A418
JML NewVaneCheck
org $07A441
VanePassed:
org $07A44F
VaneFailed:
org $07A453
SpawnBird:
; Disable the area check for the weather vane sprite.
org $06C2EB
LDA $8A : CMP.b #!AREAID : BNE .outside_village
; Check if we have the flute activated already:
LDA $7EF34C : CMP.b #$03 : BNE .player_lacks_bird_enabled_flute
STZ $0DD0, X
.player_lacks_bird_enabled_flute
RTS
.outside_village
; What to do in an area outside of the village:
LDA $7EF34C : AND.b #$02 : BEQ .player_lacks_flute_completely
STZ $0DD0, X ; Suicide.
.player_lacks_flute_completely
RTS
warnpc $06C309
; Tile 1
org $1BC226
dw $06CA
; Tile 2
org $1BC232
dw $06CE
; Tile 3
org $1BC243
dw #$074C
; Bird coords
org $098DC1
LDA.w #$08C8 : STA $00
LDA.w #$0460 : STA $02
; Vane Debris coords
org $098CED
.y_coords
db $F4, $E7, $E4, $E6, $E4, $EC, $E4, $E4, $EC, $E5, $F4, $E4
.x_coords
db $7C, $5E, $6C, $60, $62, $64, $7C, $60, $64, $62, $60, $6C
; Debris Y high byte
org $098D65
db $08
; Debris X high byte
org $098D72
db $04
pullpc ; Continue extended space.
NewVaneCheck:
{
REP #$20
; Check if its the master sword area.
LDA $8A : CMP.w #!AREAID : BNE .not_weathervane_trigger2
LDA $20
CMP.w #$0068 : BCC .not_weathervane_trigger1
CMP.w #$00A0 : BCS .not_weathervane_trigger1
LDA $22
CMP.w #$0040 : BCC .not_weathervane_trigger1
CMP.w #$00A0 : BCS .not_weathervane_trigger1
SEP #$20
; Cancel other sounds
STZ $012E
STZ $012F
; Stop player input
INC InCutScene
; Trigger Zelda
INC $0642
JML VaneFailed
.not_weathervane_trigger2
SEP #$20
; Check if we already have the bird.
LDA $7EF34C : CMP.b #$02 : BNE .travel_bird_not_already_released
REP #$20
; Check the area for #$22.
LDA $8A : CMP.w #$0022 : BNE .not_weathervane_trigger1
LDA $20
CMP.w #$0900 : BCC .not_weathervane_trigger1
CMP.w #$0920 : BCS .not_weathervane_trigger1
LDA $22
CMP.w #$0450 : BCC .not_weathervane_trigger1
CMP.w #$0470 : BCS .not_weathervane_trigger1
SEP #$20
LDA $7EF2A2 : ORA.b #$20 : STA $7EF2A2
REP #$20
STZ FluteIndex
JML VanePassed
.not_weathervane_trigger1
JML VaneFailed
.travel_bird_not_already_released
JML SpawnBird
}
pushpc ; Pause expanded space.
; ==============================================================================

View File

@@ -0,0 +1,22 @@
;#ENABLED=True
;#PATCH_NAME=Lost Woods Exit Music
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes the room that plays the lost woods theme when exiting. Note: This only works for the first byte, so if you want to play music when leaving room 13, this will also cause room 113 to play the music. In vanilla this is used for room E1 Cave (Lost Woods HP).
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=The room number.
;#type=byte
!MusicRoomNumber = $E1
;#DEFINE_END
pushpc
org $02844F
db !MusicRoomNumber
pullpc

View File

@@ -0,0 +1,29 @@
;#ENABLED=True
;#PATCH_NAME=Bottle Vendor
;#PATCH_AUTHOR=Zarby89
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Modify the item and price of sold by the Kakariko bottle vendor
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Item Price
;#type=word
;#decimal=true
!ItemPrice = 100
;#name=Item ID
;#type=item
!ItemID = $16
;#DEFINE_END
pushpc
org $05EAF9
dw !BottlePrice
org $05EB34
dw !BottlePrice
org $05EB18
db !ItemID
pullpc

View File

@@ -0,0 +1,153 @@
;#ENABLED=false
;#PATCH_NAME=Overworld Tail Map Expansion
;#PATCH_AUTHOR=yaze
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Expands the overworld pointer tables from 160 to 192 entries,
;enabling support for Special World tail maps (0xA0-0xBF).
;
;IMPORTANT: This patch MUST be applied AFTER ZSCustomOverworld v3.
;If applied manually via Asar:
; 1. First apply ZSCustomOverworld v3 patch to your ROM
; 2. Then apply this patch: asar TailMapExpansion.asm your_rom.sfc
;
;If applied via yaze CLI:
; z3ed overworld-doctor --rom=your_rom.sfc --apply-tail-expansion
;
;Technical details:
; - Relocates Map32 pointer tables from $02:F94D to $28:A400
; - Adds 32 blank entries for maps 0xA0-0xBF pointing to $30:8000
; - Patches 8 code locations in bank $02 to use new table addresses
; - Writes detection marker 0xEA at $28:A3FF
;#ENDPATCH_DESCRIPTION
lorom
;==============================================================================
; Constants
;==============================================================================
!OldHighTable = $02F94D ; Vanilla high pointer table (160 entries)
!OldLowTable = $02FB2D ; Vanilla low pointer table (160 entries)
!NewHighTable = $28A400 ; New high pointer table (192 entries)
!NewLowTable = $28A640 ; New low pointer table (192 entries)
!BlankMapHigh = $308000 ; Blank map data for tail maps (high bytes)
!BlankMapLow = $309000 ; Blank map data for tail maps (low bytes)
!MarkerAddr = $28A3FF ; Detection marker location
!MarkerValue = $EA ; Detection marker value (NOP opcode = "expanded")
;==============================================================================
; PC address macros (for read3 function)
;==============================================================================
; LoROM: PC = (bank * $8000) + (addr - $8000)
; $02F94D -> PC = (2 * $8000) + ($F94D - $8000) = $10000 + $794D = $1794D
!OldHighTablePC = $01794D
!OldLowTablePC = $017B2D
;==============================================================================
; Detection marker - allows yaze to detect expanded tables
;==============================================================================
org !MarkerAddr
db !MarkerValue
;==============================================================================
; Blank map data for tail maps (0xA0-0xBF)
; Located in safe free space at bank $30
;==============================================================================
org !BlankMapHigh ; PC $180000
BlankMap32High:
fillbyte $00
fill 188 ; 0xBC bytes - compressed blank map32 high data
org !BlankMapLow ; PC $181000
BlankMap32Low:
fillbyte $00
fill 4 ; 0x04 bytes - compressed blank map32 low data
;==============================================================================
; New expanded High pointer table (192 entries x 3 bytes = 576 bytes)
; Copies existing 160 entries, adds 32 blank entries for tail maps
;==============================================================================
org !NewHighTable ; PC $142400
ExpandedMap32HPointers:
; Copy 160 vanilla entries (preserves any ZSCustomOverworld modifications)
; Using Asar's read1() to copy bytes from current ROM state
!n = 0
while !n < 160
db read1(!OldHighTablePC+(!n*3)+0)
db read1(!OldHighTablePC+(!n*3)+1)
db read1(!OldHighTablePC+(!n*3)+2)
!n #= !n+1
endwhile
; Add 32 blank entries for tail maps (0xA0-0xBF)
; Each entry is a 24-bit pointer to BlankMap32High ($30:8000)
!n = 0
while !n < 32
dl BlankMap32High
!n #= !n+1
endwhile
;==============================================================================
; New expanded Low pointer table (192 entries x 3 bytes = 576 bytes)
;==============================================================================
org !NewLowTable ; PC $142640
ExpandedMap32LPointers:
; Copy 160 vanilla entries (preserves any ZSCustomOverworld modifications)
!n = 0
while !n < 160
db read1(!OldLowTablePC+(!n*3)+0)
db read1(!OldLowTablePC+(!n*3)+1)
db read1(!OldLowTablePC+(!n*3)+2)
!n #= !n+1
endwhile
; Add 32 blank entries for tail maps (0xA0-0xBF)
!n = 0
while !n < 32
dl BlankMap32Low
!n #= !n+1
endwhile
;==============================================================================
; Patch game code to use new pointer tables
; Each LDA.l instruction is 4 bytes: AF xx xx xx (opcode + 24-bit address)
; We patch the 3 address bytes to point to the new tables
;==============================================================================
; Function 1: Overworld_DecompressAndDrawOneQuadrant
; Original at PC $1F59D: LDA.l $02F94D,X -> LDA.l $28A400,X
org $02F59E ; Address bytes of instruction at $02F59D
dl ExpandedMap32HPointers ; New high table address
org $02F5A4 ; Address bytes for +1 offset access
dl ExpandedMap32HPointers+1
; Original at PC $1F5C8: LDA.l $02FB2D,X -> LDA.l $28A640,X
org $02F5C9 ; Address bytes of instruction at $02F5C8
dl ExpandedMap32LPointers ; New low table address
org $02F5CF ; Address bytes for +1 offset access
dl ExpandedMap32LPointers+1
; Function 2: Secondary quadrant loader (parallel decompression path)
org $02F7E4
dl ExpandedMap32HPointers
org $02F7EA
dl ExpandedMap32HPointers+1
org $02F80F
dl ExpandedMap32LPointers
org $02F815
dl ExpandedMap32LPointers+1
;==============================================================================
; End of patch
;==============================================================================
print "Tail Map Expansion patch applied successfully."
print "- New High Table: $28:A400 (192 entries)"
print "- New Low Table: $28:A640 (192 entries)"
print "- Blank Map Data: $30:8000, $30:9000"
print "- Detection Marker: $28:A3FF = $EA"

View File

@@ -0,0 +1,19 @@
;#ENABLED=True
;#PATCH_NAME=Crystal Switch Conveyor
;#PATCH_AUTHOR=Zarby89
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Causes crystal switches to be moved by conveyors
;#ENDPATCH_DESCRIPTION
lorom
pushpc
org $06B8D0
JSL ConveyorSwitch
NOP
pullpc
ConveyorSwitch:
JSL $06E496 ; Sprite_CheckTileCollisionLong
LDA.w $0F50, X : AND.b #$F1
RTL

View File

@@ -0,0 +1,95 @@
;#ENABLED=True
;#PATCH_NAME=Elemental Trinexx
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes Trinexx's side heads to be both ice heads, both fire heads, or swapped.
;The heads will shoot the corrisponding beams and will also appear the correct color.
;The main head will appear the elemental color if you set that option but otherwise will be the default palette.
;You will still need to set the side heads to take damage from the appropriate elemental rod with the advanced damage editor.
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Changes the arrangement of side heads.
;#type=choice
;#choice0=Inverted Heads
;#choice1=Ice Heads Only
;#choice2=Fire Heads Only
!ElementType = $00
;#name=Changes the main head palette.
;#type=choice
;#choice0=Default Palette
;#choice1=Ice Palette
;#choice2=Fire Palette
!MainHeadPalette = $00
;#DEFINE_END
pushpc
if !ElementType == 0
; Change the palettes of the side heads to the inverted palette.
org $0DB425
db $0D, $0B
; Swap which head shows the apropriate particales when charging up.
org $1DBAD9
db $F0 ; Replace a BNE with a BEQ.
; Swap which head shoots what beam.
org $1DBAE8
LDA $0E20, X : CMP.b #$CC
; Another beam related thing.
org $1DBAF8
LDA.b #$CC
elseif !ElementType == 1
; Change the palettes of the side heads to the ice palette.
org $0DB425
db $0D, $0D
; Make the fire head show ice spakles when charging up.
org $1DBAD9
db $80 ; Replace a BNE with a BRA.
; Make the fire head shoot ice instead of fire.
org $1DBAE8
LDA.b #$CD : NOP #5
elseif !ElementType == 2
; Change the palettes of the side heads to the fire palette.
org $0DB425
db $0B, $0B
; Make the ice head show flames when charging up.
org $1DBAD9
NOP : NOP ; Remove the BNE and never branch.
; Make the ice head shoot fire instead of ice.
org $1DBAE8
LDA.b #$CC : NOP : NOP : NOP
db $80 ; Replace a BNE with a BRA.
endif
if !MainHeadPalette == 1
; Change the palette of all the main head to the ice.
org $0DB424
db $4D
; Change the snake trinexx palette to ice.
org $1DB033
db $0D
elseif !MainHeadPalette == 2
; Change the palette of all the main head to the fire.
org $0DB424
db $4B
; Change the snake trinexx palette to fire.
org $1DB033
db $0B
endif
pullpc

View File

@@ -0,0 +1,43 @@
;#PATCH_NAME=Eye Lasers Active
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes the wall eye lasers to always be active or always inactive reguardless of what X position they are placed on.
;Normally in vanilla every other X position is set to be inactive unless link is looking directly at them.
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Active?
;#type=bool
!EyeActive = $00
;#DEFINE_END
pushpc
if !EyeActive == $00
; Make it so the eye lazers are always inactive reguardless of what X position they are placed on.
org $1EA50B
; Replaced code:
; LDA $0D10, X : AND.b #$10 : EOR.b #$10 : STA !requires_facing, X
NOP #5 : LDA.b #$00 : STA $0EB0, X
org $1EA52C
; Replace code:
; LDA $0D00, X : AND.b #$10 : STA !requires_facing, X
NOP #3 : LDA.b #$00 : STA $0EB0, X
elseif !EyeActive == $01
; Make it so the eye lazers are always active reguardless of what X position they are placed on.
org $1EA50B
; Replaced code:
; LDA $0D10, X : AND.b #$10 : EOR.b #$10 : STA !requires_facing, X
NOP #5 : LDA.b #$01 : STA $0EB0, X
org $1EA52C
; Replace code:
; LDA $0D00, X : AND.b #$10 : STA !requires_facing, X
NOP #3 : LDA.b #$01 : STA $0EB0, X
endif
pullpc

View File

@@ -0,0 +1,56 @@
;#ENABLED=True
;#PATCH_NAME=Kholdstare Speeds
;#PATCH_AUTHOR=Jared_Brian_
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Changes the speeds at which kholdstare can move.
;By default he will move the same speed on the x and y axis in all 4 diagonal directions.
;Values above 0x80 are negative.
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=X Value 1
;#type=byte
!XValue1 = $0010
;#name=X Value 2
;#type=byte
!XValue2 = $0010
;#name=X Value 3
;#type=byte
!XValue3 = $00F0
;#name=X Value 4
;#type=byte
!XValue4 = $00F0
;#name=Y Value 1
;#type=byte
!YValue1 = $00F0
;#name=Y Value 2
;#type=byte
!YValue2 = $0010
;#name=Y Value 3
;#type=byte
!YValue3 = $0010
;#name=Y Value 4
;#type=byte
!YValue4 = $00F0
;#DEFINE_END
pushpc
; Speed chagnes.
org $1E95DD
.x_speed_limits
db !XValue1, !XValue2, !XValue3, !XValue4
.y_speed_limits
db !YValue1, !YValue2, !YValue3, !YValue4
pullpc

View File

@@ -0,0 +1,24 @@
;#ENABLED=True
;#PATCH_NAME=Spike damage
;#PATCH_AUTHOR=kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Allows mail upgrades to reduce sprite damage
;$08 = 1 heart, $04 = half heart, $10 = 2 heart, etc...
;#ENDPATCH_DESCRIPTION
;#DEFINE_START
;#name=Green Mail Damage
!GreenMailDamage = $08
;#name=Blue Mail Damage
!BlueMailDamage = $08
;#name=Red Mail Damage
!RedMailDamage = $08
;#DEFINE_END
pushpc
org $07BA07
db !GreenMailDamage
db !BlueMailDamage
db !RedMailDamage
pullpc

View File

@@ -0,0 +1,52 @@
;#ENABLED=True
;#PATCH_NAME=More spike directions
;#PATCH_AUTHOR=Zarby89,kan
;#PATCH_VERSION=1.0
;#PATCH_DESCRIPTION
;Allows more spike blocks Subtype (sprite property)
;Default Values : (00) = normal, (08) = normal vertical
;Values, ascending by speed
;00,01,02,03,04,05,06 = Horizontal
;08,09,0A,0B,0C,0D,0E = Vertical
;#ENDPATCH_DESCRIPTION
lorom
pushpc
org $0691D7 ; SpritePrep_SpikeBlock:
JSL NewSpikePrep
RTS
org $1EBD0E
JSL NewSpikeCollision
RTS
pullpc
speedValuesH:
db $20, $10, $18, $28, $30, $38, $40, $FF
db $00, $00, $00, $00, $00, $00, $00, $FF
speedValuesV:
db $00, $00, $00, $00, $00, $00, $00, $FF
db $20, $18, $20, $28, $30, $38, $40, $FF
NewSpikePrep:
TXY
LDX.w $0E30,Y
LDA.l speedValuesH,X : STA.w $0D50,Y
LDA.l speedValuesV,X : STA.w $0D40,Y
TYX
RTL
NewSpikeCollision:
LDA.b #$04 : STA.w $0DF0, X
LDA.w $0D50, X : EOR.b #$FF : INC A : STA.w $0D50, X
LDA.w $0D40, X : EOR.b #$FF : INC A : STA.w $0D40, X
LDA.b #$05 : JSL $0DBB7C ; Sound_SetSfx2PanLong
RTL

View File

@@ -0,0 +1 @@
0000

12
cmake-format.yaml Normal file
View File

@@ -0,0 +1,12 @@
# CMake format configuration
line_width: 80
tab_size: 2
max_subargs_per_line: 3
separate_ctrl_name_with_space: true
separate_fn_name_with_space: true
dangle_parens: true
command_case: lower
keyword_case: upper
enable_sort: true
autosort: true

52
cmake/CPM.cmake Normal file
View File

@@ -0,0 +1,52 @@
# CPM.cmake - C++ Package Manager
# https://github.com/cpm-cmake/CPM.cmake
set(CPM_DOWNLOAD_VERSION 0.38.7)
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
function(download_cpm)
message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}")
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION}
)
endfunction()
if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))
download_cpm()
else()
# resume download if it previously failed
file(READ ${CPM_DOWNLOAD_LOCATION} check)
if("${check}" STREQUAL "")
download_cpm()
endif()
endif()
include(${CPM_DOWNLOAD_LOCATION})
# Set CPM options for better caching and performance
if(NOT DEFINED CPM_USE_LOCAL_PACKAGES)
set(CPM_USE_LOCAL_PACKAGES OFF)
endif()
if(NOT DEFINED CPM_LOCAL_PACKAGES_ONLY)
set(CPM_LOCAL_PACKAGES_ONLY OFF)
endif()
set(CPM_DONT_CREATE_PACKAGE_LOCK ON)
set(CPM_DONT_UPDATE_MODULE_PATH ON)
set(CPM_DONT_PREPEND_TO_MODULE_PATH ON)
# Set cache directory for CI builds
if(DEFINED ENV{GITHUB_ACTIONS})
set(CPM_SOURCE_CACHE "$ENV{HOME}/.cpm-cache")
message(STATUS "CPM cache directory: ${CPM_SOURCE_CACHE}")
endif()

View File

@@ -0,0 +1,490 @@
# TestInfrastructure.cmake
# Advanced test infrastructure configuration for yaze project
# Provides optimized test builds, parallel execution, and smart test selection
include(GoogleTest)
include(CTest)
# =============================================================================
# Test Configuration Options
# =============================================================================
option(YAZE_TEST_PARALLEL "Enable parallel test execution" ON)
option(YAZE_TEST_COVERAGE "Enable code coverage collection" OFF)
option(YAZE_TEST_SANITIZERS "Enable address and undefined behavior sanitizers" OFF)
option(YAZE_TEST_PROFILE "Enable test profiling" OFF)
option(YAZE_TEST_MINIMAL_DEPS "Use minimal dependencies for faster test builds" OFF)
option(YAZE_TEST_PCH "Use precompiled headers for tests" ON)
# Test categories
option(YAZE_TEST_SMOKE "Build smoke tests (critical path)" ON)
option(YAZE_TEST_UNIT "Build unit tests" ON)
option(YAZE_TEST_INTEGRATION "Build integration tests" ON)
option(YAZE_TEST_E2E "Build end-to-end GUI tests" OFF)
option(YAZE_TEST_BENCHMARK "Build performance benchmarks" OFF)
option(YAZE_TEST_FUZZ "Build fuzzing tests" OFF)
# Test execution settings
set(YAZE_TEST_TIMEOUT_SMOKE 30 CACHE STRING "Timeout for smoke tests (seconds)")
set(YAZE_TEST_TIMEOUT_UNIT 60 CACHE STRING "Timeout for unit tests (seconds)")
set(YAZE_TEST_TIMEOUT_INTEGRATION 300 CACHE STRING "Timeout for integration tests (seconds)")
set(YAZE_TEST_TIMEOUT_E2E 600 CACHE STRING "Timeout for E2E tests (seconds)")
# =============================================================================
# Precompiled Headers Configuration
# =============================================================================
if(YAZE_TEST_PCH)
set(YAZE_TEST_PCH_HEADERS
<gtest/gtest.h>
<gmock/gmock.h>
<absl/status/status.h>
<absl/status/statusor.h>
<absl/strings/string_view.h>
<memory>
<vector>
<string>
<unordered_map>
)
# Create PCH target
add_library(yaze_test_pch INTERFACE)
target_precompile_headers(yaze_test_pch INTERFACE ${YAZE_TEST_PCH_HEADERS})
message(STATUS "Test Infrastructure: Precompiled headers enabled")
endif()
# =============================================================================
# Test Interface Library
# =============================================================================
add_library(yaze_test_interface INTERFACE)
# Common compile options
target_compile_options(yaze_test_interface INTERFACE
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
$<$<CONFIG:RelWithDebInfo>:-O2 -g>
$<$<BOOL:${YAZE_TEST_COVERAGE}>:--coverage -fprofile-arcs -ftest-coverage>
$<$<BOOL:${YAZE_TEST_PROFILE}>:-pg>
)
# Common link options
target_link_options(yaze_test_interface INTERFACE
$<$<BOOL:${YAZE_TEST_COVERAGE}>:--coverage>
$<$<BOOL:${YAZE_TEST_PROFILE}>:-pg>
)
# Sanitizers
if(YAZE_TEST_SANITIZERS)
target_compile_options(yaze_test_interface INTERFACE
-fsanitize=address,undefined
-fno-omit-frame-pointer
-fno-optimize-sibling-calls
)
target_link_options(yaze_test_interface INTERFACE
-fsanitize=address,undefined
)
message(STATUS "Test Infrastructure: Sanitizers enabled (ASan + UBSan)")
endif()
# Coverage
if(YAZE_TEST_COVERAGE)
find_program(LCOV lcov)
find_program(GENHTML genhtml)
if(LCOV AND GENHTML)
add_custom_target(coverage
COMMAND ${LCOV} --capture --directory ${CMAKE_BINARY_DIR}
--output-file ${CMAKE_BINARY_DIR}/coverage.info
--ignore-errors source
COMMAND ${LCOV} --remove ${CMAKE_BINARY_DIR}/coverage.info
'*/test/*' '*/ext/*' '/usr/*' '*/build/*'
--output-file ${CMAKE_BINARY_DIR}/coverage_filtered.info
COMMAND ${GENHTML} ${CMAKE_BINARY_DIR}/coverage_filtered.info
--output-directory ${CMAKE_BINARY_DIR}/coverage_html
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating code coverage report"
)
message(STATUS "Test Infrastructure: Coverage target available")
else()
message(WARNING "lcov/genhtml not found, coverage target disabled")
endif()
endif()
# =============================================================================
# Test Creation Function
# =============================================================================
function(yaze_create_test_suite)
set(options GUI_TEST REQUIRES_ROM USE_PCH)
set(oneValueArgs NAME CATEGORY TIMEOUT PARALLEL_JOBS OUTPUT_DIR)
set(multiValueArgs SOURCES DEPENDENCIES LABELS COMPILE_DEFINITIONS)
cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Validate required arguments
if(NOT TEST_NAME)
message(FATAL_ERROR "yaze_create_test_suite: NAME is required")
endif()
if(NOT TEST_SOURCES)
message(FATAL_ERROR "yaze_create_test_suite: SOURCES is required")
endif()
# Set defaults
if(NOT TEST_CATEGORY)
set(TEST_CATEGORY "general")
endif()
if(NOT TEST_TIMEOUT)
set(TEST_TIMEOUT 60)
endif()
if(NOT TEST_OUTPUT_DIR)
set(TEST_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin/test")
endif()
# Create test executable
set(target_name yaze_test_${TEST_NAME})
add_executable(${target_name} ${TEST_SOURCES})
# Set output directory
set_target_properties(${target_name} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${TEST_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${TEST_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TEST_OUTPUT_DIR}"
)
# Link libraries
target_link_libraries(${target_name} PRIVATE
yaze_test_interface
yaze_test_support
GTest::gtest_main
GTest::gmock
${TEST_DEPENDENCIES}
)
# Apply PCH if requested
if(TEST_USE_PCH AND TARGET yaze_test_pch)
target_link_libraries(${target_name} PRIVATE yaze_test_pch)
target_precompile_headers(${target_name} REUSE_FROM yaze_test_pch)
endif()
# Add compile definitions
if(TEST_COMPILE_DEFINITIONS)
target_compile_definitions(${target_name} PRIVATE ${TEST_COMPILE_DEFINITIONS})
endif()
# GUI test configuration
if(TEST_GUI_TEST)
target_compile_definitions(${target_name} PRIVATE
IMGUI_ENABLE_TEST_ENGINE=1
YAZE_GUI_TEST_TARGET=1
)
if(TARGET ImGuiTestEngine)
target_link_libraries(${target_name} PRIVATE ImGuiTestEngine)
endif()
endif()
# ROM test configuration
if(TEST_REQUIRES_ROM)
target_compile_definitions(${target_name} PRIVATE
YAZE_ROM_TEST=1
)
endif()
# Discover tests with CTest
set(test_labels "${TEST_CATEGORY}")
if(TEST_LABELS)
list(APPEND test_labels ${TEST_LABELS})
endif()
gtest_discover_tests(${target_name}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
PROPERTIES
LABELS "${test_labels}"
TIMEOUT ${TEST_TIMEOUT}
DISCOVERY_MODE PRE_TEST
DISCOVERY_TIMEOUT 30
)
# Set parallel execution if specified
if(TEST_PARALLEL_JOBS)
set_property(TEST ${target_name} PROPERTY PROCESSORS ${TEST_PARALLEL_JOBS})
endif()
# Create category-specific test target
add_custom_target(test-${TEST_CATEGORY}-${TEST_NAME}
COMMAND ${CMAKE_CTEST_COMMAND} -R "^${target_name}" --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running ${TEST_CATEGORY} tests: ${TEST_NAME}"
)
message(STATUS "Test Suite: ${target_name} (${TEST_CATEGORY})")
endfunction()
# =============================================================================
# Test Suites Configuration
# =============================================================================
# Smoke Tests (Critical Path)
if(YAZE_TEST_SMOKE)
file(GLOB SMOKE_TEST_SOURCES
${CMAKE_SOURCE_DIR}/test/smoke/*.cc
${CMAKE_SOURCE_DIR}/test/smoke/*.h
)
if(SMOKE_TEST_SOURCES)
yaze_create_test_suite(
NAME smoke
CATEGORY smoke
SOURCES ${SMOKE_TEST_SOURCES}
LABELS critical fast ci-stage1
TIMEOUT ${YAZE_TEST_TIMEOUT_SMOKE}
USE_PCH
)
else()
message(WARNING "No smoke test sources found")
endif()
endif()
# Unit Tests
if(YAZE_TEST_UNIT)
file(GLOB_RECURSE UNIT_TEST_SOURCES
${CMAKE_SOURCE_DIR}/test/unit/*.cc
)
if(UNIT_TEST_SOURCES)
# Split unit tests into multiple binaries for parallel execution
list(LENGTH UNIT_TEST_SOURCES num_unit_tests)
if(num_unit_tests GREATER 50)
# Create sharded unit test executables
set(shard_size 25)
math(EXPR num_shards "(${num_unit_tests} + ${shard_size} - 1) / ${shard_size}")
foreach(shard RANGE 1 ${num_shards})
math(EXPR start "(${shard} - 1) * ${shard_size}")
math(EXPR end "${shard} * ${shard_size} - 1")
if(end GREATER ${num_unit_tests})
set(end ${num_unit_tests})
endif()
list(SUBLIST UNIT_TEST_SOURCES ${start} ${shard_size} shard_sources)
yaze_create_test_suite(
NAME unit_shard${shard}
CATEGORY unit
SOURCES ${shard_sources}
LABELS unit fast ci-stage2 shard${shard}
TIMEOUT ${YAZE_TEST_TIMEOUT_UNIT}
USE_PCH
)
endforeach()
else()
# Single unit test executable
yaze_create_test_suite(
NAME unit
CATEGORY unit
SOURCES ${UNIT_TEST_SOURCES}
LABELS unit fast ci-stage2
TIMEOUT ${YAZE_TEST_TIMEOUT_UNIT}
USE_PCH
)
endif()
endif()
endif()
# Integration Tests
if(YAZE_TEST_INTEGRATION)
file(GLOB_RECURSE INTEGRATION_TEST_SOURCES
${CMAKE_SOURCE_DIR}/test/integration/*.cc
)
if(INTEGRATION_TEST_SOURCES)
yaze_create_test_suite(
NAME integration
CATEGORY integration
SOURCES ${INTEGRATION_TEST_SOURCES}
LABELS integration medium ci-stage3
TIMEOUT ${YAZE_TEST_TIMEOUT_INTEGRATION}
PARALLEL_JOBS 2
USE_PCH
)
endif()
endif()
# E2E Tests
if(YAZE_TEST_E2E)
file(GLOB_RECURSE E2E_TEST_SOURCES
${CMAKE_SOURCE_DIR}/test/e2e/*.cc
)
if(E2E_TEST_SOURCES)
yaze_create_test_suite(
NAME e2e
CATEGORY e2e
SOURCES ${E2E_TEST_SOURCES}
LABELS e2e slow gui ci-stage3
TIMEOUT ${YAZE_TEST_TIMEOUT_E2E}
GUI_TEST
USE_PCH
)
endif()
endif()
# Benchmark Tests
if(YAZE_TEST_BENCHMARK)
file(GLOB_RECURSE BENCHMARK_TEST_SOURCES
${CMAKE_SOURCE_DIR}/test/benchmarks/*.cc
)
if(BENCHMARK_TEST_SOURCES)
yaze_create_test_suite(
NAME benchmark
CATEGORY benchmark
SOURCES ${BENCHMARK_TEST_SOURCES}
LABELS benchmark performance nightly
TIMEOUT 1800
COMPILE_DEFINITIONS BENCHMARK_BUILD=1
)
endif()
endif()
# =============================================================================
# Custom Test Commands
# =============================================================================
# Run all fast tests
add_custom_target(test-fast
COMMAND ${CMAKE_CTEST_COMMAND} -L "fast" --parallel ${CMAKE_NUMBER_OF_CORES} --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running fast tests"
)
# Run tests by stage
add_custom_target(test-stage1
COMMAND ${CMAKE_CTEST_COMMAND} -L "ci-stage1" --parallel 4 --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running Stage 1 (Smoke) tests"
)
add_custom_target(test-stage2
COMMAND ${CMAKE_CTEST_COMMAND} -L "ci-stage2" --parallel ${CMAKE_NUMBER_OF_CORES} --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running Stage 2 (Unit) tests"
)
add_custom_target(test-stage3
COMMAND ${CMAKE_CTEST_COMMAND} -L "ci-stage3" --parallel 2 --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running Stage 3 (Integration/E2E) tests"
)
# Smart test selection based on changed files
add_custom_target(test-changed
COMMAND ${CMAKE_COMMAND} -E env python3 ${CMAKE_SOURCE_DIR}/scripts/smart_test_selector.py
--output filter | xargs ${CMAKE_CTEST_COMMAND} -R --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running tests for changed files"
)
# Parallel test execution with sharding
add_custom_target(test-parallel
COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/test_runner.py
${CMAKE_BINARY_DIR}/bin/test/yaze_test_unit
--shards ${CMAKE_NUMBER_OF_CORES}
--output-dir ${CMAKE_BINARY_DIR}/test_results
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running tests with parallel sharding"
)
# Test with retries for flaky tests
add_custom_target(test-retry
COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/test_runner.py
${CMAKE_BINARY_DIR}/bin/test/yaze_test_unit
--retry 2
--output-dir ${CMAKE_BINARY_DIR}/test_results
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running tests with retry for failures"
)
# =============================================================================
# CTest Configuration
# =============================================================================
set(CTEST_PARALLEL_LEVEL ${CMAKE_NUMBER_OF_CORES} CACHE STRING "Default parallel level for CTest")
set(CTEST_OUTPUT_ON_FAILURE ON CACHE BOOL "Show output on test failure")
set(CTEST_PROGRESS_OUTPUT ON CACHE BOOL "Show progress during test execution")
# Configure test output
set(CMAKE_CTEST_ARGUMENTS
--output-on-failure
--progress
--parallel ${CTEST_PARALLEL_LEVEL}
)
# Configure test timeout multiplier for slower systems
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CTEST_TEST_TIMEOUT_MULTIPLIER 1.5)
endif()
# =============================================================================
# Test Data Management
# =============================================================================
# Download test data if needed
if(YAZE_TEST_INTEGRATION OR YAZE_TEST_E2E)
include(FetchContent)
# Check if test data exists
if(NOT EXISTS "${CMAKE_BINARY_DIR}/test_data/VERSION")
message(STATUS "Downloading test data...")
FetchContent_Declare(
yaze_test_data
URL https://github.com/yaze/test-data/archive/refs/tags/v1.0.0.tar.gz
URL_HASH SHA256=abcdef1234567890 # Replace with actual hash
DOWNLOAD_EXTRACT_TIMESTAMP ON
)
FetchContent_MakeAvailable(yaze_test_data)
set(YAZE_TEST_DATA_DIR ${yaze_test_data_SOURCE_DIR} CACHE PATH "Test data directory")
else()
set(YAZE_TEST_DATA_DIR ${CMAKE_BINARY_DIR}/test_data CACHE PATH "Test data directory")
endif()
endif()
# =============================================================================
# Test Report Generation
# =============================================================================
add_custom_target(test-report
COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/aggregate_test_results.py
--input-dir ${CMAKE_BINARY_DIR}/Testing
--output ${CMAKE_BINARY_DIR}/test_report.json
--generate-html ${CMAKE_BINARY_DIR}/test_report.html
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating test report"
DEPENDS test-all
)
# =============================================================================
# Summary Message
# =============================================================================
message(STATUS "========================================")
message(STATUS "Test Infrastructure Configuration:")
message(STATUS " Parallel Testing: ${YAZE_TEST_PARALLEL}")
message(STATUS " Precompiled Headers: ${YAZE_TEST_PCH}")
message(STATUS " Code Coverage: ${YAZE_TEST_COVERAGE}")
message(STATUS " Sanitizers: ${YAZE_TEST_SANITIZERS}")
message(STATUS " Test Categories:")
message(STATUS " Smoke Tests: ${YAZE_TEST_SMOKE}")
message(STATUS " Unit Tests: ${YAZE_TEST_UNIT}")
message(STATUS " Integration Tests: ${YAZE_TEST_INTEGRATION}")
message(STATUS " E2E Tests: ${YAZE_TEST_E2E}")
message(STATUS " Benchmarks: ${YAZE_TEST_BENCHMARK}")
message(STATUS " Parallel Level: ${CTEST_PARALLEL_LEVEL}")
message(STATUS "========================================")

View File

@@ -38,6 +38,9 @@ if(_yaze_use_fetched_absl)
set(ABSL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) set(ABSL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(absl) FetchContent_MakeAvailable(absl)
message(STATUS "Fetched Abseil ${YAZE_ABSL_GIT_TAG}") message(STATUS "Fetched Abseil ${YAZE_ABSL_GIT_TAG}")
# NEW: Export source directory for Windows builds that need explicit include paths
set(YAZE_ABSL_SOURCE_DIR "${absl_SOURCE_DIR}" CACHE INTERNAL "Abseil source directory")
endif() endif()
endif() endif()
@@ -97,7 +100,17 @@ endif()
# ABSL_TARGETS is now available to the rest of the project via include() # ABSL_TARGETS is now available to the rest of the project via include()
if(APPLE AND DEFINED CMAKE_OSX_ARCHITECTURES AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(_yaze_absl_arm64 FALSE)
if(APPLE)
if(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
set(_yaze_absl_arm64 TRUE)
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "" AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
# Homebrew LLVM doesn't honor -Xarch_x86_64; strip x86 flags on arm64-only builds.
set(_yaze_absl_arm64 TRUE)
endif()
endif()
if(_yaze_absl_arm64)
foreach(_absl_target IN ITEMS absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl) foreach(_absl_target IN ITEMS absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl)
if(TARGET ${_absl_target}) if(TARGET ${_absl_target})
get_target_property(_absl_opts ${_absl_target} COMPILE_OPTIONS) get_target_property(_absl_opts ${_absl_target} COMPILE_OPTIONS)

View File

@@ -2,7 +2,9 @@
# Improved cross-platform support for macOS, Linux, and Windows # Improved cross-platform support for macOS, Linux, and Windows
# Configure Asar build options # Configure Asar build options
set(ASAR_GEN_EXE OFF CACHE BOOL "Build Asar standalone executable") # Build the standalone executable so we can fall back to a bundled CLI when the
# static library misbehaves.
set(ASAR_GEN_EXE ON CACHE BOOL "Build Asar standalone executable")
set(ASAR_GEN_DLL ON CACHE BOOL "Build Asar shared library") set(ASAR_GEN_DLL ON CACHE BOOL "Build Asar shared library")
set(ASAR_GEN_LIB ON CACHE BOOL "Build Asar static library") set(ASAR_GEN_LIB ON CACHE BOOL "Build Asar static library")
set(ASAR_GEN_EXE_TEST OFF CACHE BOOL "Build Asar executable tests") set(ASAR_GEN_EXE_TEST OFF CACHE BOOL "Build Asar executable tests")
@@ -14,10 +16,10 @@ if(MSVC)
endif() endif()
# Set Asar source directory # Set Asar source directory
set(ASAR_SRC_DIR "${CMAKE_SOURCE_DIR}/src/lib/asar/src") set(ASAR_SRC_DIR "${CMAKE_SOURCE_DIR}/ext/asar/src")
# Add Asar as subdirectory # Add Asar as subdirectory with explicit binary directory
add_subdirectory(${ASAR_SRC_DIR} EXCLUDE_FROM_ALL) add_subdirectory(${ASAR_SRC_DIR} ${CMAKE_BINARY_DIR}/asar EXCLUDE_FROM_ALL)
# Create modern CMake target for Asar integration # Create modern CMake target for Asar integration
if(TARGET asar-static) if(TARGET asar-static)
@@ -42,11 +44,16 @@ if(TARGET asar-static)
linux linux
stricmp=strcasecmp stricmp=strcasecmp
) )
elseif(APPLE) elseif(YAZE_PLATFORM_MACOS)
target_compile_definitions(asar-static PRIVATE target_compile_definitions(asar-static PRIVATE
MACOS MACOS
stricmp=strcasecmp stricmp=strcasecmp
) )
elseif(YAZE_PLATFORM_IOS)
target_compile_definitions(asar-static PRIVATE
YAZE_IOS
stricmp=strcasecmp
)
endif() endif()
# Add include directories # Add include directories

View File

@@ -1,155 +1,120 @@
# This file centralizes the management of all third-party dependencies. # YAZE Dependencies Management
# It provides functions to find or fetch dependencies and creates alias targets # Centralized dependency management using CPM.cmake
# for consistent usage throughout the project.
include(FetchContent) # Include CPM and options
include(cmake/CPM.cmake)
include(cmake/options.cmake)
include(cmake/dependencies.lock)
# ============================================================================ message(STATUS "=== Setting up YAZE dependencies with CPM.cmake ===")
# Helper function to add a dependency
# ============================================================================
function(yaze_add_dependency name)
set(options)
set(oneValueArgs GIT_REPOSITORY GIT_TAG URL)
set(multiValueArgs)
cmake_parse_arguments(DEP "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(TARGET yaze::${name}) # Only prefer local/system packages when explicitly requested.
return() set(CPM_USE_LOCAL_PACKAGES ${YAZE_USE_SYSTEM_DEPS} CACHE BOOL "" FORCE)
endif()
# Try to find the package via find_package first # Clear any previous dependency targets
find_package(${name} QUIET) set(YAZE_ALL_DEPENDENCIES "")
set(YAZE_SDL2_TARGETS "")
set(YAZE_YAML_TARGETS "")
set(YAZE_IMGUI_TARGETS "")
set(YAZE_IMPLOT_TARGETS "")
set(YAZE_JSON_TARGETS "")
set(YAZE_HTTPLIB_TARGETS "")
set(YAZE_GRPC_TARGETS "")
set(YAZE_FTXUI_TARGETS "")
set(YAZE_TESTING_TARGETS "")
if(${name}_FOUND) # Core dependencies (always required)
message(STATUS "Found ${name} via find_package") # SDL selection: SDL2 (default) or SDL3 (experimental)
if(TARGET ${name}::${name}) if(YAZE_USE_SDL3)
add_library(yaze::${name} ALIAS ${name}::${name}) include(cmake/dependencies/sdl3.cmake)
else() # Debug: message(STATUS "After SDL3 setup, YAZE_SDL3_TARGETS = '${YAZE_SDL3_TARGETS}'")
# Handle cases where find_package doesn't create an imported target list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_SDL3_TARGETS})
# This is a simplified approach; more logic may be needed for specific packages else()
add_library(yaze::${name} INTERFACE IMPORTED) include(cmake/dependencies/sdl2.cmake)
target_include_directories(yaze::${name} INTERFACE ${${name}_INCLUDE_DIRS}) # Debug: message(STATUS "After SDL2 setup, YAZE_SDL2_TARGETS = '${YAZE_SDL2_TARGETS}'")
target_link_libraries(yaze::${name} INTERFACE ${${name}_LIBRARIES}) list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_SDL2_TARGETS})
endif()
return()
endif()
# If not found, use FetchContent
message(STATUS "Could not find ${name}, fetching from source.")
FetchContent_Declare(
${name}
GIT_REPOSITORY ${DEP_GIT_REPOSITORY}
GIT_TAG ${DEP_GIT_TAG}
)
FetchContent_GetProperties(${name})
if(NOT ${name}_POPULATED)
FetchContent_Populate(${name})
add_subdirectory(${${name}_SOURCE_DIR} ${${name}_BINARY_DIR})
endif()
if(TARGET ${name})
add_library(yaze::${name} ALIAS ${name})
elseif(TARGET ${name}::${name})
add_library(yaze::${name} ALIAS ${name}::${name})
else()
message(FATAL_ERROR "Failed to create target for ${name}")
endif()
endfunction()
# ============================================================================
# Dependency Declarations
# ============================================================================
# gRPC (must come before Abseil - provides its own compatible Abseil)
if(YAZE_WITH_GRPC)
include(cmake/grpc.cmake)
# Verify ABSL_TARGETS was populated by gRPC
list(LENGTH ABSL_TARGETS _absl_count)
if(_absl_count EQUAL 0)
message(FATAL_ERROR "ABSL_TARGETS is empty after including grpc.cmake!")
else()
message(STATUS "gRPC provides ${_absl_count} Abseil targets for linking")
endif()
endif() endif()
# Abseil (only if gRPC didn't provide it) include(cmake/dependencies/yaml.cmake)
if(NOT YAZE_WITH_GRPC) list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_YAML_TARGETS})
include(cmake/dependencies/imgui.cmake)
# Debug: message(STATUS "After ImGui setup, YAZE_IMGUI_TARGETS = '${YAZE_IMGUI_TARGETS}'")
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_IMGUI_TARGETS})
include(cmake/dependencies/implot.cmake)
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_IMPLOT_TARGETS})
# Abseil is required for failure_signal_handler, status, and other utilities
# Only include standalone Abseil when gRPC is disabled - when gRPC is enabled,
# it provides its own bundled Abseil via CPM
if(NOT YAZE_ENABLE_GRPC)
include(cmake/absl.cmake) include(cmake/absl.cmake)
# Verify ABSL_TARGETS was populated
list(LENGTH ABSL_TARGETS _absl_count)
if(_absl_count EQUAL 0)
message(FATAL_ERROR "ABSL_TARGETS is empty after including absl.cmake!")
else()
message(STATUS "Abseil provides ${_absl_count} targets for linking")
endif()
endif() endif()
set(YAZE_PROTOBUF_TARGETS) # Optional dependencies based on feature flags
if(YAZE_ENABLE_JSON)
if(TARGET protobuf::libprotobuf) include(cmake/dependencies/json.cmake)
list(APPEND YAZE_PROTOBUF_TARGETS protobuf::libprotobuf) list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_JSON_TARGETS})
else()
if(TARGET libprotobuf)
list(APPEND YAZE_PROTOBUF_TARGETS libprotobuf)
endif()
endif() endif()
set(YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS ${YAZE_PROTOBUF_TARGETS}) # Native HTTP/HTTPS (cpp-httplib) for non-WASM builds
if(NOT EMSCRIPTEN)
if(YAZE_PROTOBUF_TARGETS) include(cmake/dependencies/httplib.cmake)
list(GET YAZE_PROTOBUF_TARGETS 0 YAZE_PROTOBUF_TARGET) list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_HTTPLIB_TARGETS})
else()
set(YAZE_PROTOBUF_TARGET "")
endif() endif()
# SDL2 # CRITICAL: Load testing dependencies BEFORE gRPC when both are enabled
include(cmake/sdl2.cmake) # This ensures gmock is available before Abseil (bundled with gRPC) tries to export test_allocator
# which depends on gmock. This prevents CMake export errors.
if(YAZE_BUILD_TESTS AND YAZE_ENABLE_GRPC)
include(cmake/dependencies/testing.cmake)
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_TESTING_TARGETS})
endif()
# Asar if(YAZE_ENABLE_GRPC)
include(cmake/asar.cmake) include(cmake/dependencies/grpc.cmake)
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_GRPC_TARGETS})
endif()
# Google Test if(YAZE_BUILD_CLI AND NOT EMSCRIPTEN)
include(cmake/dependencies/ftxui.cmake)
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_FTXUI_TARGETS})
endif()
# Load testing dependencies after gRPC if tests are enabled but gRPC is not
if(YAZE_BUILD_TESTS AND NOT YAZE_ENABLE_GRPC)
include(cmake/dependencies/testing.cmake)
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_TESTING_TARGETS})
endif()
# ASAR dependency (for ROM assembly) - temporarily disabled
# TODO: Add CMakeLists.txt to bundled ASAR or find working repository
message(STATUS "ASAR dependency temporarily disabled - will be added later")
# Print dependency summary
message(STATUS "=== YAZE Dependencies Summary ===")
message(STATUS "Total dependencies: ${YAZE_ALL_DEPENDENCIES}")
message(STATUS "SDL2: ${YAZE_SDL2_TARGETS}")
message(STATUS "YAML: ${YAZE_YAML_TARGETS}")
message(STATUS "ImGui: ${YAZE_IMGUI_TARGETS}")
message(STATUS "ImPlot: ${YAZE_IMPLOT_TARGETS}")
if(YAZE_ENABLE_JSON)
message(STATUS "JSON: ${YAZE_JSON_TARGETS}")
endif()
if(YAZE_ENABLE_GRPC)
message(STATUS "gRPC: ${YAZE_GRPC_TARGETS}")
endif()
if(NOT EMSCRIPTEN)
message(STATUS "httplib: ${YAZE_HTTPLIB_TARGETS}")
endif()
if(YAZE_BUILD_CLI AND NOT EMSCRIPTEN)
message(STATUS "FTXUI: ${YAZE_FTXUI_TARGETS}")
endif()
if(YAZE_BUILD_TESTS) if(YAZE_BUILD_TESTS)
include(cmake/gtest.cmake) message(STATUS "Testing: ${YAZE_TESTING_TARGETS}")
endif() endif()
message(STATUS "=================================")
# ImGui # Export all dependency targets for use in other CMake files
include(cmake/imgui.cmake) set(YAZE_ALL_DEPENDENCIES ${YAZE_ALL_DEPENDENCIES})
# FTXUI (for z3ed)
if(YAZE_BUILD_Z3ED)
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
GIT_TAG v5.0.0
)
FetchContent_MakeAvailable(ftxui)
endif()
# yaml-cpp (always available for configuration files)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Disable yaml-cpp tests" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Disable yaml-cpp contrib" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Disable yaml-cpp tools" FORCE)
set(YAML_CPP_INSTALL OFF CACHE BOOL "Disable yaml-cpp install" FORCE)
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "Disable yaml-cpp format target" FORCE)
# yaml-cpp (uses CMAKE_POLICY_VERSION_MINIMUM set in root CMakeLists.txt)
FetchContent_Declare(yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG 0.8.0
)
FetchContent_MakeAvailable(yaml-cpp)
# Fix MSVC exception handling warning for yaml-cpp
if(MSVC AND TARGET yaml-cpp)
target_compile_options(yaml-cpp PRIVATE /EHsc)
endif()
# nlohmann_json (header only)
if(YAZE_WITH_JSON)
set(JSON_BuildTests OFF CACHE INTERNAL "Disable nlohmann_json tests")
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/json ${CMAKE_BINARY_DIR}/third_party/json EXCLUDE_FROM_ALL)
endif()
# httplib (header only)
# No action needed here as it's included directly.

30
cmake/dependencies.lock Normal file
View File

@@ -0,0 +1,30 @@
# CPM Dependencies Lock File
# This file pins exact versions to ensure reproducible builds
# Update versions here when upgrading dependencies
# Core dependencies
set(SDL2_VERSION "2.30.0" CACHE STRING "SDL2 version")
set(YAML_CPP_VERSION "0.8.0" CACHE STRING "yaml-cpp version")
set(NLOHMANN_JSON_VERSION "3.11.3" CACHE STRING "nlohmann/json version")
set(HTTPLIB_VERSION "0.26.0" CACHE STRING "cpp-httplib version")
# gRPC and related
# Using v1.67.1 for MSVC compatibility (v1.75.1 has UPB compilation errors on Windows)
set(GRPC_VERSION "1.67.1" CACHE STRING "gRPC version - MSVC compatible")
set(PROTOBUF_VERSION "3.25.1" CACHE STRING "Protobuf version")
set(ABSEIL_VERSION "20240116.0" CACHE STRING "Abseil version")
# Cache revision: increment to force CPM cache invalidation (current: 2)
# Testing
set(GTEST_VERSION "1.14.0" CACHE STRING "Google Test version")
set(BENCHMARK_VERSION "1.8.3" CACHE STRING "Google Benchmark version")
# CLI tools
set(FTXUI_VERSION "5.0.0" CACHE STRING "FTXUI version")
# ImGui
set(IMGUI_VERSION "1.90.4" CACHE STRING "Dear ImGui version")
# Cache revision: increment to force rebuild (current: 2)
# ASAR
set(ASAR_VERSION "main" CACHE STRING "ASAR version")

View File

@@ -0,0 +1,55 @@
# FTXUI dependency management for CLI tools
# Uses CPM.cmake for consistent cross-platform builds
if(NOT YAZE_BUILD_CLI)
return()
endif()
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up FTXUI ${FTXUI_VERSION} with CPM.cmake")
# Use CPM to fetch FTXUI
CPMAddPackage(
NAME ftxui
VERSION ${FTXUI_VERSION}
GITHUB_REPOSITORY ArthurSonzogni/ftxui
GIT_TAG v${FTXUI_VERSION}
OPTIONS
"FTXUI_BUILD_EXAMPLES OFF"
"FTXUI_BUILD_TESTS OFF"
"FTXUI_ENABLE_INSTALL OFF"
)
# FTXUI targets are created during the build phase
# We'll create our own interface target and link when available
add_library(yaze_ftxui INTERFACE)
# Link to the actual FTXUI targets
if(TARGET ftxui::screen AND TARGET ftxui::dom AND TARGET ftxui::component)
target_link_libraries(yaze_ftxui INTERFACE
ftxui::screen
ftxui::dom
ftxui::component
)
else()
# Fallback for when targets aren't namespaced
target_link_libraries(yaze_ftxui INTERFACE
screen
dom
component
)
endif()
# Add include path with compile options for Ninja Multi-Config compatibility
# The -isystem-after flag doesn't work properly with some generator/compiler combinations
if(ftxui_SOURCE_DIR)
add_compile_options(-I${ftxui_SOURCE_DIR}/include)
message(STATUS " Added FTXUI include: ${ftxui_SOURCE_DIR}/include")
endif()
# Export FTXUI targets for use in other CMake files
set(YAZE_FTXUI_TARGETS yaze_ftxui)
message(STATUS "FTXUI setup complete")

View File

@@ -0,0 +1,581 @@
# gRPC and Protobuf dependency management
# Uses CPM.cmake for consistent cross-platform builds, with optional system package fallback
if(NOT YAZE_ENABLE_GRPC)
return()
endif()
# Include CPM and dependencies lock
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up gRPC ${GRPC_VERSION}")
#-----------------------------------------------------------------------
# Option: YAZE_PREFER_SYSTEM_GRPC - Use system-installed gRPC/protobuf/abseil
# when available (e.g., from Homebrew, apt, vcpkg).
#
# Benefits: Much faster configure/build times for local development
# Trade-off: May have version mismatches between system packages
#
# Example: cmake --preset mac-ai-fast (uses system packages)
#-----------------------------------------------------------------------
option(YAZE_PREFER_SYSTEM_GRPC "Prefer system-installed gRPC/protobuf over CPM" OFF)
if(YAZE_PREFER_SYSTEM_GRPC OR YAZE_USE_SYSTEM_DEPS)
message(STATUS "Attempting to use system gRPC/protobuf packages...")
# Try CMake's find_package first (works with Homebrew on macOS)
find_package(gRPC CONFIG QUIET)
find_package(Protobuf CONFIG QUIET)
find_package(absl CONFIG QUIET)
if(gRPC_FOUND AND Protobuf_FOUND AND absl_FOUND)
message(STATUS "✓ Found system gRPC: ${gRPC_VERSION}")
message(STATUS "✓ Found system Protobuf: ${Protobuf_VERSION}")
message(STATUS "✓ Found system Abseil")
# Find protoc and grpc_cpp_plugin executables
find_program(PROTOC_EXECUTABLE protoc)
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin)
if(PROTOC_EXECUTABLE AND GRPC_CPP_PLUGIN)
message(STATUS "✓ Found protoc: ${PROTOC_EXECUTABLE}")
message(STATUS "✓ Found grpc_cpp_plugin: ${GRPC_CPP_PLUGIN}")
# Create imported targets for the executables if they don't exist
if(NOT TARGET protoc)
add_executable(protoc IMPORTED)
set_target_properties(protoc PROPERTIES IMPORTED_LOCATION "${PROTOC_EXECUTABLE}")
endif()
if(NOT TARGET grpc_cpp_plugin)
add_executable(grpc_cpp_plugin IMPORTED)
set_target_properties(grpc_cpp_plugin PROPERTIES IMPORTED_LOCATION "${GRPC_CPP_PLUGIN}")
endif()
# Create convenience interface for basic gRPC linking
add_library(yaze_grpc_deps INTERFACE)
target_link_libraries(yaze_grpc_deps INTERFACE
gRPC::grpc++
gRPC::grpc++_reflection
protobuf::libprotobuf
)
# Ensure Abseil include directories are available
# Homebrew's abseil may not properly export include dirs
get_target_property(_ABSL_BASE_INCLUDE absl::base INTERFACE_INCLUDE_DIRECTORIES)
if(_ABSL_BASE_INCLUDE)
target_include_directories(yaze_grpc_deps INTERFACE ${_ABSL_BASE_INCLUDE})
message(STATUS " Added Abseil include: ${_ABSL_BASE_INCLUDE}")
elseif(APPLE)
# Fallback for Homebrew on macOS
target_include_directories(yaze_grpc_deps INTERFACE /opt/homebrew/include)
message(STATUS " Added Homebrew Abseil include: /opt/homebrew/include")
endif()
# Create interface libraries for compatibility with CPM target names
# CPM gRPC creates lowercase 'grpc++' targets
# System gRPC (Homebrew) creates namespaced 'gRPC::grpc++' targets
# We create interface libs (not aliases) so we can add include directories
if(NOT TARGET grpc++)
add_library(grpc++ INTERFACE)
target_link_libraries(grpc++ INTERFACE gRPC::grpc++)
# Add abseil includes for targets linking to grpc++
if(_ABSL_BASE_INCLUDE)
target_include_directories(grpc++ INTERFACE ${_ABSL_BASE_INCLUDE})
elseif(APPLE)
target_include_directories(grpc++ INTERFACE /opt/homebrew/include)
endif()
endif()
if(NOT TARGET grpc++_reflection)
add_library(grpc++_reflection INTERFACE)
target_link_libraries(grpc++_reflection INTERFACE gRPC::grpc++_reflection)
endif()
if(NOT TARGET grpc::grpc++)
add_library(grpc::grpc++ ALIAS gRPC::grpc++)
endif()
if(NOT TARGET grpc::grpc++_reflection)
add_library(grpc::grpc++_reflection ALIAS gRPC::grpc++_reflection)
endif()
# Export targets
set(YAZE_GRPC_TARGETS
gRPC::grpc++
gRPC::grpc++_reflection
protobuf::libprotobuf
)
# Setup protobuf generation directory
set(_gRPC_PROTO_GENS_DIR ${CMAKE_BINARY_DIR}/gens CACHE INTERNAL "Protobuf generated files directory")
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/gens)
# Get protobuf include directory from package
get_target_property(_PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
if(_PROTOBUF_INCLUDE_DIRS)
list(GET _PROTOBUF_INCLUDE_DIRS 0 _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR)
set(_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR} CACHE INTERNAL "Protobuf include directory")
endif()
# Add global include directories for system packages
# This ensures all targets can find abseil headers even if target propagation fails
# Use add_compile_options for reliable include path propagation with Ninja Multi-Config
if(_ABSL_BASE_INCLUDE)
add_compile_options(-I${_ABSL_BASE_INCLUDE})
message(STATUS " Added Abseil include via compile options: ${_ABSL_BASE_INCLUDE}")
elseif(APPLE)
add_compile_options(-I/opt/homebrew/include)
message(STATUS " Added Homebrew include via compile options: /opt/homebrew/include")
endif()
message(STATUS "✓ Using SYSTEM gRPC stack - fast configure!")
message(STATUS " Protobuf gens dir: ${_gRPC_PROTO_GENS_DIR}")
message(STATUS " Protobuf include dir: ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}")
set(_YAZE_USING_SYSTEM_GRPC TRUE)
else()
message(STATUS "○ System gRPC found but protoc/grpc_cpp_plugin missing, falling back to CPM")
set(_YAZE_USING_SYSTEM_GRPC FALSE)
endif()
else()
message(STATUS "○ System gRPC/protobuf not found, falling back to CPM")
set(_YAZE_USING_SYSTEM_GRPC FALSE)
endif()
endif()
# If we're using system gRPC, skip CPM entirely and jump to function definition
if(_YAZE_USING_SYSTEM_GRPC)
message(STATUS "Skipping CPM gRPC build - using system packages")
else()
# CPM build path
message(STATUS "Building gRPC from source via CPM (this takes 15-20 minutes on first build)")
message(STATUS " Tip: Install gRPC via Homebrew and use -DYAZE_PREFER_SYSTEM_GRPC=ON for faster builds")
#-----------------------------------------------------------------------
# Guard CMake's package lookup so CPM always downloads a consistent gRPC
# toolchain instead of picking up partially-installed Homebrew/apt copies.
#-----------------------------------------------------------------------
if(DEFINED CPM_USE_LOCAL_PACKAGES)
set(_YAZE_GRPC_SAVED_CPM_USE_LOCAL_PACKAGES "${CPM_USE_LOCAL_PACKAGES}")
else()
set(_YAZE_GRPC_SAVED_CPM_USE_LOCAL_PACKAGES "__YAZE_UNSET__")
endif()
set(CPM_USE_LOCAL_PACKAGES OFF)
foreach(_yaze_pkg IN ITEMS gRPC Protobuf absl)
string(TOUPPER "CMAKE_DISABLE_FIND_PACKAGE_${_yaze_pkg}" _yaze_disable_var)
if(DEFINED ${_yaze_disable_var})
set("_YAZE_GRPC_SAVE_${_yaze_disable_var}" "${${_yaze_disable_var}}")
else()
set("_YAZE_GRPC_SAVE_${_yaze_disable_var}" "__YAZE_UNSET__")
endif()
set(${_yaze_disable_var} TRUE)
endforeach()
if(DEFINED PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
set(_YAZE_GRPC_SAVED_PKG_CONFIG_USE_CMAKE_PREFIX_PATH "${PKG_CONFIG_USE_CMAKE_PREFIX_PATH}")
else()
set(_YAZE_GRPC_SAVED_PKG_CONFIG_USE_CMAKE_PREFIX_PATH "__YAZE_UNSET__")
endif()
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH FALSE)
set(_YAZE_GRPC_SAVED_PREFIX_PATH "${CMAKE_PREFIX_PATH}")
set(CMAKE_PREFIX_PATH "")
if(DEFINED CMAKE_CROSSCOMPILING)
set(_YAZE_GRPC_SAVED_CROSSCOMPILING "${CMAKE_CROSSCOMPILING}")
else()
set(_YAZE_GRPC_SAVED_CROSSCOMPILING "__YAZE_UNSET__")
endif()
if(CMAKE_HOST_SYSTEM_NAME STREQUAL CMAKE_SYSTEM_NAME
AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_CROSSCOMPILING FALSE)
endif()
if(DEFINED CMAKE_CXX_STANDARD)
set(_YAZE_GRPC_SAVED_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
else()
set(_YAZE_GRPC_SAVED_CXX_STANDARD "__YAZE_UNSET__")
endif()
set(CMAKE_CXX_STANDARD 17)
# Set gRPC options before adding package
set(gRPC_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_CODEGEN ON CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_CPP_PLUGIN ON CACHE BOOL "" FORCE)
set(gRPC_BUILD_CSHARP_EXT OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_CSHARP_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_NODE_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_CPP_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPCPP_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BENCHMARK_PROVIDER "none" CACHE STRING "" FORCE)
set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "" FORCE)
set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "" FORCE)
set(gRPC_ABSL_PROVIDER "module" CACHE STRING "" FORCE)
set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_CONFORMANCE OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_PROTOC_BINARIES ON CACHE BOOL "" FORCE)
set(protobuf_WITH_ZLIB ON CACHE BOOL "" FORCE)
set(protobuf_INSTALL OFF CACHE BOOL "" FORCE)
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "" FORCE)
set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE)
set(ABSL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(utf8_range_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(utf8_range_INSTALL OFF CACHE BOOL "" FORCE)
set(utf8_range_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
# Force consistent MSVC runtime library across all gRPC components (Windows only)
# This ensures gRPC, protobuf, and Abseil all use the same CRT linking mode
if(WIN32 AND MSVC)
# Use dynamic CRT (/MD for Release, /MDd for Debug) to avoid undefined math symbols
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL" CACHE STRING "" FORCE)
# Also ensure protobuf doesn't try to use static runtime
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "" FORCE)
message(STATUS "Forcing dynamic MSVC runtime for gRPC dependencies: ${CMAKE_MSVC_RUNTIME_LIBRARY}")
endif()
# Temporarily disable installation to prevent utf8_range export errors
# This is a workaround for gRPC 1.67.1 where utf8_range tries to install targets
# that depend on Abseil, but we have ABSL_ENABLE_INSTALL=OFF
set(CMAKE_SKIP_INSTALL_RULES TRUE)
# Use CPM to fetch gRPC with bundled dependencies
# GIT_SUBMODULES "" disables submodule recursion since gRPC handles its own deps via CMake
if(WIN32)
set(GRPC_VERSION_TO_USE "1.67.1")
else()
set(GRPC_VERSION_TO_USE "1.76.0")
endif()
message(STATUS "Selected gRPC version ${GRPC_VERSION_TO_USE} for platform ${CMAKE_SYSTEM_NAME}")
CPMAddPackage(
NAME grpc
VERSION ${GRPC_VERSION_TO_USE}
GITHUB_REPOSITORY grpc/grpc
GIT_TAG v${GRPC_VERSION_TO_USE}
GIT_SUBMODULES ""
GIT_SHALLOW TRUE
)
# Re-enable installation rules after gRPC is loaded
set(CMAKE_SKIP_INSTALL_RULES FALSE)
# Restore CPM lookup behaviour and toolchain detection environment early so
# subsequent dependency configuration isn't polluted even if we hit errors.
if("${_YAZE_GRPC_SAVED_CPM_USE_LOCAL_PACKAGES}" STREQUAL "__YAZE_UNSET__")
unset(CPM_USE_LOCAL_PACKAGES)
else()
set(CPM_USE_LOCAL_PACKAGES "${_YAZE_GRPC_SAVED_CPM_USE_LOCAL_PACKAGES}")
endif()
foreach(_yaze_pkg IN ITEMS gRPC Protobuf absl)
string(TOUPPER "CMAKE_DISABLE_FIND_PACKAGE_${_yaze_pkg}" _yaze_disable_var)
string(TOUPPER "_YAZE_GRPC_SAVE_${_yaze_disable_var}" _yaze_saved_key)
if(NOT DEFINED ${_yaze_saved_key})
continue()
endif()
if("${${_yaze_saved_key}}" STREQUAL "__YAZE_UNSET__")
unset(${_yaze_disable_var})
else()
set(${_yaze_disable_var} "${${_yaze_saved_key}}")
endif()
unset(${_yaze_saved_key})
endforeach()
if("${_YAZE_GRPC_SAVED_PKG_CONFIG_USE_CMAKE_PREFIX_PATH}" STREQUAL "__YAZE_UNSET__")
unset(PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
else()
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH "${_YAZE_GRPC_SAVED_PKG_CONFIG_USE_CMAKE_PREFIX_PATH}")
endif()
unset(_YAZE_GRPC_SAVED_PKG_CONFIG_USE_CMAKE_PREFIX_PATH)
set(CMAKE_PREFIX_PATH "${_YAZE_GRPC_SAVED_PREFIX_PATH}")
unset(_YAZE_GRPC_SAVED_PREFIX_PATH)
if("${_YAZE_GRPC_SAVED_CROSSCOMPILING}" STREQUAL "__YAZE_UNSET__")
unset(CMAKE_CROSSCOMPILING)
else()
set(CMAKE_CROSSCOMPILING "${_YAZE_GRPC_SAVED_CROSSCOMPILING}")
endif()
unset(_YAZE_GRPC_SAVED_CROSSCOMPILING)
if("${_YAZE_GRPC_SAVED_CXX_STANDARD}" STREQUAL "__YAZE_UNSET__")
unset(CMAKE_CXX_STANDARD)
else()
set(CMAKE_CXX_STANDARD "${_YAZE_GRPC_SAVED_CXX_STANDARD}")
endif()
unset(_YAZE_GRPC_SAVED_CXX_STANDARD)
# Check which target naming convention is used
if(TARGET grpc++)
message(STATUS "Found non-namespaced gRPC target grpc++")
if(NOT TARGET grpc::grpc++)
add_library(grpc::grpc++ ALIAS grpc++)
endif()
if(NOT TARGET grpc::grpc++_reflection AND TARGET grpc++_reflection)
add_library(grpc::grpc++_reflection ALIAS grpc++_reflection)
endif()
endif()
set(_YAZE_GRPC_ERRORS "")
if(NOT TARGET grpc++ AND NOT TARGET grpc::grpc++)
list(APPEND _YAZE_GRPC_ERRORS "gRPC target not found after CPM fetch")
endif()
if(NOT TARGET protoc)
list(APPEND _YAZE_GRPC_ERRORS "protoc target not found after gRPC setup")
endif()
if(NOT TARGET grpc_cpp_plugin)
list(APPEND _YAZE_GRPC_ERRORS "grpc_cpp_plugin target not found after gRPC setup")
endif()
if(_YAZE_GRPC_ERRORS)
list(JOIN _YAZE_GRPC_ERRORS "\n" _YAZE_GRPC_ERROR_MESSAGE)
message(FATAL_ERROR "${_YAZE_GRPC_ERROR_MESSAGE}")
endif()
# Create convenience interface for basic gRPC linking (renamed to avoid conflict with yaze_grpc_support STATIC library)
add_library(yaze_grpc_deps INTERFACE)
target_link_libraries(yaze_grpc_deps INTERFACE
grpc::grpc++
grpc::grpc++_reflection
protobuf::libprotobuf
)
# Define Windows macro guards once so protobuf-generated headers stay clean
if(WIN32)
add_compile_definitions(
WIN32_LEAN_AND_MEAN
NOMINMAX
NOGDI
)
endif()
# Export Abseil targets from gRPC's bundled Abseil
# When gRPC_ABSL_PROVIDER is "module", gRPC fetches and builds Abseil
# All Abseil targets are available, we just need to list them
# Note: All targets are available even if not listed here, but listing ensures consistency
set(ABSL_TARGETS
absl::base
absl::config
absl::core_headers
absl::utility
absl::memory
absl::container_memory
absl::strings
absl::strings_internal
absl::str_format
absl::str_format_internal
absl::cord
absl::hash
absl::time
absl::time_zone
absl::status
absl::statusor
absl::flags
absl::flags_parse
absl::flags_usage
absl::flags_commandlineflag
absl::flags_marshalling
absl::flags_private_handle_accessor
absl::flags_program_name
absl::flags_config
absl::flags_reflection
absl::examine_stack
absl::stacktrace
absl::failure_signal_handler
absl::flat_hash_map
absl::synchronization
absl::symbolize
absl::strerror
)
# Only expose absl::int128 when it's supported without warnings
if(NOT WIN32)
list(APPEND ABSL_TARGETS absl::int128)
endif()
# Export gRPC targets for use in other CMake files
set(YAZE_GRPC_TARGETS
grpc::grpc++
grpc::grpc++_reflection
protobuf::libprotobuf
protoc
grpc_cpp_plugin
)
message(STATUS "gRPC setup complete - targets available: ${YAZE_GRPC_TARGETS}")
# Setup protobuf generation directory (use CACHE so it's available in functions)
set(_gRPC_PROTO_GENS_DIR ${CMAKE_BINARY_DIR}/gens CACHE INTERNAL "Protobuf generated files directory")
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/gens)
# Get protobuf include directories (extract from generator expression or direct path)
if(TARGET libprotobuf)
get_target_property(_PROTOBUF_INCLUDE_DIRS libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
# Handle generator expressions
string(REGEX REPLACE "\\$<BUILD_INTERFACE:([^>]+)>" "\\1" _PROTOBUF_INCLUDE_DIR_CLEAN "${_PROTOBUF_INCLUDE_DIRS}")
list(GET _PROTOBUF_INCLUDE_DIR_CLEAN 0 _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR)
set(_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR} CACHE INTERNAL "Protobuf include directory")
elseif(TARGET protobuf::libprotobuf)
get_target_property(_PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
string(REGEX REPLACE "\\$<BUILD_INTERFACE:([^>]+)>" "\\1" _PROTOBUF_INCLUDE_DIR_CLEAN "${_PROTOBUF_INCLUDE_DIRS}")
list(GET _PROTOBUF_INCLUDE_DIR_CLEAN 0 _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR)
set(_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR} CACHE INTERNAL "Protobuf include directory")
endif()
# Remove x86-only Abseil compile flags when building on ARM64 macOS runners
set(_YAZE_PATCH_ABSL_FOR_APPLE FALSE)
if(APPLE)
if(CMAKE_OSX_ARCHITECTURES)
string(TOLOWER "${CMAKE_OSX_ARCHITECTURES}" _yaze_osx_archs)
if(_yaze_osx_archs MATCHES "arm64")
set(_YAZE_PATCH_ABSL_FOR_APPLE TRUE)
endif()
else()
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _yaze_proc)
if(_yaze_proc MATCHES "arm64" OR _yaze_proc MATCHES "aarch64")
set(_YAZE_PATCH_ABSL_FOR_APPLE TRUE)
endif()
endif()
endif()
if(_YAZE_PATCH_ABSL_FOR_APPLE)
set(_YAZE_ABSL_X86_TARGETS
absl_random_internal_randen_hwaes
absl_random_internal_randen_hwaes_impl
absl_crc_internal_cpu_detect
)
foreach(_yaze_absl_target IN LISTS _YAZE_ABSL_X86_TARGETS)
if(TARGET ${_yaze_absl_target})
get_target_property(_yaze_absl_opts ${_yaze_absl_target} COMPILE_OPTIONS)
if(_yaze_absl_opts AND NOT _yaze_absl_opts STREQUAL "NOTFOUND")
set(_yaze_filtered_opts)
foreach(_yaze_opt IN LISTS _yaze_absl_opts)
if(_yaze_opt STREQUAL "-Xarch_x86_64")
continue()
endif()
if(_yaze_opt MATCHES "^-m(sse|avx)")
continue()
endif()
if(_yaze_opt STREQUAL "-maes")
continue()
endif()
list(APPEND _yaze_filtered_opts "${_yaze_opt}")
endforeach()
set_property(TARGET ${_yaze_absl_target} PROPERTY COMPILE_OPTIONS ${_yaze_filtered_opts})
message(STATUS "Patched ${_yaze_absl_target} compile options for ARM64 macOS")
endif()
endif()
endforeach()
endif()
unset(_YAZE_GRPC_SAVED_CPM_USE_LOCAL_PACKAGES)
unset(_YAZE_GRPC_ERRORS)
unset(_YAZE_GRPC_ERROR_MESSAGE)
message(STATUS "Protobuf gens dir: ${_gRPC_PROTO_GENS_DIR}")
message(STATUS "Protobuf include dir: ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}")
# Export protobuf targets
set(YAZE_PROTOBUF_TARGETS
protobuf::libprotobuf
)
endif() # End of CPM build path (if NOT _YAZE_USING_SYSTEM_GRPC)
# Function to add protobuf/gRPC code generation to a target
# This function works with both system and CPM-built gRPC
function(target_add_protobuf target)
if(NOT TARGET ${target})
message(FATAL_ERROR "Target ${target} doesn't exist")
endif()
if(NOT ARGN)
message(SEND_ERROR "Error: target_add_protobuf() called without any proto files")
return()
endif()
# Determine protoc and grpc_cpp_plugin paths
# For IMPORTED targets (system gRPC), use IMPORTED_LOCATION
# For built targets (CPM gRPC), use TARGET_FILE generator expression
get_target_property(_PROTOC_IMPORTED protoc IMPORTED)
get_target_property(_GRPC_PLUGIN_IMPORTED grpc_cpp_plugin IMPORTED)
if(_PROTOC_IMPORTED)
get_target_property(_PROTOC_EXECUTABLE protoc IMPORTED_LOCATION)
set(_PROTOC_DEPENDS "") # No build dependency for system protoc
else()
set(_PROTOC_EXECUTABLE "$<TARGET_FILE:protoc>")
set(_PROTOC_DEPENDS "protoc")
endif()
if(_GRPC_PLUGIN_IMPORTED)
get_target_property(_GRPC_PLUGIN_EXECUTABLE grpc_cpp_plugin IMPORTED_LOCATION)
set(_GRPC_PLUGIN_DEPENDS "") # No build dependency for system plugin
else()
set(_GRPC_PLUGIN_EXECUTABLE "$<TARGET_FILE:grpc_cpp_plugin>")
set(_GRPC_PLUGIN_DEPENDS "grpc_cpp_plugin")
endif()
set(_protobuf_include_path -I ${CMAKE_SOURCE_DIR}/src -I ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR})
foreach(FIL ${ARGN})
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
get_filename_component(FIL_WE ${FIL} NAME_WE)
file(RELATIVE_PATH REL_FIL ${CMAKE_SOURCE_DIR}/src ${ABS_FIL})
get_filename_component(REL_DIR ${REL_FIL} DIRECTORY)
if(NOT REL_DIR OR REL_DIR STREQUAL ".")
set(RELFIL_WE "${FIL_WE}")
else()
set(RELFIL_WE "${REL_DIR}/${FIL_WE}")
endif()
message(STATUS " Proto file: ${FIL_WE}")
message(STATUS " ABS_FIL = ${ABS_FIL}")
message(STATUS " REL_FIL = ${REL_FIL}")
message(STATUS " REL_DIR = ${REL_DIR}")
message(STATUS " RELFIL_WE = ${RELFIL_WE}")
message(STATUS " Output = ${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h")
add_custom_command(
OUTPUT "${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.cc"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.h"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}_mock.grpc.pb.h"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h"
COMMAND ${_PROTOC_EXECUTABLE}
ARGS --grpc_out=generate_mock_code=true:${_gRPC_PROTO_GENS_DIR}
--cpp_out=${_gRPC_PROTO_GENS_DIR}
--plugin=protoc-gen-grpc=${_GRPC_PLUGIN_EXECUTABLE}
${_protobuf_include_path}
${ABS_FIL}
DEPENDS ${ABS_FIL} ${_PROTOC_DEPENDS} ${_GRPC_PLUGIN_DEPENDS}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src
COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
VERBATIM)
target_sources(${target} PRIVATE
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.cc"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.grpc.pb.h"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}_mock.grpc.pb.h"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h"
)
target_include_directories(${target} PUBLIC
$<BUILD_INTERFACE:${_gRPC_PROTO_GENS_DIR}>
$<BUILD_INTERFACE:${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR}>
)
endforeach()
endfunction()

View File

@@ -0,0 +1,78 @@
# cpp-httplib dependency management
if(EMSCRIPTEN)
set(YAZE_HTTPLIB_TARGETS "" CACHE INTERNAL "cpp-httplib targets")
return()
endif()
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up cpp-httplib ${HTTPLIB_VERSION}")
if(YAZE_PLATFORM_IOS)
CPMAddPackage(
NAME httplib
VERSION ${HTTPLIB_VERSION}
GITHUB_REPOSITORY yhirose/cpp-httplib
GIT_TAG v${HTTPLIB_VERSION}
DOWNLOAD_ONLY YES
)
if(NOT TARGET httplib)
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ${httplib_SOURCE_DIR})
endif()
if(NOT TARGET httplib::httplib)
add_library(httplib::httplib ALIAS httplib)
endif()
set(YAZE_HTTPLIB_TARGETS httplib::httplib)
set(YAZE_HTTPLIB_TARGETS httplib::httplib CACHE INTERNAL "cpp-httplib targets")
message(STATUS "cpp-httplib configured as header-only for iOS")
return()
endif()
set(_YAZE_USE_SYSTEM_HTTPLIB ${YAZE_USE_SYSTEM_DEPS})
# Try to use system packages first
if(_YAZE_USE_SYSTEM_HTTPLIB)
find_package(httplib QUIET)
if(httplib_FOUND)
message(STATUS "Using system httplib")
set(YAZE_HTTPLIB_TARGETS httplib::httplib CACHE INTERNAL "cpp-httplib targets")
return()
elseif(YAZE_USE_SYSTEM_DEPS)
message(WARNING "System httplib not found despite YAZE_USE_SYSTEM_DEPS=ON; falling back to CPM download")
endif()
endif()
CPMAddPackage(
NAME httplib
VERSION ${HTTPLIB_VERSION}
GITHUB_REPOSITORY yhirose/cpp-httplib
GIT_TAG v${HTTPLIB_VERSION}
OPTIONS
"HTTPLIB_INSTALL OFF"
"HTTPLIB_TEST OFF"
"HTTPLIB_COMPILE OFF"
"HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF"
"HTTPLIB_USE_ZLIB_IF_AVAILABLE OFF"
"HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF"
"HTTPLIB_USE_ZSTD_IF_AVAILABLE OFF"
)
# Verify target is available
if(TARGET httplib::httplib)
message(STATUS "httplib::httplib target found")
elseif(TARGET httplib)
add_library(httplib::httplib ALIAS httplib)
message(STATUS "Created httplib::httplib alias")
else()
message(FATAL_ERROR "httplib target not found after CPM fetch")
endif()
set(YAZE_HTTPLIB_TARGETS httplib::httplib)
set(YAZE_HTTPLIB_TARGETS httplib::httplib CACHE INTERNAL "cpp-httplib targets")
message(STATUS "cpp-httplib setup complete")

View File

@@ -0,0 +1,100 @@
# Dear ImGui dependency management
# Uses the bundled ImGui in ext/imgui
message(STATUS "Setting up Dear ImGui from bundled sources")
# Use the bundled ImGui from ext/imgui
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/ext/imgui)
# Select ImGui backend sources based on SDL version
if(YAZE_USE_SDL3)
set(IMGUI_BACKEND_SOURCES
${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
${IMGUI_DIR}/backends/imgui_impl_sdlrenderer3.cpp
)
message(STATUS "Using ImGui SDL3 backend")
else()
set(IMGUI_BACKEND_SOURCES
${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp
${IMGUI_DIR}/backends/imgui_impl_sdlrenderer2.cpp
)
message(STATUS "Using ImGui SDL2 backend")
endif()
if(YAZE_PLATFORM_IOS)
list(APPEND IMGUI_BACKEND_SOURCES
${IMGUI_DIR}/backends/imgui_impl_metal.mm
)
endif()
# Create ImGui library with core files from bundled source
add_library(ImGui STATIC
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
# SDL backend (version-dependent)
${IMGUI_BACKEND_SOURCES}
# C++ stdlib helpers (for std::string support)
${IMGUI_DIR}/misc/cpp/imgui_stdlib.cpp
)
target_include_directories(ImGui PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends
)
# Set C++ standard requirement (ImGui 1.90+ requires C++11, we use C++17 for consistency)
target_compile_features(ImGui PUBLIC cxx_std_17)
# Link to SDL (version-dependent)
if(YAZE_USE_SDL3)
target_link_libraries(ImGui PUBLIC ${YAZE_SDL3_TARGETS})
else()
target_link_libraries(ImGui PUBLIC ${YAZE_SDL2_TARGETS})
endif()
message(STATUS "Created ImGui target from bundled source at ${IMGUI_DIR}")
# Create ImGui Test Engine for test automation (if tests are enabled)
if(YAZE_BUILD_TESTS)
set(IMGUI_TEST_ENGINE_DIR ${CMAKE_SOURCE_DIR}/ext/imgui_test_engine/imgui_test_engine)
if(EXISTS ${IMGUI_TEST_ENGINE_DIR})
set(IMGUI_TEST_ENGINE_SOURCES
${IMGUI_TEST_ENGINE_DIR}/imgui_te_context.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_coroutine.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_engine.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_exporters.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_perftool.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_ui.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_te_utils.cpp
${IMGUI_TEST_ENGINE_DIR}/imgui_capture_tool.cpp
)
add_library(ImGuiTestEngine STATIC ${IMGUI_TEST_ENGINE_SOURCES})
target_include_directories(ImGuiTestEngine PUBLIC
${IMGUI_DIR}
${IMGUI_TEST_ENGINE_DIR}
${CMAKE_SOURCE_DIR}/ext
)
target_compile_features(ImGuiTestEngine PUBLIC cxx_std_17)
if(YAZE_USE_SDL3)
target_link_libraries(ImGuiTestEngine PUBLIC ImGui ${YAZE_SDL3_TARGETS})
else()
target_link_libraries(ImGuiTestEngine PUBLIC ImGui ${YAZE_SDL2_TARGETS})
endif()
target_compile_definitions(ImGuiTestEngine PUBLIC
IMGUI_ENABLE_TEST_ENGINE=1
IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL=1
)
message(STATUS "Created ImGuiTestEngine target for test automation")
endif()
endif()
# Export ImGui targets for use in other CMake files
set(YAZE_IMGUI_TARGETS ImGui)
message(STATUS "Dear ImGui setup complete - YAZE_IMGUI_TARGETS = ${YAZE_IMGUI_TARGETS}")

View File

@@ -0,0 +1,27 @@
# ImPlot dependency management
# Uses the bundled ImPlot sources that ship with the ImGui Test Engine
set(YAZE_IMPLOT_TARGETS "")
set(IMPLOT_DIR ${CMAKE_SOURCE_DIR}/ext/imgui_test_engine/imgui_test_suite/thirdparty/implot)
if(EXISTS ${IMPLOT_DIR}/implot.h)
message(STATUS "Setting up ImPlot from bundled sources")
add_library(ImPlot STATIC
${IMPLOT_DIR}/implot.cpp
${IMPLOT_DIR}/implot_items.cpp
)
target_include_directories(ImPlot PUBLIC
${IMPLOT_DIR}
${IMGUI_DIR}
)
target_link_libraries(ImPlot PUBLIC ImGui)
target_compile_features(ImPlot PUBLIC cxx_std_17)
set(YAZE_IMPLOT_TARGETS ImPlot)
else()
message(WARNING "ImPlot sources not found at ${IMPLOT_DIR}. Plot widgets will be unavailable.")
endif()

View File

@@ -0,0 +1,60 @@
# nlohmann_json dependency management
if(NOT YAZE_ENABLE_JSON)
return()
endif()
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up nlohmann_json ${NLOHMANN_JSON_VERSION}")
set(_YAZE_USE_SYSTEM_JSON ${YAZE_USE_SYSTEM_DEPS})
if(NOT _YAZE_USE_SYSTEM_JSON)
unset(nlohmann_json_DIR CACHE)
endif()
# Try to use system packages first
if(_YAZE_USE_SYSTEM_JSON)
find_package(nlohmann_json QUIET)
if(nlohmann_json_FOUND)
message(STATUS "Using system nlohmann_json")
set(YAZE_JSON_TARGETS nlohmann_json::nlohmann_json CACHE INTERNAL "nlohmann_json targets")
return()
elseif(YAZE_USE_SYSTEM_DEPS)
message(WARNING "System nlohmann_json not found despite YAZE_USE_SYSTEM_DEPS=ON; falling back to CPM download")
endif()
endif()
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
set(JSON_Install OFF CACHE BOOL "" FORCE)
set(JSON_MultipleHeaders OFF CACHE BOOL "" FORCE)
CPMAddPackage(
NAME nlohmann_json
VERSION ${NLOHMANN_JSON_VERSION}
GITHUB_REPOSITORY nlohmann/json
GIT_TAG v${NLOHMANN_JSON_VERSION}
OPTIONS
"JSON_BuildTests OFF"
"JSON_Install OFF"
"JSON_MultipleHeaders OFF"
)
# Verify target is available
if(TARGET nlohmann_json::nlohmann_json)
message(STATUS "nlohmann_json target found")
elseif(TARGET nlohmann_json)
# Create alias if only non-namespaced target exists
add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json)
message(STATUS "Created nlohmann_json::nlohmann_json alias")
else()
message(FATAL_ERROR "nlohmann_json target not found after CPM fetch")
endif()
# Export for use in other CMake files
set(YAZE_JSON_TARGETS nlohmann_json::nlohmann_json)
set(YAZE_JSON_TARGETS nlohmann_json::nlohmann_json CACHE INTERNAL "nlohmann_json targets")
message(STATUS "nlohmann_json setup complete")

View File

@@ -0,0 +1,130 @@
# SDL2 dependency management
# Uses CPM.cmake for consistent cross-platform builds
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
# For Emscripten, use the built-in SDL2 port
if(EMSCRIPTEN)
message(STATUS "Using Emscripten built-in SDL2")
if(NOT TARGET yaze_sdl2)
add_library(yaze_sdl2 INTERFACE)
# Flags are already set in CMakePresets.json or toolchain (-s USE_SDL=2)
# But we can enforce them here too if needed, or just leave empty as an interface
# to satisfy linking requirements of other targets.
target_link_options(yaze_sdl2 INTERFACE "SHELL:-s USE_SDL=2")
target_compile_options(yaze_sdl2 INTERFACE "SHELL:-s USE_SDL=2")
endif()
set(YAZE_SDL2_TARGETS yaze_sdl2)
return()
endif()
message(STATUS "Setting up SDL2 ${SDL2_VERSION} with CPM.cmake")
# Try to use system packages first if requested
if(YAZE_USE_SYSTEM_DEPS)
find_package(SDL2 QUIET)
if(SDL2_FOUND)
message(STATUS "Using system SDL2")
if(NOT TARGET yaze_sdl2)
add_library(yaze_sdl2 INTERFACE)
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2)
if(TARGET SDL2::SDL2main)
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2main)
endif()
endif()
set(YAZE_SDL2_TARGETS yaze_sdl2 CACHE INTERNAL "")
return()
endif()
endif()
# Use CPM to fetch SDL2
CPMAddPackage(
NAME SDL2
VERSION ${SDL2_VERSION}
GITHUB_REPOSITORY libsdl-org/SDL
GIT_TAG release-${SDL2_VERSION}
OPTIONS
"SDL_SHARED OFF"
"SDL_STATIC ON"
"SDL_TEST OFF"
"SDL_INSTALL OFF"
"SDL_CMAKE_DEBUG_POSTFIX d"
)
# Verify SDL2 targets are available
if(NOT TARGET SDL2-static AND NOT TARGET SDL2::SDL2-static AND NOT TARGET SDL2::SDL2)
message(FATAL_ERROR "SDL2 target not found after CPM fetch")
endif()
# Create convenience targets for the rest of the project
if(NOT TARGET yaze_sdl2)
add_library(yaze_sdl2 INTERFACE)
# SDL2 from CPM might use SDL2-static or SDL2::SDL2-static
if(TARGET SDL2-static)
message(STATUS "Using SDL2-static target")
target_link_libraries(yaze_sdl2 INTERFACE SDL2-static)
# Also explicitly add include directories if they exist
if(SDL2_SOURCE_DIR)
target_include_directories(yaze_sdl2 INTERFACE ${SDL2_SOURCE_DIR}/include)
message(STATUS "Added SDL2 include: ${SDL2_SOURCE_DIR}/include")
endif()
elseif(TARGET SDL2::SDL2-static)
message(STATUS "Using SDL2::SDL2-static target")
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2-static)
# For local Homebrew SDL2, also add include path explicitly
# SDL headers are in the SDL2 subdirectory
if(YAZE_PLATFORM_MACOS AND EXISTS "/opt/homebrew/opt/sdl2/include/SDL2")
target_include_directories(yaze_sdl2 INTERFACE /opt/homebrew/opt/sdl2/include/SDL2)
message(STATUS "Added Homebrew SDL2 include path: /opt/homebrew/opt/sdl2/include/SDL2")
endif()
else()
message(STATUS "Using SDL2::SDL2 target")
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2)
endif()
endif()
# Add platform-specific libraries
if(WIN32)
target_link_libraries(yaze_sdl2 INTERFACE
winmm
imm32
version
setupapi
wbemuuid
)
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
elseif(YAZE_PLATFORM_MACOS)
target_link_libraries(yaze_sdl2 INTERFACE
"-framework Cocoa"
"-framework IOKit"
"-framework CoreVideo"
"-framework ForceFeedback"
)
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
elseif(YAZE_PLATFORM_IOS)
target_link_libraries(yaze_sdl2 INTERFACE
"-framework UIKit"
"-framework Foundation"
"-framework CoreGraphics"
"-framework CoreVideo"
"-framework CoreMotion"
"-framework QuartzCore"
"-framework AVFoundation"
"-framework AudioToolbox"
"-framework Metal"
"-framework GameController"
)
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
elseif(UNIX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_link_libraries(yaze_sdl2 INTERFACE ${GTK3_LIBRARIES})
target_include_directories(yaze_sdl2 INTERFACE ${GTK3_INCLUDE_DIRS})
target_compile_options(yaze_sdl2 INTERFACE ${GTK3_CFLAGS_OTHER})
endif()
# Export SDL2 targets for use in other CMake files
set(YAZE_SDL2_TARGETS yaze_sdl2)
message(STATUS "SDL2 setup complete - YAZE_SDL2_TARGETS = ${YAZE_SDL2_TARGETS}")

View File

@@ -0,0 +1,124 @@
# SDL3 dependency management
# Uses CPM.cmake for consistent cross-platform builds
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up SDL3 (experimental) with CPM.cmake")
# SDL3 specific version (using latest stable 3.2 release)
set(SDL3_VERSION "3.2.26")
# Try to use system packages first if requested
if(YAZE_USE_SYSTEM_DEPS)
find_package(SDL3 QUIET)
if(SDL3_FOUND)
message(STATUS "Using system SDL3")
if(NOT TARGET yaze_sdl3)
add_library(yaze_sdl3 INTERFACE)
target_link_libraries(yaze_sdl3 INTERFACE SDL3::SDL3)
if(TARGET SDL3::SDL3main)
target_link_libraries(yaze_sdl3 INTERFACE SDL3::SDL3main)
endif()
endif()
set(YAZE_SDL3_TARGETS yaze_sdl3 CACHE INTERNAL "")
return()
endif()
endif()
# Use CPM to fetch SDL3
CPMAddPackage(
NAME SDL3
VERSION ${SDL3_VERSION}
GITHUB_REPOSITORY libsdl-org/SDL
GIT_TAG release-${SDL3_VERSION}
OPTIONS
"SDL_SHARED OFF"
"SDL_STATIC ON"
"SDL_TEST OFF"
"SDL_INSTALL OFF"
"SDL_CMAKE_DEBUG_POSTFIX d"
"SDL3_DISABLE_INSTALL ON"
"SDL3_DISABLE_UNINSTALL ON"
)
# Verify SDL3 targets are available
if(NOT TARGET SDL3-static AND NOT TARGET SDL3::SDL3-static AND NOT TARGET SDL3::SDL3)
message(FATAL_ERROR "SDL3 target not found after CPM fetch")
endif()
# Create convenience targets for the rest of the project
if(NOT TARGET yaze_sdl3)
add_library(yaze_sdl3 INTERFACE)
# SDL3 from CPM might use SDL3-static or SDL3::SDL3-static
if(TARGET SDL3-static)
message(STATUS "Using SDL3-static target")
target_link_libraries(yaze_sdl3 INTERFACE SDL3-static)
# Also explicitly add include directories if they exist
if(SDL3_SOURCE_DIR)
target_include_directories(yaze_sdl3 INTERFACE ${SDL3_SOURCE_DIR}/include)
message(STATUS "Added SDL3 include: ${SDL3_SOURCE_DIR}/include")
endif()
elseif(TARGET SDL3::SDL3-static)
message(STATUS "Using SDL3::SDL3-static target")
target_link_libraries(yaze_sdl3 INTERFACE SDL3::SDL3-static)
# For local Homebrew SDL3, also add include path explicitly
if(YAZE_PLATFORM_MACOS AND EXISTS "/opt/homebrew/opt/sdl3/include/SDL3")
target_include_directories(yaze_sdl3 INTERFACE /opt/homebrew/opt/sdl3/include/SDL3)
message(STATUS "Added Homebrew SDL3 include path: /opt/homebrew/opt/sdl3/include/SDL3")
endif()
else()
message(STATUS "Using SDL3::SDL3 target")
target_link_libraries(yaze_sdl3 INTERFACE SDL3::SDL3)
endif()
endif()
# Add platform-specific libraries
if(WIN32)
target_link_libraries(yaze_sdl3 INTERFACE
winmm
imm32
version
setupapi
wbemuuid
)
target_compile_definitions(yaze_sdl3 INTERFACE SDL_MAIN_HANDLED)
elseif(YAZE_PLATFORM_MACOS)
target_link_libraries(yaze_sdl3 INTERFACE
"-framework Cocoa"
"-framework IOKit"
"-framework CoreVideo"
"-framework CoreHaptics"
"-framework ForceFeedback"
"-framework GameController"
)
target_compile_definitions(yaze_sdl3 INTERFACE SDL_MAIN_HANDLED)
elseif(YAZE_PLATFORM_IOS)
target_link_libraries(yaze_sdl3 INTERFACE
"-framework UIKit"
"-framework Foundation"
"-framework CoreGraphics"
"-framework CoreVideo"
"-framework CoreMotion"
"-framework QuartzCore"
"-framework AVFoundation"
"-framework AudioToolbox"
"-framework Metal"
"-framework GameController"
)
target_compile_definitions(yaze_sdl3 INTERFACE SDL_MAIN_HANDLED)
elseif(UNIX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_link_libraries(yaze_sdl3 INTERFACE ${GTK3_LIBRARIES})
target_include_directories(yaze_sdl3 INTERFACE ${GTK3_INCLUDE_DIRS})
target_compile_options(yaze_sdl3 INTERFACE ${GTK3_CFLAGS_OTHER})
endif()
# Export SDL3 targets for use in other CMake files
set(YAZE_SDL3_TARGETS yaze_sdl3)
# Set a flag to indicate SDL3 is being used
set(YAZE_SDL2_TARGETS ${YAZE_SDL3_TARGETS}) # For compatibility with existing code
message(STATUS "SDL3 setup complete - YAZE_SDL3_TARGETS = ${YAZE_SDL3_TARGETS}")

View File

@@ -0,0 +1,157 @@
# Testing dependencies (GTest, Benchmark)
# Uses CPM.cmake for consistent cross-platform builds
if(NOT YAZE_BUILD_TESTS)
return()
endif()
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
message(STATUS "Setting up testing dependencies with CPM.cmake")
set(_YAZE_USE_SYSTEM_GTEST ${YAZE_USE_SYSTEM_DEPS})
# Detect Homebrew installation automatically (helps offline builds)
if(APPLE AND NOT _YAZE_USE_SYSTEM_GTEST)
set(_YAZE_GTEST_PREFIX_CANDIDATES)
set(_YAZE_HOST_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
if(NOT _YAZE_HOST_ARCH)
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE _YAZE_HOST_ARCH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
endif()
if(_YAZE_HOST_ARCH STREQUAL "arm64")
list(APPEND _YAZE_GTEST_PREFIX_CANDIDATES
/opt/homebrew/opt/googletest)
else()
list(APPEND _YAZE_GTEST_PREFIX_CANDIDATES
/usr/local/opt/googletest
/opt/homebrew/opt/googletest)
endif()
foreach(_prefix IN LISTS _YAZE_GTEST_PREFIX_CANDIDATES)
if(EXISTS "${_prefix}")
list(APPEND CMAKE_PREFIX_PATH "${_prefix}")
message(STATUS "Added Homebrew googletest prefix: ${_prefix}")
set(_YAZE_USE_SYSTEM_GTEST ON)
break()
endif()
endforeach()
if(NOT _YAZE_USE_SYSTEM_GTEST)
find_program(HOMEBREW_EXECUTABLE brew)
if(HOMEBREW_EXECUTABLE)
execute_process(
COMMAND "${HOMEBREW_EXECUTABLE}" --prefix googletest
OUTPUT_VARIABLE HOMEBREW_GTEST_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE HOMEBREW_GTEST_RESULT
ERROR_QUIET)
if(HOMEBREW_GTEST_RESULT EQUAL 0 AND EXISTS "${HOMEBREW_GTEST_PREFIX}")
if(_YAZE_HOST_ARCH STREQUAL "arm64" AND NOT HOMEBREW_GTEST_PREFIX MATCHES "^/opt/homebrew")
message(STATUS "Skipping Homebrew googletest prefix on arm64: ${HOMEBREW_GTEST_PREFIX}")
else()
list(APPEND CMAKE_PREFIX_PATH "${HOMEBREW_GTEST_PREFIX}")
message(STATUS "Added Homebrew googletest prefix: ${HOMEBREW_GTEST_PREFIX}")
set(_YAZE_USE_SYSTEM_GTEST ON)
endif()
endif()
endif()
endif()
endif()
# Try to use system packages first
if(_YAZE_USE_SYSTEM_GTEST)
find_package(GTest QUIET)
if(GTest_FOUND)
message(STATUS "Using system googletest")
# GTest found, targets should already be available
# Verify targets exist
if(NOT TARGET GTest::gtest)
message(WARNING "GTest::gtest target not found despite GTest_FOUND=TRUE; falling back to CPM download")
set(_YAZE_USE_SYSTEM_GTEST OFF)
else()
# Create aliases to match CPM target names
if(NOT TARGET gtest)
add_library(gtest ALIAS GTest::gtest)
endif()
if(NOT TARGET gtest_main)
add_library(gtest_main ALIAS GTest::gtest_main)
endif()
if(TARGET GTest::gmock AND NOT TARGET gmock)
add_library(gmock ALIAS GTest::gmock)
endif()
if(TARGET GTest::gmock_main AND NOT TARGET gmock_main)
add_library(gmock_main ALIAS GTest::gmock_main)
endif()
# Skip CPM fetch
set(_YAZE_GTEST_SYSTEM_USED ON)
endif()
elseif(YAZE_USE_SYSTEM_DEPS)
message(WARNING "System googletest not found despite YAZE_USE_SYSTEM_DEPS=ON; falling back to CPM download")
endif()
endif()
# Use CPM to fetch googletest if not using system version
if(NOT _YAZE_GTEST_SYSTEM_USED)
CPMAddPackage(
NAME googletest
VERSION ${GTEST_VERSION}
GITHUB_REPOSITORY google/googletest
GIT_TAG v${GTEST_VERSION}
OPTIONS
"BUILD_GMOCK ON"
"INSTALL_GTEST OFF"
"gtest_force_shared_crt ON"
)
endif()
# Verify GTest and GMock targets are available
if(NOT TARGET gtest)
message(FATAL_ERROR "GTest target not found after CPM fetch")
endif()
if(NOT TARGET gmock)
message(FATAL_ERROR "GMock target not found after CPM fetch")
endif()
# Google Benchmark (optional, for performance tests)
if(YAZE_ENABLE_COVERAGE OR DEFINED ENV{YAZE_ENABLE_BENCHMARKS})
CPMAddPackage(
NAME benchmark
VERSION ${BENCHMARK_VERSION}
GITHUB_REPOSITORY google/benchmark
GIT_TAG v${BENCHMARK_VERSION}
OPTIONS
"BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
)
if(NOT TARGET benchmark::benchmark)
message(FATAL_ERROR "Benchmark target not found after CPM fetch")
endif()
set(YAZE_BENCHMARK_TARGETS benchmark::benchmark)
endif()
# Create convenience targets for the rest of the project
add_library(yaze_testing INTERFACE)
target_link_libraries(yaze_testing INTERFACE
gtest
gtest_main
gmock
gmock_main
)
if(TARGET benchmark::benchmark)
target_link_libraries(yaze_testing INTERFACE benchmark::benchmark)
endif()
# Export testing targets for use in other CMake files
set(YAZE_TESTING_TARGETS yaze_testing)
message(STATUS "Testing dependencies setup complete - GTest + GMock available")

View File

@@ -0,0 +1,105 @@
# yaml-cpp dependency management
# Uses CPM.cmake for consistent cross-platform builds
include(cmake/CPM.cmake)
include(cmake/dependencies.lock)
if(NOT YAZE_ENABLE_AI AND NOT YAZE_ENABLE_AI_RUNTIME)
message(STATUS "Skipping yaml-cpp (AI runtime and CLI agent features disabled)")
set(YAZE_YAML_TARGETS "")
return()
endif()
message(STATUS "Setting up yaml-cpp ${YAML_CPP_VERSION} with CPM.cmake")
set(_YAZE_USE_SYSTEM_YAML ${YAZE_USE_SYSTEM_DEPS})
# Detect Homebrew installation automatically (helps offline builds)
if(APPLE AND NOT _YAZE_USE_SYSTEM_YAML)
set(_YAZE_YAML_PREFIX_CANDIDATES
/opt/homebrew/opt/yaml-cpp
/usr/local/opt/yaml-cpp)
foreach(_prefix IN LISTS _YAZE_YAML_PREFIX_CANDIDATES)
if(EXISTS "${_prefix}")
list(APPEND CMAKE_PREFIX_PATH "${_prefix}")
message(STATUS "Added Homebrew yaml-cpp prefix: ${_prefix}")
set(_YAZE_USE_SYSTEM_YAML ON)
break()
endif()
endforeach()
if(NOT _YAZE_USE_SYSTEM_YAML)
find_program(HOMEBREW_EXECUTABLE brew)
if(HOMEBREW_EXECUTABLE)
execute_process(
COMMAND "${HOMEBREW_EXECUTABLE}" --prefix yaml-cpp
OUTPUT_VARIABLE HOMEBREW_YAML_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE HOMEBREW_YAML_RESULT
ERROR_QUIET)
if(HOMEBREW_YAML_RESULT EQUAL 0 AND EXISTS "${HOMEBREW_YAML_PREFIX}")
list(APPEND CMAKE_PREFIX_PATH "${HOMEBREW_YAML_PREFIX}")
message(STATUS "Added Homebrew yaml-cpp prefix: ${HOMEBREW_YAML_PREFIX}")
set(_YAZE_USE_SYSTEM_YAML ON)
endif()
endif()
endif()
endif()
# Try to use system packages first
if(_YAZE_USE_SYSTEM_YAML)
find_package(yaml-cpp QUIET)
if(yaml-cpp_FOUND)
message(STATUS "Using system yaml-cpp")
add_library(yaze_yaml INTERFACE)
target_compile_definitions(yaze_yaml INTERFACE YAZE_HAS_YAML_CPP=1)
if(TARGET yaml-cpp::yaml-cpp)
message(STATUS "Linking yaze_yaml against yaml-cpp::yaml-cpp")
target_link_libraries(yaze_yaml INTERFACE yaml-cpp::yaml-cpp)
# HACK: Explicitly add the library directory for Homebrew if detected
# This fixes 'ld: library not found for -lyaml-cpp' when the imported target
# doesn't propagate the library path correctly to the linker command line
if(EXISTS "/opt/homebrew/opt/yaml-cpp/lib")
link_directories("/opt/homebrew/opt/yaml-cpp/lib")
message(STATUS "Added yaml-cpp link directory: /opt/homebrew/opt/yaml-cpp/lib")
endif()
else()
message(STATUS "Linking yaze_yaml against yaml-cpp (legacy)")
target_link_libraries(yaze_yaml INTERFACE yaml-cpp)
endif()
set(YAZE_YAML_TARGETS yaze_yaml)
return()
elseif(YAZE_USE_SYSTEM_DEPS)
message(WARNING "System yaml-cpp not found despite YAZE_USE_SYSTEM_DEPS=ON; falling back to CPM download")
endif()
endif()
# Use CPM to fetch yaml-cpp
CPMAddPackage(
NAME yaml-cpp
VERSION ${YAML_CPP_VERSION}
GITHUB_REPOSITORY jbeder/yaml-cpp
GIT_TAG 0.8.0
OPTIONS
"YAML_CPP_BUILD_TESTS OFF"
"YAML_CPP_BUILD_CONTRIB OFF"
"YAML_CPP_BUILD_TOOLS OFF"
"YAML_CPP_INSTALL OFF"
)
# Verify yaml-cpp targets are available
if(NOT TARGET yaml-cpp)
message(FATAL_ERROR "yaml-cpp target not found after CPM fetch")
endif()
# Create convenience targets for the rest of the project
add_library(yaze_yaml INTERFACE)
target_link_libraries(yaze_yaml INTERFACE yaml-cpp)
target_compile_definitions(yaze_yaml INTERFACE YAZE_HAS_YAML_CPP=1)
# Export yaml-cpp targets for use in other CMake files
set(YAZE_YAML_TARGETS yaze_yaml)
message(STATUS "yaml-cpp setup complete")

View File

@@ -5,25 +5,6 @@ set(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
# Include FetchContent module # Include FetchContent module
include(FetchContent) include(FetchContent)
# Try Windows-optimized path first
if(WIN32)
include(${CMAKE_CURRENT_LIST_DIR}/grpc_windows.cmake)
if(YAZE_GRPC_CONFIGURED)
# Validate that grpc_windows.cmake properly exported required targets/variables
if(NOT COMMAND target_add_protobuf)
message(FATAL_ERROR "grpc_windows.cmake did not define target_add_protobuf function")
endif()
if(NOT DEFINED ABSL_TARGETS OR NOT ABSL_TARGETS)
message(FATAL_ERROR "grpc_windows.cmake did not export ABSL_TARGETS")
endif()
if(NOT DEFINED YAZE_PROTOBUF_TARGETS OR NOT YAZE_PROTOBUF_TARGETS)
message(FATAL_ERROR "grpc_windows.cmake did not export YAZE_PROTOBUF_TARGETS")
endif()
message(STATUS "✓ Windows vcpkg gRPC configuration validated")
return()
endif()
endif()
# Set minimum CMake version for subprojects (fixes c-ares compatibility) # Set minimum CMake version for subprojects (fixes c-ares compatibility)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
@@ -32,44 +13,19 @@ set(FETCHCONTENT_QUIET OFF)
# CRITICAL: Prevent CMake from finding system-installed protobuf/abseil # CRITICAL: Prevent CMake from finding system-installed protobuf/abseil
# This ensures gRPC uses its own bundled versions # This ensures gRPC uses its own bundled versions
set(CMAKE_DISABLE_FIND_PACKAGE_Protobuf TRUE) set(CMAKE_DISABLE_FIND_PACKAGE_Protobuf TRUE)
set(CMAKE_DISABLE_FIND_PACKAGE_gRPC TRUE)
set(CMAKE_DISABLE_FIND_PACKAGE_absl TRUE) set(CMAKE_DISABLE_FIND_PACKAGE_absl TRUE)
set(CMAKE_DISABLE_FIND_PACKAGE_gRPC TRUE)
# Also prevent pkg-config from finding system packages # Also prevent pkg-config from finding system packages
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH FALSE) set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH FALSE)
# Add compiler flags for modern compiler compatibility
# These flags are scoped to gRPC and its dependencies only
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Clang 15+ compatibility for gRPC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=missing-template-arg-list-after-template-kw")
add_compile_definitions(_LIBCPP_ENABLE_CXX20_REMOVED_TYPE_TRAITS)
elseif(MSVC)
# MSVC/Visual Studio compatibility for gRPC templates
# v1.67.1 fixes most issues, but these flags help with large template instantiations
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") # Large object files
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-") # Standards conformance
# Suppress common gRPC warnings on MSVC (don't use add_compile_options to avoid affecting user code)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244")
# Increase template instantiation depth for complex promise chains (MSVC 2019+)
if(MSVC_VERSION GREATER_EQUAL 1920)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /constexpr:depth2048")
endif()
# Prevent Windows macro pollution in protobuf-generated headers
add_compile_definitions(
WIN32_LEAN_AND_MEAN # Exclude rarely-used Windows headers
NOMINMAX # Don't define min/max macros
NOGDI # Exclude GDI (prevents DWORD and other macro conflicts)
)
endif()
# Save YAZE's C++ standard and temporarily set to C++17 for gRPC # Save YAZE's C++ standard and temporarily set to C++17 for gRPC
set(_SAVED_CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD}) set(_SAVED_CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD})
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
# ZLIB is provided by gRPC module (gRPC_ZLIB_PROVIDER="module")
# find_package(ZLIB REQUIRED) not needed - gRPC bundles its own ZLIB
# Configure gRPC build options before fetching # Configure gRPC build options before fetching
set(gRPC_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_CODEGEN ON CACHE BOOL "" FORCE) set(gRPC_BUILD_CODEGEN ON CACHE BOOL "" FORCE)
@@ -81,16 +37,15 @@ set(gRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE) set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE)
# Disable C++ reflection support (avoids extra proto generation)
set(gRPC_BUILD_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_CPP_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPCPP_REFLECTION OFF CACHE BOOL "" FORCE)
set(gRPC_BENCHMARK_PROVIDER "none" CACHE STRING "" FORCE) set(gRPC_BENCHMARK_PROVIDER "none" CACHE STRING "" FORCE)
set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "" FORCE) set(gRPC_ZLIB_PROVIDER "module" CACHE STRING "" FORCE)
# Skip install rule generation inside gRPC's dependency graph. This avoids
# configure-time checks that require every transitive dependency (like Abseil
# compatibility shims) to participate in install export sets, which we do not
# need for the editor builds.
set(CMAKE_SKIP_INSTALL_RULES ON CACHE BOOL "" FORCE)
# Let gRPC fetch and build its own protobuf and abseil # Let gRPC fetch and build its own protobuf and abseil
set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "" FORCE) set(gRPC_PROTOBUF_PROVIDER "module" CACHE STRING "" FORCE)
set(gRPC_ABSL_PROVIDER "module" CACHE STRING "" FORCE) set(gRPC_ABSL_PROVIDER "module" CACHE STRING "" FORCE)
@@ -101,32 +56,27 @@ set(protobuf_BUILD_CONFORMANCE OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(protobuf_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_PROTOC_BINARIES ON CACHE BOOL "" FORCE) set(protobuf_BUILD_PROTOC_BINARIES ON CACHE BOOL "" FORCE)
set(protobuf_WITH_ZLIB ON CACHE BOOL "" FORCE) set(protobuf_WITH_ZLIB ON CACHE BOOL "" FORCE)
set(protobuf_MSVC_STATIC_RUNTIME ON CACHE BOOL "" FORCE)
# Abseil configuration # Abseil configuration
set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE) set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE)
set(ABSL_ENABLE_INSTALL ON CACHE BOOL "" FORCE) set(ABSL_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE) set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(ABSL_MSVC_STATIC_RUNTIME ON CACHE BOOL "" FORCE)
set(gRPC_MSVC_STATIC_RUNTIME ON CACHE BOOL "" FORCE)
# Disable x86-specific optimizations for ARM64 macOS builds # Additional protobuf settings to avoid export conflicts
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(protobuf_BUILD_LIBPROTOC ON CACHE BOOL "" FORCE)
set(ABSL_USE_EXTERNAL_GOOGLETEST OFF CACHE BOOL "" FORCE) set(protobuf_BUILD_LIBPROTOBUF ON CACHE BOOL "" FORCE)
set(ABSL_BUILD_TEST_HELPERS OFF CACHE BOOL "" FORCE) set(protobuf_BUILD_LIBPROTOBUF_LITE ON CACHE BOOL "" FORCE)
endif() set(protobuf_INSTALL OFF CACHE BOOL "" FORCE)
set(utf8_range_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(utf8_range_INSTALL OFF CACHE BOOL "" FORCE)
# Declare gRPC with platform-specific versions # Declare gRPC with platform-specific versions
# - macOS/Linux: v1.75.1 (has ARM64 + modern Clang fixes) if(WIN32 AND MSVC)
# - Windows: v1.75.1 (better NASM/clang-cl support than v1.67.1) set(_GRPC_VERSION "v1.67.1")
set(_GRPC_VERSION "v1.75.1") set(_GRPC_VERSION_REASON "MSVC-compatible, avoids linker regressions")
if(WIN32)
set(_GRPC_VERSION_REASON "Windows clang-cl + MSVC compatibility")
# Disable BoringSSL ASM to avoid NASM build issues on Windows
# ASM optimizations cause NASM flag conflicts with clang-cl
set(OPENSSL_NO_ASM ON CACHE BOOL "" FORCE)
message(STATUS "Disabling BoringSSL ASM optimizations for Windows build compatibility")
else() else()
set(_GRPC_VERSION "v1.75.1")
set(_GRPC_VERSION_REASON "ARM64 macOS + modern Clang compatibility") set(_GRPC_VERSION_REASON "ARM64 macOS + modern Clang compatibility")
endif() endif()
@@ -146,9 +96,23 @@ FetchContent_Declare(
set(_SAVED_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) set(_SAVED_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH})
set(CMAKE_PREFIX_PATH "") set(CMAKE_PREFIX_PATH "")
# Some toolchain presets set CMAKE_CROSSCOMPILING even when building for the
# host (macOS arm64). gRPC treats that as a signal to locate host-side protoc
# binaries via find_program, which fails since we rely on the bundled targets.
# Suppress the flag when the host and target platforms match so the generator
# expressions remain intact.
set(_SAVED_CMAKE_CROSSCOMPILING ${CMAKE_CROSSCOMPILING})
if(CMAKE_HOST_SYSTEM_NAME STREQUAL CMAKE_SYSTEM_NAME
AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_CROSSCOMPILING FALSE)
endif()
# Download and build in isolation # Download and build in isolation
FetchContent_MakeAvailable(grpc) FetchContent_MakeAvailable(grpc)
# Restore cross-compiling flag
set(CMAKE_CROSSCOMPILING ${_SAVED_CMAKE_CROSSCOMPILING})
# Restore CMAKE_PREFIX_PATH # Restore CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH ${_SAVED_CMAKE_PREFIX_PATH}) set(CMAKE_PREFIX_PATH ${_SAVED_CMAKE_PREFIX_PATH})
@@ -163,14 +127,15 @@ if(NOT TARGET grpc_cpp_plugin)
message(FATAL_ERROR "Can not find target grpc_cpp_plugin") message(FATAL_ERROR "Can not find target grpc_cpp_plugin")
endif() endif()
set(_gRPC_PROTOBUF_PROTOC_EXECUTABLE $<TARGET_FILE:protoc>)
set(_gRPC_CPP_PLUGIN $<TARGET_FILE:grpc_cpp_plugin>)
set(_gRPC_PROTO_GENS_DIR ${CMAKE_BINARY_DIR}/gens) set(_gRPC_PROTO_GENS_DIR ${CMAKE_BINARY_DIR}/gens)
file(REMOVE_RECURSE ${_gRPC_PROTO_GENS_DIR})
file(MAKE_DIRECTORY ${_gRPC_PROTO_GENS_DIR}) file(MAKE_DIRECTORY ${_gRPC_PROTO_GENS_DIR})
get_target_property(_PROTOBUF_INCLUDE_DIRS libprotobuf INTERFACE_INCLUDE_DIRECTORIES) get_target_property(_PROTOBUF_INCLUDE_DIRS libprotobuf INTERFACE_INCLUDE_DIRECTORIES)
list(GET _PROTOBUF_INCLUDE_DIRS 0 _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR) list(GET _PROTOBUF_INCLUDE_DIRS 0 _gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR)
message(STATUS "gRPC setup complete")
# Export Abseil targets from gRPC's bundled abseil for use by the rest of the project # Export Abseil targets from gRPC's bundled abseil for use by the rest of the project
# This ensures version compatibility between gRPC and our project # This ensures version compatibility between gRPC and our project
# Note: Order matters for some linkers - put base libraries first # Note: Order matters for some linkers - put base libraries first
@@ -213,36 +178,6 @@ endif()
# ABSL_TARGETS is now available to the rest of the project via include() # ABSL_TARGETS is now available to the rest of the project via include()
# Fix Abseil ARM64 macOS compile flags (remove x86-specific flags)
if(APPLE AND DEFINED CMAKE_OSX_ARCHITECTURES AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
foreach(_absl_target IN ITEMS absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl)
if(TARGET ${_absl_target})
get_target_property(_absl_opts ${_absl_target} COMPILE_OPTIONS)
if(_absl_opts AND NOT _absl_opts STREQUAL "NOTFOUND")
set(_absl_filtered_opts)
set(_absl_skip_next FALSE)
foreach(_absl_opt IN LISTS _absl_opts)
if(_absl_skip_next)
set(_absl_skip_next FALSE)
continue()
endif()
if(_absl_opt STREQUAL "-Xarch_x86_64")
set(_absl_skip_next TRUE)
continue()
endif()
if(_absl_opt STREQUAL "-maes" OR _absl_opt STREQUAL "-msse4.1")
continue()
endif()
list(APPEND _absl_filtered_opts ${_absl_opt})
endforeach()
set_property(TARGET ${_absl_target} PROPERTY COMPILE_OPTIONS ${_absl_filtered_opts})
endif()
endif()
endforeach()
endif()
message(STATUS "gRPC setup complete (includes bundled Abseil)")
function(target_add_protobuf target) function(target_add_protobuf target)
if(NOT TARGET ${target}) if(NOT TARGET ${target})
message(FATAL_ERROR "Target ${target} doesn't exist") message(FATAL_ERROR "Target ${target} doesn't exist")
@@ -270,10 +205,10 @@ function(target_add_protobuf target)
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}_mock.grpc.pb.h" "${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}_mock.grpc.pb.h"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc" "${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.cc"
"${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h" "${_gRPC_PROTO_GENS_DIR}/${RELFIL_WE}.pb.h"
COMMAND ${_gRPC_PROTOBUF_PROTOC_EXECUTABLE} COMMAND $<TARGET_FILE:protoc>
ARGS --grpc_out=generate_mock_code=true:${_gRPC_PROTO_GENS_DIR} ARGS --grpc_out=generate_mock_code=true:${_gRPC_PROTO_GENS_DIR}
--cpp_out=${_gRPC_PROTO_GENS_DIR} --cpp_out=${_gRPC_PROTO_GENS_DIR}
--plugin=protoc-gen-grpc=${_gRPC_CPP_PLUGIN} --plugin=protoc-gen-grpc=$<TARGET_FILE:grpc_cpp_plugin>
${_protobuf_include_path} ${_protobuf_include_path}
${REL_FIL} ${REL_FIL}
DEPENDS ${ABS_FIL} protoc grpc_cpp_plugin DEPENDS ${ABS_FIL} protoc grpc_cpp_plugin

View File

@@ -11,10 +11,10 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
# Option to use vcpkg for gRPC on Windows # Option to use vcpkg for gRPC on Windows (default OFF for CI reliability)
option(YAZE_USE_VCPKG_GRPC "Use vcpkg pre-compiled gRPC packages (Windows only)" ON) option(YAZE_USE_VCPKG_GRPC "Use vcpkg pre-compiled gRPC packages (Windows only)" OFF)
if(WIN32 AND YAZE_USE_VCPKG_GRPC) if(WIN32 AND YAZE_USE_VCPKG_GRPC AND DEFINED CMAKE_TOOLCHAIN_FILE)
message(STATUS "Attempting to use vcpkg gRPC packages for faster Windows builds...") message(STATUS "Attempting to use vcpkg gRPC packages for faster Windows builds...")
message(STATUS " Note: If gRPC not in vcpkg.json, will fallback to FetchContent (recommended)") message(STATUS " Note: If gRPC not in vcpkg.json, will fallback to FetchContent (recommended)")
@@ -144,10 +144,13 @@ if(WIN32 AND YAZE_USE_VCPKG_GRPC)
absl::memory absl::memory
absl::container_memory absl::container_memory
absl::strings absl::strings
absl::strings_internal
absl::str_format absl::str_format
absl::str_format_internal
absl::cord absl::cord
absl::hash absl::hash
absl::time absl::time
absl::time_zone
absl::status absl::status
absl::statusor absl::statusor
absl::flags absl::flags
@@ -165,12 +168,12 @@ if(WIN32 AND YAZE_USE_VCPKG_GRPC)
absl::flat_hash_map absl::flat_hash_map
absl::synchronization absl::synchronization
absl::symbolize absl::symbolize
absl::strerror
PARENT_SCOPE PARENT_SCOPE
) )
# Export protobuf targets (vcpkg uses protobuf:: namespace) # Export protobuf targets (vcpkg uses protobuf:: namespace)
set(YAZE_PROTOBUF_TARGETS protobuf::libprotobuf PARENT_SCOPE) set(YAZE_PROTOBUF_TARGETS protobuf::libprotobuf PARENT_SCOPE)
set(YAZE_PROTOBUF_WHOLEARCHIVE_TARGETS protobuf::libprotobuf PARENT_SCOPE)
# Get protobuf include directories for proto generation # Get protobuf include directories for proto generation
get_target_property(_PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf get_target_property(_PROTOBUF_INCLUDE_DIRS protobuf::libprotobuf
@@ -242,7 +245,7 @@ if(WIN32 AND YAZE_USE_VCPKG_GRPC)
message(STATUS " vcpkg gRPC not found (expected if removed from vcpkg.json)") message(STATUS " vcpkg gRPC not found (expected if removed from vcpkg.json)")
message(STATUS " Using FetchContent build (faster with caching)") message(STATUS " Using FetchContent build (faster with caching)")
message(STATUS " First build: ~10-15 min, subsequent: <1 min (cached)") message(STATUS " First build: ~10-15 min, subsequent: <1 min (cached)")
message(STATUS " Using gRPC v1.75.1 with Windows compatibility fixes") message(STATUS " Using gRPC v1.75.1 (latest stable)")
message(STATUS " Note: BoringSSL ASM disabled for clang-cl compatibility") message(STATUS " Note: BoringSSL ASM disabled for clang-cl compatibility")
message(STATUS "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") message(STATUS "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
endif() endif()

View File

@@ -1,42 +0,0 @@
# gui libraries ---------------------------------------------------------------
set(IMGUI_PATH ${CMAKE_SOURCE_DIR}/src/lib/imgui)
file(GLOB IMGUI_SOURCES ${IMGUI_PATH}/*.cpp)
set(IMGUI_BACKEND_SOURCES
${IMGUI_PATH}/backends/imgui_impl_sdl2.cpp
${IMGUI_PATH}/backends/imgui_impl_sdlrenderer2.cpp
${IMGUI_PATH}/misc/cpp/imgui_stdlib.cpp
)
add_library("ImGui" STATIC ${IMGUI_SOURCES} ${IMGUI_BACKEND_SOURCES})
target_include_directories("ImGui" PUBLIC ${IMGUI_PATH} ${IMGUI_PATH}/backends)
target_include_directories(ImGui PUBLIC ${SDL2_INCLUDE_DIR})
target_compile_definitions(ImGui PUBLIC
IMGUI_IMPL_OPENGL_LOADER_CUSTOM=<SDL2/SDL_opengl.h> GL_GLEXT_PROTOTYPES=1)
# ImGui Test Engine - Always built when tests are enabled for simplified integration
# The test infrastructure is tightly coupled with the editor, so we always include it
if(YAZE_BUILD_TESTS)
set(IMGUI_TEST_ENGINE_PATH ${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine/imgui_test_engine)
file(GLOB IMGUI_TEST_ENGINE_SOURCES ${IMGUI_TEST_ENGINE_PATH}/*.cpp)
add_library("ImGuiTestEngine" STATIC ${IMGUI_TEST_ENGINE_SOURCES})
target_include_directories(ImGuiTestEngine PUBLIC ${IMGUI_PATH} ${CMAKE_SOURCE_DIR}/src/lib)
target_link_libraries(ImGuiTestEngine PUBLIC ImGui)
target_compile_definitions(ImGuiTestEngine PUBLIC
IMGUI_ENABLE_TEST_ENGINE=1
IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL=1)
message(STATUS "✓ ImGui Test Engine enabled (tests are ON)")
else()
message(STATUS "✗ ImGui Test Engine disabled (tests are OFF)")
endif()
set(
IMGUI_SRC
${IMGUI_PATH}/imgui.cpp
${IMGUI_PATH}/imgui_demo.cpp
${IMGUI_PATH}/imgui_draw.cpp
${IMGUI_PATH}/imgui_widgets.cpp
${IMGUI_PATH}/backends/imgui_impl_sdl2.cpp
${IMGUI_PATH}/backends/imgui_impl_sdlrenderer2.cpp
${IMGUI_PATH}/misc/cpp/imgui_stdlib.cpp
)

View File

@@ -7,24 +7,64 @@
# 1. Set the target system (macOS) # 1. Set the target system (macOS)
set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_SYSTEM_NAME Darwin)
# Ensure a non-empty system version for third-party CMake logic.
if(NOT CMAKE_SYSTEM_VERSION)
execute_process(
COMMAND sw_vers -productVersion
OUTPUT_VARIABLE _yaze_macos_version
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(_yaze_macos_version)
set(CMAKE_SYSTEM_VERSION "${_yaze_macos_version}")
else()
set(CMAKE_SYSTEM_VERSION "0")
endif()
endif()
# 2. Find the Homebrew LLVM installation path # 2. Find the Homebrew LLVM installation path
# We use execute_process to make this portable across machine architectures. # We use execute_process to make this portable across machine architectures.
execute_process( set(_yaze_llvm_candidates llvm@21 llvm@20 llvm@19 llvm@18 llvm)
COMMAND brew --prefix llvm@18 foreach(_yaze_llvm_candidate IN LISTS _yaze_llvm_candidates)
OUTPUT_VARIABLE HOMEBREW_LLVM_PREFIX execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND brew --prefix ${_yaze_llvm_candidate}
) OUTPUT_VARIABLE _yaze_llvm_prefix
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _yaze_llvm_result
)
if(_yaze_llvm_result EQUAL 0 AND EXISTS "${_yaze_llvm_prefix}")
set(HOMEBREW_LLVM_PREFIX "${_yaze_llvm_prefix}")
break()
endif()
endforeach()
if(NOT EXISTS "${HOMEBREW_LLVM_PREFIX}") if(NOT EXISTS "${HOMEBREW_LLVM_PREFIX}")
message(FATAL_ERROR "Homebrew LLVM not found. Please run 'brew install llvm'. Path: ${HOMEBREW_LLVM_PREFIX}") message(FATAL_ERROR "Homebrew LLVM not found. Please run 'brew install llvm'.")
endif() endif()
# Cache this variable so it's available in the main CMakeLists.txt
set(HOMEBREW_LLVM_PREFIX "${HOMEBREW_LLVM_PREFIX}" CACHE PATH "Path to Homebrew LLVM installation")
message(STATUS "Using Homebrew LLVM from: ${HOMEBREW_LLVM_PREFIX}") message(STATUS "Using Homebrew LLVM from: ${HOMEBREW_LLVM_PREFIX}")
# 3. Set the C and C++ compilers # 3. Set the C and C++ compilers
set(CMAKE_C_COMPILER "${HOMEBREW_LLVM_PREFIX}/bin/clang") set(CMAKE_C_COMPILER "${HOMEBREW_LLVM_PREFIX}/bin/clang")
set(CMAKE_CXX_COMPILER "${HOMEBREW_LLVM_PREFIX}/bin/clang++") set(CMAKE_CXX_COMPILER "${HOMEBREW_LLVM_PREFIX}/bin/clang++")
# 3.5 Find and configure clang-tidy
find_program(CLANG_TIDY_EXE
NAMES clang-tidy
HINTS "${HOMEBREW_LLVM_PREFIX}/bin"
NO_DEFAULT_PATH
)
if(CLANG_TIDY_EXE)
message(STATUS "Found Homebrew clang-tidy: ${CLANG_TIDY_EXE}")
set(YAZE_CLANG_TIDY_EXE "${CLANG_TIDY_EXE}" CACHE FILEPATH "Path to clang-tidy executable")
else()
message(WARNING "clang-tidy not found in ${HOMEBREW_LLVM_PREFIX}/bin")
endif()
# 4. Set the system root (sysroot) to the macOS SDK # 4. Set the system root (sysroot) to the macOS SDK
# This correctly points to the system-level headers and libraries. # This correctly points to the system-level headers and libraries.
execute_process( execute_process(
@@ -35,11 +75,29 @@ execute_process(
set(CMAKE_SYSROOT "${CMAKE_OSX_SYSROOT}") set(CMAKE_SYSROOT "${CMAKE_OSX_SYSROOT}")
message(STATUS "Using macOS SDK at: ${CMAKE_SYSROOT}") message(STATUS "Using macOS SDK at: ${CMAKE_SYSROOT}")
# 5. **[THE CRITICAL FIX]** Explicitly define the C++ standard library include directory. # 5. Ensure Homebrew libc++ + Clang resource headers are searched before SDK headers.
# This forces CMake to add Homebrew's libc++ headers to the search path *before* execute_process(
# any other system paths, resolving the header conflict for the main project COMMAND "${HOMEBREW_LLVM_PREFIX}/bin/clang++" -print-resource-dir
# and all dependencies. OUTPUT_VARIABLE HOMEBREW_LLVM_RESOURCE_DIR
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES "${HOMEBREW_LLVM_PREFIX}/include/c++/v1") OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(HOMEBREW_LLVM_RESOURCE_INCLUDE "${HOMEBREW_LLVM_RESOURCE_DIR}/include")
if(EXISTS "${HOMEBREW_LLVM_PREFIX}/include/c++/v1")
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
include_directories(BEFORE SYSTEM "${HOMEBREW_LLVM_PREFIX}/include/c++/v1")
endif()
if(EXISTS "${HOMEBREW_LLVM_RESOURCE_INCLUDE}")
include_directories(BEFORE SYSTEM "${HOMEBREW_LLVM_RESOURCE_INCLUDE}")
endif()
# 5.5 Ensure Homebrew libc++ is linked to avoid mixing ABI with system libc++.
set(_yaze_llvm_lib_dir "${HOMEBREW_LLVM_PREFIX}/lib")
set(_yaze_llvm_libcxx_dir "${HOMEBREW_LLVM_PREFIX}/lib/c++")
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -L${_yaze_llvm_lib_dir} -Wl,-rpath,${_yaze_llvm_lib_dir} -L${_yaze_llvm_libcxx_dir} -Wl,-rpath,${_yaze_llvm_libcxx_dir}")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "${CMAKE_SHARED_LINKER_FLAGS_INIT} -L${_yaze_llvm_lib_dir} -Wl,-rpath,${_yaze_llvm_lib_dir} -L${_yaze_llvm_libcxx_dir} -Wl,-rpath,${_yaze_llvm_libcxx_dir}")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "${CMAKE_MODULE_LINKER_FLAGS_INIT} -L${_yaze_llvm_lib_dir} -Wl,-rpath,${_yaze_llvm_lib_dir} -L${_yaze_llvm_libcxx_dir} -Wl,-rpath,${_yaze_llvm_libcxx_dir}")
# 6. Set the default installation path for macOS frameworks # 6. Set the default installation path for macOS frameworks
set(CMAKE_FIND_FRAMEWORK FIRST) set(CMAKE_FIND_FRAMEWORK FIRST)

154
cmake/options.cmake Normal file
View File

@@ -0,0 +1,154 @@
# YAZE Build Options
# Centralized feature flags and build configuration
# Core build options
option(YAZE_BUILD_GUI "Build GUI application" ON)
option(YAZE_BUILD_CLI "Build CLI tools (shared libraries)" ON)
option(YAZE_BUILD_Z3ED "Build z3ed CLI executable" ON)
option(YAZE_BUILD_EMU "Build emulator components" ON)
option(YAZE_BUILD_LIB "Build static library" ON)
option(YAZE_BUILD_TESTS "Build test suite" ON)
option(YAZE_BUILD_TOOLS "Build development utility tools" ${YAZE_BUILD_TESTS})
option(YAZE_BUILD_LAB "Build lab sandbox executable" OFF)
# Feature flags
option(YAZE_ENABLE_GRPC "Enable gRPC agent support" ON)
option(YAZE_ENABLE_JSON "Enable JSON support" ON)
option(YAZE_ENABLE_AI "Enable AI agent features" OFF)
option(YAZE_ENABLE_CLANG_TIDY "Enable clang-tidy linting during build" ON)
option(YAZE_ENABLE_OPENCV "Enable OpenCV for advanced visual analysis" OFF)
# Advanced feature toggles
option(YAZE_ENABLE_REMOTE_AUTOMATION
"Enable remote automation services (gRPC/protobuf servers + GUI automation clients)"
${YAZE_ENABLE_GRPC})
option(YAZE_ENABLE_AI_RUNTIME
"Enable AI runtime integrations (Gemini/Ollama, advanced routing, proposal planning)"
${YAZE_ENABLE_AI})
option(YAZE_BUILD_AGENT_UI
"Build ImGui-based agent/chat panels inside the GUI"
${YAZE_BUILD_GUI})
option(YAZE_ENABLE_AGENT_CLI
"Build the conversational agent CLI stack (z3ed agent commands)"
${YAZE_BUILD_CLI})
option(YAZE_ENABLE_HTTP_API
"Enable HTTP REST API server for external agent access"
${YAZE_ENABLE_AGENT_CLI})
if((YAZE_BUILD_CLI OR YAZE_BUILD_Z3ED) AND NOT YAZE_ENABLE_AGENT_CLI)
set(YAZE_ENABLE_AGENT_CLI ON CACHE BOOL "Build the conversational agent CLI stack (z3ed agent commands)" FORCE)
endif()
if(YAZE_ENABLE_HTTP_API AND NOT YAZE_ENABLE_AGENT_CLI)
set(YAZE_ENABLE_AGENT_CLI ON CACHE BOOL "Build the conversational agent CLI stack (z3ed agent commands)" FORCE)
endif()
# Build optimizations
option(YAZE_ENABLE_LTO "Enable link-time optimization" OFF)
option(YAZE_ENABLE_SANITIZERS "Enable AddressSanitizer/UBSanitizer" OFF)
option(YAZE_ENABLE_COVERAGE "Enable code coverage" OFF)
option(YAZE_UNITY_BUILD "Enable Unity (Jumbo) builds" OFF)
# Platform-specific options
option(YAZE_USE_VCPKG "Use vcpkg for Windows dependencies" OFF)
option(YAZE_USE_SYSTEM_DEPS "Use system package manager for dependencies" OFF)
option(YAZE_USE_SDL3 "Use SDL3 instead of SDL2 (experimental)" OFF)
option(YAZE_WASM_TERMINAL "Build z3ed for WASM terminal mode (no TUI)" OFF)
# Development options
option(YAZE_ENABLE_ROM_TESTS "Enable tests that require ROM files" OFF)
option(YAZE_ENABLE_BENCHMARK_TESTS "Enable benchmark/performance tests" OFF)
option(YAZE_MINIMAL_BUILD "Minimal build for CI (disable optional features)" OFF)
option(YAZE_VERBOSE_BUILD "Verbose build output" OFF)
# Install options
option(YAZE_INSTALL_LIB "Install static library" OFF)
option(YAZE_INSTALL_HEADERS "Install public headers" ON)
# Set preprocessor definitions based on options
if(YAZE_ENABLE_REMOTE_AUTOMATION AND NOT YAZE_ENABLE_GRPC)
set(YAZE_ENABLE_GRPC ON CACHE BOOL "Enable gRPC agent support" FORCE)
endif()
if(NOT YAZE_ENABLE_REMOTE_AUTOMATION)
set(YAZE_ENABLE_GRPC OFF CACHE BOOL "Enable gRPC agent support" FORCE)
endif()
if(YAZE_ENABLE_GRPC)
add_compile_definitions(YAZE_WITH_GRPC)
endif()
if(YAZE_ENABLE_JSON)
add_compile_definitions(YAZE_WITH_JSON)
endif()
if(YAZE_ENABLE_AI_RUNTIME AND NOT YAZE_ENABLE_AI)
set(YAZE_ENABLE_AI ON CACHE BOOL "Enable AI agent features" FORCE)
endif()
if(NOT YAZE_ENABLE_AI_RUNTIME)
set(YAZE_ENABLE_AI OFF CACHE BOOL "Enable AI agent features" FORCE)
endif()
if(YAZE_ENABLE_AI)
add_compile_definitions(Z3ED_AI)
endif()
if(YAZE_ENABLE_AI_RUNTIME)
add_compile_definitions(YAZE_AI_RUNTIME_AVAILABLE)
endif()
if(YAZE_ENABLE_HTTP_API)
add_compile_definitions(YAZE_HTTP_API_ENABLED)
endif()
if(YAZE_WASM_TERMINAL)
add_compile_definitions(YAZE_WASM_TERMINAL_MODE)
endif()
if(YAZE_USE_SDL3)
add_compile_definitions(YAZE_USE_SDL3)
endif()
if(YAZE_BUILD_AGENT_UI)
add_compile_definitions(YAZE_BUILD_AGENT_UI)
endif()
if(YAZE_ENABLE_OPENCV)
find_package(OpenCV QUIET)
if(OpenCV_FOUND)
add_compile_definitions(YAZE_WITH_OPENCV)
message(STATUS "✓ OpenCV found: ${OpenCV_VERSION}")
else()
message(WARNING "OpenCV requested but not found - visual analysis will use fallback")
set(YAZE_ENABLE_OPENCV OFF CACHE BOOL "Enable OpenCV for advanced visual analysis" FORCE)
endif()
endif()
# Print configuration summary
message(STATUS "=== YAZE Build Configuration ===")
message(STATUS "GUI Application: ${YAZE_BUILD_GUI}")
message(STATUS "CLI Tools: ${YAZE_BUILD_CLI}")
message(STATUS "z3ed CLI: ${YAZE_BUILD_Z3ED}")
message(STATUS "Emulator: ${YAZE_BUILD_EMU}")
message(STATUS "Static Library: ${YAZE_BUILD_LIB}")
message(STATUS "Tests: ${YAZE_BUILD_TESTS}")
message(STATUS "Lab Sandbox: ${YAZE_BUILD_LAB}")
if(YAZE_USE_SDL3)
message(STATUS "SDL Version: SDL3 (experimental)")
else()
message(STATUS "SDL Version: SDL2 (stable)")
endif()
message(STATUS "gRPC Support: ${YAZE_ENABLE_GRPC}")
message(STATUS "Remote Automation: ${YAZE_ENABLE_REMOTE_AUTOMATION}")
message(STATUS "JSON Support: ${YAZE_ENABLE_JSON}")
message(STATUS "AI Runtime: ${YAZE_ENABLE_AI_RUNTIME}")
message(STATUS "AI Features (legacy): ${YAZE_ENABLE_AI}")
message(STATUS "Agent UI Panels: ${YAZE_BUILD_AGENT_UI}")
message(STATUS "Agent CLI Stack: ${YAZE_ENABLE_AGENT_CLI}")
message(STATUS "HTTP API Server: ${YAZE_ENABLE_HTTP_API}")
message(STATUS "LTO: ${YAZE_ENABLE_LTO}")
message(STATUS "Sanitizers: ${YAZE_ENABLE_SANITIZERS}")
message(STATUS "Coverage: ${YAZE_ENABLE_COVERAGE}")
message(STATUS "OpenCV Visual Analysis: ${YAZE_ENABLE_OPENCV}")
message(STATUS "=================================")

View File

@@ -141,23 +141,42 @@ set(CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation")
set(CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "User and developer documentation") set(CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "User and developer documentation")
set(CPACK_COMPONENT_DOCUMENTATION_REQUIRED FALSE) set(CPACK_COMPONENT_DOCUMENTATION_REQUIRED FALSE)
# Platform-specific install paths
# The asset paths must match what platform_paths.cc FindAsset() searches for
if(WIN32)
# Windows: flat structure (exe and assets/ at same level)
set(YAZE_INSTALL_BINDIR ".")
set(YAZE_INSTALL_DATADIR ".")
set(YAZE_INSTALL_DOCDIR ".")
elseif(APPLE)
# macOS: flat structure for DMG (app bundle handles its own resources)
set(YAZE_INSTALL_BINDIR ".")
set(YAZE_INSTALL_DATADIR ".")
set(YAZE_INSTALL_DOCDIR ".")
else()
# Linux: FHS structure - assets at share/yaze/assets (matches FindAsset search)
set(YAZE_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR})
set(YAZE_INSTALL_DATADIR "${CMAKE_INSTALL_DATADIR}/yaze")
set(YAZE_INSTALL_DOCDIR "${CMAKE_INSTALL_DOCDIR}")
endif()
# Installation components # Installation components
if(APPLE) if(APPLE)
install(TARGETS yaze install(TARGETS yaze
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${YAZE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} BUNDLE DESTINATION ${YAZE_INSTALL_BINDIR}
COMPONENT applications COMPONENT applications
) )
else() else()
install(TARGETS yaze install(TARGETS yaze
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${YAZE_INSTALL_BINDIR}
COMPONENT applications COMPONENT applications
) )
endif() endif()
# Install assets # Install assets
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/ install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
DESTINATION ${CMAKE_INSTALL_DATADIR}/yaze/assets DESTINATION ${YAZE_INSTALL_DATADIR}/assets
COMPONENT applications COMPONENT applications
PATTERN "*.png" PATTERN "*.png"
PATTERN "*.ttf" PATTERN "*.ttf"
@@ -168,12 +187,12 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
install(FILES install(FILES
${CMAKE_SOURCE_DIR}/README.md ${CMAKE_SOURCE_DIR}/README.md
${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_SOURCE_DIR}/LICENSE
DESTINATION ${CMAKE_INSTALL_DOCDIR} DESTINATION ${YAZE_INSTALL_DOCDIR}
COMPONENT documentation COMPONENT documentation
) )
install(DIRECTORY ${CMAKE_SOURCE_DIR}/docs/ install(DIRECTORY ${CMAKE_SOURCE_DIR}/docs/
DESTINATION ${CMAKE_INSTALL_DOCDIR} DESTINATION ${YAZE_INSTALL_DOCDIR}
COMPONENT documentation COMPONENT documentation
PATTERN "*.md" PATTERN "*.md"
PATTERN "*.html" PATTERN "*.html"

108
cmake/packaging/cpack.cmake Normal file
View File

@@ -0,0 +1,108 @@
# CPack Configuration - flat packages for all platforms
#
# Structure:
# root/
# yaze(.exe)
# z3ed(.exe) (if built)
# README.md
# LICENSE
# assets/...
set(CPACK_PACKAGE_NAME "yaze")
set(CPACK_PACKAGE_VENDOR "scawful")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet Another Zelda3 Editor")
set(CPACK_PACKAGE_VERSION_MAJOR ${YAZE_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${YAZE_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${YAZE_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION "${YAZE_VERSION_MAJOR}.${YAZE_VERSION_MINOR}.${YAZE_VERSION_PATCH}")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/packages")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
set(CPACK_COMPONENTS_ALL yaze)
set(CPACK_COMPONENT_YAZE_DISPLAY_NAME "YAZE Editor")
set(CPACK_COMPONENT_YAZE_DESCRIPTION "Main YAZE application and libraries")
# Populate runtime library list (needed on Windows)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON)
set(CMAKE_INSTALL_UCRT_LIBRARIES ON)
include(InstallRequiredSystemLibraries)
if(APPLE)
include(cmake/packaging/macos.cmake)
install(TARGETS yaze
BUNDLE DESTINATION .
COMPONENT yaze)
if(TARGET z3ed)
install(TARGETS z3ed
RUNTIME DESTINATION .
COMPONENT yaze)
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
DESTINATION assets
COMPONENT yaze)
install(FILES
${CMAKE_SOURCE_DIR}/README.md
${CMAKE_SOURCE_DIR}/LICENSE
DESTINATION .
COMPONENT yaze)
elseif(WIN32)
include(cmake/packaging/windows.cmake)
install(TARGETS yaze
RUNTIME DESTINATION .
COMPONENT yaze)
if(TARGET z3ed)
install(TARGETS z3ed
RUNTIME DESTINATION .
COMPONENT yaze)
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
DESTINATION assets
COMPONENT yaze)
install(FILES
${CMAKE_SOURCE_DIR}/README.md
${CMAKE_SOURCE_DIR}/LICENSE
DESTINATION .
COMPONENT yaze)
# Bundle MSVC/UCRT runtime dependencies if detected
if(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
DESTINATION .
COMPONENT yaze)
endif()
else()
include(cmake/packaging/linux.cmake)
install(TARGETS yaze
RUNTIME DESTINATION .
COMPONENT yaze)
if(TARGET z3ed)
install(TARGETS z3ed
RUNTIME DESTINATION .
COMPONENT yaze)
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
DESTINATION assets
COMPONENT yaze)
install(FILES
${CMAKE_SOURCE_DIR}/README.md
${CMAKE_SOURCE_DIR}/LICENSE
DESTINATION .
COMPONENT yaze)
endif()
include(CPack)

View File

@@ -0,0 +1,17 @@
# Linux Packaging Configuration
# DEB package
set(CPACK_GENERATOR "DEB;TGZ")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "scawful")
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libstdc++6, libsdl2-2.0-0")
# RPM package
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Games")
set(CPACK_RPM_PACKAGE_REQUIRES "glibc, libstdc++, SDL2")
# Tarball
set(CPACK_TGZ_PACKAGE_NAME "yaze-${CPACK_PACKAGE_VERSION}-linux-x64")

View File

@@ -0,0 +1,24 @@
# macOS Packaging Configuration
# Create .dmg package
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_DMG_VOLUME_NAME "yaze")
set(CPACK_DMG_FORMAT "UDZO")
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/yaze.png")
# App bundle configuration
set(CPACK_BUNDLE_NAME "yaze")
set(CPACK_BUNDLE_PACKAGE_TYPE "APPL")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/assets/yaze.icns")
# Code signing (if available)
if(DEFINED ENV{CODESIGN_IDENTITY})
set(CPACK_BUNDLE_APPLE_CERT_APP "$ENV{CODESIGN_IDENTITY}")
set(CPACK_BUNDLE_APPLE_CODESIGN_FORCE "ON")
endif()
# Notarization (if available)
if(DEFINED ENV{NOTARIZATION_CREDENTIALS})
set(CPACK_BUNDLE_APPLE_NOTARIZATION_CREDENTIALS "$ENV{NOTARIZATION_CREDENTIALS}")
endif()

View File

@@ -0,0 +1,22 @@
# Windows Packaging Configuration
# NSIS installer
set(CPACK_GENERATOR "NSIS;ZIP")
set(CPACK_NSIS_PACKAGE_NAME "YAZE Editor")
set(CPACK_NSIS_DISPLAY_NAME "YAZE Editor v${CPACK_PACKAGE_VERSION}")
set(CPACK_NSIS_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}")
set(CPACK_NSIS_CONTACT "scawful")
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/scawful/yaze")
set(CPACK_NSIS_HELP_LINK "https://github.com/scawful/yaze")
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/scawful/yaze")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
# ZIP package
set(CPACK_ZIP_PACKAGE_NAME "yaze-${CPACK_PACKAGE_VERSION}-windows-x64")
# Code signing (if available)
if(DEFINED ENV{SIGNTOOL_CERTIFICATE})
set(CPACK_NSIS_SIGN_TOOL "signtool.exe")
set(CPACK_NSIS_SIGN_COMMAND "${ENV{SIGNTOOL_CERTIFICATE}}")
endif()

View File

@@ -27,14 +27,14 @@ if(WIN32)
endif() endif()
# Fall back to bundled SDL if vcpkg not available or SDL2 not found # Fall back to bundled SDL if vcpkg not available or SDL2 not found
if(EXISTS "${CMAKE_SOURCE_DIR}/src/lib/SDL/CMakeLists.txt") if(EXISTS "${CMAKE_SOURCE_DIR}/ext/SDL/CMakeLists.txt")
message(STATUS "○ vcpkg SDL2 not found, using bundled SDL2") message(STATUS "○ vcpkg SDL2 not found, using bundled SDL2")
add_subdirectory(src/lib/SDL) add_subdirectory(ext/SDL)
set(SDL_TARGETS SDL2-static) set(SDL_TARGETS SDL2-static)
set(SDL2_INCLUDE_DIR set(SDL2_INCLUDE_DIR
${CMAKE_SOURCE_DIR}/src/lib/SDL/include ${CMAKE_SOURCE_DIR}/ext/SDL/include
${CMAKE_BINARY_DIR}/src/lib/SDL/include ${CMAKE_BINARY_DIR}/ext/SDL/include
${CMAKE_BINARY_DIR}/src/lib/SDL/include-config-${CMAKE_BUILD_TYPE} ${CMAKE_BINARY_DIR}/ext/SDL/include-config-${CMAKE_BUILD_TYPE}
) )
set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR})
if(TARGET SDL2main) if(TARGET SDL2main)
@@ -47,12 +47,12 @@ if(WIN32)
endif() endif()
elseif(UNIX OR MINGW) elseif(UNIX OR MINGW)
# Non-Windows: use bundled SDL # Non-Windows: use bundled SDL
add_subdirectory(src/lib/SDL) add_subdirectory(ext/SDL)
set(SDL_TARGETS SDL2-static) set(SDL_TARGETS SDL2-static)
set(SDL2_INCLUDE_DIR set(SDL2_INCLUDE_DIR
${CMAKE_SOURCE_DIR}/src/lib/SDL/include ${CMAKE_SOURCE_DIR}/ext/SDL/include
${CMAKE_BINARY_DIR}/src/lib/SDL/include ${CMAKE_BINARY_DIR}/ext/SDL/include
${CMAKE_BINARY_DIR}/src/lib/SDL/include-config-${CMAKE_BUILD_TYPE} ${CMAKE_BINARY_DIR}/ext/SDL/include-config-${CMAKE_BUILD_TYPE}
) )
set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR})
message(STATUS "Using bundled SDL2") message(STATUS "Using bundled SDL2")

View File

@@ -0,0 +1,22 @@
# Linux GCC Toolchain
# Optimized for Ubuntu 22.04+ with GCC 12+
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG")
# Link flags
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
# Find packages
find_package(PkgConfig REQUIRED)

View File

@@ -0,0 +1,25 @@
# macOS Clang Toolchain
# Optimized for macOS 14+ with Clang 18+
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# macOS deployment target
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "macOS deployment target")
# Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG")
# Link flags
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip")
# Find packages
find_package(PkgConfig REQUIRED)

View File

@@ -0,0 +1,26 @@
# Windows MSVC Toolchain
# Optimized for Visual Studio 2022 with MSVC 19.30+
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_C_COMPILER cl)
set(CMAKE_CXX_COMPILER cl)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# MSVC runtime library (static)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /permissive-")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /Ob2 /DNDEBUG")
# Link flags
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
# Windows-specific definitions
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-DNOMINMAX)

View File

@@ -34,6 +34,8 @@ function(yaze_add_compiler_flags)
target_compile_definitions(yaze_common INTERFACE linux stricmp=strcasecmp) target_compile_definitions(yaze_common INTERFACE linux stricmp=strcasecmp)
elseif(YAZE_PLATFORM_MACOS) elseif(YAZE_PLATFORM_MACOS)
target_compile_definitions(yaze_common INTERFACE MACOS) target_compile_definitions(yaze_common INTERFACE MACOS)
elseif(YAZE_PLATFORM_IOS)
target_compile_definitions(yaze_common INTERFACE YAZE_IOS)
elseif(YAZE_PLATFORM_WINDOWS) elseif(YAZE_PLATFORM_WINDOWS)
target_compile_definitions(yaze_common INTERFACE WINDOWS) target_compile_definitions(yaze_common INTERFACE WINDOWS)
endif() endif()

View File

@@ -1,65 +0,0 @@
# Getting Started
This software allows you to modify "The Legend of Zelda: A Link to the Past" (US or JP) ROMs. It is built for compatibility with ZScream projects and designed to be cross-platform.
## Quick Start
1. **Download** the latest release for your platform from the [releases page](https://github.com/scawful/yaze/releases).
2. **Load ROM** via `File > Open ROM`.
3. **Select an Editor** from the main toolbar (e.g., Overworld, Dungeon, Graphics).
4. **Make Changes** and save your project.
## General Tips
- **Experiment Flags**: Enable or disable new features in `File > Options > Experiment Flags`.
- **Backup Files**: Enabled by default. Each save creates a timestamped backup of your ROM.
- **Extensions**: Load custom tools via the `Extensions` menu (C library and Python module support is planned).
## Feature Status
| Feature | State | Notes |
|---|---|---|
| Overworld Editor | Stable | Supports vanilla and ZSCustomOverworld v2/v3 projects. |
| Dungeon Editor | Experimental | Requires extensive manual testing before production use. |
| Tile16 Editor | Experimental | Palette and tile workflows are still being tuned. |
| Palette Editor | Stable | Reference implementation for palette utilities. |
| Graphics Editor | Experimental | Rendering pipeline under active refactor. |
| Sprite Editor | Experimental | Card/UI patterns mid-migration. |
| Message Editor | Stable | Re-test after recent palette fixes. |
| Hex Editor | Stable | Direct ROM editing utility. |
| Asar Patching | Stable | Wraps the bundled Asar assembler. |
## Command-Line Interface (`z3ed`)
`z3ed` provides scripted access to the same ROM editors.
### AI Agent Chat
Chat with an AI to perform edits using natural language.
```bash
# Start an interactive chat session with the AI agent
z3ed agent chat --rom zelda3.sfc
```
> **Prompt:** "What sprites are in dungeon 2?"
### Resource Inspection
Directly query ROM data.
```bash
# List all sprites in the Eastern Palace (dungeon 2)
z3ed dungeon list-sprites --rom zelda3.sfc --dungeon 2
# Get information about a specific overworld map area
z3ed overworld describe-map --rom zelda3.sfc --map 80
```
### Patching
Apply assembly patches using the integrated Asar assembler.
```bash
# Apply an assembly patch to the ROM
z3ed asar patch.asm --rom zelda3.sfc
```
## Extending Functionality
YAZE exports a C API that is still evolving. Treat it as experimental and expect breaking changes while the plugin system is built out.

View File

@@ -1,150 +0,0 @@
# 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
### 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`.

File diff suppressed because it is too large Load Diff

View File

@@ -1,206 +0,0 @@
# Build Presets Guide
This document explains the reorganized CMake preset system for Yaze.
## Design Principles
1. **Short, memorable names** - No more `macos-dev-z3ed-ai`, just `mac-ai`
2. **Warnings off by default** - Add `-v` suffix for verbose (e.g., `mac-dbg-v`)
3. **Clear architecture support** - Explicit ARM64 and x86_64 presets
4. **Platform prefixes** - `mac-`, `win-`, `lin-` for easy identification
## Quick Start
### macOS Development
```bash
# Most common: AI-enabled development
cmake --preset mac-ai
cmake --build --preset mac-ai
# Basic debug build (no AI)
cmake --preset mac-dbg
cmake --build --preset mac-dbg
# Verbose warnings for debugging
cmake --preset mac-dbg-v
cmake --build --preset mac-dbg-v
# Release build
cmake --preset mac-rel
cmake --build --preset mac-rel
```
### Windows Development
```bash
# Debug build (x64)
cmake --preset win-dbg
cmake --build --preset win-dbg
# AI-enabled development
cmake --preset win-ai
cmake --build --preset win-ai
# ARM64 support
cmake --preset win-arm
cmake --build --preset win-arm
```
## All Presets
### macOS Presets
| Preset | Description | Arch | Warnings | Features |
|--------|-------------|------|----------|----------|
| `mac-dbg` | Debug build | ARM64 | Off | Basic |
| `mac-dbg-v` | Debug verbose | ARM64 | On | Basic |
| `mac-rel` | Release | ARM64 | Off | Basic |
| `mac-x64` | Debug x86_64 | x86_64 | Off | Basic |
| `mac-uni` | Universal binary | Both | Off | Basic |
| `mac-dev` | Development | ARM64 | Off | ROM tests |
| `mac-ai` | AI development | ARM64 | Off | z3ed, JSON, gRPC, ROM tests |
| `mac-z3ed` | z3ed CLI | ARM64 | Off | AI agent support |
| `mac-rooms` | Dungeon editor | ARM64 | Off | Minimal build for room editing |
### Windows Presets
| Preset | Description | Arch | Warnings | Features |
|--------|-------------|------|----------|----------|
| `win-dbg` | Debug build | x64 | Off | Basic |
| `win-dbg-v` | Debug verbose | x64 | On | Basic |
| `win-rel` | Release | x64 | Off | Basic |
| `win-arm` | Debug ARM64 | ARM64 | Off | Basic |
| `win-arm-rel` | Release ARM64 | ARM64 | Off | Basic |
| `win-dev` | Development | x64 | Off | ROM tests |
| `win-ai` | AI development | x64 | Off | z3ed, JSON, gRPC, ROM tests |
| `win-z3ed` | z3ed CLI | x64 | Off | AI agent support |
### Linux Presets
| Preset | Description | Compiler | Warnings |
|--------|-------------|----------|----------|
| `lin-dbg` | Debug | GCC | Off |
| `lin-clang` | Debug | Clang | Off |
### Special Purpose
| Preset | Description |
|--------|-------------|
| `ci` | Continuous integration (no ROM tests) |
| `asan` | AddressSanitizer build |
| `coverage` | Code coverage build |
## Warning Control
By default, all presets suppress compiler warnings with `-w` for a cleaner build experience.
### To Enable Verbose Warnings:
1. Use a preset with `-v` suffix (e.g., `mac-dbg-v`, `win-dbg-v`)
2. Or set `YAZE_SUPPRESS_WARNINGS=OFF` manually:
```bash
cmake --preset mac-dbg -DYAZE_SUPPRESS_WARNINGS=OFF
```
## Architecture Support
### macOS
- **ARM64 (Apple Silicon)**: `mac-dbg`, `mac-rel`, `mac-ai`, etc.
- **x86_64 (Intel)**: `mac-x64`
- **Universal Binary**: `mac-uni` (both ARM64 + x86_64)
### Windows
- **x64**: `win-dbg`, `win-rel`, `win-ai`, etc.
- **ARM64**: `win-arm`, `win-arm-rel`
## Build Directories
Most presets use `build/` directory. Exceptions:
- `mac-rooms`: Uses `build_rooms/` to avoid conflicts
## Feature Flags
Common CMake options you can override:
```bash
# Enable/disable components
-DYAZE_BUILD_TESTS=ON/OFF
-DYAZE_BUILD_Z3ED=ON/OFF
-DYAZE_BUILD_EMU=ON/OFF
-DYAZE_BUILD_APP=ON/OFF
# AI features
-DZ3ED_AI=ON/OFF
-DYAZE_WITH_JSON=ON/OFF
-DYAZE_WITH_GRPC=ON/OFF
# Testing
-DYAZE_ENABLE_ROM_TESTS=ON/OFF
-DYAZE_TEST_ROM_PATH=/path/to/zelda3.sfc
# Build optimization
-DYAZE_MINIMAL_BUILD=ON/OFF
```
## Examples
### Development with AI features and verbose warnings
```bash
cmake --preset mac-dbg-v -DZ3ED_AI=ON -DYAZE_WITH_GRPC=ON
cmake --build --preset mac-dbg-v
```
### Release build for distribution (macOS Universal)
```bash
cmake --preset mac-uni
cmake --build --preset mac-uni
cpack --preset mac-uni
```
### Quick minimal build for testing
```bash
cmake --preset mac-dbg -DYAZE_MINIMAL_BUILD=ON
cmake --build --preset mac-dbg -j12
```
## Updating compile_commands.json
After configuring with a new preset, copy the compile commands for IDE support:
```bash
cp build/compile_commands.json .
```
This ensures clangd and other LSP servers can find headers and understand build flags.
## Migration from Old Presets
Old preset names have been simplified:
| Old Name | New Name |
|----------|----------|
| `macos-dev-z3ed-ai` | `mac-ai` |
| `macos-debug` | `mac-dbg` |
| `macos-release` | `mac-rel` |
| `macos-debug-universal` | `mac-uni` |
| `macos-dungeon-dev` | `mac-rooms` |
| `windows-debug` | `win-dbg` |
| `windows-release` | `win-rel` |
| `windows-arm64-debug` | `win-arm` |
| `linux-debug` | `lin-dbg` |
| `linux-clang` | `lin-clang` |
## Troubleshooting
### Warnings are still showing
- Make sure you're using a preset without `-v` suffix
- Check `cmake` output for `✓ Warnings suppressed` message
- Reconfigure and rebuild: `rm -rf build && cmake --preset mac-dbg`
### clangd can't find nlohmann/json
- Run `cmake --preset <your-preset>` to regenerate compile_commands.json
- Copy to root: `cp build/compile_commands.json .`
- Restart your IDE or LSP server
### Build fails with missing dependencies
- Ensure submodules are initialized: `git submodule update --init --recursive`
- For AI features, make sure you have OpenSSL: `brew install openssl` (macOS)

File diff suppressed because it is too large Load Diff

View File

@@ -1,263 +0,0 @@
# F2: Dungeon Editor v2 - Complete Guide
**Last Updated**: October 10, 2025
**Related**: [E2-development-guide.md](E2-development-guide.md), [E5-debugging-guide.md](E5-debugging-guide.md)
---
## Overview
The Dungeon Editor uses a modern card-based architecture (DungeonEditorV2) with self-contained room rendering. This guide covers the architecture, recent refactoring work, and next development steps.
### Key Features
- **Visual room editing** with 512x512 canvas per room
- **Object position visualization** - Colored outlines by layer (Red/Green/Blue)
- **Per-room settings** - Independent BG1/BG2 visibility and layer types
- **Flexible docking** - EditorCard system for custom workspace layouts
- **Self-contained rooms** - Each room owns its bitmaps and palettes
- **Overworld integration** - Double-click entrances to open dungeon rooms
---
### Architecture Improvements
1. **Room Buffers Decoupled** - No dependency on Arena graphics sheets
2. **ObjectRenderer Removed** - Standardized on ObjectDrawer (~1000 lines deleted)
3. **LoadGraphicsSheetsIntoArena Removed** - Using per-room graphics (~66 lines)
4. **Old Tab System Removed** - EditorCard is the standard
5. **Texture Atlas Infrastructure** - Future-proof stub created
6. **Test Suite Cleaned** - Deleted 1270 lines of redundant tests
### UI Improvements
- Room ID in card title: `[003] Room Name`
- Properties reorganized into clean 4-column table
- Compact layer controls (1 row instead of 3)
- Room graphics canvas height fixed (1025px → 257px)
- Object count in status bar
---
## Architecture
### Component Overview
```
DungeonEditorV2 (UI Layer)
├─ Card-based UI system
├─ Room window management
├─ Component coordination
└─ Lazy loading
DungeonEditorSystem (Backend Layer)
├─ Sprite/Item/Entrance/Door/Chest management
├─ Undo/Redo functionality
├─ Room properties management
└─ Dungeon-wide operations
Room (Data Layer)
├─ Self-contained buffers (bg1_buffer_, bg2_buffer_)
├─ Object storage (tile_objects_)
├─ Graphics loading
└─ Rendering pipeline
```
### Room Rendering Pipeline
TODO: Update this to latest code.
```
1. LoadRoomGraphics(blockset)
└─> Reads blocks[] from ROM
└─> Loads blockset data → current_gfx16_
2. LoadObjects()
└─> Parses object data from ROM
└─> Creates tile_objects_[]
└─> SETS floor1_graphics_, floor2_graphics_ ← CRITICAL!
3. RenderRoomGraphics() [SELF-CONTAINED]
├─> DrawFloor(floor1_graphics_, floor2_graphics_)
├─> DrawBackground(current_gfx16_)
├─> SetPalette(full_90_color_dungeon_palette)
├─> RenderObjectsToBackground()
│ └─> ObjectDrawer::DrawObjectList()
└─> QueueTextureCommand(UPDATE/CREATE)
4. DrawRoomBackgroundLayers(room_id)
└─> ProcessTextureQueue() → GPU textures
└─> canvas_.DrawBitmap(bg1, bg2)
5. DrawObjectPositionOutlines(room)
└─> Colored rectangles by layer
└─> Object ID labels
```
### Room Structure (Bottom to Top)
Understanding ALTTP dungeon composition is critical:
```
Room Composition:
├─ Room Layout (BASE LAYER - immovable)
│ ├─ Walls (structural boundaries, 7 configurations of squares in 2x2 grid)
│ ├─ Floors (walkable areas, repeated tile pattern set to BG1/BG2)
├─ Layer 0 Objects (floor decorations, some walls)
├─ Layer 1 Objects (chests, decorations)
└─ Layer 2 Objects (stairs, transitions)
Doors: Positioned at room edges to connect rooms
```
**Key Insight**: Layouts are immovable base structure. Objects are placed ON TOP and can be moved/edited. This allows for large rooms, 4-quadrant rooms, tall/wide rooms, etc.
---
## Next Development Steps
### High Priority (Must Do)
#### 1. Door Rendering at Room Edges
**What**: Render doors with proper patterns at room connections
**Pattern Reference**: ZScream's door drawing patterns
**Implementation**:
```cpp
void DungeonCanvasViewer::DrawDoors(const zelda3::Room& room) {
// Doors stored in room data
// Position at room edges (North/South/East/West)
// Use current_gfx16_ graphics data
// TODO: Get door data from room.GetDoors() or similar
// TODO: Use ObjectDrawer patterns for door graphics
// TODO: Draw at interpolation points between rooms
}
```
---
#### 2. Object Name Labels from String Array
**File**: `dungeon_canvas_viewer.cc:416` (DrawObjectPositionOutlines)
**What**: Show real object names instead of just IDs
**Implementation**:
```cpp
// Instead of:
std::string label = absl::StrFormat("0x%02X", obj.id_);
// Use:
std::string object_name = GetObjectName(obj.id_);
std::string label = absl::StrFormat("%s\n0x%02X", object_name.c_str(), obj.id_);
// Helper function:
std::string GetObjectName(int16_t object_id) {
// TODO: Reference ZScream's object name arrays
// TODO: Map object ID → name string
// Example: 0x10 → "Wall (North)"
return "Object";
}
```
---
#### 4. Fix Plus Button to Select Any Room
**File**: `dungeon_editor_v2.cc:228` (DrawToolset)
**Current Issue**: Opens Room 0x00 (Ganon) always
**Fix**:
```cpp
if (toolbar.AddAction(ICON_MD_ADD, "Open Room")) {
// Show room selector dialog instead of opening room 0
show_room_selector_ = true;
// Or: show room picker popup
ImGui::OpenPopup("SelectRoomToOpen");
}
// Add popup:
if (ImGui::BeginPopup("SelectRoomToOpen")) {
static int selected_room = 0;
ImGui::InputInt("Room ID", &selected_room);
if (ImGui::Button("Open")) {
OnRoomSelected(selected_room);
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
```
---
### Medium Priority (Should Do)
#### 6. Fix InputHexByte +/- Button Events
**File**: `src/app/gui/input.cc` (likely)
**Issue**: Buttons don't respond to clicks
**Investigation Needed**:
- Check if button click events are being captured
- Verify event logic matches working examples
- Keep existing event style if it works elsewhere
### Lower Priority (Nice to Have)
#### 9. Move Backend Logic to DungeonEditorSystem
**What**: Separate UI (V2) from data operations (System)
**Migration**:
- Sprite management → DungeonEditorSystem
- Item management → DungeonEditorSystem
- Entrance/Door/Chest → DungeonEditorSystem
- Undo/Redo → DungeonEditorSystem
**Result**: DungeonEditorV2 becomes pure UI coordinator
---
## Quick Start
### Build & Run
```bash
cd /Users/scawful/Code/yaze
cmake --preset mac-ai -B build_ai
cmake --build build_ai --target yaze -j12
# Run dungeon editor
./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon
# Open specific room
./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0x00"
```
---
## Testing & Verification
### Debug Commands
```bash
# Verify floor values load correctly
./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon 2>&1 | grep "floor1="
# Expected: floor1=4, floor2=8 (NOT 0!)
# Check object rendering
./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon 2>&1 | grep "Drawing.*objects"
# Check object drawing details
./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon 2>&1 | grep "Writing Tile16"
```
## Related Documentation
- **E2-development-guide.md** - Core architectural patterns
- **E5-debugging-guide.md** - Debugging workflows
- **F1-dungeon-editor-guide.md** - Original dungeon guide (may be outdated)
---
**Last Updated**: October 10, 2025
**Contributors**: Dungeon Editor Refactoring Session

View File

@@ -1,70 +0,0 @@
# Canvas System Overview
## Canvas Architecture
- **Canvas States**: track `canvas`, `content`, and `draw` rectangles independently; expose size/scale through `CanvasState` inspection panel
- **Layer Stack**: background ➝ bitmaps ➝ entity overlays ➝ selection/tooltip layers
- **Interaction Modes**: Tile Paint, Tile Select, Rectangle Select, Entity Manipulation, Palette Editing, Diagnostics
- **Context Menu**: persistent menu with material icon sections (Mode, View, Info, Bitmap, Palette, BPP, Performance, Layout, Custom)
## Core API Patterns
- Modern usage: `Begin/End` (auto grid/overlay, persistent context menu)
- Legacy helpers still available (`DrawBackground`, `DrawGrid`, `DrawSelectRect`, etc.)
- Unified state snapshot: `CanvasState` exposes geometry, zoom, scroll
- Interaction handler manages mode-specific tools (tile brush, select rect, entity gizmo)
## Context Menu Sections
- **Mode Selector**: switch modes with icons (Brush, Select, Rect, Bitmap, Palette, BPP, Perf)
- **View & Grid**: reset/zoom, toggle grid/labels, advanced/scaling dialogs
- **Canvas Info**: real-time canvas/content size, scale, scroll, mouse position
- **Bitmap/Palette/BPP**: format conversion, palette analysis, BPP workflows with persistent modals
- **Performance**: profiler metrics, dashboard, usage report
- **Layout**: draggable toggle, auto resize, grid step
- **Custom Actions**: consumer-provided menu items
## Interaction Modes & Capabilities
- **Tile Painting**: tile16 painter, brush size, finish stroke callbacks
- Operations: finish_paint, reset_view, zoom, grid, scaling
- **Tile Selection**: multi-select rectangle, copy/paste selection
- Operations: select_all, clear_selection, reset_view, zoom, grid, scaling
- **Rectangle Selection**: drag-select area, clear selection
- Operations: clear_selection, reset_view, zoom, grid, scaling
- **Bitmap Editing**: format conversion, bitmap manipulation
- Operations: bitmap_convert, palette_edit, bpp_analysis, reset_view, zoom, grid, scaling
- **Palette Editing**: inline palette editor, ROM palette picker, color analysis
- Operations: palette_edit, palette_analysis, reset_palette, reset_view, zoom, grid, scaling
- **BPP Conversion**: format analysis, conversion workflows
- Operations: bpp_analysis, bpp_conversion, bitmap_convert, reset_view, zoom, grid, scaling
- **Performance Mode**: diagnostics, texture queue, performance overlays
- Operations: performance, usage_report, copy_metrics, reset_view, zoom, grid, scaling
## Debug & Diagnostics
- Persistent modals (`View→Advanced`, `View→Scaling`, `Palette`, `BPP`) stay open until closed
- Texture inspector shows current bitmap, VRAM sheet, palette group, usage stats
- State overlay: canvas size, content size, global scale, scroll, highlight entity
- Performance HUD: operation counts, timing graphs, usage recommendations
## Automation API
- CanvasAutomationAPI: tile operations (`SetTileAt`, `SelectRect`), view control (`ScrollToTile`, `SetZoom`), entity manipulation hooks
- Exposed through CLI (`z3ed`) and gRPC service, matching UI modes
## Integration Steps for Editors
1. Construct `Canvas`, set renderer (optional) and ID
2. Call `InitializePaletteEditor` and `SetUsageMode`
3. Configure available modes: `SetAvailableModes({kTilePainting, kTileSelecting})`
4. Register mode callbacks (tile paint finish, selection clear, etc.)
5. During frame: `canvas.Begin(size)` → draw bitmaps/entities → `canvas.End()`
6. Provide custom menu items via `AddMenuItem`/`AddMenuItem(item, usage)`
7. Use `GetConfig()`/`GetSelection()` for state; respond to context menu commands via callback lambda in `Render`
## Migration Checklist
- Replace direct `DrawContextMenu` logic with new render callback signature
- Move palette/BPP helpers into `canvas/` module; update includes
- Ensure persistent modals wired (advanced/scaling/palette/bpp/perf)
- Update usage tracker integrations to record mode switches
- Validate overworld/tile16/dungeon editors in tile paint, select, entity modes
## Testing Notes
- Manual regression: overworld paint/select, tile16 painter, dungeon entity drag
- Verify context menu persists and modals remain until closed
- Ensure palette/BPP modals populate with correct bitmap/palette data
- Automation: run CanvasAutomation API tests/end-to-end scripts for overworld edits

View File

@@ -1,401 +0,0 @@
# Canvas Coordinate Synchronization and Scroll Fix
**Date**: October 10, 2025
**Issues**:
1. Overworld map highlighting regression after canvas refactoring
2. Overworld canvas scrolling unexpectedly when selecting tiles
3. Vanilla Dark/Special World large map outlines not displaying
**Status**: Fixed
## Problem Summary
After the canvas refactoring (commits f538775954, 60ddf76331), two critical bugs appeared:
1. **Map highlighting broken**: The overworld editor stopped properly highlighting the current map when hovering. The map highlighting only worked during active tile painting, not during normal mouse hover.
2. **Wrong canvas scrolling**: When right-clicking to select tiles (especially on Dark World), the overworld canvas would scroll unexpectedly instead of the tile16 blockset selector.
## Root Cause
The regression had **FIVE** issues:
### Issue 1: Wrong Coordinate System (Line 1041)
**File**: `src/app/editor/overworld/overworld_editor.cc:1041`
**Before (BROKEN)**:
```cpp
const auto mouse_position = ImGui::GetIO().MousePos; // ❌ Screen coordinates!
const auto canvas_zero_point = ow_map_canvas_.zero_point();
int map_x = (mouse_position.x - canvas_zero_point.x) / kOverworldMapSize;
```
**After (FIXED)**:
```cpp
const auto mouse_position = ow_map_canvas_.hover_mouse_pos(); // World coordinates!
int map_x = mouse_position.x / kOverworldMapSize;
```
**Why This Was Wrong**:
- `ImGui::GetIO().MousePos` returns **screen space** coordinates (absolute position on screen)
- The canvas may be scrolled, scaled, or positioned anywhere on screen
- Screen coordinates don't account for canvas scrolling/offset
- `hover_mouse_pos()` returns **canvas/world space** coordinates (relative to canvas content)
### Issue 2: Hover Position Not Updated (Line 416)
**File**: `src/app/gui/canvas.cc:416`
**Before (BROKEN)**:
```cpp
void Canvas::DrawBackground(ImVec2 canvas_size) {
// ... setup code ...
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
// ❌ mouse_pos_in_canvas_ only updated in DrawTilePainter() during painting!
if (config_.is_draggable && IsItemHovered()) {
// ... pan handling ...
}
}
```
`mouse_pos_in_canvas_` was only updated inside painting methods:
- `DrawTilePainter()` at line 741
- `DrawSolidTilePainter()` at line 860
- `DrawTileSelector()` at line 929
**After (FIXED)**:
```cpp
void Canvas::DrawBackground(ImVec2 canvas_size) {
// ... setup code ...
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
// CRITICAL FIX: Always update hover position when hovering
if (IsItemHovered()) {
const ImGuiIO& io = GetIO();
const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
mouse_pos_in_canvas_ = mouse_pos; // Updated every frame during hover
}
if (config_.is_draggable && IsItemHovered()) {
// ... pan handling ...
}
}
```
## Technical Details
### Coordinate Spaces
yaze has three coordinate spaces:
1. **Screen Space**: Absolute pixel coordinates on the monitor
- `ImGui::GetIO().MousePos` returns this
- Never use this for canvas operations!
2. **Canvas/World Space**: Coordinates relative to canvas content
- Accounts for canvas scrolling and offset
- `Canvas::hover_mouse_pos()` returns this
- Use this for map calculations, entity positioning, etc.
3. **Tile/Grid Space**: Coordinates in tile units (not pixels)
- `Canvas::CanvasToTile()` converts from canvas to grid space
- Used by automation API
### Usage Patterns
**For Hover/Highlighting** (CheckForCurrentMap):
```cpp
auto hover_pos = canvas.hover_mouse_pos(); // Updates continuously
int map_x = hover_pos.x / kOverworldMapSize;
```
**For Active Painting** (DrawOverworldEdits):
```cpp
auto paint_pos = canvas.drawn_tile_position(); // Updates only during drag
int map_x = paint_pos.x / kOverworldMapSize;
```
## Testing
### Visual Testing
**Map Highlighting Test**:
1. Open overworld editor
2. Hover mouse over different maps (without clicking)
3. Verify current map highlights correctly
4. Test with different scale levels (0.25x - 4.0x)
5. Test with scrolled canvas
**Scroll Regression Test**:
1. Open overworld editor
2. Switch to Dark World (or any world)
3. Right-click on overworld canvas to select a tile
4. **Expected**: Tile16 blockset selector scrolls to show the selected tile
5. **Expected**: Overworld canvas does NOT scroll
6.**Before fix**: Overworld canvas would scroll unexpectedly
### Unit Tests
Created `test/unit/gui/canvas_coordinate_sync_test.cc` with regression tests:
- `HoverMousePos_IndependentFromDrawnPos`: Verifies hover vs paint separation
- `CoordinateSpace_WorldNotScreen`: Ensures world coordinates used
- `MapCalculation_SmallMaps`: Tests 512x512 map boundaries
- `MapCalculation_LargeMaps`: Tests 1024x1024 v3 ASM maps
- `OverworldMapHighlight_UsesHoverNotDrawn`: Critical regression test
- `OverworldMapIndex_From8x8Grid`: Tests all three worlds (Light/Dark/Special)
Run tests:
```bash
./build/bin/yaze_test --unit
```
## Impact Analysis
### Files Changed
1. `src/app/editor/overworld/overworld_editor.cc` (line 1041-1049)
- Changed from screen coordinates to canvas hover coordinates
- Removed incorrect `canvas_zero_point` subtraction
2. `src/app/gui/canvas.cc` (line 414-421)
- Added continuous hover position tracking in `DrawBackground()`
- Now updates `mouse_pos_in_canvas_` every frame when hovering
3. `src/app/editor/overworld/overworld_editor.cc` (line 2344-2360)
- Removed fallback scroll code that scrolled the wrong canvas
- Now only uses `blockset_selector_->ScrollToTile()` which targets the correct canvas
4. `src/app/editor/overworld/overworld_editor.cc` (line 1403-1408)
- Changed from `ImGui::IsItemHovered()` (checks last drawn item)
- To `ow_map_canvas_.IsMouseHovering()` (checks canvas hover state directly)
5. `src/app/editor/overworld/overworld_editor.cc` (line 1133-1151)
- Added world offset subtraction for vanilla large map parent coordinates
- Now properly accounts for Dark World (0x40-0x7F) and Special World (0x80-0x9F)
### Affected Functionality
- **Fixed**: Overworld map highlighting during hover (all worlds, all ROM types)
- **Fixed**: Vanilla Dark World large map highlighting (was drawing off-screen)
- **Fixed**: Vanilla Special World large map highlighting (was drawing off-screen)
- **Fixed**: Overworld canvas no longer scrolls when selecting tiles
- **Fixed**: Tile16 selector properly scrolls to show selected tile (via blockset_selector_)
- **Fixed**: Entity renderer using `hover_mouse_pos()` (already worked correctly)
- **Preserved**: Tile painting using `drawn_tile_position()` (unchanged)
- **Preserved**: Multi-area map support (512x512, 1024x1024)
- **Preserved**: All three worlds (Light 0x00-0x3F, Dark 0x40-0x7F, Special 0x80+)
- **Preserved**: ZSCustomOverworld v3 large maps (already worked correctly)
### Related Code That Works Correctly
These files already use the correct pattern:
- `src/app/editor/overworld/overworld_entity_renderer.cc:68-69` - Uses `hover_mouse_pos()` for entity placement
- `src/app/editor/overworld/overworld_editor.cc:664` - Uses `drawn_tile_position()` for painting
## Multi-Area Map Support
The fix properly handles all area sizes:
### Standard Maps (512x512)
```cpp
int map_x = hover_pos.x / 512; // 0-7 range
int map_y = hover_pos.y / 512; // 0-7 range
int map_index = map_x + map_y * 8; // 0-63 (8x8 grid)
```
### ZSCustomOverworld v3 Large Maps (1024x1024)
```cpp
int map_x = hover_pos.x / 1024; // Large map X
int map_y = hover_pos.y / 1024; // Large map Y
// Parent map calculation handled in lines 1073-1190
```
The existing multi-area logic (lines 1068-1190) remains unchanged and works correctly with the fix.
## Issue 3: Wrong Canvas Being Scrolled (Line 2344-2366)
**File**: `src/app/editor/overworld/overworld_editor.cc:2344`
**Problem**: When selecting tiles with right-click on the overworld canvas, `ScrollBlocksetCanvasToCurrentTile()` was calling `ImGui::SetScrollX/Y()` which scrolls **the current ImGui window**, not a specific canvas.
**Call Stack**:
```
DrawOverworldCanvas() // Overworld canvas is current window
└─ CheckForOverworldEdits() (line 1401)
└─ CheckForSelectRectangle() (line 793)
└─ ScrollBlocksetCanvasToCurrentTile() (line 916)
└─ ImGui::SetScrollX/Y() (lines 2364-2365) // ❌ Scrolls CURRENT window!
```
**Before (BROKEN)**:
```cpp
void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() {
if (blockset_selector_) {
blockset_selector_->ScrollToTile(current_tile16_);
return;
}
// Fallback: maintain legacy behavior when the selector is unavailable.
constexpr int kTilesPerRow = 8;
constexpr int kTileDisplaySize = 32;
int tile_col = current_tile16_ % kTilesPerRow;
int tile_row = current_tile16_ / kTilesPerRow;
float tile_x = static_cast<float>(tile_col * kTileDisplaySize);
float tile_y = static_cast<float>(tile_row * kTileDisplaySize);
const ImVec2 window_size = ImGui::GetWindowSize();
float scroll_x = tile_x - (window_size.x / 2.0F) + (kTileDisplaySize / 2.0F);
float scroll_y = tile_y - (window_size.y / 2.0F) + (kTileDisplaySize / 2.0F);
// ❌ BUG: This scrolls whatever ImGui window is currently active!
// When called from overworld canvas, it scrolls the overworld instead of tile16 selector!
ImGui::SetScrollX(std::max(0.0f, scroll_x));
ImGui::SetScrollY(std::max(0.0f, scroll_y));
}
```
**After (FIXED)**:
```cpp
void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() {
if (blockset_selector_) {
blockset_selector_->ScrollToTile(current_tile16_); // Correct: Targets specific canvas
return;
}
// CRITICAL FIX: Do NOT use fallback scrolling from overworld canvas context!
// The fallback code uses ImGui::SetScrollX/Y which scrolls the CURRENT window,
// and when called from CheckForSelectRectangle() during overworld canvas rendering,
// it incorrectly scrolls the overworld canvas instead of the tile16 selector.
//
// The blockset_selector_ should always be available in modern code paths.
// If it's not available, we skip scrolling rather than scroll the wrong window.
}
```
**Why This Fixes It**:
- The modern `blockset_selector_->ScrollToTile()` targets the specific tile16 selector canvas
- The fallback `ImGui::SetScrollX/Y()` has no context - it just scrolls the active window
- By removing the fallback, we prevent scrolling the wrong canvas
- If `blockset_selector_` is null (shouldn't happen in modern builds), we safely do nothing instead of breaking user interaction
## Issue 4: Wrong Hover Check (Line 1403)
**File**: `src/app/editor/overworld/overworld_editor.cc:1403`
**Problem**: The code was using `ImGui::IsItemHovered()` to check if the mouse was over the canvas, but this checks the **last drawn ImGui item**, which could be entities, overlays, or anything drawn after the canvas's InvisibleButton. This meant hover detection was completely broken.
**Call Stack**:
```
DrawOverworldCanvas()
└─ DrawBackground() at line 1350 // Creates InvisibleButton (item A)
└─ DrawExits/Entrances/Items/Sprites() // Draws entities (items B, C, D...)
└─ DrawOverlayPreviewOnMap() // Draws overlay (item E)
└─ IsItemHovered() at line 1403 // ❌ Checks item E, not item A!
```
**Before (BROKEN)**:
```cpp
if (current_mode == EditingMode::DRAW_TILE) {
CheckForOverworldEdits();
}
if (IsItemHovered()) // ❌ Checks LAST item (overlay/entity), not canvas!
status_ = CheckForCurrentMap();
```
**After (FIXED)**:
```cpp
if (current_mode == EditingMode::DRAW_TILE) {
CheckForOverworldEdits();
}
// CRITICAL FIX: Use canvas hover state, not ImGui::IsItemHovered()
// IsItemHovered() checks the LAST drawn item, which could be entities/overlay,
// not the canvas InvisibleButton. ow_map_canvas_.IsMouseHovering() correctly
// tracks whether mouse is over the canvas area.
if (ow_map_canvas_.IsMouseHovering()) // Checks canvas hover state directly
status_ = CheckForCurrentMap();
```
**Why This Fixes It**:
- `IsItemHovered()` is context-sensitive - it checks whatever the last `ImGui::*()` call was
- After drawing entities and overlays, the "last item" is NOT the canvas
- `Canvas::IsMouseHovering()` tracks the hover state from the InvisibleButton in `DrawBackground()`
- This state is set correctly when the InvisibleButton is hovered (line 416 in canvas.cc)
## Issue 5: Vanilla Large Map World Offset (Line 1132-1136)
**File**: `src/app/editor/overworld/overworld_editor.cc:1132-1136`
**Problem**: For vanilla ROMs, the large map highlighting logic wasn't accounting for world offsets when calculating parent map coordinates. Dark World maps (0x40-0x7F) and Special World maps (0x80-0x9F) use map IDs with offsets, but the display grid coordinates are 0-7.
**Before (BROKEN)**:
```cpp
if (overworld_.overworld_map(current_map_)->is_large_map() ||
overworld_.overworld_map(current_map_)->large_index() != 0) {
const int highlight_parent =
overworld_.overworld_map(current_highlighted_map)->parent();
const int parent_map_x = highlight_parent % 8; // ❌ Wrong for Dark/Special!
const int parent_map_y = highlight_parent / 8;
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
parent_map_y * kOverworldMapSize,
large_map_size, large_map_size);
}
```
**Example Bug**:
- Dark World map 0x42 (parent) → `0x42 % 8 = 2`, `0x42 / 8 = 8`
- This draws the outline at grid position (2, 8) which is **off the screen**!
- Correct position should be (2, 0) in the Dark World display grid
**After (FIXED)**:
```cpp
if (overworld_.overworld_map(current_map_)->is_large_map() ||
overworld_.overworld_map(current_map_)->large_index() != 0) {
const int highlight_parent =
overworld_.overworld_map(current_highlighted_map)->parent();
// CRITICAL FIX: Account for world offset when calculating parent coordinates
int parent_map_x;
int parent_map_y;
if (current_world_ == 0) {
// Light World (0x00-0x3F)
parent_map_x = highlight_parent % 8;
parent_map_y = highlight_parent / 8;
} else if (current_world_ == 1) {
// Dark World (0x40-0x7F) - subtract 0x40 to get display coordinates
parent_map_x = (highlight_parent - 0x40) % 8;
parent_map_y = (highlight_parent - 0x40) / 8;
} else {
// Special World (0x80-0x9F) - subtract 0x80 to get display coordinates
parent_map_x = (highlight_parent - 0x80) % 8;
parent_map_y = (highlight_parent - 0x80) / 8;
}
ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize,
parent_map_y * kOverworldMapSize,
large_map_size, large_map_size);
}
```
**Why This Fixes It**:
- Map IDs are **absolute**: Light World 0x00-0x3F, Dark World 0x40-0x7F, Special 0x80-0x9F
- Display coordinates are **relative**: Each world displays in an 8x8 grid (0-7, 0-7)
- Without subtracting the world offset, coordinates overflow the display grid
- This matches the same logic used for v3 large maps (lines 1084-1096) and small maps (lines 1141-1172)
## Commit Reference
**Canvas Refactoring Commits**:
- `f538775954` - Organize Canvas Utilities and BPP Format Management
- `60ddf76331` - Integrate Canvas Automation API and Simplify Overworld Editor Controls
These commits moved canvas utilities to modular components but introduced the regression by not maintaining hover position tracking.
## Future Improvements
1. **Canvas Mode System**: Complete the interaction handler modes (tile paint, select, etc.)
2. **Persistent Context Menus**: Implement mode switching through context menu popups
3. **Debugging Visualization**: Add canvas coordinate overlay for debugging
4. **E2E Tests**: Create end-to-end tests for overworld map highlighting workflow
## Related Documentation
- `docs/G1-canvas-guide.md` - Canvas system architecture
- `docs/E5-debugging-guide.md` - Debugging techniques
- `docs/debugging-startup-flags.md` - CLI flags for editor testing

View File

@@ -1,104 +0,0 @@
# Roadmap
**Last Updated: October 4, 2025**
This roadmap tracks upcoming releases and major ongoing initiatives.
## Current Focus
- Finish overworld editor parity (sprite workflows, performance tuning).
- Resolve dungeon object rendering and tile painting gaps.
- Close out Tile16 palette inconsistencies.
- Harden the `z3ed` automation paths before expanding functionality.
## 0.4.0 (Next Major Release) - SDL3 Modernization & Core Improvements
**Status:** Planning
**Type:** Major Breaking Release
**Timeline:** 6-8 weeks
### Primary Goals
1. SDL3 migration across graphics, audio, and input
2. Dependency reorganization (`src/lib/` + `third_party/``external/`)
3. Backend abstraction layer for renderer/audio/input
4. Editor polish and UX clean-up
### Phase 1: Infrastructure (Week 1-2)
- Merge `src/lib/` and `third_party/` into `external/`
- Update CMake, submodules, and CI presets
- Validate builds on Windows, macOS, Linux
### Phase 2: SDL3 Core Migration (Week 3-4)
- Switch to SDL3 with GPU-based rendering
- Introduce `GraphicsBackend` abstraction
- Restore window creation and baseline editor rendering
- Update the ImGui SDL3 backend
### Phase 3: Complete SDL3 Integration (Week 5-6)
- Port editors (Overworld, Dungeon, Graphics, Palette, Screen, Music) to the new backend
- Implement SDL3 audio backend for the emulator
- Implement SDL3 input backend with improved gamepad support
- Benchmark and tune rendering performance
### Phase 4: Editor Features & UX (Week 7-8)
- Resolve Tile16 palette inconsistencies
- Complete overworld sprite add/remove/move workflow
- Improve dungeon editor labels and tab management
- Add lazy loading for rooms to cut load times
### Phase 5: AI Agent Enhancements (Throughout)
- Vim-style editing in `simple-chat` (complete)
- Autocomplete engine with fuzzy matching (complete)
- Harden live LLM integration (Gemini function-calling, prompts)
- Attach AI workflows to GUI regression harness
- Extend tool coverage for dialogue, music, sprite data
### Success Criteria
- SDL3 builds pass on Windows, macOS, Linux
- No performance regression versus v0.3.x
- Editors function on the new backend
- Emulator audio/input verified
- Documentation and migration guide updated
**Breaking Changes:**
- SDL2 → SDL3 (requires recompilation)
- Directory restructure (requires submodule re-init)
- API changes in graphics backend (for extensions)
---
## 0.5.X - Feature Expansion
- **Plugin Architecture**: Design and implement the initial framework for community-developed extensions and custom tools.
- **Advanced Graphics Editing**: Implement functionality to edit and re-import full graphics sheets.
- **`z3ed` AI Agent Enhancements**:
- **Collaborative Sessions**: Enhance the network collaboration mode with shared AI proposals and ROM synchronization.
- **Multi-modal Input**: Integrate screenshot capabilities to send visual context to Gemini for more accurate, context-aware commands.
---
## 0.6.X - Content & Integration
- **Advanced Content Editors**:
- Implement a user interface for the music editing system.
- Enhance the Hex Editor with better search and data interpretation features.
- **Documentation Overhaul**:
- Implement a system to auto-generate C++ API documentation from Doxygen comments.
- Write a comprehensive user guide for ROM hackers, covering all major editor workflows.
---
## Recently Completed (v0.3.3 - October 6, 2025)
- Vim mode for `simple-chat`: modal editing, navigation, history, autocomplete
- Autocomplete engine with fuzzy matching and FTXUI dropdown
- TUI enhancements: integrated autocomplete UI components and CMake wiring
## Recently Completed (v0.3.2)
- Dungeon editor: migrated to `TestRomManager`, resolved crash backlog
- Windows build: fixed stack overflows and file dialog regressions
- `z3ed learn`: added persistent storage for AI preferences and ROM metadata
- Gemini integration: switched to native function calling API
- Tile16 editor: refactored layout, added dynamic zoom controls

View File

@@ -1,104 +1,9 @@
# yaze Documentation # Documentation Relocated
Welcome to the official documentation for yaze, a comprehensive ROM editor for The Legend of Zelda: A Link to the Past. The published documentation now lives under [`docs/public`](public/index.md) so that Doxygen can
focus on the curated guides and references. All planning notes, AI/agent workflows, and research
documents have been moved to the repository-level [`docs/internal/`](../docs/internal/README.md).
## A: Getting Started & Testing Update your bookmarks:
- [A1: Getting Started](A1-getting-started.md) - Basic setup and usage - Public site entry point: [`docs/public/index.md`](public/index.md)
- [A1: Testing Guide](A1-testing-guide.md) - Testing framework and best practices - Internal docs: [`docs/internal/README.md`](../docs/internal/README.md)
- [A2: Test Dashboard Refactoring](A2-test-dashboard-refactoring.md) - In-app test dashboard architecture
## B: Build & Platform
- [B1: Build Instructions](B1-build-instructions.md) - How to build yaze on Windows, macOS, and Linux
- [B2: Platform Compatibility](B2-platform-compatibility.md) - Cross-platform support details
- [B3: Build Presets](B3-build-presets.md) - CMake preset system guide
- [B4: Git Workflow](B4-git-workflow.md) - Branching strategy, commit conventions, and release process
- [B5: Architecture and Networking](B5-architecture-and-networking.md) - System architecture, gRPC, and networking
- [B6: Zelda3 Library Refactoring](B6-zelda3-library-refactoring.md) - Core library modularization plan
## C: `z3ed` CLI
- [C1: `z3ed` Agent Guide](C1-z3ed-agent-guide.md) - AI-powered command-line interface
- [C2: Testing Without ROMs](C2-testing-without-roms.md) - Mock ROM mode for testing and CI/CD
- [C3: Agent Architecture](C3-agent-architecture.md) - AI agent system architecture
- [C4: z3ed Refactoring](C4-z3ed-refactoring.md) - CLI tool refactoring summary
- [C5: z3ed Command Abstraction](C5-z3ed-command-abstraction.md) - Command system design
## E: Development & API
- [E1: Assembly Style Guide](E1-asm-style-guide.md) - 65816 assembly coding standards
- [E2: Development Guide](E2-development-guide.md) - Core architectural patterns, UI systems, and best practices
- [E3: API Reference](E3-api-reference.md) - C/C++ API documentation for extensions
- [E4: Emulator Development Guide](E4-Emulator-Development-Guide.md) - SNES emulator subsystem implementation guide
- [E5: Debugging Guide](E5-debugging-guide.md) - Debugging techniques and workflows
- [E6: Emulator Improvements](E6-emulator-improvements.md) - Core accuracy and performance improvements roadmap
- [E7: Debugging Startup Flags](E7-debugging-startup-flags.md) - CLI flags for quick editor access and testing
- [E8: Emulator Debugging Vision](E8-emulator-debugging-vision.md) - Long-term vision for Mesen2-level debugging features
- [E9: AI Agent Debugging Guide](E9-ai-agent-debugging-guide.md) - Debugging AI agent integration
- [E10: APU Timing Analysis](E10-apu-timing-analysis.md) - APU timing fix technical analysis
## F: Technical Documentation
- [F1: Dungeon Editor Guide](F1-dungeon-editor-guide.md) - Master guide to the dungeon editing system
- [F2: Tile16 Editor Palette System](F2-tile16-editor-palette-system.md) - Design of the palette system
- [F3: Overworld Loading](F3-overworld-loading.md) - How vanilla and ZSCustomOverworld maps are loaded
- [F4: Overworld Agent Guide](F4-overworld-agent-guide.md) - AI agent integration for overworld editing
## G: Graphics & GUI Systems
- [G1: Canvas System and Automation](G1-canvas-guide.md) - Core GUI drawing and interaction system
- [G2: Renderer Migration Plan](G2-renderer-migration-plan.md) - Historical plan for renderer refactoring
- [G3: Palette System Overview](G3-palete-system-overview.md) - SNES palette system and editor integration
- [G3: Renderer Migration Complete](G3-renderer-migration-complete.md) - Post-migration analysis and results
- [G4: Canvas Coordinate Fix](G4-canvas-coordinate-fix.md) - Canvas coordinate synchronization bug fix
- [G5: GUI Consistency Guide](G5-gui-consistency-guide.md) - Card-based architecture and UI standards
## H: Project Info
- [H1: Changelog](H1-changelog.md)
## I: Roadmap & Vision
- [I1: Roadmap](I1-roadmap.md) - Current development roadmap and planned releases
- [I2: Future Improvements](I2-future-improvements.md) - Long-term vision and aspirational features
## R: ROM Reference
- [R1: A Link to the Past ROM Reference](R1-alttp-rom-reference.md) - ALTTP ROM structures, graphics, palettes, and compression
---
## Documentation Standards
### Naming Convention
- **A-series**: Getting Started & Testing
- **B-series**: Build, Platform & Git Workflow
- **C-series**: CLI Tools (`z3ed`)
- **E-series**: Development, API & Emulator
- **F-series**: Feature-Specific Technical Docs
- **G-series**: Graphics & GUI Systems
- **H-series**: Project Info (Changelog, etc.)
- **I-series**: Roadmap & Vision
- **R-series**: ROM Technical Reference
### File Naming
- Use descriptive, kebab-case names
- Prefix with series letter and number (e.g., `E4-emulator-development-guide.md`)
- Keep filenames concise but clear
### Organization Tips
For Doxygen integration, this index can be enhanced with:
- `@mainpage` directive to make this the main documentation page
- `@defgroup` to create logical groupings across files
- `@tableofcontents` for automatic TOC generation
- See individual files for `@page` and `@section` usage
### Doxygen Integration Tips
- Add a short `@mainpage` block to `docs/index.md` so generated HTML mirrors the
manual structure.
- Define high-level groups with `@defgroup` (`getting_started`, `building`,
`graphics_gui`, etc.) and attach individual docs using `@page ... @ingroup`.
- Use `@subpage`, `@section`, and `@subsection` when a document benefits from
nested navigation.
- Configure `Doxyfile` with `USE_MDFILE_AS_MAINPAGE = docs/index.md`,
`FILE_PATTERNS = *.md *.h *.cc`, and `EXTENSION_MAPPING = md=C++` to combine
Markdown and source comments.
- Keep Markdown reader-friendly—wrap Doxygen directives in comment fences (`/**`
`*/`) so they are ignored by GitHub while remaining visible to the
generator.
---
*Last updated: October 13, 2025 - Version 0.3.2*

42
docs/internal/README.md Normal file
View File

@@ -0,0 +1,42 @@
# YAZE Internal Documentation
**Last Updated**: December 8, 2025
Internal documentation for planning, AI agents, research, and historical build notes. These files are intentionally excluded from the public Doxygen site so they can remain verbose and speculative.
## Quick Links
- **Active Work**: [Coordination Board](agents/coordination-board.md)
- **Roadmap**: [roadmap.md](roadmap.md)
- **Doc Hygiene Rules**: [agents/doc-hygiene.md](agents/doc-hygiene.md)
- **Templates**: [templates/](templates/)
## Directory Structure
| Directory | Purpose |
|-----------|---------|
| `agents/` | AI agent coordination, personas, and board |
| `architecture/` | System design and architectural documentation |
| `archive/` | Retired plans, completed features, closed investigations, and maintenance logs |
| `debug/` | Debugging guides, active logs, and accuracy reports |
| `hand-off/` | Active handoff documents for in-progress work |
| `plans/` | Active implementation plans and roadmaps |
| `research/` | Exploratory notes, ideas, and technical analysis |
| `templates/` | Document templates (checklists, initiatives) |
| `testing/` | Test infrastructure configuration and strategy |
| `wasm/` | WASM/Web port documentation and guides |
| `zelda3/` | Game-specific documentation (ALTTP internals) |
| `roadmap.md` | Master project roadmap |
## Doc Hygiene
- **Single Source of Truth**: Maintain one canonical spec per initiative.
- **Templates**: Use `templates/` for new initiatives and release checklists.
- **Archiving**: Move completed specs to `archive/completed_features/` or `archive/investigations/`.
- **Coordination**: Use the [Coordination Board](agents/coordination-board.md) for active tasks.
## Version Control & Safety
- **Coordinate before forceful changes**: Never rewrite history on shared branches.
- **Back up ROMs and assets**: Work on copies, enable automatic backup.
- **Run `scripts/verify-build-environment.*`** after pulling significant build changes.

View File

@@ -0,0 +1,75 @@
# Agent Coordination Hub
Welcome to the `yaze` internal agent workspace. This directory contains the rules, roles, and records for AI agents contributing to the project.
## Quick Start
1. **Identify Your Role**: Check [personas.md](./personas.md) to choose the correct Agent ID.
2. **Load Your Prompt**: Open `.claude/agents/<agent-id>.md` for your personas system prompt (available to all agents).
3. **Follow the Protocol**: Read [AGENTS.md](../../../AGENTS.md) for rules on communication, task logging, and handoffs.
4. **Check Status**: Review the [coordination-board.md](./coordination-board.md) for active tasks and blockers.
5. **Keep docs lean**: Use [doc-hygiene.md](./doc-hygiene.md) and avoid creating duplicate specs; archive idle docs.
## Documentation Index
### Core Coordination (Keep Visible)
| File | Purpose |
|------|---------|
| [AGENTS.md](../../../AGENTS.md) | **The Core Protocol.** Read this first. Defines how to work. |
| [personas.md](./personas.md) | **Who is Who.** Canonical list of Agent IDs and their scopes. |
| [.claude/agents/*](../../../.claude/agents) | **System Prompts.** Persona-specific prompts for all agents. |
| [coordination-board.md](./coordination-board.md) | **The Live Board.** Shared state, active tasks, and requests. |
| [collaboration-framework.md](./collaboration-framework.md) | **Team Organization.** Architecture vs Automation team structure and protocols. |
### Active Projects
| File | Purpose | Status |
|------|---------|--------|
| [initiative-v040.md](./initiative-v040.md) | v0.4.0 development plan with 5 parallel workstreams | ACTIVE |
| [handoff-sidebar-menubar-sessions.md](./handoff-sidebar-menubar-sessions.md) | UI systems architecture reference | Active Reference |
| [wasm-development-guide.md](./wasm-development-guide.md) | WASM build and development guide | UPDATED |
| [wasm-antigravity-playbook.md](./wasm-antigravity-playbook.md) | WASM with Gemini integration and debugging | UPDATED |
| [web-port-handoff.md](./web-port-handoff.md) | WASM web build status and blockers | IN_PROGRESS |
### Documentation Hygiene
| File | Purpose |
|------|---------|
| [doc-hygiene.md](./doc-hygiene.md) | Rules to keep specs/notes lean and archived on time. |
## Tools
Agents have built-in CLI capabilities to assist with this workflow:
* `z3ed agent todo`: Manage your personal task list.
* `z3ed agent handoff`: Generate context bundles for the next agent.
* `z3ed agent chat`: (If enabled) Inter-agent communication channel.
## Picking the right agent
- **ai-infra-architect**: AI/agent infra, MCP/gRPC, z3ed tooling, model plumbing.
- **backend-infra-engineer**: Build/packaging/toolchains, CI reliability, release plumbing.
- **imgui-frontend-engineer**: ImGui/editor UI, renderer/backends, canvas/docking UX.
- **snes-emulator-expert**: Emulator core (CPU/APU/PPU), performance/accuracy/debugging.
- **zelda3-hacking-expert**: Gameplay/ROM logic, data formats, hacking workflows.
- **test-infrastructure-expert**: Test harnesses, CTest/gMock infra, flake/bloat triage.
- **docs-janitor**: Docs/process hygiene, onboarding, checklists.
Pick the persona that owns the dominant surface of your task; cross-surface work should add both owners to the board entry.
## Build & Test
Default builds now share `build/` (native) and `build-wasm/` (WASM). If you need isolation, set `YAZE_BUILD_DIR` or create a local `CMakeUserPresets.json`.
* **Build**: `./scripts/agent_build.sh [target]` (Default target: `yaze`)
* **Test**: `./scripts/agent_build.sh yaze_test && ./build/bin/yaze_test`
* **Directory**: `build/` by default (override via `YAZE_BUILD_DIR`).
## Archive
This directory maintains a **lean, active documentation set**. Historical or reference-only documents are organized in `archive/`:
### Archived Categories
- **large-ref-docs/** (148KB) - Large reference documents (40KB+): dungeon rendering, ZSOW, design specs
- **foundation-docs-old/** (68KB) - Older foundational docs (October) on agent architecture and CLI design
- **utility-tools/** (52KB) - Tool documentation: filesystem, dev-assist, modularity, AI dev tools
- **wasm-planning-2025/** (64KB) - Earlier WASM planning iterations
- **testing-docs-2025/** (112KB) - Test infrastructure and CI documentation archives
- **gemini-session-2025-11-23/** (40KB) - Gemini-specific session context and task planning
- **plans-2025-11/** (56KB) - Completed or archived feature/project plans
- **legacy-2025-11/** (28KB) - Completed initiatives and coordination improvements
- **session-handoffs/** (24KB) - Agent handoff and onboarding documents
- **reports/** (24KB) - Audit reports and documentation assessments
**Target: 10-15 active files in root directory. Archive completed work within 1-2 weeks of completion.**

View File

@@ -0,0 +1,182 @@
# Documentation Cleanup - November 27, 2025
## Summary
Comprehensive review and update of YAZE documentation, focusing on public-facing docs, web app support, and organizational cleanup.
## Changes Made
### 1. Web App Documentation
**Created: `docs/public/usage/web-app.md`**
- Comprehensive guide for the WASM web application
- Clearly marked as **Preview** status (not production-ready)
- Detailed feature status table showing incomplete editors
- Browser requirements and compatibility
- Performance tips and troubleshooting
- Comparison table: Web vs Desktop
- Developer tools and API references
- Deployment instructions
- Privacy and storage information
**Key Points:**
- ⚠️ Emphasized preview/experimental status throughout
- Listed editor completeness accurately (Preview/Incomplete vs Working)
- Recommended desktop build for serious ROM hacking
- Linked to internal technical docs for developers
### 2. Main README Updates
**Updated: `README.md`**
- Added web preview mention in highlights section
- Added "Web App (Preview)" to Applications & Workflows
- Clearly linked to web-app.md guide
- Maintained focus on desktop as primary platform
### 3. Public Docs Index
**Updated: `docs/public/index.md`**
- Added Web App (Preview) to Usage Guides section
- Placed at top for visibility
### 4. Directory Organization
**Moved technical implementation docs to internal:**
- `docs/web/drag-drop-rom-loading.md``docs/internal/web-drag-drop-implementation.md`
- `docs/wasm/patch_export.md``docs/internal/wasm-patch-export-implementation.md`
- Removed empty `docs/web/` and `docs/wasm/` directories
**Organized format documentation:**
Moved to `docs/public/reference/` for better discoverability:
- `SAVE_STATE_FORMAT.md`
- `SNES_COMPRESSION.md`
- `SNES_GRAPHICS.md`
- `SYMBOL_FORMAT.md`
- `ZSM_FORMAT.md`
**Updated: `docs/public/reference/rom-reference.md`**
- Added "Additional Format Documentation" section
- Linked to all format specification docs
- Updated last modified date to November 27, 2025
### 5. Documentation Accuracy
**Updated: `docs/public/build/platform-compatibility.md`**
- Updated "Last Updated" from October 9, 2025 to November 27, 2025
**Reviewed for accuracy:**
-`docs/public/build/quick-reference.md` - Accurate
-`docs/public/build/build-from-source.md` - Accurate
-`docs/public/build/presets.md` - Accurate
-`docs/public/developer/architecture.md` - Accurate (updated Nov 2025)
-`docs/public/developer/testing-quick-start.md` - Accurate
### 6. Coordination Board
**Updated: `docs/internal/agents/coordination-board.md`**
- Added entry for docs-janitor work session
- Marked status as COMPLETE
- Listed all changes made
## File Structure After Cleanup
```
docs/
├── public/
│ ├── build/ [5 docs - build system]
│ ├── deployment/ [1 doc - collaboration server]
│ ├── developer/ [18 docs - developer guides]
│ ├── examples/ [1 doc - code examples]
│ ├── guides/ [1 doc - z3ed workflows]
│ ├── overview/ [1 doc - getting started]
│ ├── reference/ [8 docs - ROM & format specs] ⭐ IMPROVED
│ │ ├── rom-reference.md
│ │ ├── SAVE_STATE_FORMAT.md ⬅️ MOVED HERE
│ │ ├── SNES_COMPRESSION.md ⬅️ MOVED HERE
│ │ ├── SNES_GRAPHICS.md ⬅️ MOVED HERE
│ │ ├── SYMBOL_FORMAT.md ⬅️ MOVED HERE
│ │ └── ZSM_FORMAT.md ⬅️ MOVED HERE
│ ├── usage/ [4 docs including web-app] ⭐ NEW
│ │ ├── web-app.md ⬅️ NEW
│ │ ├── dungeon-editor.md
│ │ ├── overworld-loading.md
│ │ └── z3ed-cli.md
│ ├── index.md
│ └── README.md
├── internal/
│ ├── agents/ [Agent coordination & playbooks]
│ ├── architecture/ [System architecture docs]
│ ├── blueprints/ [Refactoring plans]
│ ├── plans/ [Implementation plans]
│ ├── reports/ [Investigation reports]
│ ├── roadmaps/ [Feature roadmaps]
│ ├── testing/ [Test infrastructure]
│ ├── web-drag-drop-implementation.md ⬅️ MOVED HERE
│ ├── wasm-patch-export-implementation.md ⬅️ MOVED HERE
│ └── [other internal docs]
├── examples/ [Code examples]
├── GIGALEAK_INTEGRATION.md
└── index.md
```
## Removed Directories
-`docs/web/` - consolidated into internal
-`docs/wasm/` - consolidated into internal
## Documentation Principles Applied
1. **Public vs Internal Separation**
- Public: User-facing, stable, external developers
- Internal: AI agents, implementation details, planning
2. **Accuracy & Honesty**
- Web app clearly marked as preview/experimental
- Editor status accurately reflects incomplete state
- Recommended desktop for production work
3. **Organization**
- Format docs in reference section for easy discovery
- Technical implementation in internal for developers
- Clear navigation through index files
4. **Currency**
- Updated "Last Modified" dates
- Removed outdated content
- Consolidated duplicate information
## Impact
### For Users
- ✅ Clear understanding that web app is preview
- ✅ Easy access to format documentation
- ✅ Better organized public docs
- ✅ Honest feature status
### For Developers
- ✅ Technical docs in predictable locations
- ✅ Format specs easy to find in reference/
- ✅ Implementation details separated from user guides
- ✅ Clear documentation hierarchy
### For AI Agents
- ✅ Updated coordination board with session
- ✅ Clear doc hygiene maintained
- ✅ No doc sprawl in root directories
## Follow-up Actions
None required. Documentation is now:
- Organized
- Accurate
- Complete for web app preview
- Properly separated (public vs internal)
- Up to date
## Agent
**Agent ID:** docs-janitor
**Session Date:** November 27, 2025
**Duration:** Single session
**Status:** Complete

View File

@@ -0,0 +1,95 @@
# Draw Routine Fixes - Phase 2
## Status
**Owner:** ai-dungeon-specialist
**Created:** 2025-12-07
**Status:** Partial Complete - Build Verified
## Summary
This document tracks fixes for specific dungeon object draw routines and outline sizing issues identified during testing. These issues complement the Phase 1 diagonal/edge/spacing fixes.
## Issues to Fix
### Issue 1: Block 0x5E Draw Routine Inverted
**Object:** 0x5E (RoomDraw_RightwardsBlock2x2spaced2_1to16)
**Problem:** Draw routine is inverted for the simple block pattern
**ASM Reference:** bank_01.asm line 363
**Fix Required:** Review tile ordering and column-major vs row-major layout
### Issue 2: Vertical Pegs Wrong Outline Shape
**Objects:** 0x95 (DownwardsPots2x2), 0x96 (DownwardsHammerPegs2x2)
**Problem:** Selection outline shows 2x2 square but should be vertical single row (1 tile wide)
**ASM Reference:** RoomDraw_DownwardsPots2x2_1to16, RoomDraw_DownwardsHammerPegs2x2_1to16
**Analysis:** The pots/pegs are 2x2 objects that repeat vertically with 2-row spacing (0x100). However, user reports they should display as 1-tile-wide vertical strips. Need to verify actual drawn dimensions from ASM.
**Fix Required:** Update dimension calculations in CalculateObjectDimensions and ObjectDimensionTable
### Issue 3: Thick Rail Horizontal/Vertical Draw Issues
**Objects:** 0x5D (RightwardsBigRail1x3), 0x88 (DownwardsBigRail3x1)
**Problem:** Repeats far left edge rather than inner parts of the thick rail
**ASM Reference:** RoomDraw_RightwardsBigRail1x3_1to16plus5, RoomDraw_DownwardsBigRail3x1_1to16plus5
**Analysis:** ASM shows:
- First draws a 2x2 block, then advances tile pointer by 8
- Then draws middle section (1x3 tiles per iteration)
- Finally draws end cap (2 tiles)
**Fix Required:** Fix draw routine to draw: left cap → middle repeating → right cap
### Issue 4: Large Decor Outline Too Small, Draw Routine Repeats Incorrectly
**Problem:** Large decoration objects have outlines that are too small and draw routines that repeat when they shouldn't
**Fix Required:** Identify specific objects and verify repetition count logic (size+1 vs size)
### Issue 5: Ceiling 0xC0 Doesn't Draw Properly
**Object:** 0xC0 (RoomDraw_4x4BlocksIn4x4SuperSquare)
**Problem:** Object doesn't draw at all or draws incorrectly
**ASM Reference:** bank_01.asm lines 1779-1831
**Analysis:** Uses complex 4x4 super-square pattern with 8 buffer pointers ($BF through $D4). Draws 4x4 blocks in a grid pattern based on size parameters B2 and B4.
**Fix Required:** Implement proper super-square drawing routine
### Issue 6: 0xF99 Chest Outline Correct But Draw Routine Repeats
**Object:** 0xF99 (RoomDraw_Chest - Type 3 chest)
**Problem:** Outline is correct but draw routine repeats when it shouldn't
**ASM Reference:** RoomDraw_Chest at bank_01.asm line 4707
**Analysis:** Chest should only draw once (single 2x2 pattern), not repeat based on size
**Fix Required:** Remove size-based repetition from chest draw routine
### Issue 7: Pit Edges Outlines Too Thin
**Problem:** Pit edge outlines shouldn't be single tile thin based on direction
**Objects:** Various pit edge objects
**Fix Required:** Update pit edge dimension calculations to include proper width based on direction
### Issue 8: 0x3D Tall Torches Wrong Top Half Graphics
**Object:** 0x3D (mapped to RoomDraw_RightwardsPillar2x4spaced4_1to16)
**Problem:** Bottom half draws correctly but top half with fire draws pegs instead
**ASM Reference:** bank_01.asm line 330 - uses RoomDraw_RightwardsPillar2x4spaced4_1to16 (same as 0x39)
**Analysis:** Object 0x3D and 0x39 share the same draw routine but use different tile data. Need to verify tile data offset is correct.
**Fix Required:** Verify tile data loading for 0x3D or create dedicated draw routine
## Implementation Status
### Completed Fixes
1. **Block 0x5E** ✅ - Fixed tile ordering in DrawRightwardsBlock2x2spaced2_1to16 to use column-major order
2. **Thick Rails 0x5D/0x88** ✅ - Rewrote DrawRightwardsBigRail and DrawDownwardsBigRail to correctly draw cap-middle-cap pattern
3. **Chest 0xF99** ✅ - Fixed chest to draw single 2x2 pattern without size-based repetition
4. **Large Decor 0xFEB** ✅ - Created Single4x4 routine (routine 113) - no repetition, correct 32x32 outline
5. **Water Grate 0xFED** ✅ - Created Single4x3 routine (routine 114) - no repetition, correct 32x24 outline
6. **Big Chest 0xFB1** ✅ - Mapped to Single4x3 routine (routine 114) - no repetition
7. **Blue Rupees 0xF92** ✅ - Created DrawRupeeFloor routine (routine 115) - special 6x8 pattern with gaps
### Pending Investigation
8. **Rails 0x022** - Uses RoomDraw_RightwardsHasEdge1x1_1to16_plus3 - needs internal rail drawing
9. **Ceiling 0xC0** - Draw4x4BlocksIn4x4SuperSquare may have tile loading issue
10. **Vertical Pegs 0x95/0x96** - Current dimensions look correct. May be UI display issue.
11. **Pit Edges** - Need specific object IDs to investigate
12. **Torches 0x3D** - May be ROM tile data issue (verify data at 0x807A)
## New Routines Added
- Routine 113: DrawSingle4x4 - Single 4x4 block, no repetition
- Routine 114: DrawSingle4x3 - Single 4x3 block, no repetition
- Routine 115: DrawRupeeFloor - Special 6x8 pattern with gaps at rows 2 and 5
## Files Modified
- `src/zelda3/dungeon/object_drawer.cc`
- `src/zelda3/dungeon/object_drawer.h`
- `docs/internal/agents/draw_routine_fixes_phase2.md`

View File

@@ -0,0 +1,125 @@
# Draw Routine Fixes - Handoff Document
**Status:** Handoff
**Owner:** draw-routine-engineer
**Created:** 2025-12-07
**Last Session:** 2025-12-07
---
## Summary
This session made significant progress on dungeon object draw routines. Many fixes were completed, but some objects still have minor issues requiring further investigation.
---
## Completed Fixes ✅
| Object ID | Name | Fix Applied |
|-----------|------|-------------|
| 0x5E | Block | Fixed column-major tile ordering |
| 0x5D/0x88 | Thick Rails | Fixed cap-middle-cap pattern |
| 0xF99 | Chest | Changed to DrawSingle2x2 (no size-based repeat) |
| 0xFB1 | Big Chest | Changed to DrawSingle4x3 (no repeat) |
| 0xF92 | Blue Rupees | Implemented DrawRupeeFloor per ASM (3x8 pattern) |
| 0xFED | Water Grate | Changed to DrawSingle4x3 (no repeat) |
| 0x3A | Wall Decors | Fixed spacing from 6 to 8 tiles |
| 0x39/0x3D | Pillars | Fixed spacing from 6 to 4 tiles |
| 0xFEB | Large Decor | Now 64x64 (4x4 tile16s = 8x8 tile8s) |
| 0x138-0x13B | Spiral Stairs | Fixed to 4x3 pattern per ASM |
| 0xC0/0xC2 | SuperSquare Ceilings | Dimensions now use size parameter |
| 0xFE6 | Pit | Uses DrawActual4x4 (32x32, no repeat) |
| 0x55-0x56 | Wall Torches | Fixed to 1x8 column with 12-tile spacing |
| 0x22 | Small Rails | Now CORNER+MIDDLE*count+END pattern |
| 0x23-0x2E | Carpet Trim | Now CORNER+MIDDLE*count+END pattern |
---
## Known Issues - Need Further Work ⚠️
### 1. Horizontal vs Vertical Rails Asymmetry
**Objects:** 0x22 (horizontal) vs vertical counterparts
**Issue:** Horizontal rails now work with CORNER+MIDDLE+END pattern, but vertical rails may not be updated to match.
**Files to check:**
- `DrawDownwardsHasEdge1x1_*` routines
- Look for routines mapped to 0x8A-0x8E (vertical equivalents)
### 2. Diagonal Ceiling Outlines (0xA0-0xA3)
**Issue:** Draw routine is correct (triangular fill), but outline dimensions still too large for selection purposes.
**Current state:**
- Draw: `count = (size & 0x0F) + 4`
- Outline: `count = (size & 0x0F) + 2` (still too big)
**Suggestion:** May need special handling in selection code rather than dimension calculation, since triangles only fill half the bounding box.
### 3. Torch Object 0x3D
**Issue:** Top half draws pegs instead of fire graphics.
**Likely cause:** ROM tile data issue - tiles at offset 0x807A may be incorrect or tile loading is wrong.
**Uses same routine as pillars (0x39).**
### 4. Vertical Pegs 0x95/0x96
**Issue:** Outline appears square (2x2) but should be vertical single row.
**May be UI/selection display issue rather than draw routine.**
---
## Pending Tasks 📋
### High Priority
1. **0xDC Chest Platform** - Complex routine with multiple segments, not currently working
2. **Staircases audit** - Objects 0x12D-0x137, 0x21B-0x221, 0x226-0x229, 0x233
### Medium Priority
3. **Vertical rail patterns** - Match horizontal rail fixes
4. **Pit edges** - Need specific object IDs to investigate
### Low Priority
5. **Diagonal ceiling selection** - May need UI-level fix
---
## Key Formulas Reference
### Size Calculations (from ASM)
| Pattern | Formula |
|---------|---------|
| GetSize_1to16 | `count = (size & 0x0F) + 1` |
| GetSize_1to16_timesA | `count = (size & 0x0F + 1) * A` |
| GetSize_1to15or26 | `count = size; if 0, count = 26` |
| GetSize_1to15or32 | `count = size; if 0, count = 32` |
| SuperSquare | `size_x = (size & 0x0F) + 1; size_y = ((size >> 4) & 0x0F) + 1` |
### Rail Pattern Structure
```
[CORNER tile 0] -> [MIDDLE tile 1 × count] -> [END tile 2]
```
### Tile Ordering
Most routines use **COLUMN-MAJOR** order: tiles advance down each column, then right.
---
## Files Modified
- `src/zelda3/dungeon/object_drawer.cc` - Main draw routines and mappings
- `src/zelda3/dungeon/object_drawer.h` - Added DrawActual4x4 declaration
- `src/zelda3/dungeon/draw_routines/special_routines.cc` - Spiral stairs fix
- `docs/internal/agents/draw_routine_tracker.md` - Tracking document
---
## Testing Notes
- Log file at `logs/dungeon-object-draw.log` shows object draw data
- Most testing was done on limited room set - need broader room testing
- Use grep on log file to find specific object draws: `grep "obj=0xXX" logs/dungeon-object-draw.log`
---
## Next Steps for Continuation
1. Test the rail fixes in rooms with both horizontal and vertical rails
2. Find and fix the vertical rail equivalents (likely routines 23-28 or similar)
3. Investigate 0xDC chest platform ASM in detail
4. Consider UI-level fix for diagonal ceiling selection bounds

Some files were not shown because too many files have changed in this diff Show More