Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4944066fa7 | ||
|
|
cfed3c1c5a | ||
|
|
4a517a384c | ||
|
|
675d7bb4f9 | ||
|
|
1b167061a1 | ||
|
|
d4b2ce50a5 | ||
|
|
9b3597e981 | ||
|
|
43d622ee08 | ||
|
|
607f6aa1e8 | ||
|
|
f6a92669da | ||
|
|
d9a6de9c43 | ||
|
|
b45d2ba97a | ||
|
|
b354e6f3d7 | ||
|
|
b0906baa6d | ||
|
|
63a36ea295 | ||
|
|
26b35642b4 | ||
|
|
8b45b16431 | ||
|
|
583ec35475 | ||
|
|
c3fa3f6074 | ||
|
|
eed51b8d17 | ||
|
|
7f28b377dd | ||
|
|
51aac88868 | ||
|
|
79bfcf8306 | ||
|
|
65c4fe13cd | ||
|
|
636229c097 | ||
|
|
c9df4372d7 | ||
|
|
954c612f69 | ||
|
|
a65fc1d975 | ||
|
|
14a3084c7f | ||
|
|
8d011bf094 | ||
|
|
403590585d | ||
|
|
b78e167105 | ||
|
|
d002f5556d | ||
|
|
a256b589ac | ||
|
|
07291a0eac | ||
|
|
1d297d5228 | ||
|
|
326594f3e6 | ||
|
|
d5e06e943f | ||
|
|
26ce12cd6f | ||
|
|
c8ec329f14 | ||
|
|
2fd740e529 | ||
|
|
572bfe5df7 | ||
|
|
2c72709003 | ||
|
|
face88a940 | ||
|
|
76512daba2 | ||
|
|
319e903f24 | ||
|
|
42ae359abc | ||
|
|
df866b3f7f | ||
|
|
7b72b2e3d4 | ||
|
|
3c9c3455af | ||
|
|
e733bc78d6 | ||
|
|
ffc3ddd854 | ||
|
|
ea4e1873de | ||
|
|
9333498538 | ||
|
|
344ef39d66 | ||
|
|
62734f3727 | ||
|
|
ca71140a88 | ||
|
|
e674fc3214 | ||
|
|
5c4cd57ff8 | ||
|
|
2934c82b75 | ||
|
|
c8289bffda |
3
.github/actions/run-tests/action.yml
vendored
3
.github/actions/run-tests/action.yml
vendored
@@ -28,6 +28,7 @@ runs:
|
|||||||
cd build
|
cd build
|
||||||
ctest --preset stable${CTEST_SUFFIX} \
|
ctest --preset stable${CTEST_SUFFIX} \
|
||||||
--output-on-failure \
|
--output-on-failure \
|
||||||
|
--timeout 300 \
|
||||||
--output-junit stable_test_results.xml || true
|
--output-junit stable_test_results.xml || true
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
@@ -37,6 +38,7 @@ runs:
|
|||||||
cd build
|
cd build
|
||||||
ctest --preset unit${CTEST_SUFFIX} \
|
ctest --preset unit${CTEST_SUFFIX} \
|
||||||
--output-on-failure \
|
--output-on-failure \
|
||||||
|
--timeout 300 \
|
||||||
--output-junit unit_test_results.xml || true
|
--output-junit unit_test_results.xml || true
|
||||||
|
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
@@ -46,6 +48,7 @@ runs:
|
|||||||
cd build
|
cd build
|
||||||
ctest --preset integration${CTEST_SUFFIX} \
|
ctest --preset integration${CTEST_SUFFIX} \
|
||||||
--output-on-failure \
|
--output-on-failure \
|
||||||
|
--timeout 300 \
|
||||||
--output-junit integration_test_results.xml || true
|
--output-junit integration_test_results.xml || true
|
||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
|
|||||||
6
.github/actions/setup-build/action.yml
vendored
6
.github/actions/setup-build/action.yml
vendored
@@ -66,6 +66,12 @@ runs:
|
|||||||
max-size: 500M
|
max-size: 500M
|
||||||
variant: sccache
|
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
|
- name: Configure compiler for Windows
|
||||||
if: inputs.platform == 'windows'
|
if: inputs.platform == 'windows'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|||||||
68
.github/workflows/ci.yml
vendored
68
.github/workflows/ci.yml
vendored
@@ -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" ]
|
||||||
@@ -79,7 +93,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
platform: ${{ matrix.platform }}
|
||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
cache-key: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
uses: ./.github/actions/build-project
|
uses: ./.github/actions/build-project
|
||||||
@@ -132,7 +146,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
platform: ${{ matrix.platform }}
|
||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
cache-key: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
uses: ./.github/actions/build-project
|
uses: ./.github/actions/build-project
|
||||||
@@ -141,17 +155,19 @@ jobs:
|
|||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
build-type: ${{ env.BUILD_TYPE }}
|
build-type: ${{ env.BUILD_TYPE }}
|
||||||
|
|
||||||
- name: Run stable tests
|
- name: Run stable tests only
|
||||||
uses: ./.github/actions/run-tests
|
uses: ./.github/actions/run-tests
|
||||||
with:
|
with:
|
||||||
test-type: stable
|
test-type: stable
|
||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run smoke tests (GUI framework validation)
|
||||||
uses: ./.github/actions/run-tests
|
if: matrix.platform == 'linux'
|
||||||
with:
|
run: |
|
||||||
test-type: unit
|
cd build
|
||||||
preset: ${{ matrix.preset }}
|
# Run just the smoke tests to validate GUI framework is working
|
||||||
|
./bin/yaze_test_gui -nogui --gtest_filter="*Smoke*" || true
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Run HTTP API tests
|
- name: Run HTTP API tests
|
||||||
if: github.event.inputs.enable_http_api_tests == 'true'
|
if: github.event.inputs.enable_http_api_tests == 'true'
|
||||||
@@ -174,7 +190,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
platform: windows
|
platform: windows
|
||||||
preset: ci-windows-ai
|
preset: ci-windows-ai
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
cache-key: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
uses: ./.github/actions/build-project
|
uses: ./.github/actions/build-project
|
||||||
@@ -183,17 +199,39 @@ jobs:
|
|||||||
preset: ci-windows-ai
|
preset: ci-windows-ai
|
||||||
build-type: ${{ env.BUILD_TYPE }}
|
build-type: ${{ env.BUILD_TYPE }}
|
||||||
|
|
||||||
- name: Run stable tests (agent stack)
|
- name: Run stable tests only (agent stack)
|
||||||
uses: ./.github/actions/run-tests
|
uses: ./.github/actions/run-tests
|
||||||
with:
|
with:
|
||||||
test-type: stable
|
test-type: stable
|
||||||
preset: ci-windows-ai
|
preset: ci-windows-ai
|
||||||
|
|
||||||
- name: Run unit tests (agent stack)
|
wasm-smoke-test:
|
||||||
uses: ./.github/actions/run-tests
|
name: "WASM Build Smoke Test"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
test-type: unit
|
submodules: recursive
|
||||||
preset: ci-windows-ai
|
|
||||||
|
- 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: Quick WASM build test
|
||||||
|
run: |
|
||||||
|
emcmake cmake --preset wasm-debug
|
||||||
|
cmake --build build-wasm-debug --target yaze --parallel
|
||||||
|
# Verify output exists
|
||||||
|
test -f build-wasm-debug/bin/yaze.wasm
|
||||||
|
echo "WASM build successful!"
|
||||||
|
|
||||||
code-quality:
|
code-quality:
|
||||||
name: "Code Quality"
|
name: "Code Quality"
|
||||||
@@ -261,6 +299,8 @@ jobs:
|
|||||||
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
|
||||||
|
|||||||
61
.github/workflows/code-quality.yml
vendored
61
.github/workflows/code-quality.yml
vendored
@@ -1,61 +0,0 @@
|
|||||||
name: Code Quality
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master", "develop" ]
|
|
||||||
paths:
|
|
||||||
- 'src/**'
|
|
||||||
- 'test/**'
|
|
||||||
- 'cmake/**'
|
|
||||||
- 'CMakeLists.txt'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
format-lint:
|
|
||||||
name: "Format & Lint"
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install tooling
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y clang-format-14 clang-tidy-14 cppcheck
|
|
||||||
|
|
||||||
- name: Check Formatting
|
|
||||||
run: |
|
|
||||||
find src test -name "*.cc" -o -name "*.h" | xargs clang-format-14 --dry-run --Werror
|
|
||||||
|
|
||||||
- name: Run cppcheck
|
|
||||||
run: |
|
|
||||||
cppcheck --enable=warning,style,performance --error-exitcode=0 \
|
|
||||||
--suppress=missingIncludeSystem --suppress=unusedFunction --inconclusive src/
|
|
||||||
|
|
||||||
- name: Run clang-tidy
|
|
||||||
run: |
|
|
||||||
find src -name "*.cc" -not -path "*/lib/*" | head -20 | \
|
|
||||||
xargs clang-tidy-14 --header-filter='src/.*\.(h|hpp)$'
|
|
||||||
|
|
||||||
build-check:
|
|
||||||
name: "Build Check"
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- 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') }}
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
uses: ./.github/actions/build-project
|
|
||||||
with:
|
|
||||||
platform: linux
|
|
||||||
preset: ci
|
|
||||||
build-type: RelWithDebInfo
|
|
||||||
68
.github/workflows/doxy.yml
vendored
68
.github/workflows/doxy.yml
vendored
@@ -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 build/docs
|
|
||||||
|
|
||||||
- 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: ./build/docs/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
|
||||||
|
|||||||
334
.github/workflows/matrix-test.yml
vendored
334
.github/workflows/matrix-test.yml
vendored
@@ -1,334 +0,0 @@
|
|||||||
name: Configuration Matrix Testing
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Run nightly at 2 AM UTC
|
|
||||||
- cron: '0 2 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tier:
|
|
||||||
description: 'Test tier to run'
|
|
||||||
required: false
|
|
||||||
default: 'tier2'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- all
|
|
||||||
- tier1
|
|
||||||
- tier2
|
|
||||||
- tier2-linux
|
|
||||||
- tier2-macos
|
|
||||||
- tier2-windows
|
|
||||||
verbose:
|
|
||||||
description: 'Verbose output'
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
env:
|
|
||||||
BUILD_TYPE: RelWithDebInfo
|
|
||||||
CMAKE_BUILD_PARALLEL_LEVEL: 4
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
matrix-linux:
|
|
||||||
name: "Config Matrix - Linux - ${{ matrix.name }}"
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
if: |
|
|
||||||
github.event_name == 'schedule' ||
|
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
contains(github.event.head_commit.message, '[matrix]')
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Tier 2: Feature Combination Tests
|
|
||||||
- name: "Minimal (no AI, no gRPC)"
|
|
||||||
config: minimal
|
|
||||||
preset: minimal
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=OFF -DYAZE_ENABLE_AI=OFF -DYAZE_ENABLE_JSON=ON"
|
|
||||||
|
|
||||||
- name: "gRPC Only"
|
|
||||||
config: grpc-only
|
|
||||||
preset: ci-linux
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_REMOTE_AUTOMATION=OFF -DYAZE_ENABLE_AI_RUNTIME=OFF"
|
|
||||||
|
|
||||||
- name: "Full AI Stack"
|
|
||||||
config: full-ai
|
|
||||||
preset: ci-linux
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_REMOTE_AUTOMATION=ON -DYAZE_ENABLE_AI_RUNTIME=ON -DYAZE_ENABLE_JSON=ON"
|
|
||||||
|
|
||||||
- name: "CLI Only (no gRPC)"
|
|
||||||
config: cli-only-no-grpc
|
|
||||||
preset: minimal
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=OFF -DYAZE_BUILD_GUI=OFF -DYAZE_BUILD_EMU=OFF"
|
|
||||||
|
|
||||||
- name: "HTTP API (gRPC + JSON)"
|
|
||||||
config: http-api
|
|
||||||
preset: ci-linux
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_HTTP_API=ON -DYAZE_ENABLE_JSON=ON"
|
|
||||||
|
|
||||||
- name: "No JSON (Ollama only)"
|
|
||||||
config: no-json
|
|
||||||
preset: ci-linux
|
|
||||||
cflags: "-DYAZE_ENABLE_JSON=OFF -DYAZE_ENABLE_GRPC=ON"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Setup build environment
|
|
||||||
uses: ./.github/actions/setup-build
|
|
||||||
with:
|
|
||||||
platform: linux
|
|
||||||
preset: ${{ matrix.preset }}
|
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
|
||||||
|
|
||||||
- name: Configure CMake
|
|
||||||
run: |
|
|
||||||
cmake --preset ${{ matrix.preset }} \
|
|
||||||
-B build_matrix_${{ matrix.config }} \
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
|
||||||
${{ matrix.cflags }}
|
|
||||||
|
|
||||||
- name: Verify configuration
|
|
||||||
run: |
|
|
||||||
# Print resolved configuration
|
|
||||||
echo "=== Configuration Summary ==="
|
|
||||||
grep "YAZE_BUILD\|YAZE_ENABLE" build_matrix_${{ matrix.config }}/CMakeCache.txt | sort || true
|
|
||||||
echo "=============================="
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
run: |
|
|
||||||
cmake --build build_matrix_${{ matrix.config }} \
|
|
||||||
--config ${{ env.BUILD_TYPE }} \
|
|
||||||
--parallel 4
|
|
||||||
|
|
||||||
- name: Run unit tests (if built)
|
|
||||||
if: ${{ hashFiles(format('build_matrix_{0}/bin/yaze_test', matrix.config)) != '' }}
|
|
||||||
run: |
|
|
||||||
./build_matrix_${{ matrix.config }}/bin/yaze_test --unit 2>&1 | head -100
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Run stable tests
|
|
||||||
if: ${{ hashFiles(format('build_matrix_{0}/bin/yaze_test', matrix.config)) != '' }}
|
|
||||||
run: |
|
|
||||||
./build_matrix_${{ matrix.config }}/bin/yaze_test --stable 2>&1 | head -100
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Report results
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ -f build_matrix_${{ matrix.config }}/CMakeCache.txt ]; then
|
|
||||||
echo "✓ Configuration: ${{ matrix.name }}"
|
|
||||||
echo "✓ Build: SUCCESS"
|
|
||||||
else
|
|
||||||
echo "✗ Configuration: ${{ matrix.name }}"
|
|
||||||
echo "✗ Build: FAILED"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
matrix-macos:
|
|
||||||
name: "Config Matrix - macOS - ${{ matrix.name }}"
|
|
||||||
runs-on: macos-14
|
|
||||||
if: |
|
|
||||||
github.event_name == 'schedule' ||
|
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
contains(github.event.head_commit.message, '[matrix]')
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Tier 2: macOS-specific tests
|
|
||||||
- name: "Minimal (GUI only, no AI)"
|
|
||||||
config: minimal
|
|
||||||
preset: mac-dbg
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=OFF -DYAZE_ENABLE_AI=OFF"
|
|
||||||
|
|
||||||
- name: "Full Stack (gRPC + AI)"
|
|
||||||
config: full-ai
|
|
||||||
preset: mac-ai
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_AI_RUNTIME=ON"
|
|
||||||
|
|
||||||
- name: "Agent UI Only"
|
|
||||||
config: agent-ui
|
|
||||||
preset: mac-dbg
|
|
||||||
cflags: "-DYAZE_BUILD_AGENT_UI=ON -DYAZE_ENABLE_GRPC=OFF -DYAZE_ENABLE_AI=OFF"
|
|
||||||
|
|
||||||
- name: "Universal Binary (Intel + ARM)"
|
|
||||||
config: universal
|
|
||||||
preset: mac-uni
|
|
||||||
cflags: "-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' -DYAZE_ENABLE_GRPC=OFF"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Setup build environment
|
|
||||||
uses: ./.github/actions/setup-build
|
|
||||||
with:
|
|
||||||
platform: macos
|
|
||||||
preset: ${{ matrix.preset }}
|
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
|
||||||
|
|
||||||
- name: Configure CMake
|
|
||||||
run: |
|
|
||||||
cmake --preset ${{ matrix.preset }} \
|
|
||||||
-B build_matrix_${{ matrix.config }} \
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
|
||||||
${{ matrix.cflags }}
|
|
||||||
|
|
||||||
- name: Verify configuration
|
|
||||||
run: |
|
|
||||||
echo "=== Configuration Summary ==="
|
|
||||||
grep "YAZE_BUILD\|YAZE_ENABLE" build_matrix_${{ matrix.config }}/CMakeCache.txt | sort || true
|
|
||||||
echo "=============================="
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
run: |
|
|
||||||
cmake --build build_matrix_${{ matrix.config }} \
|
|
||||||
--config ${{ env.BUILD_TYPE }} \
|
|
||||||
--parallel 4
|
|
||||||
|
|
||||||
- name: Run unit tests
|
|
||||||
if: ${{ hashFiles(format('build_matrix_{0}/bin/yaze_test', matrix.config)) != '' }}
|
|
||||||
run: |
|
|
||||||
./build_matrix_${{ matrix.config }}/bin/yaze_test --unit 2>&1 | head -100
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Report results
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ -f build_matrix_${{ matrix.config }}/CMakeCache.txt ]; then
|
|
||||||
echo "✓ Configuration: ${{ matrix.name }}"
|
|
||||||
echo "✓ Build: SUCCESS"
|
|
||||||
else
|
|
||||||
echo "✗ Configuration: ${{ matrix.name }}"
|
|
||||||
echo "✗ Build: FAILED"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
matrix-windows:
|
|
||||||
name: "Config Matrix - Windows - ${{ matrix.name }}"
|
|
||||||
runs-on: windows-2022
|
|
||||||
if: |
|
|
||||||
github.event_name == 'schedule' ||
|
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
contains(github.event.head_commit.message, '[matrix]')
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Tier 2: Windows-specific tests
|
|
||||||
- name: "Minimal (no AI, no gRPC)"
|
|
||||||
config: minimal
|
|
||||||
preset: win-dbg
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=OFF -DYAZE_ENABLE_AI=OFF"
|
|
||||||
|
|
||||||
- name: "Full AI Stack"
|
|
||||||
config: full-ai
|
|
||||||
preset: win-ai
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_AI_RUNTIME=ON"
|
|
||||||
|
|
||||||
- name: "gRPC + Remote Automation"
|
|
||||||
config: grpc-remote
|
|
||||||
preset: ci-windows
|
|
||||||
cflags: "-DYAZE_ENABLE_GRPC=ON -DYAZE_ENABLE_REMOTE_AUTOMATION=ON"
|
|
||||||
|
|
||||||
- name: "z3ed CLI Only"
|
|
||||||
config: z3ed-cli
|
|
||||||
preset: win-z3ed
|
|
||||||
cflags: "-DYAZE_BUILD_Z3ED=ON -DYAZE_BUILD_GUI=OFF -DYAZE_ENABLE_GRPC=ON"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Setup build environment
|
|
||||||
uses: ./.github/actions/setup-build
|
|
||||||
with:
|
|
||||||
platform: windows
|
|
||||||
preset: ${{ matrix.preset }}
|
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
|
||||||
|
|
||||||
- name: Configure CMake
|
|
||||||
run: |
|
|
||||||
cmake --preset ${{ matrix.preset }} `
|
|
||||||
-B build_matrix_${{ matrix.config }} `
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
|
||||||
${{ matrix.cflags }}
|
|
||||||
|
|
||||||
- name: Verify configuration
|
|
||||||
run: |
|
|
||||||
Write-Output "=== Configuration Summary ==="
|
|
||||||
Select-String "YAZE_BUILD|YAZE_ENABLE" build_matrix_${{ matrix.config }}/CMakeCache.txt | Sort-Object | Write-Output
|
|
||||||
Write-Output "==============================="
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
run: |
|
|
||||||
cmake --build build_matrix_${{ matrix.config }} `
|
|
||||||
--config ${{ env.BUILD_TYPE }} `
|
|
||||||
--parallel 4
|
|
||||||
|
|
||||||
- name: Run unit tests
|
|
||||||
if: ${{ hashFiles(format('build_matrix_{0}\bin\{1}\yaze_test.exe', matrix.config, env.BUILD_TYPE)) != '' }}
|
|
||||||
run: |
|
|
||||||
.\build_matrix_${{ matrix.config }}\bin\${{ env.BUILD_TYPE }}\yaze_test.exe --unit 2>&1 | Select-Object -First 100
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Report results
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if (Test-Path build_matrix_${{ matrix.config }}/CMakeCache.txt) {
|
|
||||||
Write-Output "✓ Configuration: ${{ matrix.name }}"
|
|
||||||
Write-Output "✓ Build: SUCCESS"
|
|
||||||
} else {
|
|
||||||
Write-Output "✗ Configuration: ${{ matrix.name }}"
|
|
||||||
Write-Output "✗ Build: FAILED"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Aggregation job that depends on all matrix jobs
|
|
||||||
matrix-summary:
|
|
||||||
name: Matrix Test Summary
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: always()
|
|
||||||
needs: [matrix-linux, matrix-macos, matrix-windows]
|
|
||||||
steps:
|
|
||||||
- name: Check all matrix tests
|
|
||||||
run: |
|
|
||||||
echo "=== Matrix Test Results ==="
|
|
||||||
if [ "${{ needs.matrix-linux.result }}" == "failure" ]; then
|
|
||||||
echo "✗ Linux tests FAILED"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "✓ Linux tests passed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${{ needs.matrix-macos.result }}" == "failure" ]; then
|
|
||||||
echo "✗ macOS tests FAILED"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "✓ macOS tests passed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${{ needs.matrix-windows.result }}" == "failure" ]; then
|
|
||||||
echo "✗ Windows tests FAILED"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "✓ Windows tests passed"
|
|
||||||
fi
|
|
||||||
echo "==============================="
|
|
||||||
|
|
||||||
- name: Report success
|
|
||||||
if: success()
|
|
||||||
run: echo "✓ All matrix tests passed!"
|
|
||||||
|
|
||||||
- name: Post to coordination board (if configured)
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "Matrix testing complete. Update coordination-board.md if needed."
|
|
||||||
352
.github/workflows/nightly.yml
vendored
Normal file
352
.github/workflows/nightly.yml
vendored
Normal 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
|
||||||
85
.github/workflows/release.yml
vendored
85
.github/workflows/release.yml
vendored
@@ -54,12 +54,33 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- name: Setup build environment
|
||||||
uses: ./.github/actions/setup-build
|
uses: ./.github/actions/setup-build
|
||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
platform: ${{ matrix.platform }}
|
||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
cache-key: ${{ steps.deps-hash.outputs.hash }}
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
uses: ./.github/actions/build-project
|
uses: ./.github/actions/build-project
|
||||||
@@ -87,25 +108,33 @@ jobs:
|
|||||||
print("Patched cmake_install.cmake to handle missing dependency install scripts")
|
print("Patched cmake_install.cmake to handle missing dependency install scripts")
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
- 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)
|
- name: Package artifacts (Linux)
|
||||||
if: matrix.platform == 'linux'
|
if: matrix.platform == 'linux'
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
cpack -G DEB -G TGZ
|
cpack -G DEB -G TGZ
|
||||||
echo "=== Contents of build directory ==="
|
echo "=== Contents of packages directory ==="
|
||||||
ls -la
|
ls -la packages/ 2>/dev/null || echo "No packages directory"
|
||||||
echo "=== Package files created ==="
|
echo "=== Package files created ==="
|
||||||
ls -la *.deb *.tar.gz 2>/dev/null || echo "No packages found in build/"
|
ls -la packages/*.deb packages/*.tar.gz 2>/dev/null || echo "No packages found"
|
||||||
|
|
||||||
- name: Package artifacts (macOS)
|
- name: Package artifacts (macOS)
|
||||||
if: matrix.platform == 'macos'
|
if: matrix.platform == 'macos'
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
cpack -G DragNDrop
|
cpack -G DragNDrop
|
||||||
echo "=== Contents of build directory ==="
|
echo "=== Contents of packages directory ==="
|
||||||
ls -la
|
ls -la packages/ 2>/dev/null || echo "No packages directory"
|
||||||
echo "=== Package files created ==="
|
echo "=== Package files created ==="
|
||||||
ls -la *.dmg 2>/dev/null || echo "No packages found in build/"
|
ls -la packages/*.dmg 2>/dev/null || echo "No packages found"
|
||||||
|
|
||||||
- name: Create notarized bundle (macOS)
|
- name: Create notarized bundle (macOS)
|
||||||
if: matrix.platform == 'macos'
|
if: matrix.platform == 'macos'
|
||||||
@@ -114,7 +143,8 @@ jobs:
|
|||||||
chmod +x ./scripts/create-macos-bundle.sh
|
chmod +x ./scripts/create-macos-bundle.sh
|
||||||
./scripts/create-macos-bundle.sh ${{ env.VERSION }} yaze-${{ env.VERSION }}-bundle || true
|
./scripts/create-macos-bundle.sh ${{ env.VERSION }} yaze-${{ env.VERSION }}-bundle || true
|
||||||
if [ -f "yaze-${{ env.VERSION }}-bundle.dmg" ]; then
|
if [ -f "yaze-${{ env.VERSION }}-bundle.dmg" ]; then
|
||||||
mv yaze-${{ env.VERSION }}-bundle.dmg build/
|
mkdir -p build/packages
|
||||||
|
mv yaze-${{ env.VERSION }}-bundle.dmg build/packages/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Patch cmake_install.cmake (Windows)
|
- name: Patch cmake_install.cmake (Windows)
|
||||||
@@ -135,21 +165,21 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
cpack -G NSIS -G ZIP
|
cpack -G NSIS -G ZIP
|
||||||
Write-Host "=== Contents of build directory ==="
|
Write-Host "=== Contents of packages directory ==="
|
||||||
Get-ChildItem
|
Get-ChildItem packages -ErrorAction SilentlyContinue
|
||||||
Write-Host "=== Package files created ==="
|
Write-Host "=== Package files created ==="
|
||||||
Get-ChildItem *.exe, *.zip -ErrorAction SilentlyContinue
|
Get-ChildItem packages/*.exe, packages/*.zip -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: yaze-${{ matrix.platform }}-${{ env.VERSION }}
|
name: yaze-${{ matrix.platform }}-${{ env.VERSION }}
|
||||||
path: |
|
path: |
|
||||||
build/*.deb
|
build/packages/*.deb
|
||||||
build/*.tar.gz
|
build/packages/*.tar.gz
|
||||||
build/*.dmg
|
build/packages/*.dmg
|
||||||
build/*.exe
|
build/packages/*.exe
|
||||||
build/*.zip
|
build/packages/*.zip
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
@@ -180,12 +210,33 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- name: Setup build environment
|
||||||
uses: ./.github/actions/setup-build
|
uses: ./.github/actions/setup-build
|
||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
platform: ${{ matrix.platform }}
|
||||||
preset: ${{ matrix.preset }}
|
preset: ${{ matrix.preset }}
|
||||||
cache-key: ${{ hashFiles('cmake/dependencies.lock') }}
|
cache-key: ${{ steps.deps-hash.outputs.hash }}
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
uses: ./.github/actions/build-project
|
uses: ./.github/actions/build-project
|
||||||
|
|||||||
157
.github/workflows/symbol-detection.yml
vendored
157
.github/workflows/symbol-detection.yml
vendored
@@ -1,157 +0,0 @@
|
|||||||
name: Symbol Conflict Detection
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master", "develop" ]
|
|
||||||
paths:
|
|
||||||
- 'src/**/*.cc'
|
|
||||||
- 'src/**/*.h'
|
|
||||||
- 'test/**/*.cc'
|
|
||||||
- 'test/**/*.h'
|
|
||||||
- '.github/workflows/symbol-detection.yml'
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master", "develop" ]
|
|
||||||
paths:
|
|
||||||
- 'src/**/*.cc'
|
|
||||||
- 'src/**/*.h'
|
|
||||||
- 'test/**/*.cc'
|
|
||||||
- 'test/**/*.h'
|
|
||||||
- '.github/workflows/symbol-detection.yml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
symbol-detection:
|
|
||||||
name: "Symbol Conflict Detection"
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: "Linux"
|
|
||||||
os: ubuntu-22.04
|
|
||||||
platform: linux
|
|
||||||
preset: ci-linux
|
|
||||||
- name: "macOS"
|
|
||||||
os: macos-14
|
|
||||||
platform: macos
|
|
||||||
preset: ci-macos
|
|
||||||
- name: "Windows"
|
|
||||||
os: windows-2022
|
|
||||||
platform: windows
|
|
||||||
preset: ci-windows
|
|
||||||
|
|
||||||
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: Build project (Release)
|
|
||||||
uses: ./.github/actions/build-project
|
|
||||||
with:
|
|
||||||
platform: ${{ matrix.platform }}
|
|
||||||
preset: ${{ matrix.preset }}
|
|
||||||
build-type: Release
|
|
||||||
|
|
||||||
- name: Extract symbols (Unix/macOS)
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
run: |
|
|
||||||
echo "Extracting symbols from object files..."
|
|
||||||
./scripts/extract-symbols.sh build
|
|
||||||
|
|
||||||
- name: Extract symbols (Windows)
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
echo "Extracting symbols from object files..."
|
|
||||||
# Note: Windows version uses dumpbin, may require Visual Studio
|
|
||||||
bash ./scripts/extract-symbols.sh build
|
|
||||||
|
|
||||||
- name: Check for duplicate symbols
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "Checking for symbol conflicts..."
|
|
||||||
./scripts/check-duplicate-symbols.sh build/symbol_database.json || {
|
|
||||||
echo "Symbol conflicts detected!"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Upload symbol database
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: symbol-database-${{ matrix.platform }}
|
|
||||||
path: build/symbol_database.json
|
|
||||||
if-no-files-found: warn
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Generate symbol report
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
python3 << 'EOF'
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open("build/symbol_database.json") as f:
|
|
||||||
data = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("Symbol database not found")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
meta = data.get("metadata", {})
|
|
||||||
conflicts = data.get("conflicts", [])
|
|
||||||
|
|
||||||
print("\n=== Symbol Analysis Report ===")
|
|
||||||
print(f"Platform: {meta.get('platform', '?')}")
|
|
||||||
print(f"Object files scanned: {meta.get('object_files_scanned', 0)}")
|
|
||||||
print(f"Total symbols: {meta.get('total_symbols', 0)}")
|
|
||||||
print(f"Total conflicts: {len(conflicts)}")
|
|
||||||
|
|
||||||
if conflicts:
|
|
||||||
print("\nSymbol Conflicts:")
|
|
||||||
for i, conflict in enumerate(conflicts[:10], 1):
|
|
||||||
symbol = conflict.get("symbol", "?")
|
|
||||||
count = conflict.get("count", 0)
|
|
||||||
defs = conflict.get("definitions", [])
|
|
||||||
print(f"\n {i}. {symbol} (defined {count} times)")
|
|
||||||
for d in defs:
|
|
||||||
obj = d.get("object_file", "?")
|
|
||||||
print(f" - {obj}")
|
|
||||||
if len(conflicts) > 10:
|
|
||||||
print(f"\n ... and {len(conflicts) - 10} more conflicts")
|
|
||||||
else:
|
|
||||||
print("\nNo symbol conflicts detected - symbol table is clean!")
|
|
||||||
|
|
||||||
print("\n" + "="*40)
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Fail on conflicts
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
python3 << 'EOF'
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open("build/symbol_database.json") as f:
|
|
||||||
data = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
conflicts = data.get("conflicts", [])
|
|
||||||
if conflicts:
|
|
||||||
print(f"\nFAILED: Found {len(conflicts)} symbol conflicts")
|
|
||||||
print("See artifact for detailed symbol database")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
print("\nPASSED: No symbol conflicts detected")
|
|
||||||
sys.exit(0)
|
|
||||||
EOF
|
|
||||||
175
.github/workflows/wasm-dev.yml
vendored
Normal file
175
.github/workflows/wasm-dev.yml
vendored
Normal 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
209
.github/workflows/web-build.yml
vendored
Normal 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
|
||||||
42
.gitignore
vendored
42
.gitignore
vendored
@@ -14,6 +14,7 @@ compile_commands.json
|
|||||||
CPackConfig.cmake
|
CPackConfig.cmake
|
||||||
CPackSourceConfig.cmake
|
CPackSourceConfig.cmake
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
|
CMakeUserPresets.json
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
*.o
|
*.o
|
||||||
@@ -32,16 +33,15 @@ CTestTestfile.cmake
|
|||||||
*.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/
|
||||||
|
|
||||||
@@ -78,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 +94,36 @@ recent_files.txt
|
|||||||
.genkit
|
.genkit
|
||||||
.claude
|
.claude
|
||||||
scripts/__pycache__/
|
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/
|
||||||
|
|||||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -1,9 +1,6 @@
|
|||||||
[submodule "ext/imgui"]
|
[submodule "ext/imgui"]
|
||||||
path = ext/imgui
|
path = ext/imgui
|
||||||
url = https://github.com/ocornut/imgui.git
|
url = https://github.com/ocornut/imgui.git
|
||||||
[submodule "assets/asm/alttp-hacker-workspace"]
|
|
||||||
path = assets/asm/alttp-hacker-workspace
|
|
||||||
url = https://github.com/scawful/alttp-hacker-workspace.git
|
|
||||||
[submodule "ext/SDL"]
|
[submodule "ext/SDL"]
|
||||||
path = ext/SDL
|
path = ext/SDL
|
||||||
url = https://github.com/libsdl-org/SDL.git
|
url = https://github.com/libsdl-org/SDL.git
|
||||||
@@ -16,12 +13,3 @@
|
|||||||
[submodule "ext/nativefiledialog-extended"]
|
[submodule "ext/nativefiledialog-extended"]
|
||||||
path = ext/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 "ext/json"]
|
|
||||||
path = ext/json
|
|
||||||
url = https://github.com/nlohmann/json.git
|
|
||||||
[submodule "ext/httplib"]
|
|
||||||
path = ext/httplib
|
|
||||||
url = https://github.com/yhirose/cpp-httplib.git
|
|
||||||
|
|||||||
89
AGENTS.md
89
AGENTS.md
@@ -1,45 +1,60 @@
|
|||||||
## Inter-Agent Collaboration Protocol
|
# Agent Protocol
|
||||||
|
|
||||||
Multiple assistants may work in this repository at the same time. To avoid conflicts, every agent
|
_Extends: ~/AGENTS.md_
|
||||||
must follow the shared protocol defined in
|
|
||||||
[`docs/internal/agents/coordination-board.md`](docs/internal/agents/coordination-board.md).
|
|
||||||
|
|
||||||
### Required Steps
|
Project-specific operating procedures for AI agents contributing to `yaze`.
|
||||||
1. **Read the board** before starting a task to understand active work, blockers, or pending
|
|
||||||
requests.
|
|
||||||
2. **Append a new entry** (format described in the coordination board) outlining your intent,
|
|
||||||
affected files, and any dependencies.
|
|
||||||
3. **Respond to requests** addressed to your agent ID before taking on new work whenever possible.
|
|
||||||
4. **Record completion or handoffs** so the next agent has a clear state snapshot.
|
|
||||||
5. For multi-day initiatives, fill out the template in
|
|
||||||
[`docs/internal/agents/initiative-template.md`](docs/internal/agents/initiative-template.md) and
|
|
||||||
link it from your board entry instead of duplicating long notes.
|
|
||||||
|
|
||||||
### Agent IDs
|
## 1. Persona Adoption
|
||||||
Use the following canonical identifiers in board entries and handoffs (see
|
**Rule:** You must adopt a specific persona for every session.
|
||||||
[`docs/internal/agents/personas.md`](docs/internal/agents/personas.md) for details):
|
* **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.
|
||||||
|
|
||||||
| Agent ID | Description |
|
## 2. Workflows & Coordination
|
||||||
|-----------------|--------------------------------------------------|
|
|
||||||
| `CLAUDE_CORE` | Claude agent handling general editor/engine work |
|
|
||||||
| `CLAUDE_AIINF` | Claude agent focused on AI/agent infrastructure |
|
|
||||||
| `CLAUDE_DOCS` | Claude agent dedicated to docs/product guidance |
|
|
||||||
| `GEMINI_FLASH_AUTOM` | Gemini agent focused on automation/CLI/test work |
|
|
||||||
| `CODEX` | This Codex CLI assistant |
|
|
||||||
| `OTHER` | Any future agent (define in entry) |
|
|
||||||
|
|
||||||
If you introduce a new agent persona, add it to the table along with a short description.
|
### 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.
|
||||||
|
|
||||||
### Helper Scripts
|
### Substantial Work (> 30 min / Multi-file)
|
||||||
Common automation helpers live under [`scripts/agents/`](scripts/agents). Use them whenever possible:
|
1. **Check Context:**
|
||||||
- `run-gh-workflow.sh` – trigger GitHub workflows (`ci.yml`, etc.) with parameters such as `enable_http_api_tests`.
|
* Read [docs/internal/agents/coordination-board.md](docs/internal/agents/coordination-board.md) for `REQUEST` or `BLOCKER` tags.
|
||||||
- `smoke-build.sh` – configure/build a preset in place and report how long it took.
|
* Run `git status` and `git diff` to understand the current state.
|
||||||
- `run-tests.sh` – configure/build a preset and run `ctest` (`scripts/agents/run-tests.sh mac-dbg --output-on-failure`).
|
2. **Declare Intent:**
|
||||||
- `test-http-api.sh` – poll the `/api/v1/health` endpoint once the HTTP server is running.
|
* 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.
|
||||||
|
|
||||||
Log command results and workflow URLs on the coordination board so other agents know what ran and where to find artifacts.
|
### 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.
|
||||||
|
|
||||||
### Escalation
|
### Specs & Docs
|
||||||
If two agents need the same subsystem concurrently, negotiate via the board using the
|
* Keep one canonical spec per initiative (link it from the board entry and back).
|
||||||
`REQUEST`/`BLOCKER` keywords. When in doubt, prefer smaller, well-defined handoffs instead of broad
|
* Add a header with Status/Owner/Created/Last Reviewed/Next Review (≤14 days) and validation/exit criteria.
|
||||||
claims over directories.
|
* 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.
|
||||||
|
|||||||
307
CLAUDE.md
307
CLAUDE.md
@@ -1,295 +1,64 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
_Extends: ~/AGENTS.md, ~/CLAUDE.md_
|
||||||
|
|
||||||
> **Coordination Requirement**
|
C++23 ROM editor for Zelda: A Link to the Past. GUI editor + SNES emulator + AI CLI (`z3ed`).
|
||||||
> Before starting or handing off work, read and update the shared protocol in
|
|
||||||
> [`docs/internal/agents/coordination-board.md`](docs/internal/agents/coordination-board.md) as
|
|
||||||
> described in `AGENTS.md`. Always acknowledge pending `REQUEST`/`BLOCKER` entries addressed to the
|
|
||||||
> Claude persona you are using (`CLAUDE_CORE`, `CLAUDE_AIINF`, or `CLAUDE_DOCS`). See
|
|
||||||
> [`docs/internal/agents/personas.md`](docs/internal/agents/personas.md) for responsibilities.
|
|
||||||
|
|
||||||
## Project Overview
|
## Build & Test
|
||||||
|
|
||||||
**yaze** (Yet Another Zelda3 Editor) is a modern, cross-platform ROM editor for The Legend of Zelda: A Link to the Past, built with C++23. It features:
|
|
||||||
- Complete GUI editor for overworld, dungeons, sprites, graphics, and palettes
|
|
||||||
- Integrated Asar 65816 assembler for ROM patching
|
|
||||||
- SNES emulator for testing modifications
|
|
||||||
- AI-powered CLI tool (`z3ed`) for ROM hacking assistance
|
|
||||||
- ZSCustomOverworld v3 support for enhanced overworld editing
|
|
||||||
|
|
||||||
## Build System
|
|
||||||
|
|
||||||
- Use the presets defined in `CMakePresets.json` (debug: `mac-dbg` / `lin-dbg` / `win-dbg`, AI:
|
|
||||||
`mac-ai` / `win-ai`, release: `*-rel`, etc.). Add `-v` for verbose builds.
|
|
||||||
- Always run builds from a dedicated directory when acting as an AI agent (`build_ai`, `build_agent`,
|
|
||||||
…) so you do not interfere with the user’s `build`/`build_test` trees.
|
|
||||||
- Treat [`docs/public/build/quick-reference.md`](docs/public/build/quick-reference.md) as the single
|
|
||||||
source of truth for commands, presets, testing, and environment prep. Only reference the larger
|
|
||||||
troubleshooting docs when you need platform-specific fixes.
|
|
||||||
|
|
||||||
### Test Execution
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build tests
|
cmake --preset mac-dbg && cmake --build build -j8 # Build
|
||||||
cmake --build build --target yaze_test
|
ctest --test-dir build -L stable -j4 # Test
|
||||||
|
./yaze --rom_file=zelda3.sfc --editor=Dungeon # Run
|
||||||
# Run all tests
|
|
||||||
./build/bin/yaze_test
|
|
||||||
|
|
||||||
# Run specific categories
|
|
||||||
./build/bin/yaze_test --unit # Unit tests only
|
|
||||||
./build/bin/yaze_test --integration # Integration tests
|
|
||||||
./build/bin/yaze_test --e2e --show-gui # End-to-end GUI tests
|
|
||||||
|
|
||||||
# Run with ROM-dependent tests
|
|
||||||
./build/bin/yaze_test --rom-dependent --rom-path zelda3.sfc
|
|
||||||
|
|
||||||
# Run specific test by name
|
|
||||||
./build/bin/yaze_test "*Asar*"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Presets: `mac-dbg`/`lin-dbg`/`win-dbg`, `mac-ai`/`win-ai`, `*-rel`. See `docs/public/build/quick-reference.md`.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Core Components
|
| 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 |
|
||||||
|
|
||||||
**ROM Management** (`src/app/rom.h`, `src/app/rom.cc`)
|
## Key Patterns
|
||||||
- Central `Rom` class manages all ROM data access
|
|
||||||
- Provides transaction-based read/write operations
|
|
||||||
- Handles graphics buffer, palettes, and resource labels
|
|
||||||
- Key methods: `LoadFromFile()`, `ReadByte()`, `WriteByte()`, `ReadTransaction()`, `WriteTransaction()`
|
|
||||||
|
|
||||||
**Editor System** (`src/app/editor/`)
|
**Graphics refresh**: Update model → `Load*()` → `Renderer::Get().RenderBitmap()`
|
||||||
- Base `Editor` class defines interface for all editor types
|
|
||||||
- Major editors: `OverworldEditor`, `DungeonEditor`, `GraphicsEditor`, `PaletteEditor`
|
|
||||||
- `EditorManager` coordinates multiple editor instances
|
|
||||||
- Card-based UI system for dockable editor panels
|
|
||||||
|
|
||||||
**Graphics System** (`src/app/gfx/`)
|
**Async loading**: `Arena::Get().QueueDeferredTexture(bitmap, priority)` + process in `Update()`
|
||||||
- `gfx::Bitmap`: Core bitmap class with SDL surface integration
|
|
||||||
- `gfx::Arena`: Centralized singleton for progressive asset loading (priority-based texture queuing)
|
|
||||||
- `gfx::SnesPalette` and `gfx::SnesColor`: SNES color/palette management
|
|
||||||
- `gfx::Tile16` and `gfx::SnesTile`: Tile format representations
|
|
||||||
- Graphics sheets (223 total) loaded from ROM with compression support (LC-LZ2)
|
|
||||||
|
|
||||||
**Zelda3-Specific Logic** (`src/zelda3/`)
|
**Bitmap sync**: Use `set_data()` for bulk updates (syncs `data_` and `surface_->pixels`)
|
||||||
- `zelda3::Overworld`: Manages 160+ overworld maps (Light World, Dark World, Special World)
|
|
||||||
- `zelda3::OverworldMap`: Individual map data (tiles, entities, properties)
|
|
||||||
- `zelda3::Dungeon`: Dungeon room management (296 rooms)
|
|
||||||
- `zelda3::Sprite`: Sprite and enemy data structures
|
|
||||||
|
|
||||||
**Canvas System** (`src/app/gui/canvas.h`)
|
**Theming**: Always use `AgentUI::GetTheme()`, never hardcoded colors
|
||||||
- `gui::Canvas`: ImGui-based drawable canvas with pan/zoom/grid support
|
|
||||||
- Context menu system for entity editing
|
|
||||||
- Automation API for AI agent integration
|
|
||||||
- Usage tracker for click/interaction statistics
|
|
||||||
|
|
||||||
**Asar Integration** (`src/core/asar_wrapper.h`)
|
**Multi-area maps**: Always use `Overworld::ConfigureMultiAreaMap()`, never set `area_size` directly
|
||||||
- `core::AsarWrapper`: C++ wrapper around Asar assembler library
|
|
||||||
- Provides patch application, symbol extraction, and error reporting
|
|
||||||
- Used by CLI tool and GUI for assembly patching
|
|
||||||
|
|
||||||
**z3ed CLI Tool** (`src/cli/`)
|
## Naming
|
||||||
- AI-powered command-line interface with Ollama and Gemini support
|
|
||||||
- TUI (Terminal UI) components for interactive editing
|
|
||||||
- Resource catalog system for ROM data queries
|
|
||||||
- Test suite generation and execution
|
|
||||||
- Network collaboration support (experimental)
|
|
||||||
|
|
||||||
### Key Architectural Patterns
|
- **Load**: ROM → memory
|
||||||
|
- **Render**: Data → bitmap (CPU)
|
||||||
|
- **Draw**: Bitmap → screen (GPU)
|
||||||
|
|
||||||
**Pattern 1: Modular Editor Design**
|
## Pitfalls
|
||||||
- Large editor classes decomposed into smaller, single-responsibility modules
|
|
||||||
- Separate renderer classes (e.g., `OverworldEntityRenderer`)
|
|
||||||
- UI panels managed by dedicated classes (e.g., `MapPropertiesSystem`)
|
|
||||||
- Main editor acts as coordinator, not implementer
|
|
||||||
|
|
||||||
**Pattern 2: Callback-Based Communication**
|
1. Use `set_data()` not `mutable_data()` assignment for bitmap bulk updates
|
||||||
- Child components receive callbacks from parent editors via `SetCallbacks()`
|
2. Call `ProcessTextureQueue()` every frame
|
||||||
- Avoids circular dependencies between modules
|
3. Pass `0x800` to `DecompressV2`, never `0`
|
||||||
- Example: `MapPropertiesSystem` calls `RefreshCallback` to notify `OverworldEditor`
|
4. SMC header: `size % 1MB == 512`, not `size % 32KB`
|
||||||
|
5. Check `rom_->is_loaded()` before ROM operations
|
||||||
**Pattern 3: Progressive Loading via `gfx::Arena`**
|
|
||||||
- All expensive asset loading performed asynchronously
|
|
||||||
- Queue textures with priority: `gfx::Arena::Get().QueueDeferredTexture(bitmap, priority)`
|
|
||||||
- Process in batches during `Update()`: `GetNextDeferredTextureBatch(high_count, low_count)`
|
|
||||||
- Prevents UI freezes during ROM loading
|
|
||||||
|
|
||||||
**Pattern 4: Bitmap/Surface Synchronization**
|
|
||||||
- `Bitmap::data_` (C++ vector) and `surface_->pixels` (SDL buffer) must stay in sync
|
|
||||||
- Use `set_data()` for bulk replacement (syncs both)
|
|
||||||
- Use `WriteToPixel()` for single-pixel modifications
|
|
||||||
- Never assign directly to `mutable_data()` for replacements
|
|
||||||
|
|
||||||
## Development Guidelines
|
|
||||||
|
|
||||||
### Naming Conventions
|
|
||||||
- **Load**: Reading data from ROM into memory
|
|
||||||
- **Render**: Processing graphics data into bitmaps/textures (CPU pixel operations)
|
|
||||||
- **Draw**: Displaying textures/shapes on canvas via ImGui (GPU rendering)
|
|
||||||
- **Update**: UI state changes, property updates, input handling
|
|
||||||
|
|
||||||
### Graphics Refresh Logic
|
|
||||||
When a visual property changes:
|
|
||||||
1. Update the property in the data model
|
|
||||||
2. Call relevant `Load*()` method (e.g., `map.LoadAreaGraphics()`)
|
|
||||||
3. Force a redraw: Use `Renderer::Get().RenderBitmap()` for immediate updates (not `UpdateBitmap()`)
|
|
||||||
|
|
||||||
### Graphics Sheet Management
|
|
||||||
When modifying graphics sheets:
|
|
||||||
1. Get mutable reference: `auto& sheet = Arena::Get().mutable_gfx_sheet(index);`
|
|
||||||
2. Make modifications
|
|
||||||
3. Notify Arena: `Arena::Get().NotifySheetModified(index);`
|
|
||||||
4. Changes propagate automatically to all editors
|
|
||||||
|
|
||||||
### UI Theming System
|
|
||||||
- **Never use hardcoded colors** - Always use `AgentUITheme`
|
|
||||||
- Fetch theme: `const auto& theme = AgentUI::GetTheme();`
|
|
||||||
- Use semantic colors: `theme.panel_bg_color`, `theme.status_success`, etc.
|
|
||||||
- Use helper functions: `AgentUI::PushPanelStyle()`, `AgentUI::RenderSectionHeader()`, `AgentUI::StyledButton()`
|
|
||||||
|
|
||||||
### Multi-Area Map Configuration
|
|
||||||
- **Always use** `zelda3::Overworld::ConfigureMultiAreaMap()` when changing map area size
|
|
||||||
- Never set `area_size` property directly
|
|
||||||
- Method handles parent ID assignment and ROM data persistence
|
|
||||||
|
|
||||||
### Version-Specific Features
|
|
||||||
- Check ROM's `asm_version` byte before showing UI for ZSCustomOverworld features
|
|
||||||
- Display helpful messages for unsupported features (e.g., "Requires ZSCustomOverworld v3+")
|
|
||||||
|
|
||||||
### Entity Visibility Standards
|
|
||||||
Render overworld entities with high-contrast colors at 0.85f alpha:
|
|
||||||
- **Entrances**: Bright yellow-gold
|
|
||||||
- **Exits**: Cyan-white
|
|
||||||
- **Items**: Bright red
|
|
||||||
- **Sprites**: Bright magenta
|
|
||||||
|
|
||||||
### Debugging with Startup Flags
|
|
||||||
Jump directly to editors for faster development:
|
|
||||||
```bash
|
|
||||||
# Open specific editor with ROM
|
|
||||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon
|
|
||||||
|
|
||||||
# Open with specific cards visible
|
|
||||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Object Editor"
|
|
||||||
|
|
||||||
# Enable debug logging
|
|
||||||
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Overworld
|
|
||||||
```
|
|
||||||
|
|
||||||
Available editors: Assembly, Dungeon, Graphics, Music, Overworld, Palette, Screen, Sprite, Message, Hex, Agent, Settings
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Test Organization
|
|
||||||
```
|
|
||||||
test/
|
|
||||||
├── unit/ # Fast, isolated component tests (no ROM required)
|
|
||||||
├── integration/ # Multi-component tests (may require ROM)
|
|
||||||
├── e2e/ # Full UI workflow tests (ImGui Test Engine)
|
|
||||||
├── benchmarks/ # Performance tests
|
|
||||||
└── mocks/ # Mock objects for isolation
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Categories
|
|
||||||
- **Unit Tests**: Fast, self-contained, no external dependencies (primary CI validation)
|
|
||||||
- **Integration Tests**: Test component interactions, may require ROM files
|
|
||||||
- **E2E Tests**: Full user workflows driven by ImGui Test Engine (requires GUI)
|
|
||||||
- **ROM-Dependent Tests**: Any test requiring an actual Zelda3 ROM file
|
|
||||||
|
|
||||||
### Writing New Tests
|
|
||||||
- New class `MyClass`? → Add `test/unit/my_class_test.cc`
|
|
||||||
- Testing with ROM? → Add `test/integration/my_class_rom_test.cc`
|
|
||||||
- Testing UI workflow? → Add `test/e2e/my_class_workflow_test.cc`
|
|
||||||
|
|
||||||
### GUI Test Automation
|
|
||||||
- E2E framework uses `ImGuiTestEngine` for UI automation
|
|
||||||
- All major widgets have stable IDs for discovery
|
|
||||||
- Test helpers in `test/test_utils.h`: `LoadRomInTest()`, `OpenEditorInTest()`
|
|
||||||
- AI agents can use `z3ed gui discover`, `z3ed gui click` for automation
|
|
||||||
|
|
||||||
## Platform-Specific Notes
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
- Requires Visual Studio 2022 with "Desktop development with C++" workload
|
|
||||||
- Run `scripts\verify-build-environment.ps1` before building
|
|
||||||
- gRPC builds take 15-20 minutes first time (use vcpkg for faster builds)
|
|
||||||
- Watch for path length limits: Enable long paths with `git config --global core.longpaths true`
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
- Supports both Apple Silicon (ARM64) and Intel (x86_64)
|
|
||||||
- Use `mac-uni` preset for universal binaries
|
|
||||||
- Bundled Abseil used by default to avoid deployment target mismatches
|
|
||||||
- **ARM64 Note**: gRPC v1.67.1 is the tested stable version (see BUILD-TROUBLESHOOTING.md for details)
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
- Requires GCC 13+ or Clang 16+
|
|
||||||
- Install dependencies: `libgtk-3-dev`, `libdbus-1-dev`, `pkg-config`
|
|
||||||
|
|
||||||
**Platform-specific build issues?** See `docs/BUILD-TROUBLESHOOTING.md`
|
|
||||||
|
|
||||||
## CI/CD Pipeline
|
|
||||||
|
|
||||||
The project uses GitHub Actions for continuous integration and deployment:
|
|
||||||
|
|
||||||
### Workflows
|
|
||||||
- **ci.yml**: Build and test on Linux, macOS, Windows (runs on push to master/develop, PRs)
|
|
||||||
- **release.yml**: Build release artifacts and publish GitHub releases
|
|
||||||
- **code-quality.yml**: clang-format, cppcheck, clang-tidy checks
|
|
||||||
- **security.yml**: Security scanning and dependency audits
|
|
||||||
|
|
||||||
### Composite Actions
|
|
||||||
Reusable build steps in `.github/actions/`:
|
|
||||||
- `setup-build` - Configure build environment with caching
|
|
||||||
- `build-project` - Build with CMake and optimal settings
|
|
||||||
- `run-tests` - Execute test suites with result uploads
|
|
||||||
|
|
||||||
### Key Features
|
|
||||||
- CPM dependency caching for faster builds
|
|
||||||
- sccache/ccache for incremental compilation
|
|
||||||
- Platform-specific test execution (stable, unit, integration)
|
|
||||||
- Automatic artifact uploads on build/test failures
|
|
||||||
|
|
||||||
## Git Workflow
|
|
||||||
|
|
||||||
**Current Phase:** Pre-1.0 (Relaxed Rules)
|
|
||||||
|
|
||||||
For detailed workflow documentation, see `docs/B4-git-workflow.md`.
|
|
||||||
|
|
||||||
### Quick Guidelines
|
|
||||||
- **Documentation/Small fixes**: Commit directly to `master` or `develop`
|
|
||||||
- **Experiments/Features**: Use feature branches (`feature/<description>`)
|
|
||||||
- **Breaking changes**: Use feature branches and document in changelog
|
|
||||||
- **Commit messages**: Follow Conventional Commits (`feat:`, `fix:`, `docs:`, etc.)
|
|
||||||
|
|
||||||
### Planned Workflow (Post-1.0)
|
|
||||||
When the project reaches v1.0 or has multiple active contributors, we'll transition to formal Git Flow with protected branches, required PR reviews, and release branches.
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
- Format code with clang-format: `cmake --build build --target format`
|
Google C++ Style. Use `absl::Status`/`StatusOr<T>` with `RETURN_IF_ERROR()`/`ASSIGN_OR_RETURN()`.
|
||||||
- Check format without changes: `cmake --build build --target format-check`
|
|
||||||
- Style guide: Google C++ Style Guide (enforced via clang-format)
|
|
||||||
- Use `absl::Status` and `absl::StatusOr<T>` for error handling
|
|
||||||
- Macros: `RETURN_IF_ERROR()`, `ASSIGN_OR_RETURN()` for status propagation
|
|
||||||
|
|
||||||
## Important File Locations
|
Format: `cmake --build build --target format`
|
||||||
|
|
||||||
- ROM loading: `src/app/rom.cc:Rom::LoadFromFile()`
|
## Docs
|
||||||
- Overworld editor: `src/app/editor/overworld/overworld_editor.cc`
|
|
||||||
- Dungeon editor: `src/app/editor/dungeon/dungeon_editor.cc`
|
|
||||||
- Graphics arena: `src/app/gfx/snes_tile.cc` and `src/app/gfx/bitmap.cc`
|
|
||||||
- Asar wrapper: `src/core/asar_wrapper.cc`
|
|
||||||
- Main application: `src/yaze.cc`
|
|
||||||
- CLI tool: `src/cli/z3ed.cc`
|
|
||||||
- Test runner: `test/yaze_test.cc`
|
|
||||||
|
|
||||||
## Common Pitfalls
|
- Architecture: `docs/internal/architecture/`
|
||||||
|
- Build issues: `docs/BUILD-TROUBLESHOOTING.md`
|
||||||
1. **Bitmap data desync**: Always use `set_data()` for bulk updates, not `mutable_data()` assignment
|
- Tests: `test/README.md`
|
||||||
2. **Missing texture queue processing**: Call `ProcessTextureQueue()` every frame
|
|
||||||
3. **Incorrect graphics refresh order**: Update model → Load from ROM → Force render
|
|
||||||
4. **Skipping `ConfigureMultiAreaMap()`**: Always use this method for map size changes
|
|
||||||
5. **Hardcoded colors**: Use `AgentUITheme` system, never raw `ImVec4` values
|
|
||||||
6. **Blocking texture loads**: Use `gfx::Arena` deferred loading system
|
|
||||||
7. **Missing ROM state checks**: Always verify `rom_->is_loaded()` before operations
|
|
||||||
|
|||||||
@@ -14,31 +14,56 @@ 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.3
|
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.3
|
project(yaze VERSION ${YAZE_VERSION}
|
||||||
DESCRIPTION "Yet Another Zelda3 Editor"
|
DESCRIPTION "Yet Another Zelda3 Editor"
|
||||||
LANGUAGES CXX C)
|
LANGUAGES CXX C)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Set language standards early so dependencies (e.g., Abseil) build with them.
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Include build options first
|
# Include build options first
|
||||||
include(cmake/options.cmake)
|
include(cmake/options.cmake)
|
||||||
|
|
||||||
# Enable ccache for faster rebuilds if available
|
# 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)
|
find_program(CCACHE_FOUND ccache)
|
||||||
if(CCACHE_FOUND)
|
if(CCACHE_FOUND)
|
||||||
message(STATUS "✓ ccache found, enabling for faster builds")
|
message(STATUS "✓ ccache found, enabling for faster builds")
|
||||||
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
|
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
|
||||||
set(CMAKE_C_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 3)
|
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")
|
||||||
@@ -46,6 +71,19 @@ 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})
|
||||||
|
|
||||||
|
# Platform detection
|
||||||
|
if(APPLE)
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
|
set(YAZE_PLATFORM_IOS ON)
|
||||||
|
else()
|
||||||
|
set(YAZE_PLATFORM_MACOS ON)
|
||||||
|
endif()
|
||||||
|
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
set(YAZE_PLATFORM_LINUX ON)
|
||||||
|
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||||
|
set(YAZE_PLATFORM_WINDOWS ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Include utility functions
|
# Include utility functions
|
||||||
include(cmake/utils.cmake)
|
include(cmake/utils.cmake)
|
||||||
|
|
||||||
@@ -54,20 +92,16 @@ include(cmake/dependencies.cmake)
|
|||||||
|
|
||||||
# Additional configuration options
|
# Additional configuration options
|
||||||
option(YAZE_SUPPRESS_WARNINGS "Suppress compiler warnings (use -v preset suffix for verbose)" ON)
|
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")
|
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
|
# Export compile commands for clangd/LSP
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
# Platform detection
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
|
||||||
set(YAZE_PLATFORM_MACOS ON)
|
|
||||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|
||||||
set(YAZE_PLATFORM_LINUX ON)
|
|
||||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
|
||||||
set(YAZE_PLATFORM_WINDOWS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Setup compiler flags and common interface target
|
# Setup compiler flags and common interface target
|
||||||
yaze_add_compiler_flags()
|
yaze_add_compiler_flags()
|
||||||
|
|
||||||
@@ -92,7 +126,6 @@ set(BUILD_SHARED_LIBS OFF)
|
|||||||
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)
|
||||||
@@ -104,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"
|
||||||
@@ -116,18 +169,20 @@ if(CLANG_FORMAT)
|
|||||||
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/lib/.*")
|
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/lib/.*")
|
||||||
|
|
||||||
add_custom_target(yaze-format
|
add_custom_target(yaze-format
|
||||||
COMMAND ${CLANG_FORMAT} -i --style=Google ${ALL_SOURCE_FILES}
|
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(yaze-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
|
||||||
|
if(NOT YAZE_PLATFORM_IOS)
|
||||||
include(cmake/packaging/cpack.cmake)
|
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"
|
||||||
|
|||||||
2041
CMakePresets.json
2041
CMakePresets.json
File diff suppressed because it is too large
Load Diff
49
CMakeUserPresets.json.example
Normal file
49
CMakeUserPresets.json.example
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
# Contributing to YAZE
|
|
||||||
|
|
||||||
The YAZE project reserves **master** for promoted releases and uses **develop**
|
|
||||||
for day‑to‑day work. Larger efforts should branch from `develop` and rebase
|
|
||||||
frequently. Follow the existing Conventional Commit subject format when pushing
|
|
||||||
history (e.g. `feat: add sprite tab filtering`, `fix: guard null rom path`).
|
|
||||||
|
|
||||||
The repository ships a clang-format and clang-tidy configuration (Google style,
|
|
||||||
2-space indentation, 80-column wrap). Always run formatter and address
|
|
||||||
clang-tidy warnings on the files you touch.
|
|
||||||
|
|
||||||
## Engineering Expectations
|
|
||||||
|
|
||||||
1. **Refactor deliberately**
|
|
||||||
- Break work into reviewable patches.
|
|
||||||
- Preserve behaviour while moving code; add tests before/after when possible.
|
|
||||||
- Avoid speculative abstractions—prove value in the PR description.
|
|
||||||
- When deleting or replacing major systems, document the migration (see
|
|
||||||
`handbook/blueprints/editor-manager-architecture.md` for precedent).
|
|
||||||
|
|
||||||
2. **Verify changes**
|
|
||||||
- Use the appropriate CMake preset for your platform (`mac-dbg`, `lin-dbg`,
|
|
||||||
`win-dbg`, etc.).
|
|
||||||
- Run the targeted test slice: `ctest --preset dev` for fast coverage; add new
|
|
||||||
GoogleTest cases where feasible.
|
|
||||||
- For emulator-facing work, exercise relevant UI flows manually before
|
|
||||||
submitting.
|
|
||||||
- Explicitly call out remaining manual-verification items in the PR.
|
|
||||||
|
|
||||||
3. **Work with the build & CI**
|
|
||||||
- Honour the existing `cmake --preset` structure; avoid hardcoding paths.
|
|
||||||
- Keep `vcpkg.json` and the CI workflows in sync when adding dependencies.
|
|
||||||
- Use the deferred texture queue and arena abstractions rather than talking to
|
|
||||||
SDL directly.
|
|
||||||
|
|
||||||
## Documentation Style
|
|
||||||
|
|
||||||
YAZE documentation is concise and factual. When updating `docs/public/`:
|
|
||||||
|
|
||||||
- Avoid marketing language, emojis, or decorative status badges.
|
|
||||||
- Record the actual project state (e.g., mark editors as **stable** vs
|
|
||||||
**experimental** based on current source).
|
|
||||||
- Provide concrete references (file paths, function names) when describing
|
|
||||||
behaviour.
|
|
||||||
- Prefer bullet lists and short paragraphs; keep quick-start examples runnable.
|
|
||||||
- Update cross-references when moving or renaming files.
|
|
||||||
- For review handoffs, capture what remains to be done instead of transfusing raw
|
|
||||||
planning logs.
|
|
||||||
|
|
||||||
If you notice obsolete or inaccurate docs, fix them in the same change rather
|
|
||||||
than layering new “note” files.
|
|
||||||
|
|
||||||
## Pull Request Checklist
|
|
||||||
|
|
||||||
- [ ] Tests and builds succeed on your platform (note the preset/command used).
|
|
||||||
- [ ] New code is formatted (`clang-format`) and clean (`clang-tidy`).
|
|
||||||
- [ ] Documentation and comments reflect current behaviour.
|
|
||||||
- [ ] Breaking changes are mentioned in the PR and, if relevant, `docs/public/reference/changelog.md`.
|
|
||||||
- [ ] Any new assets or scripts are referenced in the repo’s structure guides.
|
|
||||||
|
|
||||||
Respect these guidelines and we can keep the codebase approachable, accurate,
|
|
||||||
and ready for the next set of contributors.
|
|
||||||
459
GEMINI.md
459
GEMINI.md
@@ -1,215 +1,354 @@
|
|||||||
# Gemini Workflow Instructions for the `yaze` Project
|
# GEMINI.md - YAZE Build Instructions
|
||||||
|
|
||||||
This document provides a summary of the `yaze` project to guide an AI assistant in understanding the codebase, architecture, and development workflows.
|
_Extends: ~/AGENTS.md, ~/GEMINI.md_
|
||||||
|
|
||||||
> **Coordination Requirement**
|
Build and test instructions for YAZE project. Follow commands exactly.
|
||||||
> Gemini-based agents must read and update the shared coordination board
|
|
||||||
> (`docs/internal/agents/coordination-board.md`) before making changes. Follow the protocol in
|
|
||||||
> `AGENTS.md`, use the appropriate persona ID (e.g., `GEMINI_AUTOM`), and respond to any pending
|
|
||||||
> entries targeting you.
|
|
||||||
|
|
||||||
## User Profile
|
## Critical Rules
|
||||||
|
|
||||||
- **User**: A Google programmer working on ROM hacking projects on macOS.
|
1. **Use `build_ai/` directory** - Never use `build/` (reserved for user)
|
||||||
- **IDE**: Visual Studio Code with the CMake Tools extension.
|
2. **Use `*-ai` presets** - Never use `*-dbg` presets
|
||||||
- **Build System**: CMake with a preference for the "Unix Makefiles" generator.
|
3. **Load persona** - Check `.claude/agents/<agent-id>.md` for system prompt
|
||||||
- **Workflow**: Uses CMake presets and a separate `build_test` directory for test builds.
|
4. **Use helper script:**
|
||||||
- **AI Assistant Build Policy**: When the AI assistant needs to build the project, it must use a dedicated build directory (e.g., `build_ai` or `build_agent`) to avoid interrupting the user's active builds. Never use `build` or `build_test` directories.
|
```bash
|
||||||
|
./scripts/agent_build.sh [target]
|
||||||
|
```
|
||||||
|
*Example:* `./scripts/agent_build.sh yaze` or `./scripts/agent_build.sh yaze_test`
|
||||||
|
|
||||||
## Project Overview
|
---
|
||||||
|
|
||||||
- **`yaze`**: A cross-platform GUI editor for "The Legend of Zelda: A Link to the Past" (ALTTP) ROMs. It is designed for compatibility with ZScream projects.
|
## Quick Reference: Build Times
|
||||||
- **`z3ed`**: A powerful command-line interface (CLI) for `yaze`. It features a resource-oriented design (`z3ed <resource> <action>`) and serves as the primary API for an AI-driven conversational agent.
|
|
||||||
- **`yaze.org`**: The file `docs/yaze.org` is an Emacs Org-Mode file used as a development tracker for active issues and features.
|
|
||||||
|
|
||||||
## Build Instructions
|
**First Build (Cold Start)**:
|
||||||
|
- **Fast Mode (Recommended)**: 2-4 minutes (uses system gRPC/sccache)
|
||||||
|
- Standard Mode: 10-20 minutes (compiles gRPC from source)
|
||||||
|
|
||||||
- Use the presets in `CMakePresets.json` (debug, AI, release, dev, CI, etc.). Always run the verifier
|
**Incremental Builds (After Changes)**:
|
||||||
script before the first build on a machine.
|
- Typically 10-60 seconds depending on what changed
|
||||||
- Gemini agents must configure/build in dedicated directories (`build_ai`, `build_agent`, …) to avoid
|
- **sccache/ccache**: Automatically detected and used if installed (highly recommended)
|
||||||
touching the user’s `build` or `build_test` folders.
|
|
||||||
- Consult [`docs/public/build/quick-reference.md`](docs/public/build/quick-reference.md) for the
|
## Platform-Specific Build Commands
|
||||||
canonical command list, preset overview, and testing guidance.
|
|
||||||
|
### 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
|
## Testing
|
||||||
|
|
||||||
- **Framework**: GoogleTest.
|
### Running All Tests
|
||||||
- **Test Categories**:
|
|
||||||
- `STABLE`: Fast, reliable, run in CI.
|
|
||||||
- `ROM_DEPENDENT`: Require a ROM file, skipped in CI unless a ROM is provided.
|
|
||||||
- `EXPERIMENTAL`: May be unstable, allowed to fail.
|
|
||||||
- **Running Tests**:
|
|
||||||
```bash
|
```bash
|
||||||
# Run stable tests using ctest and presets
|
# Build tests first
|
||||||
ctest --preset dev
|
cmake --build build_ai --target yaze_test
|
||||||
|
|
||||||
# Run comprehensive overworld tests (requires a ROM)
|
# Run all tests
|
||||||
./scripts/run_overworld_tests.sh /path/to/zelda3.sfc
|
./build_ai/bin/yaze_test
|
||||||
```
|
```
|
||||||
- **E2E GUI Testing**: The project includes a sophisticated end-to-end testing framework using `ImGuiTestEngine`, accessible via a gRPC service. The `z3ed agent test` command can execute natural language prompts as GUI tests.
|
|
||||||
|
|
||||||
## Core Architecture & Features
|
### Running Specific Test Categories
|
||||||
|
|
||||||
- **Overworld Editor**: Full support for vanilla and `ZSCustomOverworld` v2/v3 ROMs, ensuring compatibility with ZScream projects.
|
```bash
|
||||||
- **Dungeon Editor**: A modular, component-based system for editing rooms, objects, sprites, and more. See `docs/E2-dungeon-editor-guide.md` and `docs/E3-dungeon-editor-design.md`.
|
# Unit tests only (fast, ~5-10 seconds)
|
||||||
- **Graphics System**: A performant system featuring:
|
./build/bin/yaze_test --unit
|
||||||
- `Arena`-based resource management.
|
|
||||||
- `Bitmap` class for SNES-specific graphics formats.
|
|
||||||
- `Tilemap` with an LRU cache.
|
|
||||||
- `AtlasRenderer` for batched drawing.
|
|
||||||
- **Asar Integration**: Built-in support for the Asar 65816 assembler to apply assembly patches to the ROM.
|
|
||||||
|
|
||||||
## Editor System Architecture
|
# Integration tests (requires ROM file)
|
||||||
|
./build/bin/yaze_test --integration --rom-path /path/to/zelda3.sfc
|
||||||
|
|
||||||
The editor system is designed around a central `EditorManager` that orchestrates multiple editors and UI components.
|
# End-to-end GUI tests
|
||||||
|
./build/bin/yaze_test --e2e --show-gui
|
||||||
|
|
||||||
- **`EditorManager`**: The top-level class that manages multiple `RomSession`s, the main menu, and all editor windows. It handles the application's main update loop.
|
# Run specific test by name pattern
|
||||||
- **`RomSession`**: Each session encapsulates a `Rom` instance and a corresponding `EditorSet`, allowing multiple ROMs to be open simultaneously.
|
./build/bin/yaze_test "*Asar*" # All tests with "Asar" in name
|
||||||
- **`EditorSet`**: A container for all individual editor instances (Overworld, Dungeon, etc.) associated with a single ROM session.
|
./build/bin/yaze_test "*Dungeon*" # All dungeon-related tests
|
||||||
- **`Editor` (Base Class)**: A virtual base class (`src/app/editor/editor.h`) that defines a common interface for all editors, including methods like `Initialize`, `Load`, `Update`, and `Save`.
|
```
|
||||||
|
|
||||||
### Component-Based Editors
|
### Test Output Modes
|
||||||
The project is moving towards a component-based architecture to improve modularity and maintainability. This is most evident in the Dungeon Editor.
|
|
||||||
|
|
||||||
- **Dungeon Editor**: Refactored into a collection of single-responsibility components orchestrated by `DungeonEditor`:
|
```bash
|
||||||
- `DungeonRoomSelector`: Manages the UI for selecting rooms and entrances.
|
# Minimal output (default)
|
||||||
- `DungeonCanvasViewer`: Handles the rendering of the dungeon room on the main canvas.
|
./build/bin/yaze_test
|
||||||
- `DungeonObjectSelector`: Provides the UI for browsing and selecting objects, sprites, and other editable elements.
|
|
||||||
- `DungeonObjectInteraction`: Manages mouse input, selection, and drag-and-drop on the canvas.
|
|
||||||
- `DungeonToolset`: The main toolbar for the editor.
|
|
||||||
- `DungeonRenderer`: A dedicated rendering engine for dungeon objects, featuring a cache for performance.
|
|
||||||
- `DungeonRoomLoader`: Handles the logic for loading all room data from the ROM, now optimized with parallel processing.
|
|
||||||
- `DungeonUsageTracker`: Analyzes and displays statistics on resource usage (blocksets, palettes, etc.).
|
|
||||||
|
|
||||||
- **Overworld Editor**: Also employs a component-based approach with helpers like `OverworldEditorManager` for ZSCustomOverworld v3 features and `MapPropertiesSystem` for UI panels.
|
# Verbose output (shows all test names)
|
||||||
|
./build/bin/yaze_test -v
|
||||||
|
|
||||||
### Specialized Editors
|
# Very verbose (shows detailed test execution)
|
||||||
- **Code Editors**: `AssemblyEditor` (a full-featured text editor) and `MemoryEditor` (a hex viewer).
|
./build/bin/yaze_test -vv
|
||||||
- **Graphics Editors**: `GraphicsEditor`, `PaletteEditor`, `GfxGroupEditor`, `ScreenEditor`, and the highly detailed `Tile16Editor` provide tools for all visual assets.
|
|
||||||
- **Content Editors**: `SpriteEditor`, `MessageEditor`, and `MusicEditor` manage specific game content.
|
|
||||||
|
|
||||||
### System Components
|
# List all available tests without running
|
||||||
Located in `src/app/editor/system/`, these components provide the core application framework:
|
./build/bin/yaze_test --list-tests
|
||||||
- `SettingsEditor`: Manages global and project-specific feature flags.
|
```
|
||||||
- `PopupManager`, `ToastManager`: Handle all UI dialogs and notifications.
|
|
||||||
- `ShortcutManager`, `CommandManager`: Manage keyboard shortcuts and command palette functionality.
|
|
||||||
- `ProposalDrawer`, `AgentChatWidget`: Key UI components for the AI Agent Workflow, allowing for proposal review and conversational interaction.
|
|
||||||
|
|
||||||
## Game Data Models (`zelda3` Namespace)
|
## Common Build Issues and Solutions
|
||||||
|
|
||||||
The logic and data structures for ALTTP are primarily located in `src/zelda3/`.
|
### Issue 1: "No preset found"
|
||||||
|
|
||||||
- **`Rom` Class (`app/rom.h`)**: This is the most critical data class. It holds the entire ROM content in a `std::vector<uint8_t>` and provides the central API for all data access.
|
**Error**: `CMake Error: No such preset in CMakePresets.json`
|
||||||
- **Responsibilities**: Handles loading/saving ROM files, stripping SMC headers, and providing low-level read/write primitives (e.g., `ReadByte`, `WriteWord`).
|
|
||||||
- **Game-Specific Loading**: The `LoadZelda3` method populates game-specific data structures like palettes (`palette_groups_`) and graphics groups.
|
|
||||||
- **State**: Manages a `dirty_` flag to track unsaved changes.
|
|
||||||
|
|
||||||
- **Overworld Model (`zelda3/overworld/`)**:
|
**Solution**: Check the exact preset name. Use tab-completion or check `CMakePresets.json`.
|
||||||
- `Overworld`: The main container class that orchestrates the loading of all overworld data, including maps, tiles, and entities. It correctly handles logic for both vanilla and `ZSCustomOverworld` ROMs.
|
|
||||||
- `OverworldMap`: Represents a single overworld screen, loading its own properties (palettes, graphics, music) based on the ROM version.
|
|
||||||
- `GameEntity`: A base class in `zelda3/common.h` for all interactive overworld elements like `OverworldEntrance`, `OverworldExit`, `OverworldItem`, and `Sprite`.
|
|
||||||
|
|
||||||
- **Dungeon Model (`zelda3/dungeon/`)**:
|
```bash
|
||||||
- `DungeonEditorSystem`: A high-level API that serves as the backend for the UI, managing all dungeon editing logic (adding/removing sprites, items, doors, etc.).
|
# List available presets
|
||||||
- `Room`: Represents a single dungeon room, containing its objects, sprites, layout, and header information.
|
cmake --list-presets
|
||||||
- `RoomObject` & `RoomLayout`: Define the structural elements of a room.
|
|
||||||
- `ObjectParser` & `ObjectRenderer`: High-performance components for directly parsing object data from the ROM and rendering them, avoiding the need for full SNES emulation.
|
|
||||||
|
|
||||||
- **Sprite Model (`zelda3/sprite/`)**:
|
# Common mistake: Using wrong platform prefix
|
||||||
- `Sprite`: Represents an individual sprite (enemy, NPC).
|
cmake --preset dbg # ❌ WRONG
|
||||||
- `SpriteBuilder`: A fluent API for programmatically constructing custom sprites.
|
cmake --preset mac-dbg # ✅ CORRECT (macOS)
|
||||||
- `zsprite.h`: Data structures for compatibility with Zarby's ZSpriteMaker format.
|
cmake --preset lin-dbg # ✅ CORRECT (Linux)
|
||||||
|
cmake --preset win-dbg # ✅ CORRECT (Windows)
|
||||||
|
```
|
||||||
|
|
||||||
- **Other Data Models**:
|
### Issue 2: "Build directory exists but is outdated"
|
||||||
- `MessageData` (`message/`): Handles the game's text and dialogue system.
|
|
||||||
- `Inventory`, `TitleScreen`, `DungeonMap` (`screen/`): Represent specific non-gameplay screens.
|
|
||||||
- `music::Tracker` (`music/`): Contains legacy code from Hyrule Magic for handling SNES music data.
|
|
||||||
|
|
||||||
## Graphics System (`gfx` Namespace)
|
**Error**: CMake complains about existing build directory
|
||||||
|
|
||||||
The `gfx` namespace contains a highly optimized graphics engine tailored for SNES ROM hacking.
|
**Solution**: Clean and reconfigure
|
||||||
|
|
||||||
- **Core Concepts**:
|
```bash
|
||||||
- **`Bitmap`**: The fundamental class for image data. It supports SNES pixel formats, palette management, and is optimized with features like dirty-region tracking and a hash-map-based palette lookup cache for O(1) performance.
|
# Remove old build directory
|
||||||
- **SNES Formats**: `snes_color`, `snes_palette`, and `snes_tile` provide structures and conversion functions for handling SNES-specific data (15-bit color, 4BPP/8BPP tiles, etc.).
|
rm -rf build
|
||||||
- **`Tilemap`**: Represents a collection of tiles, using a texture `atlas` and a `TileCache` (with LRU eviction) for efficient rendering.
|
|
||||||
|
|
||||||
- **Resource Management & Performance**:
|
# Reconfigure from scratch
|
||||||
- **`Arena`**: A singleton that manages all graphics resources. It pools `SDL_Texture` and `SDL_Surface` objects to reduce allocation overhead and uses custom deleters for automatic cleanup. It also manages the 223 global graphics sheets for the game.
|
cmake --preset mac-dbg # or lin-dbg / win-dbg
|
||||||
- **`MemoryPool`**: A low-level, high-performance memory allocator that provides pre-allocated blocks for common graphics sizes, reducing `malloc` overhead and memory fragmentation.
|
```
|
||||||
- **`AtlasRenderer`**: A key performance component that batches draw calls by combining multiple smaller graphics onto a single large texture atlas.
|
|
||||||
- **Batching**: The `Arena` supports batching texture updates via `QueueTextureUpdate`, which minimizes expensive, blocking calls to the SDL rendering API.
|
|
||||||
|
|
||||||
- **Format Handling & Optimization**:
|
### Issue 3: "Tests fail with 'ROM not found'"
|
||||||
- **`compression.h`**: Contains SNES-specific compression algorithms (LC-LZ2, Hyrule Magic) for handling graphics data from the ROM.
|
|
||||||
- **`BppFormatManager`**: A system for analyzing and converting between different bits-per-pixel (BPP) formats.
|
|
||||||
- **`GraphicsOptimizer`**: A high-level tool that uses the `BppFormatManager` to analyze graphics sheets and recommend memory and performance optimizations.
|
|
||||||
- **`scad_format.h`**: Provides compatibility with legacy Nintendo CAD file formats (CGX, SCR, COL) from the "gigaleak".
|
|
||||||
|
|
||||||
- **Performance Monitoring**:
|
**Error**: Integration tests fail with ROM-related errors
|
||||||
- **`PerformanceProfiler`**: A comprehensive system for timing operations using a `ScopedTimer` RAII class.
|
|
||||||
- **`PerformanceDashboard`**: An ImGui-based UI for visualizing real-time performance metrics collected by the profiler.
|
|
||||||
|
|
||||||
## GUI System (`gui` Namespace)
|
**Solution**: Some tests require a Zelda3 ROM file
|
||||||
|
|
||||||
The `yaze` user interface is built with **ImGui** and is located in `src/app/gui`. It features a modern, component-based architecture designed for modularity, performance, and testability.
|
```bash
|
||||||
|
# Skip ROM-dependent tests
|
||||||
|
./build/bin/yaze_test --unit
|
||||||
|
|
||||||
### Canvas System (`gui::Canvas`)
|
# Or provide ROM path
|
||||||
The canvas is the core of all visual editors. The main `Canvas` class (`src/app/gui/canvas.h`) has been refactored from a monolithic class into a coordinator that leverages a set of single-responsibility components found in `src/app/gui/canvas/`.
|
./build/bin/yaze_test --integration --rom-path zelda3.sfc
|
||||||
|
```
|
||||||
|
|
||||||
- **`Canvas`**: The main canvas widget. It provides a modern, ImGui-style interface (`Begin`/`End`) and coordinates the various sub-components for drawing, interaction, and configuration.
|
### Issue 4: Long build times on first run
|
||||||
- **`CanvasInteractionHandler`**: Manages all direct user input on the canvas, such as mouse clicks, drags, and selections for tile painting and object manipulation.
|
|
||||||
- **`CanvasContextMenu`**: A powerful, data-driven context menu system. It is aware of the current `CanvasUsage` mode (e.g., `TilePainting`, `PaletteEditing`) and displays relevant menu items dynamically.
|
|
||||||
- **`CanvasModals`**: Handles all modal dialogs related to the canvas, such as "Advanced Properties," "Scaling Controls," and "BPP Conversion," ensuring a consistent UX.
|
|
||||||
- **`CanvasUsageTracker` & `CanvasPerformanceIntegration`**: These components provide deep analytics and performance monitoring. They track user interactions, operation timings, and memory usage, integrating with the global `PerformanceDashboard` to identify bottlenecks.
|
|
||||||
- **`CanvasUtils`**: A collection of stateless helper functions for common canvas tasks like grid drawing, coordinate alignment, and size calculations, promoting code reuse.
|
|
||||||
|
|
||||||
### Theming and Styling
|
**Not an Error**: This is normal!
|
||||||
The visual appearance of the editor is highly customizable through a robust theming system.
|
|
||||||
|
|
||||||
- **`ThemeManager`**: A singleton that manages `EnhancedTheme` objects. It can load custom themes from `.theme` files, allowing users to personalize the editor's look and feel. It includes a built-in theme editor and selector UI.
|
**Explanation**:
|
||||||
- **`BackgroundRenderer`**: Renders the animated, futuristic grid background for the main docking space, providing a polished, modern aesthetic.
|
- CPM.cmake downloads all dependencies (~3-5 minutes)
|
||||||
- **`style.cc` & `color.cc`**: Contain custom ImGui styling functions (`ColorsYaze`), `SnesColor` conversion utilities, and other helpers to maintain a consistent visual identity.
|
- gRPC compilation (Windows only, ~15-20 minutes)
|
||||||
|
- ImGui compilation (~2-3 minutes)
|
||||||
|
- SDL2, Abseil, PNG libraries (~3-5 minutes)
|
||||||
|
|
||||||
### Specialized UI Components
|
**Solution**: Be patient on first build. Subsequent builds use ccache/sccache and are MUCH faster (10-60 seconds).
|
||||||
The `gui` namespace includes several powerful, self-contained widgets for specific ROM hacking tasks.
|
|
||||||
|
|
||||||
- **`BppFormatUI`**: A comprehensive UI for managing SNES bits-per-pixel (BPP) graphics formats. It provides a format selector, a detailed analysis panel, a side-by-side conversion preview, and a batch comparison tool.
|
```bash
|
||||||
- **`EnhancedPaletteEditor`**: An advanced tool for editing `SnesPalette` objects. It features a grid-based editor, a ROM palette manager for loading palettes directly from the game, and a color analysis view to inspect pixel distribution.
|
# Monitor build progress with verbose output
|
||||||
- **`TextEditor`**: A full-featured text editor widget with syntax highlighting for 65816 assembly, undo/redo functionality, and standard text manipulation features.
|
cmake --build build --preset mac-dbg -v | tee build.log
|
||||||
- **`AssetBrowser`**: A flexible, icon-based browser for viewing and managing game assets, such as graphics sheets.
|
|
||||||
|
|
||||||
### Widget Registry for Automation
|
# Check build log for specific step taking long
|
||||||
A key feature for test automation and AI agent integration is the discoverability of UI elements.
|
grep "Linking" build.log
|
||||||
|
```
|
||||||
|
|
||||||
- **`WidgetIdRegistry`**: A singleton that catalogs all registered GUI widgets. It assigns a stable, hierarchical path (e.g., `Overworld/Canvas/Map`) to each widget, making the UI programmatically discoverable. This is the backbone of the `z3ed agent test` command.
|
### Issue 5: Incremental builds seem slow
|
||||||
- **`WidgetIdScope`**: An RAII helper that simplifies the creation of hierarchical widget IDs by managing an ID stack, ensuring that widget paths are consistent and predictable.
|
|
||||||
|
|
||||||
## ROM Hacking Context
|
**Solution**: Only rebuild what changed
|
||||||
|
|
||||||
- **Game**: The Legend of Zelda: A Link to the Past (US/JP).
|
```bash
|
||||||
- **`ZSCustomOverworld`**: A popular system for expanding overworld editing capabilities. `yaze` is designed to be fully compatible with ZScream's implementation of v2 and v3.
|
# Instead of rebuilding everything:
|
||||||
- **Assembly**: Uses `asar` for 65816 assembly. A style guide is available at `docs/E1-asm-style-guide.md`.
|
cmake --build build --preset mac-dbg # ❌ Rebuilds all targets
|
||||||
- **`usdasm` Disassembly**: The user has a local copy of the `usdasm` ALTTP disassembly at `/Users/scawful/Code/usdasm` which can be used for reference.
|
|
||||||
|
|
||||||
## Git Workflow
|
# 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
|
||||||
|
```
|
||||||
|
|
||||||
The project follows a simplified Git workflow for pre-1.0 development, with a more formal process documented for the future. For details, see `docs/B4-git-workflow.md`.
|
## Development Workflow
|
||||||
|
|
||||||
- **Current (Pre-1.0)**: A relaxed model is in use. Direct commits to `develop` or `master` are acceptable for documentation and small fixes. Feature branches are used for larger, potentially breaking changes.
|
### Typical Development Session
|
||||||
- **Future (Post-1.0)**: The project will adopt a formal Git Flow model with `master`, `develop`, feature, release, and hotfix branches.
|
|
||||||
|
|
||||||
## AI Agent Workflow (`z3ed agent`)
|
```bash
|
||||||
|
# 1. Configure once (first time only)
|
||||||
|
cmake --preset mac-dbg
|
||||||
|
|
||||||
A primary focus of the `yaze` project is its AI-driven agentic workflow, orchestrated by the `z3ed` CLI.
|
# 2. Make code changes to src/app/editor/dungeon/object_editor_card.cc
|
||||||
|
|
||||||
- **Vision**: To create a conversational ROM hacking assistant that can inspect the ROM and perform edits based on natural language.
|
# 3. Rebuild only the affected target (fast!)
|
||||||
- **Core Loop (MCP)**:
|
cmake --build build --target yaze
|
||||||
1. **Model (Plan)**: The user provides a prompt. The agent uses an LLM (Ollama or Gemini) to create a plan, which is a sequence of `z3ed` commands.
|
|
||||||
2. **Code (Generate)**: The LLM generates the commands based on a machine-readable catalog of the CLI's capabilities.
|
# 4. Run the application to test manually
|
||||||
3. **Program (Execute)**: The `z3ed` agent executes the commands.
|
./build/bin/yaze --rom_file zelda3.sfc --editor Dungeon
|
||||||
- **Proposal System**: To ensure safety, agent-driven changes are not applied directly. They are executed in a **sandboxed ROM copy** and saved as a **proposal**.
|
|
||||||
- **Review & Acceptance**: The user can review the proposed changes via `z3ed agent diff` or a dedicated `ProposalDrawer` in the `yaze` GUI. The user must explicitly **accept** a proposal to merge the changes into the main ROM.
|
# 5. Run automated tests to verify
|
||||||
- **Tool Use**: The agent can use read-only `z3ed` commands (e.g., `overworld-find-tile`, `dungeon-list-sprites`) as "tools" to inspect the ROM and gather context to answer questions or formulate a plan.
|
./build/bin/yaze_test --unit
|
||||||
- **API Discovery**: The agent learns the available commands and their schemas by calling `z3ed agent describe`, which exports the entire CLI surface area in a machine-readable format.
|
|
||||||
- **Function Schemas**: The Gemini AI service uses function calling schemas defined in `assets/agent/function_schemas.json`. These schemas are automatically copied to the build directory and loaded at runtime. To modify the available functions, edit this JSON file rather than hardcoding them in the C++ source.
|
# 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
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -6,18 +6,19 @@
|
|||||||
[](https://github.com/scawful/yaze/actions)
|
[](https://github.com/scawful/yaze/actions)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
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 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.
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
- **All-in-one editing**: Overworld, dungeon, sprite, palette, and messaging tools with live previews.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
||||||
|
|
||||||
## Project Status
|
## Project Status
|
||||||
`0.3.x` builds are in active development. Release automation is being reworked, so packaged builds may lag behind main. Follow `develop` for the most accurate view of current functionality.
|
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.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -57,27 +58,28 @@ All bundled third-party code (SDL, ImGui, ImGui Test Engine, Asar, nlohmann/json
|
|||||||
|
|
||||||
## Applications & Workflows
|
## Applications & Workflows
|
||||||
- **`./build/bin/yaze`** – full GUI editor with multi-session dockspace, theming, and ROM patching.
|
- **`./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.
|
- **`./build/bin/z3ed --tui`** – CLI/TUI companion for scripting, AI-assisted edits, and Asar workflows.
|
||||||
- **`./build_ai/bin/yaze_test --unit|--integration|--e2e`** – structured test runner for quick regression checks.
|
- **`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.
|
- **`z3ed` + macOS automation** – pair the CLI with sketchybar/yabai/skhd or Emacs/Spacemacs to drive ROM workflows without opening the GUI.
|
||||||
|
|
||||||
Typical commands:
|
Typical commands:
|
||||||
```bash
|
```bash
|
||||||
# Launch GUI with a ROM
|
# Launch GUI with a ROM
|
||||||
./build/bin/yaze zelda3.sfc
|
./build/bin/yaze roms/alttp_vanilla.sfc
|
||||||
|
|
||||||
# Apply a patch via CLI
|
# Apply a patch via CLI
|
||||||
./build/bin/z3ed asar patch.asm --rom zelda3.sfc
|
./build/bin/z3ed asar patch.asm --rom roms/alttp_vanilla.sfc
|
||||||
|
|
||||||
# Run focused tests
|
# Run focused tests
|
||||||
cmake --build --preset mac-ai --target yaze_test
|
cmake --build --preset mac-ai --target yaze_test
|
||||||
./build_ai/bin/yaze_test --unit
|
ctest --test-dir build -L unit
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
- `./build_ai/bin/yaze_test --unit` for fast checks; add `--integration` or `--e2e --show-gui` for broader coverage.
|
- `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 CI’s stable set; `ctest --preset all` runs the full matrix.
|
- `ctest --preset dev` mirrors CI’s stable set; `ctest --preset all` runs the full matrix.
|
||||||
- Set `YAZE_TEST_ROM_PATH` or pass `--rom-path` when a test needs a real ROM image.
|
- 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).
|
- Human-readable docs live under `docs/public/` with an entry point at [`docs/public/index.md`](docs/public/index.md).
|
||||||
|
|||||||
Submodule assets/asm/alttp-hacker-workspace deleted from 2520fb70c3
Submodule assets/asm/usdasm deleted from 835b15b91f
12
assets/patches/Hex Edits/File Fairy.asm
Normal file
12
assets/patches/Hex Edits/File Fairy.asm
Normal 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
|
||||||
69
assets/patches/Hex Edits/Misc Small Patches.asm
Normal file
69
assets/patches/Hex Edits/Misc Small Patches.asm
Normal 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
|
||||||
|
|
||||||
|
|
||||||
12
assets/patches/Hex Edits/No Beams.asm
Normal file
12
assets/patches/Hex Edits/No Beams.asm
Normal 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
|
||||||
11
assets/patches/Hex Edits/No Grass Cut.asm
Normal file
11
assets/patches/Hex Edits/No Grass Cut.asm
Normal 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
|
||||||
80
assets/patches/Items/AST Boots.asm
Normal file
80
assets/patches/Items/AST Boots.asm
Normal 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
|
||||||
44
assets/patches/Misc/Big Bomb Requirements.asm
Normal file
44
assets/patches/Misc/Big Bomb Requirements.asm
Normal 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
|
||||||
41
assets/patches/Misc/HoleOverlayFix.asm
Normal file
41
assets/patches/Misc/HoleOverlayFix.asm
Normal 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
|
||||||
13
assets/patches/Misc/IntroSkip.asm
Normal file
13
assets/patches/Misc/IntroSkip.asm
Normal 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
|
||||||
39
assets/patches/Misc/JP1.0 Glitches.asm
Normal file
39
assets/patches/Misc/JP1.0 Glitches.asm
Normal 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
|
||||||
39
assets/patches/Misc/Link Bed Start Position.asm
Normal file
39
assets/patches/Misc/Link Bed Start Position.asm
Normal 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
|
||||||
28
assets/patches/Misc/NoRocks.asm
Normal file
28
assets/patches/Misc/NoRocks.asm
Normal 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
|
||||||
51
assets/patches/Misc/Rainstate Skip.asm
Normal file
51
assets/patches/Misc/Rainstate Skip.asm
Normal 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
|
||||||
30
assets/patches/Misc/TorchTags.asm
Normal file
30
assets/patches/Misc/TorchTags.asm
Normal 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
|
||||||
153
assets/patches/Misc/Weathervane.asm
Normal file
153
assets/patches/Misc/Weathervane.asm
Normal 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.
|
||||||
|
|
||||||
|
; ==============================================================================
|
||||||
22
assets/patches/Music/LostWoodsExitMusic.asm
Normal file
22
assets/patches/Music/LostWoodsExitMusic.asm
Normal 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
|
||||||
29
assets/patches/Npcs/Bottle Vendor.asm
Normal file
29
assets/patches/Npcs/Bottle Vendor.asm
Normal 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
|
||||||
153
assets/patches/Overworld/TailMapExpansion.asm
Normal file
153
assets/patches/Overworld/TailMapExpansion.asm
Normal 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"
|
||||||
19
assets/patches/Sprites/Crystalswitch Conveyor.asm
Normal file
19
assets/patches/Sprites/Crystalswitch Conveyor.asm
Normal 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
|
||||||
95
assets/patches/Sprites/Elemental Trinexx.asm
Normal file
95
assets/patches/Sprites/Elemental Trinexx.asm
Normal 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
|
||||||
43
assets/patches/Sprites/Eye Lasers Active.asm
Normal file
43
assets/patches/Sprites/Eye Lasers Active.asm
Normal 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
|
||||||
56
assets/patches/Sprites/Khodstare Speeds.asm
Normal file
56
assets/patches/Sprites/Khodstare Speeds.asm
Normal 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
|
||||||
24
assets/patches/Sprites/Spike Damage.asm
Normal file
24
assets/patches/Sprites/Spike Damage.asm
Normal 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
|
||||||
52
assets/patches/Sprites/Spikes_Subtype.asm
Normal file
52
assets/patches/Sprites/Spikes_Subtype.asm
Normal 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
|
||||||
1
assets/patches/Version.txt
Normal file
1
assets/patches/Version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0000
|
||||||
@@ -35,8 +35,12 @@ endif()
|
|||||||
include(${CPM_DOWNLOAD_LOCATION})
|
include(${CPM_DOWNLOAD_LOCATION})
|
||||||
|
|
||||||
# Set CPM options for better caching and performance
|
# Set CPM options for better caching and performance
|
||||||
set(CPM_USE_LOCAL_PACKAGES ON)
|
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)
|
set(CPM_LOCAL_PACKAGES_ONLY OFF)
|
||||||
|
endif()
|
||||||
set(CPM_DONT_CREATE_PACKAGE_LOCK ON)
|
set(CPM_DONT_CREATE_PACKAGE_LOCK ON)
|
||||||
set(CPM_DONT_UPDATE_MODULE_PATH ON)
|
set(CPM_DONT_UPDATE_MODULE_PATH ON)
|
||||||
set(CPM_DONT_PREPEND_TO_MODULE_PATH ON)
|
set(CPM_DONT_PREPEND_TO_MODULE_PATH ON)
|
||||||
@@ -46,4 +50,3 @@ if(DEFINED ENV{GITHUB_ACTIONS})
|
|||||||
set(CPM_SOURCE_CACHE "$ENV{HOME}/.cpm-cache")
|
set(CPM_SOURCE_CACHE "$ENV{HOME}/.cpm-cache")
|
||||||
message(STATUS "CPM cache directory: ${CPM_SOURCE_CACHE}")
|
message(STATUS "CPM cache directory: ${CPM_SOURCE_CACHE}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
490
cmake/TestInfrastructure.cmake
Normal file
490
cmake/TestInfrastructure.cmake
Normal 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 "========================================")
|
||||||
@@ -100,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)
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -16,8 +18,8 @@ endif()
|
|||||||
# Set Asar source directory
|
# Set Asar source directory
|
||||||
set(ASAR_SRC_DIR "${CMAKE_SOURCE_DIR}/ext/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
|
||||||
|
|||||||
@@ -8,20 +8,32 @@ include(cmake/dependencies.lock)
|
|||||||
|
|
||||||
message(STATUS "=== Setting up YAZE dependencies with CPM.cmake ===")
|
message(STATUS "=== Setting up YAZE dependencies with CPM.cmake ===")
|
||||||
|
|
||||||
|
# Only prefer local/system packages when explicitly requested.
|
||||||
|
set(CPM_USE_LOCAL_PACKAGES ${YAZE_USE_SYSTEM_DEPS} CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
# Clear any previous dependency targets
|
# Clear any previous dependency targets
|
||||||
set(YAZE_ALL_DEPENDENCIES "")
|
set(YAZE_ALL_DEPENDENCIES "")
|
||||||
set(YAZE_SDL2_TARGETS "")
|
set(YAZE_SDL2_TARGETS "")
|
||||||
set(YAZE_YAML_TARGETS "")
|
set(YAZE_YAML_TARGETS "")
|
||||||
set(YAZE_IMGUI_TARGETS "")
|
set(YAZE_IMGUI_TARGETS "")
|
||||||
|
set(YAZE_IMPLOT_TARGETS "")
|
||||||
set(YAZE_JSON_TARGETS "")
|
set(YAZE_JSON_TARGETS "")
|
||||||
|
set(YAZE_HTTPLIB_TARGETS "")
|
||||||
set(YAZE_GRPC_TARGETS "")
|
set(YAZE_GRPC_TARGETS "")
|
||||||
set(YAZE_FTXUI_TARGETS "")
|
set(YAZE_FTXUI_TARGETS "")
|
||||||
set(YAZE_TESTING_TARGETS "")
|
set(YAZE_TESTING_TARGETS "")
|
||||||
|
|
||||||
# Core dependencies (always required)
|
# Core dependencies (always required)
|
||||||
|
# SDL selection: SDL2 (default) or SDL3 (experimental)
|
||||||
|
if(YAZE_USE_SDL3)
|
||||||
|
include(cmake/dependencies/sdl3.cmake)
|
||||||
|
# Debug: message(STATUS "After SDL3 setup, YAZE_SDL3_TARGETS = '${YAZE_SDL3_TARGETS}'")
|
||||||
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_SDL3_TARGETS})
|
||||||
|
else()
|
||||||
include(cmake/dependencies/sdl2.cmake)
|
include(cmake/dependencies/sdl2.cmake)
|
||||||
# Debug: message(STATUS "After SDL2 setup, YAZE_SDL2_TARGETS = '${YAZE_SDL2_TARGETS}'")
|
# Debug: message(STATUS "After SDL2 setup, YAZE_SDL2_TARGETS = '${YAZE_SDL2_TARGETS}'")
|
||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_SDL2_TARGETS})
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_SDL2_TARGETS})
|
||||||
|
endif()
|
||||||
|
|
||||||
include(cmake/dependencies/yaml.cmake)
|
include(cmake/dependencies/yaml.cmake)
|
||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_YAML_TARGETS})
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_YAML_TARGETS})
|
||||||
@@ -30,6 +42,9 @@ include(cmake/dependencies/imgui.cmake)
|
|||||||
# Debug: message(STATUS "After ImGui setup, YAZE_IMGUI_TARGETS = '${YAZE_IMGUI_TARGETS}'")
|
# Debug: message(STATUS "After ImGui setup, YAZE_IMGUI_TARGETS = '${YAZE_IMGUI_TARGETS}'")
|
||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${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
|
# Abseil is required for failure_signal_handler, status, and other utilities
|
||||||
# Only include standalone Abseil when gRPC is disabled - when gRPC is enabled,
|
# Only include standalone Abseil when gRPC is disabled - when gRPC is enabled,
|
||||||
# it provides its own bundled Abseil via CPM
|
# it provides its own bundled Abseil via CPM
|
||||||
@@ -43,6 +58,12 @@ if(YAZE_ENABLE_JSON)
|
|||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_JSON_TARGETS})
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_JSON_TARGETS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Native HTTP/HTTPS (cpp-httplib) for non-WASM builds
|
||||||
|
if(NOT EMSCRIPTEN)
|
||||||
|
include(cmake/dependencies/httplib.cmake)
|
||||||
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_HTTPLIB_TARGETS})
|
||||||
|
endif()
|
||||||
|
|
||||||
# CRITICAL: Load testing dependencies BEFORE gRPC when both are enabled
|
# CRITICAL: Load testing dependencies BEFORE gRPC when both are enabled
|
||||||
# This ensures gmock is available before Abseil (bundled with gRPC) tries to export test_allocator
|
# This ensures gmock is available before Abseil (bundled with gRPC) tries to export test_allocator
|
||||||
# which depends on gmock. This prevents CMake export errors.
|
# which depends on gmock. This prevents CMake export errors.
|
||||||
@@ -56,7 +77,7 @@ if(YAZE_ENABLE_GRPC)
|
|||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_GRPC_TARGETS})
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_GRPC_TARGETS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(YAZE_BUILD_CLI)
|
if(YAZE_BUILD_CLI AND NOT EMSCRIPTEN)
|
||||||
include(cmake/dependencies/ftxui.cmake)
|
include(cmake/dependencies/ftxui.cmake)
|
||||||
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_FTXUI_TARGETS})
|
list(APPEND YAZE_ALL_DEPENDENCIES ${YAZE_FTXUI_TARGETS})
|
||||||
endif()
|
endif()
|
||||||
@@ -77,13 +98,17 @@ message(STATUS "Total dependencies: ${YAZE_ALL_DEPENDENCIES}")
|
|||||||
message(STATUS "SDL2: ${YAZE_SDL2_TARGETS}")
|
message(STATUS "SDL2: ${YAZE_SDL2_TARGETS}")
|
||||||
message(STATUS "YAML: ${YAZE_YAML_TARGETS}")
|
message(STATUS "YAML: ${YAZE_YAML_TARGETS}")
|
||||||
message(STATUS "ImGui: ${YAZE_IMGUI_TARGETS}")
|
message(STATUS "ImGui: ${YAZE_IMGUI_TARGETS}")
|
||||||
|
message(STATUS "ImPlot: ${YAZE_IMPLOT_TARGETS}")
|
||||||
if(YAZE_ENABLE_JSON)
|
if(YAZE_ENABLE_JSON)
|
||||||
message(STATUS "JSON: ${YAZE_JSON_TARGETS}")
|
message(STATUS "JSON: ${YAZE_JSON_TARGETS}")
|
||||||
endif()
|
endif()
|
||||||
if(YAZE_ENABLE_GRPC)
|
if(YAZE_ENABLE_GRPC)
|
||||||
message(STATUS "gRPC: ${YAZE_GRPC_TARGETS}")
|
message(STATUS "gRPC: ${YAZE_GRPC_TARGETS}")
|
||||||
endif()
|
endif()
|
||||||
if(YAZE_BUILD_CLI)
|
if(NOT EMSCRIPTEN)
|
||||||
|
message(STATUS "httplib: ${YAZE_HTTPLIB_TARGETS}")
|
||||||
|
endif()
|
||||||
|
if(YAZE_BUILD_CLI AND NOT EMSCRIPTEN)
|
||||||
message(STATUS "FTXUI: ${YAZE_FTXUI_TARGETS}")
|
message(STATUS "FTXUI: ${YAZE_FTXUI_TARGETS}")
|
||||||
endif()
|
endif()
|
||||||
if(YAZE_BUILD_TESTS)
|
if(YAZE_BUILD_TESTS)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
# Core dependencies
|
# Core dependencies
|
||||||
set(SDL2_VERSION "2.30.0" CACHE STRING "SDL2 version")
|
set(SDL2_VERSION "2.30.0" CACHE STRING "SDL2 version")
|
||||||
set(YAML_CPP_VERSION "0.8.0" CACHE STRING "yaml-cpp 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
|
# gRPC and related
|
||||||
# Using v1.67.1 for MSVC compatibility (v1.75.1 has UPB compilation errors on Windows)
|
# Using v1.67.1 for MSVC compatibility (v1.75.1 has UPB compilation errors on Windows)
|
||||||
@@ -26,4 +28,3 @@ set(IMGUI_VERSION "1.90.4" CACHE STRING "Dear ImGui version")
|
|||||||
|
|
||||||
# ASAR
|
# ASAR
|
||||||
set(ASAR_VERSION "main" CACHE STRING "ASAR version")
|
set(ASAR_VERSION "main" CACHE STRING "ASAR version")
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,28 @@ CPMAddPackage(
|
|||||||
# We'll create our own interface target and link when available
|
# We'll create our own interface target and link when available
|
||||||
add_library(yaze_ftxui INTERFACE)
|
add_library(yaze_ftxui INTERFACE)
|
||||||
|
|
||||||
# Note: FTXUI targets will be available after the build phase
|
# Link to the actual FTXUI targets
|
||||||
# For now, we'll create a placeholder that can be linked later
|
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
|
# Export FTXUI targets for use in other CMake files
|
||||||
set(YAZE_FTXUI_TARGETS yaze_ftxui)
|
set(YAZE_FTXUI_TARGETS yaze_ftxui)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# gRPC and Protobuf dependency management
|
# gRPC and Protobuf dependency management
|
||||||
# Uses CPM.cmake for consistent cross-platform builds
|
# Uses CPM.cmake for consistent cross-platform builds, with optional system package fallback
|
||||||
|
|
||||||
if(NOT YAZE_ENABLE_GRPC)
|
if(NOT YAZE_ENABLE_GRPC)
|
||||||
return()
|
return()
|
||||||
@@ -9,23 +9,146 @@ endif()
|
|||||||
include(cmake/CPM.cmake)
|
include(cmake/CPM.cmake)
|
||||||
include(cmake/dependencies.lock)
|
include(cmake/dependencies.lock)
|
||||||
|
|
||||||
message(STATUS "Setting up gRPC ${GRPC_VERSION} with CPM.cmake")
|
message(STATUS "Setting up gRPC ${GRPC_VERSION}")
|
||||||
|
|
||||||
# Try to use system packages first if requested
|
#-----------------------------------------------------------------------
|
||||||
if(YAZE_USE_SYSTEM_DEPS)
|
# Option: YAZE_PREFER_SYSTEM_GRPC - Use system-installed gRPC/protobuf/abseil
|
||||||
find_package(PkgConfig QUIET)
|
# when available (e.g., from Homebrew, apt, vcpkg).
|
||||||
if(PkgConfig_FOUND)
|
#
|
||||||
pkg_check_modules(GRPC_PC grpc++)
|
# Benefits: Much faster configure/build times for local development
|
||||||
if(GRPC_PC_FOUND)
|
# Trade-off: May have version mismatches between system packages
|
||||||
message(STATUS "Using system gRPC via pkg-config")
|
#
|
||||||
add_library(grpc::grpc++ INTERFACE IMPORTED)
|
# Example: cmake --preset mac-ai-fast (uses system packages)
|
||||||
target_include_directories(grpc::grpc++ INTERFACE ${GRPC_PC_INCLUDE_DIRS})
|
#-----------------------------------------------------------------------
|
||||||
target_link_libraries(grpc::grpc++ INTERFACE ${GRPC_PC_LIBRARIES})
|
option(YAZE_PREFER_SYSTEM_GRPC "Prefer system-installed gRPC/protobuf over CPM" OFF)
|
||||||
target_compile_options(grpc::grpc++ INTERFACE ${GRPC_PC_CFLAGS_OTHER})
|
|
||||||
return()
|
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()
|
||||||
endif()
|
endif()
|
||||||
|
if(NOT TARGET grpc++_reflection)
|
||||||
|
add_library(grpc++_reflection INTERFACE)
|
||||||
|
target_link_libraries(grpc++_reflection INTERFACE gRPC::grpc++_reflection)
|
||||||
endif()
|
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
|
# Guard CMake's package lookup so CPM always downloads a consistent gRPC
|
||||||
@@ -371,7 +494,10 @@ set(YAZE_PROTOBUF_TARGETS
|
|||||||
protobuf::libprotobuf
|
protobuf::libprotobuf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
endif() # End of CPM build path (if NOT _YAZE_USING_SYSTEM_GRPC)
|
||||||
|
|
||||||
# Function to add protobuf/gRPC code generation to a target
|
# 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)
|
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")
|
||||||
@@ -381,6 +507,28 @@ function(target_add_protobuf target)
|
|||||||
return()
|
return()
|
||||||
endif()
|
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})
|
set(_protobuf_include_path -I ${CMAKE_SOURCE_DIR}/src -I ${_gRPC_PROTOBUF_WELLKNOWN_INCLUDE_DIR})
|
||||||
foreach(FIL ${ARGN})
|
foreach(FIL ${ARGN})
|
||||||
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
|
get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
|
||||||
@@ -406,13 +554,13 @@ 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 $<TARGET_FILE:protoc>
|
COMMAND ${_PROTOC_EXECUTABLE}
|
||||||
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=$<TARGET_FILE:grpc_cpp_plugin>
|
--plugin=protoc-gen-grpc=${_GRPC_PLUGIN_EXECUTABLE}
|
||||||
${_protobuf_include_path}
|
${_protobuf_include_path}
|
||||||
${ABS_FIL}
|
${ABS_FIL}
|
||||||
DEPENDS ${ABS_FIL} protoc grpc_cpp_plugin
|
DEPENDS ${ABS_FIL} ${_PROTOC_DEPENDS} ${_GRPC_PLUGIN_DEPENDS}
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src
|
||||||
COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
|
COMMENT "Running gRPC C++ protocol buffer compiler on ${FIL}"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|||||||
78
cmake/dependencies/httplib.cmake
Normal file
78
cmake/dependencies/httplib.cmake
Normal 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")
|
||||||
@@ -6,6 +6,27 @@ message(STATUS "Setting up Dear ImGui from bundled sources")
|
|||||||
# Use the bundled ImGui from ext/imgui
|
# Use the bundled ImGui from ext/imgui
|
||||||
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/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
|
# Create ImGui library with core files from bundled source
|
||||||
add_library(ImGui STATIC
|
add_library(ImGui STATIC
|
||||||
${IMGUI_DIR}/imgui.cpp
|
${IMGUI_DIR}/imgui.cpp
|
||||||
@@ -13,9 +34,8 @@ add_library(ImGui STATIC
|
|||||||
${IMGUI_DIR}/imgui_draw.cpp
|
${IMGUI_DIR}/imgui_draw.cpp
|
||||||
${IMGUI_DIR}/imgui_tables.cpp
|
${IMGUI_DIR}/imgui_tables.cpp
|
||||||
${IMGUI_DIR}/imgui_widgets.cpp
|
${IMGUI_DIR}/imgui_widgets.cpp
|
||||||
# SDL2 backend
|
# SDL backend (version-dependent)
|
||||||
${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp
|
${IMGUI_BACKEND_SOURCES}
|
||||||
${IMGUI_DIR}/backends/imgui_impl_sdlrenderer2.cpp
|
|
||||||
# C++ stdlib helpers (for std::string support)
|
# C++ stdlib helpers (for std::string support)
|
||||||
${IMGUI_DIR}/misc/cpp/imgui_stdlib.cpp
|
${IMGUI_DIR}/misc/cpp/imgui_stdlib.cpp
|
||||||
)
|
)
|
||||||
@@ -28,8 +48,12 @@ target_include_directories(ImGui PUBLIC
|
|||||||
# Set C++ standard requirement (ImGui 1.90+ requires C++11, we use C++17 for consistency)
|
# Set C++ standard requirement (ImGui 1.90+ requires C++11, we use C++17 for consistency)
|
||||||
target_compile_features(ImGui PUBLIC cxx_std_17)
|
target_compile_features(ImGui PUBLIC cxx_std_17)
|
||||||
|
|
||||||
# Link to SDL2
|
# 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})
|
target_link_libraries(ImGui PUBLIC ${YAZE_SDL2_TARGETS})
|
||||||
|
endif()
|
||||||
|
|
||||||
message(STATUS "Created ImGui target from bundled source at ${IMGUI_DIR}")
|
message(STATUS "Created ImGui target from bundled source at ${IMGUI_DIR}")
|
||||||
|
|
||||||
@@ -56,7 +80,11 @@ if(YAZE_BUILD_TESTS)
|
|||||||
${CMAKE_SOURCE_DIR}/ext
|
${CMAKE_SOURCE_DIR}/ext
|
||||||
)
|
)
|
||||||
target_compile_features(ImGuiTestEngine PUBLIC cxx_std_17)
|
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})
|
target_link_libraries(ImGuiTestEngine PUBLIC ImGui ${YAZE_SDL2_TARGETS})
|
||||||
|
endif()
|
||||||
target_compile_definitions(ImGuiTestEngine PUBLIC
|
target_compile_definitions(ImGuiTestEngine PUBLIC
|
||||||
IMGUI_ENABLE_TEST_ENGINE=1
|
IMGUI_ENABLE_TEST_ENGINE=1
|
||||||
IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL=1
|
IMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL=1
|
||||||
|
|||||||
27
cmake/dependencies/implot.cmake
Normal file
27
cmake/dependencies/implot.cmake
Normal 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()
|
||||||
@@ -4,14 +4,43 @@ if(NOT YAZE_ENABLE_JSON)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Setting up nlohmann_json with local ext directory")
|
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()
|
||||||
|
|
||||||
# Use the bundled nlohmann_json from ext/json
|
|
||||||
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
|
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
|
||||||
set(JSON_Install OFF CACHE BOOL "" FORCE)
|
set(JSON_Install OFF CACHE BOOL "" FORCE)
|
||||||
set(JSON_MultipleHeaders OFF CACHE BOOL "" FORCE)
|
set(JSON_MultipleHeaders OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/ext/json EXCLUDE_FROM_ALL)
|
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
|
# Verify target is available
|
||||||
if(TARGET nlohmann_json::nlohmann_json)
|
if(TARGET nlohmann_json::nlohmann_json)
|
||||||
@@ -21,11 +50,11 @@ elseif(TARGET nlohmann_json)
|
|||||||
add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json)
|
add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json)
|
||||||
message(STATUS "Created nlohmann_json::nlohmann_json alias")
|
message(STATUS "Created nlohmann_json::nlohmann_json alias")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "nlohmann_json target not found after add_subdirectory")
|
message(FATAL_ERROR "nlohmann_json target not found after CPM fetch")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Export for use in other CMake files
|
# 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")
|
set(YAZE_JSON_TARGETS nlohmann_json::nlohmann_json CACHE INTERNAL "nlohmann_json targets")
|
||||||
|
|
||||||
message(STATUS "nlohmann_json setup complete")
|
message(STATUS "nlohmann_json setup complete")
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,21 @@
|
|||||||
include(cmake/CPM.cmake)
|
include(cmake/CPM.cmake)
|
||||||
include(cmake/dependencies.lock)
|
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")
|
message(STATUS "Setting up SDL2 ${SDL2_VERSION} with CPM.cmake")
|
||||||
|
|
||||||
# Try to use system packages first if requested
|
# Try to use system packages first if requested
|
||||||
@@ -59,7 +74,7 @@ if(NOT TARGET yaze_sdl2)
|
|||||||
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2-static)
|
target_link_libraries(yaze_sdl2 INTERFACE SDL2::SDL2-static)
|
||||||
# For local Homebrew SDL2, also add include path explicitly
|
# For local Homebrew SDL2, also add include path explicitly
|
||||||
# SDL headers are in the SDL2 subdirectory
|
# SDL headers are in the SDL2 subdirectory
|
||||||
if(APPLE AND EXISTS "/opt/homebrew/opt/sdl2/include/SDL2")
|
if(YAZE_PLATFORM_MACOS AND EXISTS "/opt/homebrew/opt/sdl2/include/SDL2")
|
||||||
target_include_directories(yaze_sdl2 INTERFACE /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")
|
message(STATUS "Added Homebrew SDL2 include path: /opt/homebrew/opt/sdl2/include/SDL2")
|
||||||
endif()
|
endif()
|
||||||
@@ -79,7 +94,7 @@ if(WIN32)
|
|||||||
wbemuuid
|
wbemuuid
|
||||||
)
|
)
|
||||||
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
|
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
|
||||||
elseif(APPLE)
|
elseif(YAZE_PLATFORM_MACOS)
|
||||||
target_link_libraries(yaze_sdl2 INTERFACE
|
target_link_libraries(yaze_sdl2 INTERFACE
|
||||||
"-framework Cocoa"
|
"-framework Cocoa"
|
||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
@@ -87,6 +102,20 @@ elseif(APPLE)
|
|||||||
"-framework ForceFeedback"
|
"-framework ForceFeedback"
|
||||||
)
|
)
|
||||||
target_compile_definitions(yaze_sdl2 INTERFACE SDL_MAIN_HANDLED)
|
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)
|
elseif(UNIX)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
|
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
|
||||||
|
|||||||
124
cmake/dependencies/sdl3.cmake
Normal file
124
cmake/dependencies/sdl3.cmake
Normal 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}")
|
||||||
@@ -14,9 +14,24 @@ set(_YAZE_USE_SYSTEM_GTEST ${YAZE_USE_SYSTEM_DEPS})
|
|||||||
|
|
||||||
# Detect Homebrew installation automatically (helps offline builds)
|
# Detect Homebrew installation automatically (helps offline builds)
|
||||||
if(APPLE AND NOT _YAZE_USE_SYSTEM_GTEST)
|
if(APPLE AND NOT _YAZE_USE_SYSTEM_GTEST)
|
||||||
set(_YAZE_GTEST_PREFIX_CANDIDATES
|
set(_YAZE_GTEST_PREFIX_CANDIDATES)
|
||||||
/opt/homebrew/opt/googletest
|
set(_YAZE_HOST_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
/usr/local/opt/googletest)
|
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)
|
foreach(_prefix IN LISTS _YAZE_GTEST_PREFIX_CANDIDATES)
|
||||||
if(EXISTS "${_prefix}")
|
if(EXISTS "${_prefix}")
|
||||||
@@ -37,6 +52,9 @@ if(APPLE AND NOT _YAZE_USE_SYSTEM_GTEST)
|
|||||||
RESULT_VARIABLE HOMEBREW_GTEST_RESULT
|
RESULT_VARIABLE HOMEBREW_GTEST_RESULT
|
||||||
ERROR_QUIET)
|
ERROR_QUIET)
|
||||||
if(HOMEBREW_GTEST_RESULT EQUAL 0 AND EXISTS "${HOMEBREW_GTEST_PREFIX}")
|
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}")
|
list(APPEND CMAKE_PREFIX_PATH "${HOMEBREW_GTEST_PREFIX}")
|
||||||
message(STATUS "Added Homebrew googletest prefix: ${HOMEBREW_GTEST_PREFIX}")
|
message(STATUS "Added Homebrew googletest prefix: ${HOMEBREW_GTEST_PREFIX}")
|
||||||
set(_YAZE_USE_SYSTEM_GTEST ON)
|
set(_YAZE_USE_SYSTEM_GTEST ON)
|
||||||
@@ -44,6 +62,7 @@ if(APPLE AND NOT _YAZE_USE_SYSTEM_GTEST)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Try to use system packages first
|
# Try to use system packages first
|
||||||
if(_YAZE_USE_SYSTEM_GTEST)
|
if(_YAZE_USE_SYSTEM_GTEST)
|
||||||
|
|||||||
@@ -52,8 +52,23 @@ if(_YAZE_USE_SYSTEM_YAML)
|
|||||||
find_package(yaml-cpp QUIET)
|
find_package(yaml-cpp QUIET)
|
||||||
if(yaml-cpp_FOUND)
|
if(yaml-cpp_FOUND)
|
||||||
message(STATUS "Using system yaml-cpp")
|
message(STATUS "Using system yaml-cpp")
|
||||||
add_library(yaze_yaml INTERFACE IMPORTED)
|
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)
|
target_link_libraries(yaze_yaml INTERFACE yaml-cpp)
|
||||||
|
endif()
|
||||||
set(YAZE_YAML_TARGETS yaze_yaml)
|
set(YAZE_YAML_TARGETS yaze_yaml)
|
||||||
return()
|
return()
|
||||||
elseif(YAZE_USE_SYSTEM_DEPS)
|
elseif(YAZE_USE_SYSTEM_DEPS)
|
||||||
@@ -82,6 +97,7 @@ endif()
|
|||||||
# Create convenience targets for the rest of the project
|
# Create convenience targets for the rest of the project
|
||||||
add_library(yaze_yaml INTERFACE)
|
add_library(yaze_yaml INTERFACE)
|
||||||
target_link_libraries(yaze_yaml INTERFACE yaml-cpp)
|
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
|
# Export yaml-cpp targets for use in other CMake files
|
||||||
set(YAZE_YAML_TARGETS yaze_yaml)
|
set(YAZE_YAML_TARGETS yaze_yaml)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
set(_yaze_llvm_candidates llvm@21 llvm@20 llvm@19 llvm@18 llvm)
|
||||||
|
foreach(_yaze_llvm_candidate IN LISTS _yaze_llvm_candidates)
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND brew --prefix llvm@18
|
COMMAND brew --prefix ${_yaze_llvm_candidate}
|
||||||
OUTPUT_VARIABLE HOMEBREW_LLVM_PREFIX
|
OUTPUT_VARIABLE _yaze_llvm_prefix
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
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)
|
||||||
@@ -8,11 +8,15 @@ option(YAZE_BUILD_Z3ED "Build z3ed CLI executable" ON)
|
|||||||
option(YAZE_BUILD_EMU "Build emulator components" ON)
|
option(YAZE_BUILD_EMU "Build emulator components" ON)
|
||||||
option(YAZE_BUILD_LIB "Build static library" ON)
|
option(YAZE_BUILD_LIB "Build static library" ON)
|
||||||
option(YAZE_BUILD_TESTS "Build test suite" 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
|
# Feature flags
|
||||||
option(YAZE_ENABLE_GRPC "Enable gRPC agent support" ON)
|
option(YAZE_ENABLE_GRPC "Enable gRPC agent support" ON)
|
||||||
option(YAZE_ENABLE_JSON "Enable JSON support" ON)
|
option(YAZE_ENABLE_JSON "Enable JSON support" ON)
|
||||||
option(YAZE_ENABLE_AI "Enable AI agent features" 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
|
# Advanced feature toggles
|
||||||
option(YAZE_ENABLE_REMOTE_AUTOMATION
|
option(YAZE_ENABLE_REMOTE_AUTOMATION
|
||||||
@@ -48,9 +52,12 @@ option(YAZE_UNITY_BUILD "Enable Unity (Jumbo) builds" OFF)
|
|||||||
# Platform-specific options
|
# Platform-specific options
|
||||||
option(YAZE_USE_VCPKG "Use vcpkg for Windows dependencies" OFF)
|
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_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
|
# Development options
|
||||||
option(YAZE_ENABLE_ROM_TESTS "Enable tests that require ROM files" OFF)
|
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_MINIMAL_BUILD "Minimal build for CI (disable optional features)" OFF)
|
||||||
option(YAZE_VERBOSE_BUILD "Verbose build output" OFF)
|
option(YAZE_VERBOSE_BUILD "Verbose build output" OFF)
|
||||||
|
|
||||||
@@ -95,6 +102,29 @@ if(YAZE_ENABLE_HTTP_API)
|
|||||||
add_compile_definitions(YAZE_HTTP_API_ENABLED)
|
add_compile_definitions(YAZE_HTTP_API_ENABLED)
|
||||||
endif()
|
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
|
# Print configuration summary
|
||||||
message(STATUS "=== YAZE Build Configuration ===")
|
message(STATUS "=== YAZE Build Configuration ===")
|
||||||
message(STATUS "GUI Application: ${YAZE_BUILD_GUI}")
|
message(STATUS "GUI Application: ${YAZE_BUILD_GUI}")
|
||||||
@@ -103,6 +133,12 @@ message(STATUS "z3ed CLI: ${YAZE_BUILD_Z3ED}")
|
|||||||
message(STATUS "Emulator: ${YAZE_BUILD_EMU}")
|
message(STATUS "Emulator: ${YAZE_BUILD_EMU}")
|
||||||
message(STATUS "Static Library: ${YAZE_BUILD_LIB}")
|
message(STATUS "Static Library: ${YAZE_BUILD_LIB}")
|
||||||
message(STATUS "Tests: ${YAZE_BUILD_TESTS}")
|
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 "gRPC Support: ${YAZE_ENABLE_GRPC}")
|
||||||
message(STATUS "Remote Automation: ${YAZE_ENABLE_REMOTE_AUTOMATION}")
|
message(STATUS "Remote Automation: ${YAZE_ENABLE_REMOTE_AUTOMATION}")
|
||||||
message(STATUS "JSON Support: ${YAZE_ENABLE_JSON}")
|
message(STATUS "JSON Support: ${YAZE_ENABLE_JSON}")
|
||||||
@@ -114,5 +150,5 @@ message(STATUS "HTTP API Server: ${YAZE_ENABLE_HTTP_API}")
|
|||||||
message(STATUS "LTO: ${YAZE_ENABLE_LTO}")
|
message(STATUS "LTO: ${YAZE_ENABLE_LTO}")
|
||||||
message(STATUS "Sanitizers: ${YAZE_ENABLE_SANITIZERS}")
|
message(STATUS "Sanitizers: ${YAZE_ENABLE_SANITIZERS}")
|
||||||
message(STATUS "Coverage: ${YAZE_ENABLE_COVERAGE}")
|
message(STATUS "Coverage: ${YAZE_ENABLE_COVERAGE}")
|
||||||
|
message(STATUS "OpenCV Visual Analysis: ${YAZE_ENABLE_OPENCV}")
|
||||||
message(STATUS "=================================")
|
message(STATUS "=================================")
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
# CPack Configuration
|
# CPack Configuration - flat packages for all platforms
|
||||||
# Cross-platform packaging using CPack
|
#
|
||||||
|
# Structure:
|
||||||
|
# root/
|
||||||
|
# yaze(.exe)
|
||||||
|
# z3ed(.exe) (if built)
|
||||||
|
# README.md
|
||||||
|
# LICENSE
|
||||||
|
# assets/...
|
||||||
|
|
||||||
include(CPack)
|
|
||||||
|
|
||||||
# Set package information
|
|
||||||
set(CPACK_PACKAGE_NAME "yaze")
|
set(CPACK_PACKAGE_NAME "yaze")
|
||||||
set(CPACK_PACKAGE_VENDOR "scawful")
|
set(CPACK_PACKAGE_VENDOR "scawful")
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet Another Zelda3 Editor")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Yet Another Zelda3 Editor")
|
||||||
@@ -11,59 +15,94 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${YAZE_VERSION_MAJOR})
|
|||||||
set(CPACK_PACKAGE_VERSION_MINOR ${YAZE_VERSION_MINOR})
|
set(CPACK_PACKAGE_VERSION_MINOR ${YAZE_VERSION_MINOR})
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${YAZE_VERSION_PATCH})
|
set(CPACK_PACKAGE_VERSION_PATCH ${YAZE_VERSION_PATCH})
|
||||||
set(CPACK_PACKAGE_VERSION "${YAZE_VERSION_MAJOR}.${YAZE_VERSION_MINOR}.${YAZE_VERSION_PATCH}")
|
set(CPACK_PACKAGE_VERSION "${YAZE_VERSION_MAJOR}.${YAZE_VERSION_MINOR}.${YAZE_VERSION_PATCH}")
|
||||||
|
|
||||||
# Set package directory
|
|
||||||
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/packages")
|
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/packages")
|
||||||
|
|
||||||
# Platform-specific packaging
|
|
||||||
if(APPLE)
|
|
||||||
include(cmake/packaging/macos.cmake)
|
|
||||||
elseif(WIN32)
|
|
||||||
include(cmake/packaging/windows.cmake)
|
|
||||||
elseif(UNIX)
|
|
||||||
include(cmake/packaging/linux.cmake)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Common files to include
|
|
||||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
|
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
|
||||||
|
|
||||||
# Set default component
|
|
||||||
set(CPACK_COMPONENTS_ALL yaze)
|
set(CPACK_COMPONENTS_ALL yaze)
|
||||||
set(CPACK_COMPONENT_YAZE_DISPLAY_NAME "YAZE Editor")
|
set(CPACK_COMPONENT_YAZE_DISPLAY_NAME "YAZE Editor")
|
||||||
set(CPACK_COMPONENT_YAZE_DESCRIPTION "Main YAZE application and libraries")
|
set(CPACK_COMPONENT_YAZE_DESCRIPTION "Main YAZE application and libraries")
|
||||||
|
|
||||||
# Install rules - these define what CPack packages
|
# Populate runtime library list (needed on Windows)
|
||||||
include(GNUInstallDirs)
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON)
|
||||||
|
set(CMAKE_INSTALL_UCRT_LIBRARIES ON)
|
||||||
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
# Install main executable
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
include(cmake/packaging/macos.cmake)
|
||||||
|
|
||||||
install(TARGETS yaze
|
install(TARGETS yaze
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
COMPONENT yaze
|
COMPONENT yaze)
|
||||||
)
|
|
||||||
else()
|
if(TARGET z3ed)
|
||||||
install(TARGETS yaze
|
install(TARGETS z3ed
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
RUNTIME DESTINATION .
|
||||||
COMPONENT yaze
|
COMPONENT yaze)
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install assets
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
|
||||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/yaze/assets
|
DESTINATION assets
|
||||||
COMPONENT yaze
|
COMPONENT yaze)
|
||||||
PATTERN "*.png"
|
|
||||||
PATTERN "*.ttf"
|
|
||||||
PATTERN "*.asm"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Install documentation
|
|
||||||
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 .
|
||||||
COMPONENT yaze
|
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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -1,289 +0,0 @@
|
|||||||
# AI API & Agentic Workflow Enhancement - Handoff Document
|
|
||||||
|
|
||||||
**Date**: 2025-01-XX
|
|
||||||
**Status**: Phase 1 Complete, Phase 2-4 Pending
|
|
||||||
**Branch**: (to be determined)
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
This document tracks progress on transforming Yaze into an AI-native platform with unified model management, API interface, and enhanced agentic workflows. Phase 1 (Unified Model Management) is complete. Phases 2-4 require implementation.
|
|
||||||
|
|
||||||
## Completed Work (Phase 1)
|
|
||||||
|
|
||||||
### 1. Unified AI Model Management ✅
|
|
||||||
|
|
||||||
#### Core Infrastructure
|
|
||||||
- **`ModelInfo` struct** (`src/cli/service/ai/common.h`)
|
|
||||||
- Standardized model representation across all providers
|
|
||||||
- Fields: `name`, `display_name`, `provider`, `description`, `family`, `parameter_size`, `quantization`, `size_bytes`, `is_local`
|
|
||||||
|
|
||||||
- **`ModelRegistry` class** (`src/cli/service/ai/model_registry.h/.cc`)
|
|
||||||
- Singleton pattern for managing multiple `AIService` instances
|
|
||||||
- `RegisterService()` - Add service instances
|
|
||||||
- `ListAllModels()` - Aggregate models from all registered services
|
|
||||||
- Thread-safe with mutex protection
|
|
||||||
|
|
||||||
#### AIService Interface Updates
|
|
||||||
- **`AIService::ListAvailableModels()`** - Virtual method returning `std::vector<ModelInfo>`
|
|
||||||
- **`AIService::GetProviderName()`** - Virtual method returning provider identifier
|
|
||||||
- Default implementations provided in base class
|
|
||||||
|
|
||||||
#### Provider Implementations
|
|
||||||
- **`OllamaAIService::ListAvailableModels()`**
|
|
||||||
- Queries `/api/tags` endpoint
|
|
||||||
- Maps Ollama's model structure to `ModelInfo`
|
|
||||||
- Handles size, quantization, family metadata
|
|
||||||
|
|
||||||
- **`GeminiAIService::ListAvailableModels()`**
|
|
||||||
- Queries Gemini API `/v1beta/models` endpoint
|
|
||||||
- Falls back to known defaults if API key missing
|
|
||||||
- Filters for `gemini*` models
|
|
||||||
|
|
||||||
#### UI Integration
|
|
||||||
- **`AgentChatWidget::RefreshModels()`**
|
|
||||||
- Registers Ollama and Gemini services with `ModelRegistry`
|
|
||||||
- Aggregates models from all providers
|
|
||||||
- Caches results in `model_info_cache_`
|
|
||||||
|
|
||||||
- **Header updates** (`agent_chat_widget.h`)
|
|
||||||
- Replaced `ollama_model_info_cache_` with unified `model_info_cache_`
|
|
||||||
- Replaced `ollama_model_cache_` with `model_name_cache_`
|
|
||||||
- Replaced `ollama_models_loading_` with `models_loading_`
|
|
||||||
|
|
||||||
### Files Modified
|
|
||||||
- `src/cli/service/ai/common.h` - Added `ModelInfo` struct
|
|
||||||
- `src/cli/service/ai/ai_service.h` - Added `ListAvailableModels()` and `GetProviderName()`
|
|
||||||
- `src/cli/service/ai/ollama_ai_service.h/.cc` - Implemented model listing
|
|
||||||
- `src/cli/service/ai/gemini_ai_service.h/.cc` - Implemented model listing
|
|
||||||
- `src/cli/service/ai/model_registry.h/.cc` - New registry class
|
|
||||||
- `src/app/editor/agent/agent_chat_widget.h/.cc` - Updated to use registry
|
|
||||||
|
|
||||||
## In Progress
|
|
||||||
|
|
||||||
### UI Rendering Updates (Partial)
|
|
||||||
The `RenderModelConfigControls()` function in `agent_chat_widget.cc` still references old Ollama-specific code. It needs to be updated to:
|
|
||||||
- Use unified `model_info_cache_` instead of `ollama_model_info_cache_`
|
|
||||||
- Display models from all providers in a single list
|
|
||||||
- Filter by provider when a specific provider is selected
|
|
||||||
- Show provider badges/indicators for each model
|
|
||||||
|
|
||||||
**Location**: `src/app/editor/agent/agent_chat_widget.cc:2083-2318`
|
|
||||||
|
|
||||||
**Current State**: Function still has provider-specific branches that should be unified.
|
|
||||||
|
|
||||||
## Remaining Work
|
|
||||||
|
|
||||||
### Phase 2: API Interface & Headless Mode
|
|
||||||
|
|
||||||
#### 2.1 HTTP Server Implementation
|
|
||||||
**Goal**: Expose Yaze functionality via REST API for external agents
|
|
||||||
|
|
||||||
**Tasks**:
|
|
||||||
1. Create `HttpServer` class in `src/cli/service/api/`
|
|
||||||
- Use `httplib` (already in tree)
|
|
||||||
- Start on configurable port (default 8080)
|
|
||||||
- Handle CORS if needed
|
|
||||||
|
|
||||||
2. Implement endpoints:
|
|
||||||
- `GET /api/v1/models` - List all available models (delegate to `ModelRegistry`)
|
|
||||||
- `POST /api/v1/chat` - Send prompt to agent
|
|
||||||
- Request: `{ "prompt": "...", "provider": "ollama", "model": "...", "history": [...] }`
|
|
||||||
- Response: `{ "text_response": "...", "tool_calls": [...], "commands": [...] }`
|
|
||||||
- `POST /api/v1/tool/{tool_name}` - Execute specific tool
|
|
||||||
- Request: `{ "args": {...} }`
|
|
||||||
- Response: `{ "result": "...", "status": "ok|error" }`
|
|
||||||
- `GET /api/v1/health` - Health check
|
|
||||||
- `GET /api/v1/rom/status` - ROM loading status
|
|
||||||
|
|
||||||
3. Integration points:
|
|
||||||
- Initialize server in `yaze.cc` main() or via CLI flag
|
|
||||||
- Share `Rom*` context with API handlers
|
|
||||||
- Use `ConversationalAgentService` for chat endpoint
|
|
||||||
- Use `ToolDispatcher` for tool endpoint
|
|
||||||
|
|
||||||
**Files to Create**:
|
|
||||||
- `src/cli/service/api/http_server.h`
|
|
||||||
- `src/cli/service/api/http_server.cc`
|
|
||||||
- `src/cli/service/api/api_handlers.h`
|
|
||||||
- `src/cli/service/api/api_handlers.cc`
|
|
||||||
|
|
||||||
**Dependencies**: `httplib`, `nlohmann/json` (already available)
|
|
||||||
|
|
||||||
### Phase 3: Enhanced Agentic Workflows
|
|
||||||
|
|
||||||
#### 3.1 Tool Expansion
|
|
||||||
|
|
||||||
**FileSystemTool** (`src/cli/handlers/tools/filesystem_commands.h/.cc`)
|
|
||||||
- **Purpose**: Allow agent to read/write files outside ROM (e.g., `src/` directory)
|
|
||||||
- **Safety**: Require user confirmation or explicit scope configuration
|
|
||||||
- **Commands**:
|
|
||||||
- `filesystem-read <path>` - Read file contents
|
|
||||||
- `filesystem-write <path> <content>` - Write file (with confirmation)
|
|
||||||
- `filesystem-list <directory>` - List directory contents
|
|
||||||
- `filesystem-search <pattern>` - Search for files matching pattern
|
|
||||||
|
|
||||||
**BuildTool** (`src/cli/handlers/tools/build_commands.h/.cc`)
|
|
||||||
- **Purpose**: Trigger builds from within agent
|
|
||||||
- **Commands**:
|
|
||||||
- `build-cmake <build_dir>` - Run cmake configuration
|
|
||||||
- `build-ninja <build_dir>` - Run ninja build
|
|
||||||
- `build-status` - Check build status
|
|
||||||
- `build-errors` - Parse and return compilation errors
|
|
||||||
|
|
||||||
**Integration**:
|
|
||||||
- Add to `ToolDispatcher::ToolCallType` enum
|
|
||||||
- Register in `ToolDispatcher::CreateHandler()`
|
|
||||||
- Add to `ToolDispatcher::ToolPreferences` struct
|
|
||||||
- Update UI toggles in `AgentChatWidget::RenderToolingControls()`
|
|
||||||
|
|
||||||
#### 3.2 Editor State Context
|
|
||||||
**Goal**: Feed editor state (open files, compilation errors) into agent context
|
|
||||||
|
|
||||||
**Tasks**:
|
|
||||||
1. Create `EditorState` struct capturing:
|
|
||||||
- Open file paths
|
|
||||||
- Active editor type
|
|
||||||
- Compilation errors (if any)
|
|
||||||
- Recent changes
|
|
||||||
|
|
||||||
2. Inject into agent prompts:
|
|
||||||
- Add to `PromptBuilder::BuildPromptFromHistory()`
|
|
||||||
- Include in system prompt when editor state changes
|
|
||||||
|
|
||||||
3. Update `ConversationalAgentService`:
|
|
||||||
- Add `SetEditorState(EditorState*)` method
|
|
||||||
- Pass to `PromptBuilder` when building prompts
|
|
||||||
|
|
||||||
**Files to Create/Modify**:
|
|
||||||
- `src/cli/service/agent/editor_state.h` (new)
|
|
||||||
- `src/cli/service/ai/prompt_builder.h/.cc` (modify)
|
|
||||||
|
|
||||||
### Phase 4: Refactoring
|
|
||||||
|
|
||||||
#### 4.1 ToolDispatcher Structured Output
|
|
||||||
**Goal**: Return JSON instead of capturing stdout
|
|
||||||
|
|
||||||
**Current State**: `ToolDispatcher::Dispatch()` returns `absl::StatusOr<std::string>` by capturing stdout from command handlers.
|
|
||||||
|
|
||||||
**Proposed Changes**:
|
|
||||||
1. Create `ToolResult` struct:
|
|
||||||
```cpp
|
|
||||||
struct ToolResult {
|
|
||||||
std::string output; // Human-readable output
|
|
||||||
nlohmann::json data; // Structured data (if applicable)
|
|
||||||
bool success;
|
|
||||||
std::vector<std::string> warnings;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Update command handlers to return `ToolResult`:
|
|
||||||
- Modify base `CommandHandler` interface
|
|
||||||
- Update each handler implementation
|
|
||||||
- Keep backward compatibility with `OutputFormatter` for CLI
|
|
||||||
|
|
||||||
3. Update `ToolDispatcher::Dispatch()`:
|
|
||||||
- Return `absl::StatusOr<ToolResult>`
|
|
||||||
- Convert to JSON for API responses
|
|
||||||
- Keep string output for CLI compatibility
|
|
||||||
|
|
||||||
**Files to Modify**:
|
|
||||||
- `src/cli/service/agent/tool_dispatcher.h/.cc`
|
|
||||||
- `src/cli/handlers/*/command_handlers.h/.cc` (all handlers)
|
|
||||||
- `src/cli/service/agent/command_handler.h` (base interface)
|
|
||||||
|
|
||||||
**Migration Strategy**:
|
|
||||||
- Add new `ExecuteStructured()` method alongside existing `Execute()`
|
|
||||||
- Gradually migrate handlers
|
|
||||||
- Keep old path for CLI until migration complete
|
|
||||||
|
|
||||||
## Technical Notes
|
|
||||||
|
|
||||||
### Model Registry Usage Pattern
|
|
||||||
```cpp
|
|
||||||
// Register services
|
|
||||||
auto& registry = cli::ModelRegistry::GetInstance();
|
|
||||||
registry.RegisterService(std::make_shared<OllamaAIService>(ollama_config));
|
|
||||||
registry.RegisterService(std::make_shared<GeminiAIService>(gemini_config));
|
|
||||||
|
|
||||||
// List all models
|
|
||||||
auto models_or = registry.ListAllModels();
|
|
||||||
// Returns unified list sorted by name
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Key Management
|
|
||||||
- Gemini API key: Currently stored in `AgentConfigState::gemini_api_key`
|
|
||||||
- Consider: Environment variable fallback, secure storage
|
|
||||||
- Future: Support multiple API keys for different providers
|
|
||||||
|
|
||||||
### Thread Safety
|
|
||||||
- `ModelRegistry` uses mutex for thread-safe access
|
|
||||||
- `HttpServer` should handle concurrent requests (httplib supports this)
|
|
||||||
- `ToolDispatcher` may need locking if shared across threads
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
### Phase 1 (Model Management)
|
|
||||||
- [ ] Verify Ollama models appear in unified list
|
|
||||||
- [ ] Verify Gemini models appear in unified list
|
|
||||||
- [ ] Test model refresh with multiple providers
|
|
||||||
- [ ] Test provider filtering in UI
|
|
||||||
- [ ] Test model selection and configuration
|
|
||||||
|
|
||||||
### Phase 2 (API)
|
|
||||||
- [ ] Test `/api/v1/models` endpoint
|
|
||||||
- [ ] Test `/api/v1/chat` with different providers
|
|
||||||
- [ ] Test `/api/v1/tool/*` endpoints
|
|
||||||
- [ ] Test error handling (missing ROM, invalid tool, etc.)
|
|
||||||
- [ ] Test concurrent requests
|
|
||||||
- [ ] Test CORS if needed
|
|
||||||
|
|
||||||
### Phase 3 (Tools)
|
|
||||||
- [ ] Test FileSystemTool with read operations
|
|
||||||
- [ ] Test FileSystemTool write confirmation flow
|
|
||||||
- [ ] Test BuildTool cmake/ninja execution
|
|
||||||
- [ ] Test BuildTool error parsing
|
|
||||||
- [ ] Test editor state injection into prompts
|
|
||||||
|
|
||||||
### Phase 4 (Refactoring)
|
|
||||||
- [ ] Verify all handlers return structured output
|
|
||||||
- [ ] Test API endpoints with new format
|
|
||||||
- [ ] Verify CLI still works with old format
|
|
||||||
- [ ] Performance test (no regressions)
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
1. **UI Rendering**: `RenderModelConfigControls()` still has provider-specific code that should be unified
|
|
||||||
2. **Model Info Display**: Some fields from `ModelInfo` (like `quantization`, `modified_at`) are not displayed in unified view
|
|
||||||
3. **Error Handling**: Model listing failures are logged but don't prevent other providers from loading
|
|
||||||
|
|
||||||
## Next Steps (Priority Order)
|
|
||||||
|
|
||||||
1. **Complete UI unification** - Update `RenderModelConfigControls()` to use unified model list
|
|
||||||
2. **Implement HTTP Server** - Start with basic server and `/api/v1/models` endpoint
|
|
||||||
3. **Add chat endpoint** - Wire up `ConversationalAgentService` to API
|
|
||||||
4. **Add tool endpoint** - Expose `ToolDispatcher` via API
|
|
||||||
5. **Implement FileSystemTool** - Start with read-only operations
|
|
||||||
6. **Implement BuildTool** - Basic cmake/ninja execution
|
|
||||||
7. **Refactor ToolDispatcher** - Begin structured output migration
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Plan document: `plan-yaze-api-agentic-workflow-enhancement.plan.md`
|
|
||||||
- Model Registry: `src/cli/service/ai/model_registry.h`
|
|
||||||
- AIService interface: `src/cli/service/ai/ai_service.h`
|
|
||||||
- ToolDispatcher: `src/cli/service/agent/tool_dispatcher.h`
|
|
||||||
- httplib docs: (in `ext/httplib/`)
|
|
||||||
|
|
||||||
## Questions for Next Developer
|
|
||||||
|
|
||||||
1. Should the HTTP server be enabled by default or require a flag?
|
|
||||||
2. What port should be used? (8080 suggested, but configurable?)
|
|
||||||
3. Should FileSystemTool require explicit user approval per operation or a "trusted scope"?
|
|
||||||
4. Should BuildTool be limited to specific directories (e.g., `build/`) for safety?
|
|
||||||
5. How should API authentication work? (API key? Localhost-only? None?)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated**: 2025-01-XX
|
|
||||||
**Contact**: (to be filled)
|
|
||||||
|
|
||||||
@@ -1,31 +1,42 @@
|
|||||||
# YAZE Handbook
|
# YAZE Internal Documentation
|
||||||
|
|
||||||
Internal documentation for planning, AI agents, research, and historical build notes. These
|
**Last Updated**: December 8, 2025
|
||||||
files are intentionally excluded from the public Doxygen site so they can remain verbose and
|
|
||||||
speculative without impacting the published docs.
|
|
||||||
|
|
||||||
## Sections
|
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.
|
||||||
- `agents/` – z3ed and AI agent playbooks, command abstractions, and debugging guides.
|
|
||||||
- `blueprints/` – architectural proposals, refactors, and technical deep dives.
|
|
||||||
- `roadmaps/` – sequencing, feature parity analysis, and postmortems.
|
|
||||||
- `research/` – emulator investigations, timing analyses, web ideas, and development trackers.
|
|
||||||
- `legacy/` – superseded build guides and other historical docs kept for reference.
|
|
||||||
- `agents/` – includes the coordination board, personas, GH Actions remote guide, and helper scripts
|
|
||||||
(`scripts/agents/`) for common agent workflows.
|
|
||||||
|
|
||||||
When adding new internal docs, place them under the appropriate subdirectory here instead of
|
## Quick Links
|
||||||
`docs/`.
|
|
||||||
|
|
||||||
## Version Control & Safety Guidelines
|
- **Active Work**: [Coordination Board](agents/coordination-board.md)
|
||||||
- **Coordinate before forceful changes**: Never rewrite history on shared branches. Use dedicated
|
- **Roadmap**: [roadmap.md](roadmap.md)
|
||||||
feature/bugfix branches (see `docs/public/developer/git-workflow.md`) and keep `develop/master`
|
- **Doc Hygiene Rules**: [agents/doc-hygiene.md](agents/doc-hygiene.md)
|
||||||
clean.
|
- **Templates**: [templates/](templates/)
|
||||||
- **Back up ROMs and assets**: Treat sample ROMs, palettes, and project files as irreplaceable. Work
|
|
||||||
on copies, and enable the editor’s automatic backup setting before testing risky changes.
|
## Directory Structure
|
||||||
- **Run scripts/verify-build-environment.* after pulling significant build changes** to avoid
|
|
||||||
drifting tooling setups.
|
| Directory | Purpose |
|
||||||
- **Document risky operations**: When touching migrations, asset packers, or scripts that modify
|
|-----------|---------|
|
||||||
files in bulk, add notes under `docs/internal/roadmaps/` or `blueprints/` so others understand the
|
| `agents/` | AI agent coordination, personas, and board |
|
||||||
impact.
|
| `architecture/` | System design and architectural documentation |
|
||||||
- **Use the coordination board** for any change that affects multiple personas or large parts of the
|
| `archive/` | Retired plans, completed features, closed investigations, and maintenance logs |
|
||||||
tree; log blockers and handoffs to reduce conflicting edits.
|
| `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.
|
||||||
|
|||||||
75
docs/internal/agents/README.md
Normal file
75
docs/internal/agents/README.md
Normal 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 persona’s 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.**
|
||||||
182
docs/internal/agents/archive/docs-cleanup-2025-11-27.md
Normal file
182
docs/internal/agents/archive/docs-cleanup-2025-11-27.md
Normal 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
|
||||||
|
|
||||||
@@ -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`
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# Gemini Pro 3 Build Setup Guide
|
||||||
|
|
||||||
|
Quick iteration setup for maximizing Gemini's effectiveness on YAZE.
|
||||||
|
|
||||||
|
## One-Time Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use the dedicated Gemini build script (recommended)
|
||||||
|
./scripts/gemini_build.sh
|
||||||
|
|
||||||
|
# Or manually configure with the Gemini preset
|
||||||
|
cmake --preset mac-gemini
|
||||||
|
|
||||||
|
# Verify setup succeeded
|
||||||
|
ls build_gemini/compile_commands.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fast Rebuild Cycle (~30s-2min)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Recommended: Use the build script
|
||||||
|
./scripts/gemini_build.sh # Build yaze (default)
|
||||||
|
./scripts/gemini_build.sh yaze_test # Build tests
|
||||||
|
./scripts/gemini_build.sh --fresh # Clean reconfigure
|
||||||
|
|
||||||
|
# Or use cmake directly
|
||||||
|
cmake --build build_gemini -j8 --target yaze
|
||||||
|
|
||||||
|
# Rebuild editor library (for src/app/editor/ changes)
|
||||||
|
cmake --build build_gemini -j8 --target yaze_editor
|
||||||
|
|
||||||
|
# Rebuild zelda3 library (for src/zelda3/ changes)
|
||||||
|
cmake --build build_gemini -j8 --target yaze_lib
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Validation (~2-3min)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run stable tests (unit + integration, no ROM required)
|
||||||
|
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
|
||||||
|
|
||||||
|
# Run GUI smoke tests only (~1min)
|
||||||
|
ctest --test-dir build_gemini -L gui --output-on-failure
|
||||||
|
|
||||||
|
# Run specific test by name
|
||||||
|
ctest --test-dir build_gemini -R "OverworldRegression" --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full Validation (when confident)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests
|
||||||
|
ctest --test-dir build_gemini --output-on-failure -j4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Format Check (before committing)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check format without modifying
|
||||||
|
cmake --build build_gemini --target format-check
|
||||||
|
|
||||||
|
# Auto-format all code
|
||||||
|
cmake --build build_gemini --target format
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Directories
|
||||||
|
|
||||||
|
| Path | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `src/app/editor/overworld/` | Overworld editor modules (8 files) |
|
||||||
|
| `src/zelda3/overworld/` | Overworld data models |
|
||||||
|
| `src/zelda3/dungeon/` | Dungeon data models |
|
||||||
|
| `src/app/editor/dungeon/` | Dungeon editor components |
|
||||||
|
| `test/unit/zelda3/` | Unit tests for zelda3 logic |
|
||||||
|
| `test/e2e/` | GUI E2E tests |
|
||||||
|
|
||||||
|
## Iteration Strategy
|
||||||
|
|
||||||
|
1. **Make changes** to target files
|
||||||
|
2. **Rebuild** with `cmake --build build_gemini -j8 --target yaze`
|
||||||
|
3. **Test** with `ctest --test-dir build_gemini -L stable -j4`
|
||||||
|
4. **Repeat** until all tests pass
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Build fails with "target not found"
|
||||||
|
```bash
|
||||||
|
# Reconfigure (CMakeLists.txt changed)
|
||||||
|
./scripts/gemini_build.sh --fresh
|
||||||
|
# Or: cmake --preset mac-gemini --fresh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests timeout
|
||||||
|
```bash
|
||||||
|
# Run with longer timeout
|
||||||
|
ctest --test-dir build_gemini -L stable --timeout 300
|
||||||
|
```
|
||||||
|
|
||||||
|
### Need to see test output
|
||||||
|
```bash
|
||||||
|
ctest --test-dir build_gemini -L stable -V # Verbose
|
||||||
|
```
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
# Gemini Pro 3 Antigravity - YAZE Development Session
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
You are working on **YAZE** (Yet Another Zelda3 Editor), a C++23 cross-platform ROM editor for The Legend of Zelda: A Link to the Past.
|
||||||
|
|
||||||
|
**Your previous session accomplished:**
|
||||||
|
- Fixed ASM version check regression using `OverworldVersionHelper` abstraction
|
||||||
|
- Improved texture queueing in `Tile16Editor`
|
||||||
|
- The insight: `0xFF >= 3` evaluates true, incorrectly treating vanilla ROMs as v3
|
||||||
|
|
||||||
|
**Reference documentation available:**
|
||||||
|
- `docs/internal/agents/gemini-overworld-system-reference.md` - Overworld architecture
|
||||||
|
- `docs/internal/agents/gemini-dungeon-system-reference.md` - Dungeon architecture
|
||||||
|
- `docs/internal/agents/gemini-build-setup.md` - Build commands
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Setup build directory (use dedicated dir, not user's build/)
|
||||||
|
cmake --preset mac-dbg -B build_gemini
|
||||||
|
|
||||||
|
# 2. Build editor
|
||||||
|
cmake --build build_gemini -j8 --target yaze
|
||||||
|
|
||||||
|
# 3. Run stable tests
|
||||||
|
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task Categories
|
||||||
|
|
||||||
|
### Category A: Overworld Editor Gaps
|
||||||
|
|
||||||
|
#### A1. Texture Queueing TODOs (6 locations)
|
||||||
|
**File:** `src/app/editor/overworld/overworld_editor.cc`
|
||||||
|
**Lines:** 1392, 1397, 1740, 1809, 1819, 1962
|
||||||
|
|
||||||
|
These commented-out Renderer calls need to be converted to the Arena queue system:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BEFORE (blocking - commented out)
|
||||||
|
// Renderer::Get().RenderBitmap(&bitmap_);
|
||||||
|
|
||||||
|
// AFTER (non-blocking)
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(gfx::TextureCommand{
|
||||||
|
.operation = gfx::TextureOperation::kCreate,
|
||||||
|
.bitmap = &bitmap_,
|
||||||
|
.priority = gfx::TexturePriority::kHigh
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### A2. Unimplemented Editor Methods
|
||||||
|
**File:** `src/app/editor/overworld/overworld_editor.h` lines 82-87
|
||||||
|
|
||||||
|
| Method | Status | Complexity |
|
||||||
|
|--------|--------|------------|
|
||||||
|
| `Undo()` | Returns `UnimplementedError` | Medium |
|
||||||
|
| `Redo()` | Returns `UnimplementedError` | Medium |
|
||||||
|
| `Cut()` | Returns `UnimplementedError` | Simple |
|
||||||
|
| `Find()` | Returns `UnimplementedError` | Medium |
|
||||||
|
|
||||||
|
#### A3. Entity Popup Static Variable Bug
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc`
|
||||||
|
|
||||||
|
Multiple popups use `static` variables that persist across calls, causing state contamination:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// CURRENT (BUG)
|
||||||
|
bool DrawExitEditorPopup() {
|
||||||
|
static bool set_done = false; // Persists! Wrong entity data shown
|
||||||
|
static int doorType = ...;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIX: Use local variables or proper state management
|
||||||
|
bool DrawExitEditorPopup(ExitEditorState& state) {
|
||||||
|
// state is passed in, not static
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### A4. Exit Editor Unconnected UI
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc` lines 216-264
|
||||||
|
|
||||||
|
UI elements exist but aren't connected to data:
|
||||||
|
- Door type editing (Wooden, Bombable, Sanctuary, Palace)
|
||||||
|
- Door X/Y position
|
||||||
|
- Center X/Y, Link posture, sprite/BG GFX, palette
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Category B: Dungeon Object Rendering
|
||||||
|
|
||||||
|
#### B1. BothBG Dual-Layer Stubs (4 locations)
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc`
|
||||||
|
|
||||||
|
These routines should draw to BOTH BG1 and BG2 but only accept one buffer:
|
||||||
|
|
||||||
|
| Line | Routine | Status |
|
||||||
|
|------|---------|--------|
|
||||||
|
| 375-381 | `DrawRightwards2x4spaced4_1to16_BothBG` | STUB |
|
||||||
|
| 437-442 | `DrawDiagonalAcute_1to16_BothBG` | STUB |
|
||||||
|
| 444-449 | `DrawDiagonalGrave_1to16_BothBG` | STUB |
|
||||||
|
| 755-761 | `DrawDownwards4x2_1to16_BothBG` | STUB |
|
||||||
|
|
||||||
|
**Fix:** Change signature to accept both buffers:
|
||||||
|
```cpp
|
||||||
|
// BEFORE
|
||||||
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||||
|
const RoomObject& obj, gfx::BackgroundBuffer& bg, ...);
|
||||||
|
|
||||||
|
// AFTER
|
||||||
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||||
|
const RoomObject& obj,
|
||||||
|
gfx::BackgroundBuffer& bg1,
|
||||||
|
gfx::BackgroundBuffer& bg2, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### B2. Diagonal Routines Unclear Logic
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc` lines 401-435
|
||||||
|
|
||||||
|
Issues with `DrawDiagonalAcute_1to16` and `DrawDiagonalGrave_1to16`:
|
||||||
|
- Hardcoded `size + 6` and 5 iterations (why?)
|
||||||
|
- Coordinate formula `obj.y_ + (i - s)` can produce negative Y
|
||||||
|
- No bounds checking
|
||||||
|
- Only uses 4 tiles from larger span
|
||||||
|
|
||||||
|
#### B3. CustomDraw and DoorSwitcherer Stubs
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc`
|
||||||
|
- Lines 524-532: `CustomDraw` - only draws first tile
|
||||||
|
- Lines 566-575: `DrawDoorSwitcherer` - only draws first tile
|
||||||
|
|
||||||
|
#### B4. Unknown Object Names (30+ items)
|
||||||
|
**File:** `src/zelda3/dungeon/room_object.h`
|
||||||
|
|
||||||
|
See `gemini-dungeon-system-reference.md` section 7 for full list of objects needing in-game verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Category C: Already Complete (Verification Only)
|
||||||
|
|
||||||
|
#### C1. Tile16Editor Undo/Redo - COMPLETE
|
||||||
|
**File:** `src/app/editor/overworld/tile16_editor.cc`
|
||||||
|
- `SaveUndoState()` at lines 547, 1476, 1511, 1546, 1586, 1620
|
||||||
|
- `Undo()` / `Redo()` at lines 1707-1760
|
||||||
|
- Ctrl+Z/Ctrl+Y at lines 224-231
|
||||||
|
- UI button at line 1101
|
||||||
|
|
||||||
|
**No work needed** - just verify it works.
|
||||||
|
|
||||||
|
#### C2. Entity Deletion Pattern - CORRECT
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc` line 319
|
||||||
|
|
||||||
|
The TODO comment is misleading. The `deleted` flag pattern IS correct for ROM editors:
|
||||||
|
- Entities at fixed ROM offsets can't be "removed"
|
||||||
|
- `entity_operations.cc` reuses deleted slots
|
||||||
|
- Just clarify the comment if desired
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prioritized Task List
|
||||||
|
|
||||||
|
### Phase 1: High Impact (45-60 min)
|
||||||
|
1. **A1** - Texture queueing (6 TODOs) - Prevents UI freezes
|
||||||
|
2. **B1** - BothBG dual-layer stubs (4 routines) - Completes dungeon rendering
|
||||||
|
|
||||||
|
### Phase 2: Medium Impact (30-45 min)
|
||||||
|
3. **A3** - Entity popup static variable bug - Fixes data corruption
|
||||||
|
4. **B2** - Diagonal routine logic - Fixes rendering artifacts
|
||||||
|
|
||||||
|
### Phase 3: Polish (30+ min)
|
||||||
|
5. **A2** - Implement Undo/Redo for OverworldEditor
|
||||||
|
6. **A4** - Connect exit editor UI to data
|
||||||
|
7. **B3** - Implement CustomDraw/DoorSwitcherer
|
||||||
|
|
||||||
|
### Stretch Goals
|
||||||
|
8. **B4** - Verify unknown object names (requires game testing)
|
||||||
|
9. E2E cinematic tests (see `docs/internal/testing/dungeon-gui-test-design.md`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Patterns
|
||||||
|
|
||||||
|
### Texture Queue (Use This!)
|
||||||
|
```cpp
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(gfx::TextureCommand{
|
||||||
|
.operation = gfx::TextureOperation::kCreate, // or kUpdate
|
||||||
|
.bitmap = &bitmap_,
|
||||||
|
.priority = gfx::TexturePriority::kHigh
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version-Aware Code
|
||||||
|
```cpp
|
||||||
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
|
if (OverworldVersionHelper::SupportsAreaEnum(version)) {
|
||||||
|
// v3+ only
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
```cpp
|
||||||
|
absl::Status MyFunction() {
|
||||||
|
ASSIGN_OR_RETURN(auto data, LoadData());
|
||||||
|
RETURN_IF_ERROR(ProcessData(data));
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After each change
|
||||||
|
cmake --build build_gemini -j8 --target yaze
|
||||||
|
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
|
||||||
|
|
||||||
|
# Before finishing
|
||||||
|
cmake --build build_gemini --target format-check
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- [ ] `grep "TODO.*texture" src/app/editor/overworld/overworld_editor.cc` returns nothing
|
||||||
|
- [ ] BothBG routines accept both buffer parameters
|
||||||
|
- [ ] Static variable bug in entity popups fixed
|
||||||
|
- [ ] `ctest -L stable` passes 100%
|
||||||
|
- [ ] Code formatted
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Quick Reference
|
||||||
|
|
||||||
|
| System | Key Files |
|
||||||
|
|--------|-----------|
|
||||||
|
| Overworld Editor | `src/app/editor/overworld/overworld_editor.cc` (3,204 lines) |
|
||||||
|
| Entity UI | `src/app/editor/overworld/entity.cc` (491 lines) |
|
||||||
|
| Tile16 Editor | `src/app/editor/overworld/tile16_editor.cc` (2,584 lines) |
|
||||||
|
| Object Drawer | `src/zelda3/dungeon/object_drawer.cc` (972 lines) |
|
||||||
|
| Room Object | `src/zelda3/dungeon/room_object.h` (633 lines) |
|
||||||
@@ -0,0 +1,376 @@
|
|||||||
|
# YAZE Overworld System - Complete Technical Reference
|
||||||
|
|
||||||
|
Comprehensive reference for AI agents working on the YAZE Overworld editing system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Architecture Overview
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
```
|
||||||
|
src/zelda3/overworld/
|
||||||
|
├── overworld.h/cc # Main Overworld class (2,500+ lines)
|
||||||
|
├── overworld_map.h/cc # Individual map (OverworldMap class)
|
||||||
|
├── overworld_version_helper.h # Version detection & feature gates
|
||||||
|
├── overworld_item.h/cc # Item entities
|
||||||
|
├── overworld_entrance.h # Entrance/hole entities
|
||||||
|
├── overworld_exit.h # Exit entities
|
||||||
|
|
||||||
|
src/app/editor/overworld/
|
||||||
|
├── overworld_editor.h/cc # Main editor (3,204 lines)
|
||||||
|
├── map_properties.cc # MapPropertiesSystem (1,759 lines)
|
||||||
|
├── tile16_editor.h/cc # Tile16Editor (2,584 lines)
|
||||||
|
├── entity.cc # Entity UI popups (491 lines)
|
||||||
|
├── entity_operations.cc # Entity CRUD helpers (239 lines)
|
||||||
|
├── overworld_entity_renderer.cc # Entity drawing (151 lines)
|
||||||
|
├── scratch_space.cc # Tile storage utilities (444 lines)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Model Hierarchy
|
||||||
|
```
|
||||||
|
Rom (raw data)
|
||||||
|
└── Overworld (coordinator)
|
||||||
|
├── tiles16_[] (3,752-4,096 Tile16 definitions)
|
||||||
|
├── tiles32_unique_[] (up to 8,864 Tile32 definitions)
|
||||||
|
├── overworld_maps_[160] (individual map screens)
|
||||||
|
│ └── OverworldMap
|
||||||
|
│ ├── area_graphics_, area_palette_
|
||||||
|
│ ├── bitmap_data_[] (256x256 rendered pixels)
|
||||||
|
│ └── current_gfx_[] (graphics buffer)
|
||||||
|
├── all_entrances_[] (~129 entrance points)
|
||||||
|
├── all_holes_[] (~19 hole entrances)
|
||||||
|
├── all_exits_[] (~79 exit points)
|
||||||
|
├── all_items_[] (collectible items)
|
||||||
|
└── all_sprites_[3][] (sprites per game state)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. ZSCustomOverworld Version System
|
||||||
|
|
||||||
|
### Version Detection
|
||||||
|
```cpp
|
||||||
|
// ROM address for version byte
|
||||||
|
constexpr int OverworldCustomASMHasBeenApplied = 0x140145;
|
||||||
|
|
||||||
|
// Version values
|
||||||
|
0xFF = Vanilla ROM (unpatched)
|
||||||
|
0x01 = ZSCustomOverworld v1
|
||||||
|
0x02 = ZSCustomOverworld v2
|
||||||
|
0x03 = ZSCustomOverworld v3+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature Matrix
|
||||||
|
|
||||||
|
| Feature | Vanilla | v1 | v2 | v3 |
|
||||||
|
|---------|---------|----|----|-----|
|
||||||
|
| Basic map editing | Y | Y | Y | Y |
|
||||||
|
| Large maps (2x2) | Y | Y | Y | Y |
|
||||||
|
| Expanded pointers (0x130000+) | - | Y | Y | Y |
|
||||||
|
| Custom BG colors | - | - | Y | Y |
|
||||||
|
| Main palette per area | - | - | Y | Y |
|
||||||
|
| Wide areas (2x1) | - | - | - | Y |
|
||||||
|
| Tall areas (1x2) | - | - | - | Y |
|
||||||
|
| Custom tile GFX (8 per map) | - | - | - | Y |
|
||||||
|
| Animated GFX | - | - | - | Y |
|
||||||
|
| Subscreen overlays | - | - | - | Y |
|
||||||
|
|
||||||
|
### OverworldVersionHelper API
|
||||||
|
```cpp
|
||||||
|
// src/zelda3/overworld/overworld_version_helper.h
|
||||||
|
|
||||||
|
enum class OverworldVersion { kVanilla=0, kZSCustomV1=1, kZSCustomV2=2, kZSCustomV3=3 };
|
||||||
|
enum class AreaSizeEnum { SmallArea=0, LargeArea=1, WideArea=2, TallArea=3 };
|
||||||
|
|
||||||
|
// Detection
|
||||||
|
static OverworldVersion GetVersion(const Rom& rom);
|
||||||
|
static uint8_t GetAsmVersion(const Rom& rom);
|
||||||
|
|
||||||
|
// Feature gates (use these, not raw version checks!)
|
||||||
|
static bool SupportsAreaEnum(OverworldVersion v); // v3 only
|
||||||
|
static bool SupportsExpandedSpace(OverworldVersion v); // v1+
|
||||||
|
static bool SupportsCustomBGColors(OverworldVersion v);// v2+
|
||||||
|
static bool SupportsCustomTileGFX(OverworldVersion v); // v3 only
|
||||||
|
static bool SupportsAnimatedGFX(OverworldVersion v); // v3 only
|
||||||
|
static bool SupportsSubscreenOverlay(OverworldVersion v); // v3 only
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Tile System Architecture
|
||||||
|
|
||||||
|
### Tile Hierarchy
|
||||||
|
```
|
||||||
|
Tile8 (8x8 pixels) - Base SNES tile
|
||||||
|
↓
|
||||||
|
Tile16 (16x16 pixels) - 2x2 grid of Tile8s
|
||||||
|
↓
|
||||||
|
Tile32 (32x32 pixels) - 2x2 grid of Tile16s
|
||||||
|
↓
|
||||||
|
Map Screen (256x256 pixels) - 8x8 grid of Tile32s
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tile16 Structure
|
||||||
|
```cpp
|
||||||
|
// src/app/gfx/types/snes_tile.h
|
||||||
|
|
||||||
|
class TileInfo {
|
||||||
|
uint16_t id_; // 9-bit tile8 ID (0-511)
|
||||||
|
uint8_t palette_; // 3-bit palette (0-7)
|
||||||
|
bool over_; // Priority flag
|
||||||
|
bool vertical_mirror_; // Y-flip
|
||||||
|
bool horizontal_mirror_; // X-flip
|
||||||
|
};
|
||||||
|
|
||||||
|
class Tile16 {
|
||||||
|
TileInfo tile0_; // Top-left
|
||||||
|
TileInfo tile1_; // Top-right
|
||||||
|
TileInfo tile2_; // Bottom-left
|
||||||
|
TileInfo tile3_; // Bottom-right
|
||||||
|
};
|
||||||
|
|
||||||
|
// ROM storage: 8 bytes per Tile16 at 0x78000 + (ID * 8)
|
||||||
|
// Total: 4,096 Tile16s (0x0000-0x0FFF)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tile16Editor Features (COMPLETE)
|
||||||
|
The Tile16Editor at `tile16_editor.cc` is **fully implemented** with:
|
||||||
|
|
||||||
|
- **Undo/Redo System** (lines 1681-1760)
|
||||||
|
- `SaveUndoState()` - captures current state
|
||||||
|
- `Undo()` / `Redo()` - restore states
|
||||||
|
- Ctrl+Z / Ctrl+Y keyboard shortcuts
|
||||||
|
- 50-state stack with rate limiting
|
||||||
|
|
||||||
|
- **Clipboard Operations**
|
||||||
|
- Copy/Paste Tile16s
|
||||||
|
- 4 scratch space slots
|
||||||
|
|
||||||
|
- **Editing Features**
|
||||||
|
- Tile8 composition into Tile16
|
||||||
|
- Flip horizontal/vertical
|
||||||
|
- Palette cycling (0-7)
|
||||||
|
- Fill with single Tile8
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Map Organization
|
||||||
|
|
||||||
|
### Index Scheme
|
||||||
|
```
|
||||||
|
Index 0x00-0x3F: Light World (64 maps, 8x8 grid)
|
||||||
|
Index 0x40-0x7F: Dark World (64 maps, 8x8 grid)
|
||||||
|
Index 0x80-0x9F: Special World (32 maps, 8x4 grid)
|
||||||
|
|
||||||
|
Total: 160 maps
|
||||||
|
|
||||||
|
Grid position: X = index % 8, Y = index / 8
|
||||||
|
World position: X * 512 pixels, Y * 512 pixels
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Area Maps
|
||||||
|
```cpp
|
||||||
|
enum class AreaSizeEnum {
|
||||||
|
SmallArea = 0, // 1x1 screen (standard)
|
||||||
|
LargeArea = 1, // 2x2 screens (4 quadrants)
|
||||||
|
WideArea = 2, // 2x1 screens (v3 only)
|
||||||
|
TallArea = 3, // 1x2 screens (v3 only)
|
||||||
|
};
|
||||||
|
|
||||||
|
// IMPORTANT: Always use ConfigureMultiAreaMap() for size changes!
|
||||||
|
// Never set area_size_ directly - it handles parent IDs and ROM persistence
|
||||||
|
absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Entity System
|
||||||
|
|
||||||
|
### Entity Types
|
||||||
|
| Type | Storage | Count | ROM Address |
|
||||||
|
|------|---------|-------|-------------|
|
||||||
|
| Entrances | `all_entrances_` | ~129 | 0xDB96F+ |
|
||||||
|
| Holes | `all_holes_` | ~19 | 0xDB800+ |
|
||||||
|
| Exits | `all_exits_` | ~79 | 0x15D8A+ |
|
||||||
|
| Items | `all_items_` | Variable | 0xDC2F9+ |
|
||||||
|
| Sprites | `all_sprites_[3]` | Variable | 0x4C881+ |
|
||||||
|
|
||||||
|
### Entity Deletion Pattern
|
||||||
|
Entities use a `deleted` flag pattern - this is **CORRECT** for ROM editors:
|
||||||
|
```cpp
|
||||||
|
// Entities live at fixed ROM offsets, cannot be truly "removed"
|
||||||
|
// Setting deleted = true marks them as inactive
|
||||||
|
// entity_operations.cc reuses deleted slots for new entities
|
||||||
|
item.deleted = true; // Proper pattern
|
||||||
|
|
||||||
|
// Renderer skips deleted entities (overworld_entity_renderer.cc)
|
||||||
|
if (!item.deleted) { /* render */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Graphics Loading Pipeline
|
||||||
|
|
||||||
|
### Load Sequence
|
||||||
|
```
|
||||||
|
1. Overworld::Load(rom)
|
||||||
|
└── LoadOverworldMaps()
|
||||||
|
└── For each map (0-159):
|
||||||
|
└── OverworldMap::ctor()
|
||||||
|
├── LoadAreaInfo()
|
||||||
|
└── LoadCustomOverworldData() [v3]
|
||||||
|
|
||||||
|
2. On map access: EnsureMapBuilt(map_index)
|
||||||
|
└── BuildMap()
|
||||||
|
├── LoadAreaGraphics()
|
||||||
|
├── BuildTileset()
|
||||||
|
├── BuildTiles16Gfx()
|
||||||
|
├── LoadPalette()
|
||||||
|
├── LoadOverlay()
|
||||||
|
└── BuildBitmap()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Texture Queue System
|
||||||
|
Use deferred texture loading via `gfx::Arena`:
|
||||||
|
```cpp
|
||||||
|
// CORRECT: Non-blocking, uses queue
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(gfx::TextureCommand{
|
||||||
|
.operation = gfx::TextureOperation::kCreate,
|
||||||
|
.bitmap = &some_bitmap_,
|
||||||
|
.priority = gfx::TexturePriority::kHigh
|
||||||
|
});
|
||||||
|
|
||||||
|
// WRONG: Blocking, causes UI freeze
|
||||||
|
Renderer::Get().RenderBitmap(&some_bitmap_);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. ROM Addresses (Key Locations)
|
||||||
|
|
||||||
|
### Vanilla Addresses
|
||||||
|
```cpp
|
||||||
|
// Tile data
|
||||||
|
kTile16Ptr = 0x78000 // Tile16 definitions
|
||||||
|
kOverworldMapSize = 0x12844 // Map size bytes
|
||||||
|
|
||||||
|
// Graphics & Palettes
|
||||||
|
kAreaGfxIdPtr = 0x7C9C // Area graphics IDs
|
||||||
|
kOverworldMapPaletteGroup = 0x7D1C // Palette IDs
|
||||||
|
|
||||||
|
// Entities
|
||||||
|
kOverworldEntranceMap = 0xDB96F // Entrance data
|
||||||
|
kOverworldExitRooms = 0x15D8A // Exit room IDs
|
||||||
|
kOverworldItemPointers = 0xDC2F9 // Item pointers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expanded Addresses (v1+)
|
||||||
|
```cpp
|
||||||
|
// Custom data at 0x140000+
|
||||||
|
OverworldCustomASMHasBeenApplied = 0x140145 // Version byte
|
||||||
|
OverworldCustomAreaSpecificBGPalette = 0x140000 // BG colors (160*2)
|
||||||
|
OverworldCustomMainPaletteArray = 0x140160 // Main palettes (160)
|
||||||
|
OverworldCustomAnimatedGFXArray = 0x1402A0 // Animated GFX (160)
|
||||||
|
OverworldCustomTileGFXGroupArray = 0x140480 // Tile GFX (160*8)
|
||||||
|
OverworldCustomSubscreenOverlayArray = 0x140340 // Overlays (160*2)
|
||||||
|
kOverworldMapParentIdExpanded = 0x140998 // Parent IDs (160)
|
||||||
|
kOverworldMessagesExpanded = 0x1417F8 // Messages (160*2)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Known Gaps in OverworldEditor
|
||||||
|
|
||||||
|
### Critical: Texture Queueing TODOs (6 locations)
|
||||||
|
```cpp
|
||||||
|
// overworld_editor.cc - these Renderer calls need to be converted:
|
||||||
|
Line 1392: // TODO: Queue texture for later rendering
|
||||||
|
Line 1397: // TODO: Queue texture for later rendering
|
||||||
|
Line 1740: // TODO: Queue texture for later rendering
|
||||||
|
Line 1809: // TODO: Queue texture for later rendering
|
||||||
|
Line 1819: // TODO: Queue texture for later rendering
|
||||||
|
Line 1962: // TODO: Queue texture for later rendering
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unimplemented Core Methods
|
||||||
|
```cpp
|
||||||
|
// overworld_editor.h lines 82-87
|
||||||
|
Undo() → Returns UnimplementedError
|
||||||
|
Redo() → Returns UnimplementedError
|
||||||
|
Cut() → Returns UnimplementedError
|
||||||
|
Find() → Returns UnimplementedError
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Popup Static Variable Bug
|
||||||
|
```cpp
|
||||||
|
// entity.cc - Multiple popups use static variables that persist
|
||||||
|
// Causes state contamination when editing multiple entities
|
||||||
|
bool DrawExitEditorPopup() {
|
||||||
|
static bool set_done = false; // BUG: persists across calls
|
||||||
|
static int doorType = ...; // BUG: wrong entity's data shown
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exit Editor Unimplemented Features
|
||||||
|
```cpp
|
||||||
|
// entity.cc lines 216-264
|
||||||
|
// UI exists but not connected to data:
|
||||||
|
- Door type editing (Wooden, Bombable, Sanctuary, Palace)
|
||||||
|
- Door X/Y position
|
||||||
|
- Center X/Y, Link posture, sprite/BG GFX, palette
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Code Patterns to Follow
|
||||||
|
|
||||||
|
### Graphics Refresh
|
||||||
|
```cpp
|
||||||
|
// 1. Update model
|
||||||
|
map.SetProperty(new_value);
|
||||||
|
|
||||||
|
// 2. Reload from ROM
|
||||||
|
map.LoadAreaGraphics();
|
||||||
|
|
||||||
|
// 3. Queue texture update (NOT RenderBitmap!)
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(gfx::TextureCommand{
|
||||||
|
.operation = gfx::TextureOperation::kUpdate,
|
||||||
|
.bitmap = &map_bitmap_,
|
||||||
|
.priority = gfx::TexturePriority::kHigh
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version-Aware Code
|
||||||
|
```cpp
|
||||||
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
|
|
||||||
|
// Use semantic helpers, not raw version checks
|
||||||
|
if (OverworldVersionHelper::SupportsAreaEnum(version)) {
|
||||||
|
// v3+ only code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Rendering Colors (0.85f alpha)
|
||||||
|
```cpp
|
||||||
|
ImVec4 entrance_color(1.0f, 0.85f, 0.0f, 0.85f); // Yellow-gold
|
||||||
|
ImVec4 exit_color(0.0f, 1.0f, 1.0f, 0.85f); // Cyan
|
||||||
|
ImVec4 item_color(1.0f, 0.0f, 0.0f, 0.85f); // Red
|
||||||
|
ImVec4 sprite_color(1.0f, 0.0f, 1.0f, 0.85f); // Magenta
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Testing
|
||||||
|
|
||||||
|
### Run Overworld Tests
|
||||||
|
```bash
|
||||||
|
# Unit tests
|
||||||
|
ctest --test-dir build -R "Overworld" -V
|
||||||
|
|
||||||
|
# Regression tests
|
||||||
|
ctest --test-dir build -R "OverworldRegression" -V
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Files
|
||||||
|
- `test/unit/zelda3/overworld_test.cc` - Core tests
|
||||||
|
- `test/unit/zelda3/overworld_regression_test.cc` - Version helper tests
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
# Gemini Pro 3 Task Checklist
|
||||||
|
|
||||||
|
Prioritized checklist of all identified work items for YAZE development.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Critical Rendering Issues (HIGH PRIORITY)
|
||||||
|
|
||||||
|
### Task A1: Texture Queueing TODOs
|
||||||
|
**File:** `src/app/editor/overworld/overworld_editor.cc`
|
||||||
|
**Impact:** Prevents UI freezes, enables proper texture loading
|
||||||
|
**Time Estimate:** 25 min
|
||||||
|
|
||||||
|
- [ ] Line 1392: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Line 1397: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Line 1740: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Line 1809: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Line 1819: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Line 1962: Convert commented Renderer call to Arena queue
|
||||||
|
- [ ] Verify: `grep "TODO.*texture" overworld_editor.cc` returns nothing
|
||||||
|
- [ ] Test: Run stable tests, no UI freezes on map load
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task B1: BothBG Dual-Layer Stubs
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc`
|
||||||
|
**Impact:** Completes dungeon object rendering for dual-layer objects
|
||||||
|
**Time Estimate:** 30 min
|
||||||
|
|
||||||
|
- [ ] Line 375-381: `DrawRightwards2x4spaced4_1to16_BothBG`
|
||||||
|
- Change signature to accept both `bg1` and `bg2`
|
||||||
|
- Call underlying routine for both buffers
|
||||||
|
- [ ] Line 437-442: `DrawDiagonalAcute_1to16_BothBG`
|
||||||
|
- Change signature to accept both buffers
|
||||||
|
- [ ] Line 444-449: `DrawDiagonalGrave_1to16_BothBG`
|
||||||
|
- Change signature to accept both buffers
|
||||||
|
- [ ] Line 755-761: `DrawDownwards4x2_1to16_BothBG`
|
||||||
|
- Change signature to accept both buffers
|
||||||
|
- [ ] Update `DrawObject()` call sites to pass both buffers for BothBG routines
|
||||||
|
- [ ] Test: Dungeon rooms with dual-layer objects render correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Bug Fixes (MEDIUM PRIORITY)
|
||||||
|
|
||||||
|
### Task A3: Entity Popup Static Variable Bug
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc`
|
||||||
|
**Impact:** Fixes data corruption when editing multiple entities
|
||||||
|
**Time Estimate:** 20 min
|
||||||
|
|
||||||
|
- [ ] `DrawExitEditorPopup()` (line 152+):
|
||||||
|
- Remove `static bool set_done`
|
||||||
|
- Remove other static variables
|
||||||
|
- Pass state via parameter or use popup ID
|
||||||
|
- [ ] `DrawItemEditorPopup()` (line 320+):
|
||||||
|
- Remove static variables
|
||||||
|
- [ ] Other popup functions:
|
||||||
|
- Audit for static state
|
||||||
|
- [ ] Test: Edit multiple exits/items in sequence, verify correct data
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task B2: Diagonal Routine Logic
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc` lines 401-435
|
||||||
|
**Impact:** Fixes rendering artifacts for diagonal objects
|
||||||
|
**Time Estimate:** 30 min
|
||||||
|
|
||||||
|
- [ ] `DrawDiagonalAcute_1to16`:
|
||||||
|
- Verify/document the `size + 6` constant
|
||||||
|
- Add bounds checking for negative Y coordinates
|
||||||
|
- Handle edge cases at canvas boundaries
|
||||||
|
- [ ] `DrawDiagonalGrave_1to16`:
|
||||||
|
- Same fixes as acute version
|
||||||
|
- [ ] Test: Place diagonal objects in dungeon editor, verify appearance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Feature Completion (POLISH)
|
||||||
|
|
||||||
|
### Task A2: OverworldEditor Undo/Redo
|
||||||
|
**File:** `src/app/editor/overworld/overworld_editor.h` lines 82-87
|
||||||
|
**Impact:** Enables undo/redo for overworld edits
|
||||||
|
**Time Estimate:** 45 min
|
||||||
|
|
||||||
|
- [ ] Design undo state structure:
|
||||||
|
```cpp
|
||||||
|
struct OverworldUndoState {
|
||||||
|
int map_index;
|
||||||
|
std::vector<uint16_t> tile_data;
|
||||||
|
// Entity changes?
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- [ ] Add `undo_stack_` and `redo_stack_` members
|
||||||
|
- [ ] Implement `Undo()` method
|
||||||
|
- [ ] Implement `Redo()` method
|
||||||
|
- [ ] Wire up Ctrl+Z / Ctrl+Y shortcuts
|
||||||
|
- [ ] Test: Make edits, undo, redo - verify state restoration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task A4: Exit Editor UI Connection
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc` lines 216-264
|
||||||
|
**Impact:** Makes exit editor fully functional
|
||||||
|
**Time Estimate:** 30 min
|
||||||
|
|
||||||
|
- [ ] Connect door type radio buttons to `exit.door_type_`
|
||||||
|
- [ ] Connect door X/Y inputs to exit data
|
||||||
|
- [ ] Connect Center X/Y to exit scroll data
|
||||||
|
- [ ] Connect palette/GFX fields to exit properties
|
||||||
|
- [ ] Test: Edit exit properties, save ROM, verify changes persisted
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task B3: CustomDraw and DoorSwitcherer
|
||||||
|
**File:** `src/zelda3/dungeon/object_drawer.cc`
|
||||||
|
**Impact:** Completes special object rendering
|
||||||
|
**Time Estimate:** 30 min
|
||||||
|
|
||||||
|
- [ ] `CustomDraw` (lines 524-532):
|
||||||
|
- Research expected behavior from ZScream/game
|
||||||
|
- Implement proper drawing logic
|
||||||
|
- [ ] `DrawDoorSwitcherer` (lines 566-575):
|
||||||
|
- Research door switching animation/logic
|
||||||
|
- Implement proper drawing
|
||||||
|
- [ ] Test: Place custom objects, verify appearance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Stretch Goals
|
||||||
|
|
||||||
|
### Task B4: Object Name Verification
|
||||||
|
**File:** `src/zelda3/dungeon/room_object.h`
|
||||||
|
**Impact:** Improves editor usability with proper names
|
||||||
|
**Time Estimate:** 2+ hours (requires game testing)
|
||||||
|
|
||||||
|
See `gemini-dungeon-system-reference.md` section 7 for full list.
|
||||||
|
|
||||||
|
High-priority unknowns:
|
||||||
|
- [ ] Line 234: Object 0x35 "WEIRD DOOR" - investigate
|
||||||
|
- [ ] Lines 392-395: Objects 0xDE-0xE1 "Moving wall flag" - WTF IS THIS?
|
||||||
|
- [ ] Lines 350-353: Diagonal layer 2 mask B objects - verify
|
||||||
|
- [ ] Multiple "Unknown" objects in Type 2 and Type 3 ranges
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task E2E: Cinematic Tests
|
||||||
|
**Reference:** `docs/internal/testing/dungeon-gui-test-design.md`
|
||||||
|
**Impact:** Visual regression testing, demo capability
|
||||||
|
**Time Estimate:** 45+ min
|
||||||
|
|
||||||
|
- [ ] Create screenshot capture utility
|
||||||
|
- [ ] Implement basic cinematic test sequence
|
||||||
|
- [ ] Add visual diff comparison
|
||||||
|
- [ ] Document test workflow
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Already Complete (Verification Only)
|
||||||
|
|
||||||
|
### Tile16Editor Undo/Redo
|
||||||
|
**File:** `src/app/editor/overworld/tile16_editor.cc`
|
||||||
|
**Status:** FULLY IMPLEMENTED
|
||||||
|
|
||||||
|
- [x] `SaveUndoState()` implemented
|
||||||
|
- [x] `Undo()` / `Redo()` implemented
|
||||||
|
- [x] Ctrl+Z / Ctrl+Y shortcuts working
|
||||||
|
- [x] UI button at line 1101
|
||||||
|
- [x] Stack management with limits
|
||||||
|
|
||||||
|
**Action:** Verify it works, no changes needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Entity Deletion Pattern
|
||||||
|
**File:** `src/app/editor/overworld/entity.cc` line 319
|
||||||
|
**Status:** CORRECT (misleading TODO)
|
||||||
|
|
||||||
|
The `deleted` flag pattern IS correct for ROM editors:
|
||||||
|
- Entities at fixed ROM offsets
|
||||||
|
- `entity_operations.cc` reuses deleted slots
|
||||||
|
- Renderer skips deleted entities
|
||||||
|
|
||||||
|
**Action:** Optionally clarify the TODO comment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference: File Locations
|
||||||
|
|
||||||
|
| Task | Primary File | Line Numbers |
|
||||||
|
|------|--------------|--------------|
|
||||||
|
| A1 | overworld_editor.cc | 1392, 1397, 1740, 1809, 1819, 1962 |
|
||||||
|
| A2 | overworld_editor.h | 82-87 |
|
||||||
|
| A3 | entity.cc | 152+, 320+ |
|
||||||
|
| A4 | entity.cc | 216-264 |
|
||||||
|
| B1 | object_drawer.cc | 375, 437, 444, 755 |
|
||||||
|
| B2 | object_drawer.cc | 401-435 |
|
||||||
|
| B3 | object_drawer.cc | 524-532, 566-575 |
|
||||||
|
| B4 | room_object.h | Multiple (see section 7 of dungeon ref) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
cmake --build build_gemini -j8 --target yaze
|
||||||
|
|
||||||
|
# Test
|
||||||
|
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
|
||||||
|
|
||||||
|
# Format check
|
||||||
|
cmake --build build_gemini --target format-check
|
||||||
|
|
||||||
|
# Specific test
|
||||||
|
ctest --test-dir build_gemini -R "Overworld" -V
|
||||||
|
ctest --test-dir build_gemini -R "Dungeon" -V
|
||||||
|
```
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# Gemini 3 Pro Prompt: Overworld Regression Fix and Improvements
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
You are working on **yaze** (Yet Another Zelda3 Editor), a C++23 ROM editor for The Legend of Zelda: A Link to the Past. A regression has been introduced that breaks loading of custom ROMs like "Oracle of Secrets" ROM hack.
|
||||||
|
|
||||||
|
## Primary Bug: ASM Version Check Inconsistency
|
||||||
|
|
||||||
|
### Root Cause Analysis
|
||||||
|
|
||||||
|
The recent refactoring introduced `OverworldVersionHelper` for centralized ROM version detection, but **not all code paths were updated to use it consistently**. Specifically:
|
||||||
|
|
||||||
|
**In `src/zelda3/overworld/overworld.cc:71`:**
|
||||||
|
```cpp
|
||||||
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
if (asm_version >= 3) { // BUG: 0xFF (255) is >= 3!
|
||||||
|
AssignMapSizes(overworld_maps_);
|
||||||
|
} else {
|
||||||
|
FetchLargeMaps(); // Vanilla ROMs need this path
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**The bug**: `asm_version >= 3` evaluates to `true` for vanilla ROMs where `asm_version == 0xFF` (255), causing vanilla ROMs and custom ROMs without ZScream ASM patches to incorrectly call `AssignMapSizes()` instead of `FetchLargeMaps()`.
|
||||||
|
|
||||||
|
**Other places correctly check**:
|
||||||
|
```cpp
|
||||||
|
if (asm_version >= 3 && asm_version != 0xFF) { ... } // Correct
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inconsistent Locations Found
|
||||||
|
|
||||||
|
Search results showing mixed patterns:
|
||||||
|
- `overworld.cc:71` - **BUG**: `if (asm_version >= 3)` - missing `&& asm_version != 0xFF`
|
||||||
|
- `overworld.cc:449` - **BUG**: `if (expanded_flag != 0x04 || asm_version >= 3)` - missing check
|
||||||
|
- `overworld.cc:506` - **BUG**: similar pattern
|
||||||
|
- `overworld.cc:281` - **CORRECT**: `(asm_version < 3 || asm_version == 0xFF)`
|
||||||
|
- `overworld.cc:373` - **CORRECT**: `if (asm_version >= 3 && asm_version != 0xFF)`
|
||||||
|
- Other files also have inconsistencies
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
### Phase 1: Fix the Regression (CRITICAL)
|
||||||
|
|
||||||
|
1. **Update all ASM version checks** in overworld code to either:
|
||||||
|
- Use `OverworldVersionHelper::GetVersion()` and semantic checks like `SupportsAreaEnum()`, OR
|
||||||
|
- Consistently use `asm_version >= 3 && asm_version != 0xFF` pattern
|
||||||
|
|
||||||
|
2. **Key files to fix**:
|
||||||
|
- `src/zelda3/overworld/overworld.cc`
|
||||||
|
- `src/zelda3/overworld/overworld_map.cc`
|
||||||
|
- `src/zelda3/overworld/overworld_item.cc`
|
||||||
|
|
||||||
|
3. **Priority fixes in `overworld.cc`**:
|
||||||
|
- Line 71: Change to `if (asm_version >= 3 && asm_version != 0xFF)`
|
||||||
|
- Line 449: Add `&& asm_version != 0xFF` check
|
||||||
|
- Line 506: Add `&& asm_version != 0xFF` check
|
||||||
|
- Review all other locations from the grep results
|
||||||
|
|
||||||
|
### Phase 2: Standardize Version Checking (Recommended)
|
||||||
|
|
||||||
|
Replace all raw `asm_version` checks with `OverworldVersionHelper`:
|
||||||
|
|
||||||
|
**Instead of:**
|
||||||
|
```cpp
|
||||||
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use:**
|
||||||
|
```cpp
|
||||||
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
|
if (OverworldVersionHelper::SupportsAreaEnum(version)) {
|
||||||
|
```
|
||||||
|
|
||||||
|
This centralizes the logic and prevents future inconsistencies.
|
||||||
|
|
||||||
|
### Phase 3: Add Unit Tests
|
||||||
|
|
||||||
|
Create tests in `test/unit/zelda3/overworld_test.cc` to verify:
|
||||||
|
1. Vanilla ROM (0xFF) uses `FetchLargeMaps()` path
|
||||||
|
2. ZScream v3 ROM (0x03) uses `AssignMapSizes()` path
|
||||||
|
3. Custom ROMs with other values behave correctly
|
||||||
|
|
||||||
|
## Key Files Reference
|
||||||
|
|
||||||
|
```
|
||||||
|
src/zelda3/overworld/
|
||||||
|
├── overworld.cc # Main loading logic
|
||||||
|
├── overworld.h
|
||||||
|
├── overworld_map.cc # Individual map handling
|
||||||
|
├── overworld_map.h
|
||||||
|
├── overworld_item.cc # Item loading
|
||||||
|
├── overworld_item.h
|
||||||
|
├── overworld_entrance.h # Entrance/Exit data
|
||||||
|
├── overworld_exit.cc
|
||||||
|
├── overworld_exit.h
|
||||||
|
├── overworld_version_helper.h # Version detection helper
|
||||||
|
```
|
||||||
|
|
||||||
|
## OverworldVersionHelper API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class OverworldVersion {
|
||||||
|
kVanilla = 0, // 0xFF in ROM - no ZScream ASM
|
||||||
|
kZSCustomV1 = 1,
|
||||||
|
kZSCustomV2 = 2,
|
||||||
|
kZSCustomV3 = 3 // Area enum system
|
||||||
|
};
|
||||||
|
|
||||||
|
class OverworldVersionHelper {
|
||||||
|
static OverworldVersion GetVersion(const Rom& rom);
|
||||||
|
static bool SupportsAreaEnum(OverworldVersion v); // v3 only
|
||||||
|
static bool SupportsExpandedSpace(OverworldVersion v); // v1+
|
||||||
|
static bool SupportsCustomBGColors(OverworldVersion v); // v2+
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commits That Introduced the Regression
|
||||||
|
|
||||||
|
1. `1e39df88a3` - "refactor: enhance overworld entity properties and version handling"
|
||||||
|
- Introduced `OverworldVersionHelper`
|
||||||
|
- 15 files changed, +546 -282 lines
|
||||||
|
|
||||||
|
2. `5894809aaf` - "refactor: improve overworld map version handling and code organization"
|
||||||
|
- Updated `OverworldMap` to use version helper
|
||||||
|
- 4 files changed, +145 -115 lines
|
||||||
|
|
||||||
|
## Build & Test Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configure
|
||||||
|
cmake --preset mac-dbg
|
||||||
|
|
||||||
|
# Build
|
||||||
|
cmake --build build --target yaze -j8
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
ctest --test-dir build -L stable -R overworld
|
||||||
|
|
||||||
|
# Run the app to test loading
|
||||||
|
./build/bin/Debug/yaze.app/Contents/MacOS/yaze --rom_file=/path/to/oracle_of_secrets.sfc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
1. Oracle of Secrets ROM loads correctly in the Overworld Editor
|
||||||
|
2. Vanilla ALTTP ROMs continue to work
|
||||||
|
3. ZScream v3 patched ROMs continue to work
|
||||||
|
4. All existing unit tests pass
|
||||||
|
5. No new compiler warnings
|
||||||
|
|
||||||
|
## Additional Context
|
||||||
|
|
||||||
|
- The editor supports multiple ROM types: Vanilla, ZScream v1/v2/v3 patched ROMs, and custom hacks
|
||||||
|
- `OverworldCustomASMHasBeenApplied` address (0x130000) stores the version byte
|
||||||
|
- 0xFF = vanilla (no patches), 1-2 = legacy ZScream, 3 = current ZScream
|
||||||
|
- "Oracle of Secrets" is a popular ROM hack that may use 0xFF or a custom value
|
||||||
|
|
||||||
|
## Code Quality Requirements
|
||||||
|
|
||||||
|
- Follow Google C++ Style Guide
|
||||||
|
- Use `absl::Status` for error handling
|
||||||
|
- Run clang-format before committing
|
||||||
|
- Update CLAUDE.md coordination board when done
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
# Gemini 3 Pro - v0.4.0 Development Session
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
You are working on **YAZE** (Yet Another Zelda3 Editor), a C++23 cross-platform ROM editor for The Legend of Zelda: A Link to the Past.
|
||||||
|
|
||||||
|
**Current Situation:**
|
||||||
|
- **v0.3.9**: CI/CD hotfix running (another agent handling - DO NOT TOUCH)
|
||||||
|
- **v0.4.0**: Your focus - "Editor Stability & OOS Support"
|
||||||
|
- **Goal**: Unblock Oracle of Secrets (OOS) ROM hack development
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use dedicated build directory (DO NOT use build/ or build_agent/)
|
||||||
|
cmake --preset mac-dbg -B build_gemini
|
||||||
|
cmake --build build_gemini -j8 --target yaze
|
||||||
|
|
||||||
|
# Run stable tests
|
||||||
|
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Completed Work
|
||||||
|
|
||||||
|
### Priority 2: Message Editor - Expanded BIN Export [COMPLETE]
|
||||||
|
|
||||||
|
**Completed by Gemini 3 Pro:**
|
||||||
|
|
||||||
|
1. **JSON Export Implementation** (`message_data.h/cc`):
|
||||||
|
- Added `SerializeMessagesToJson()` - Converts `MessageData` vector to JSON array
|
||||||
|
- Added `ExportMessagesToJson()` - Writes JSON file with error handling
|
||||||
|
- Uses nlohmann/json library with 2-space pretty printing
|
||||||
|
|
||||||
|
2. **File Dialog Integration** (`message_editor.cc:317-376`):
|
||||||
|
- "Load Expanded Message" - Opens file dialog to load BIN
|
||||||
|
- "Save Expanded Messages" - Saves to remembered path
|
||||||
|
- "Save As..." - Prompts for new path
|
||||||
|
- "Export to JSON" - JSON export with file dialog
|
||||||
|
|
||||||
|
3. **Path Persistence**:
|
||||||
|
- `expanded_message_path_` member stores last used path
|
||||||
|
- Reuses path for subsequent saves
|
||||||
|
|
||||||
|
4. **SaveExpandedMessages() Implementation** (`message_editor.cc:521-571`):
|
||||||
|
- Uses `expanded_message_bin_` member for ROM-like storage
|
||||||
|
- Handles buffer expansion for new messages
|
||||||
|
- Writes terminator byte (0xFF) after data
|
||||||
|
|
||||||
|
### Priority 1: Dungeon Editor - SaveDungeon() [IN PROGRESS]
|
||||||
|
|
||||||
|
**Completed by Gemini 3 Pro:**
|
||||||
|
|
||||||
|
1. **SaveDungeon() Implementation** (`dungeon_editor_system.cc:44-59`):
|
||||||
|
- No longer a stub! Loops through all 296 rooms
|
||||||
|
- Calls `SaveRoomData()` for each room
|
||||||
|
- Tracks dirty state and last save time
|
||||||
|
|
||||||
|
2. **SaveObjects() Implementation** (`room.cc:873-925`):
|
||||||
|
- Properly calculates available space via `CalculateRoomSize()`
|
||||||
|
- Validates encoded size fits before writing
|
||||||
|
- Returns `OutOfRangeError` if data too large
|
||||||
|
|
||||||
|
3. **SaveSprites() Implementation** (`room.cc:927-999`):
|
||||||
|
- Calculates sprite space from pointer table
|
||||||
|
- Handles sortsprites byte
|
||||||
|
- Returns `OutOfRangeError` if data too large
|
||||||
|
|
||||||
|
4. **New Tests** (`test/unit/zelda3/dungeon/dungeon_save_test.cc`):
|
||||||
|
- `SaveObjects_FitsInSpace` - verifies normal save works
|
||||||
|
- `SaveObjects_TooLarge` - verifies overflow detection
|
||||||
|
- `SaveSprites_FitsInSpace` - verifies normal save works
|
||||||
|
- `SaveSprites_TooLarge` - verifies overflow detection
|
||||||
|
|
||||||
|
**Tests Pass:**
|
||||||
|
```bash
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*"
|
||||||
|
# [ PASSED ] 4 tests.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Instructions
|
||||||
|
|
||||||
|
### Build and Run Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build test target (uses existing build_gemini)
|
||||||
|
cmake --build build_gemini --target yaze_test_stable -j8
|
||||||
|
|
||||||
|
# Run ALL stable tests
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable
|
||||||
|
|
||||||
|
# Run specific test pattern
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*"
|
||||||
|
|
||||||
|
# Run dungeon-related tests
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*Dungeon*"
|
||||||
|
|
||||||
|
# Run with verbose output
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*YourTest*" --gtest_print_time=1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Known Test Issues (Pre-existing)
|
||||||
|
|
||||||
|
**FAILING:** `RoomObjectEncodingTest.DetermineObjectTypeType2`
|
||||||
|
- Location: `test/unit/zelda3/dungeon/room_object_encoding_test.cc:29-31`
|
||||||
|
- Issue: `DetermineObjectType()` returns 1 instead of 2 for bytes 0xFC, 0xFD, 0xFF
|
||||||
|
- Status: Pre-existing failure, NOT caused by your changes
|
||||||
|
- Action: Ignore unless you're specifically working on object type detection
|
||||||
|
|
||||||
|
### Test File Locations
|
||||||
|
|
||||||
|
| Test Type | Location | Filter Pattern |
|
||||||
|
|-----------|----------|----------------|
|
||||||
|
| Dungeon Save | `test/unit/zelda3/dungeon/dungeon_save_test.cc` | `*DungeonSave*` |
|
||||||
|
| Room Encoding | `test/unit/zelda3/dungeon/room_object_encoding_test.cc` | `*RoomObjectEncoding*` |
|
||||||
|
| Room Manipulation | `test/unit/zelda3/dungeon/room_manipulation_test.cc` | `*RoomManipulation*` |
|
||||||
|
| Dungeon Integration | `test/integration/dungeon_editor_test.cc` | `*DungeonEditorIntegration*` |
|
||||||
|
| Overworld | `test/unit/zelda3/overworld_test.cc` | `*Overworld*` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Your Priorities (Pick One)
|
||||||
|
|
||||||
|
### Priority 1: Dungeon Editor - Save Infrastructure [COMPLETE] ✅
|
||||||
|
|
||||||
|
**Completed by Gemini 3 Pro:**
|
||||||
|
|
||||||
|
1. **SaveRoomData() Implementation** (`dungeon_editor_system.cc:914-973`):
|
||||||
|
- ✅ Detects if room is currently being edited in UI
|
||||||
|
- ✅ Uses editor's in-memory `Room` object (contains unsaved changes)
|
||||||
|
- ✅ Syncs sprites from `DungeonEditorSystem` to `Room` before saving
|
||||||
|
- ✅ Selectively saves objects only for current room (optimization)
|
||||||
|
|
||||||
|
2. **UI Integration** (`dungeon_editor_v2.cc:244-291`):
|
||||||
|
- ✅ `Save()` method calls `SaveDungeon()` correctly
|
||||||
|
- ✅ Palette saving via `PaletteManager`
|
||||||
|
- ✅ Room objects saved via `Room::SaveObjects()`
|
||||||
|
- ✅ Sprites saved via `DungeonEditorSystem::SaveRoom()`
|
||||||
|
|
||||||
|
3. **Edge Cases Verified:**
|
||||||
|
- ✅ Current room with unsaved changes
|
||||||
|
- ✅ Non-current rooms (sprite-only save)
|
||||||
|
- ✅ Multiple rooms open in tabs
|
||||||
|
- ✅ Empty sprite lists
|
||||||
|
|
||||||
|
**Audit Report:** `zscow_audit_report.md` (artifact)
|
||||||
|
|
||||||
|
**Minor Improvements Recommended:**
|
||||||
|
- Add integration tests for `DungeonEditorSystem` save flow
|
||||||
|
- Remove redundant `SaveObjects()` call in `DungeonEditorV2::Save()`
|
||||||
|
- Document stub methods
|
||||||
|
|
||||||
|
**Test your changes:**
|
||||||
|
```bash
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*:*RoomManipulation*"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Priority 3: ZSCOW Audit [COMPLETE] ✅
|
||||||
|
|
||||||
|
**Completed by Gemini 3 Pro:**
|
||||||
|
|
||||||
|
#### 1. Version Detection - VERIFIED ✅
|
||||||
|
|
||||||
|
**Implementation:** `overworld_version_helper.h:51-71`
|
||||||
|
|
||||||
|
| ASM Byte | Version | Status |
|
||||||
|
|----------|---------|--------|
|
||||||
|
| `0xFF` | Vanilla | ✅ Correct |
|
||||||
|
| `0x00` | Vanilla | ✅ **CORRECT** - Expanded ROMs are zero-filled |
|
||||||
|
| `0x01` | v1 | ✅ Verified |
|
||||||
|
| `0x02` | v2 | ⚠️ Not tested (no v2 ROM available) |
|
||||||
|
| `0x03+` | v3 | ✅ Verified |
|
||||||
|
|
||||||
|
**Task 1: Version 0x00 Edge Case - RESOLVED ✅**
|
||||||
|
- **Answer:** Treating `0x00` as vanilla is **CORRECT**
|
||||||
|
- **Rationale:** When vanilla ROM is expanded to 2MB, new space is zero-filled
|
||||||
|
- **Address:** `0x140145` is in expanded space (beyond 1MB)
|
||||||
|
- **Tests Added:** 5 comprehensive tests in `overworld_version_helper_test.cc`
|
||||||
|
- **Bounds Check Added:** Prevents crashes on small ROMs
|
||||||
|
|
||||||
|
**Task 2: Death Mountain GFX TODO - DOCUMENTED ⚠️**
|
||||||
|
|
||||||
|
**Location:** `overworld_map.cc:595-602`
|
||||||
|
|
||||||
|
- **Current logic:** Checks parent ranges `0x03-0x07`, `0x0B-0x0E` (LW), `0x43-0x47`, `0x4B-0x4E` (DW)
|
||||||
|
- **Recommended:** Only check `0x03`, `0x05`, `0x07` (LW) and `0x43`, `0x45`, `0x47` (DW)
|
||||||
|
- **Rationale:** Matches ZScream 3.0.4+ behavior, handles non-large DM areas correctly
|
||||||
|
- **Impact:** Low risk improvement
|
||||||
|
- **Status:** Documented in audit report, not implemented yet
|
||||||
|
|
||||||
|
**Task 3: ConfigureMultiAreaMap - FULLY VERIFIED ✅**
|
||||||
|
|
||||||
|
**Location:** `overworld.cc:267-422`
|
||||||
|
|
||||||
|
- ✅ Vanilla ROM: Correctly rejects Wide/Tall areas
|
||||||
|
- ✅ v1/v2 ROM: Same as vanilla (no area enum support)
|
||||||
|
- ✅ v3 ROM: All 4 sizes work (Small, Large, Wide, Tall)
|
||||||
|
- ✅ Sibling cleanup: Properly resets all quadrants when shrinking Large→Small
|
||||||
|
- ✅ Edge maps: Boundary conditions handled safely
|
||||||
|
|
||||||
|
**Task 4: Special World Hardcoded Cases - VERIFIED ✅**
|
||||||
|
|
||||||
|
**Location:** `overworld_map.cc:202-293`
|
||||||
|
|
||||||
|
- ✅ Triforce room (`0x88`, `0x93`): Special graphics `0x51`, palette `0x00`
|
||||||
|
- ✅ Master Sword area (`0x80`): Special GFX group
|
||||||
|
- ✅ Zora's Domain (`0x81`, `0x82`, `0x89`, `0x8A`): Sprite GFX `0x0E`
|
||||||
|
- ✅ Version-aware logic for v3 vs vanilla/v2
|
||||||
|
|
||||||
|
**Audit Report:** `zscow_audit_report.md` (artifact)
|
||||||
|
|
||||||
|
**Test Results:**
|
||||||
|
```bash
|
||||||
|
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="OverworldVersionHelperTest*"
|
||||||
|
# [ PASSED ] 5 tests.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Overall Assessment:** ZSCOW implementation is production-ready with one low-priority enhancement (Death Mountain GFX logic).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Priority 4: Agent Inspection - Wire Real Data [MEDIUM] - DETAILED
|
||||||
|
|
||||||
|
**Problem:** CLI inspection commands return stub output. Helper functions exist but aren't connected.
|
||||||
|
|
||||||
|
#### Overworld Command Handlers (`src/cli/handlers/game/overworld.cc`)
|
||||||
|
|
||||||
|
| Line | Handler | Status | Helper to Use |
|
||||||
|
|------|---------|--------|---------------|
|
||||||
|
| 33 | `OverworldGetTileCommandHandler` | TODO | Manual ROM read |
|
||||||
|
| 84 | `OverworldSetTileCommandHandler` | TODO | Manual ROM write |
|
||||||
|
| 162 | `OverworldFindTileCommandHandler` | TODO | `FindTileMatches()` |
|
||||||
|
| 325 | `OverworldDescribeMapCommandHandler` | TODO | `BuildMapSummary()` |
|
||||||
|
| 508 | `OverworldListWarpsCommandHandler` | TODO | `CollectWarpEntries()` |
|
||||||
|
| 721 | `OverworldSelectRectCommandHandler` | TODO | N/A (GUI) |
|
||||||
|
| 759 | `OverworldScrollToCommandHandler` | TODO | N/A (GUI) |
|
||||||
|
| 794 | `OverworldSetZoomCommandHandler` | TODO | N/A (GUI) |
|
||||||
|
| 822 | `OverworldGetVisibleRegionCommandHandler` | TODO | N/A (GUI) |
|
||||||
|
|
||||||
|
#### Dungeon Command Handlers (`src/cli/handlers/game/dungeon_commands.cc`)
|
||||||
|
|
||||||
|
| Line | Handler | Status |
|
||||||
|
|------|---------|--------|
|
||||||
|
| 36 | Sprite listing | TODO - need `Room::sprites()` |
|
||||||
|
| 158 | Object listing | TODO - need `Room::objects()` |
|
||||||
|
| 195 | Tile data | TODO - need `Room::floor1()`/`floor2()` |
|
||||||
|
| 237 | Property setting | TODO - need `Room::set_*()` methods |
|
||||||
|
|
||||||
|
#### Available Helper Functions (`overworld_inspect.h`)
|
||||||
|
|
||||||
|
These are fully implemented and ready to use:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Build complete map metadata summary
|
||||||
|
absl::StatusOr<MapSummary> BuildMapSummary(zelda3::Overworld& overworld, int map_id);
|
||||||
|
|
||||||
|
// Find all warps matching query (entrances, exits, holes)
|
||||||
|
absl::StatusOr<std::vector<WarpEntry>> CollectWarpEntries(
|
||||||
|
const zelda3::Overworld& overworld, const WarpQuery& query);
|
||||||
|
|
||||||
|
// Find all occurrences of a tile16 ID
|
||||||
|
absl::StatusOr<std::vector<TileMatch>> FindTileMatches(
|
||||||
|
zelda3::Overworld& overworld, uint16_t tile_id, const TileSearchOptions& options);
|
||||||
|
|
||||||
|
// Get sprites on overworld maps
|
||||||
|
absl::StatusOr<std::vector<OverworldSprite>> CollectOverworldSprites(
|
||||||
|
const zelda3::Overworld& overworld, const SpriteQuery& query);
|
||||||
|
|
||||||
|
// Get entrance details by ID
|
||||||
|
absl::StatusOr<EntranceDetails> GetEntranceDetails(
|
||||||
|
const zelda3::Overworld& overworld, uint8_t entrance_id);
|
||||||
|
|
||||||
|
// Analyze how often a tile is used
|
||||||
|
absl::StatusOr<TileStatistics> AnalyzeTileUsage(
|
||||||
|
zelda3::Overworld& overworld, uint16_t tile_id, const TileSearchOptions& options);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example Fix Pattern
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BEFORE (broken):
|
||||||
|
absl::Status OverworldFindTileCommandHandler::Execute(
|
||||||
|
CommandContext& context, std::ostream& output) {
|
||||||
|
output << "Placeholder: would find tile..."; // STUB!
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AFTER (working):
|
||||||
|
absl::Status OverworldFindTileCommandHandler::Execute(
|
||||||
|
CommandContext& context, std::ostream& output) {
|
||||||
|
auto tile_id_str = context.GetArgument("tile_id");
|
||||||
|
ASSIGN_OR_RETURN(auto tile_id, ParseHexOrDecimal(tile_id_str));
|
||||||
|
|
||||||
|
TileSearchOptions options;
|
||||||
|
if (auto world = context.GetOptionalArgument("world")) {
|
||||||
|
ASSIGN_OR_RETURN(options.world, ParseWorldSpecifier(*world));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSIGN_OR_RETURN(auto matches,
|
||||||
|
overworld::FindTileMatches(context.overworld(), tile_id, options));
|
||||||
|
|
||||||
|
output << "Found " << matches.size() << " occurrences:\n";
|
||||||
|
for (const auto& match : matches) {
|
||||||
|
output << absl::StrFormat(" Map %d (%s): (%d, %d)\n",
|
||||||
|
match.map_id, WorldName(match.world), match.local_x, match.local_y);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Priority Order
|
||||||
|
|
||||||
|
1. `OverworldDescribeMapCommandHandler` - Most useful for agents
|
||||||
|
2. `OverworldFindTileCommandHandler` - Common query
|
||||||
|
3. `OverworldListWarpsCommandHandler` - Needed for navigation
|
||||||
|
4. Dungeon sprite/object listing - For dungeon editing support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DO NOT TOUCH
|
||||||
|
|
||||||
|
- `.github/workflows/` - CI/CD hotfix in progress
|
||||||
|
- `build/`, `build_agent/`, `build_test/` - User's build directories
|
||||||
|
- `src/util/crash_handler.cc` - Being patched for Windows
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Use `absl::Status` and `absl::StatusOr<T>` for error handling
|
||||||
|
- Macros: `RETURN_IF_ERROR()`, `ASSIGN_OR_RETURN()`
|
||||||
|
- Format: `cmake --build build_gemini --target format`
|
||||||
|
- Follow Google C++ Style Guide
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference Documentation
|
||||||
|
|
||||||
|
- **CLAUDE.md** - Project conventions and patterns
|
||||||
|
- **Roadmap:** `docs/internal/roadmaps/2025-11-23-refined-roadmap.md`
|
||||||
|
- **Message Editor Plans:** `docs/internal/plans/message_editor_implementation_roadmap.md`
|
||||||
|
- **Test Guide:** `docs/public/build/quick-reference.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Approach
|
||||||
|
|
||||||
|
1. **Pick ONE priority** to focus on
|
||||||
|
2. **Read the relevant files** before making changes
|
||||||
|
3. **Run tests frequently** (`ctest --test-dir build_gemini -L stable`)
|
||||||
|
4. **Commit with clear messages** following conventional commits (`fix:`, `feat:`)
|
||||||
|
5. **Don't touch CI/CD** - that's being handled separately
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State of Uncommitted Work
|
||||||
|
|
||||||
|
The working tree has changes you should be aware of:
|
||||||
|
- `tile16_editor.cc` - Texture queueing improvements
|
||||||
|
- `entity.cc/h` - Sprite movement fixes
|
||||||
|
- `overworld_editor.cc` - Entity rendering
|
||||||
|
- `overworld_map.cc` - Map rendering
|
||||||
|
- `object_drawer.cc/h` - Dungeon objects
|
||||||
|
|
||||||
|
Review these before making overlapping changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
When you're done, one of these should be true:
|
||||||
|
- [x] ~~Dungeon save actually persists changes to ROM~~ **COMPLETE** ✅
|
||||||
|
- [x] ~~Message editor can export expanded BIN files~~ **COMPLETE** ✅
|
||||||
|
- [x] ~~ZSCOW loads correctly for vanilla + v1/v2/v3 ROMs~~ **COMPLETE** ✅
|
||||||
|
- [ ] Agent inspection returns real data
|
||||||
|
|
||||||
|
Good luck!
|
||||||
@@ -0,0 +1,482 @@
|
|||||||
|
# YAZE Dungeon System - Complete Technical Reference
|
||||||
|
|
||||||
|
Comprehensive reference for AI agents working on the YAZE Dungeon editing system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Architecture Overview
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
```
|
||||||
|
src/zelda3/dungeon/
|
||||||
|
├── dungeon.h/cc # Main Dungeon class
|
||||||
|
├── room.h/cc # Room class (1,337 lines)
|
||||||
|
├── room_object.h/cc # RoomObject encoding (633+249 lines)
|
||||||
|
├── object_drawer.h/cc # Object rendering (210+972 lines)
|
||||||
|
├── object_parser.h/cc # ROM tile parsing (172+387 lines)
|
||||||
|
├── room_entrance.h # Entrance data (367 lines)
|
||||||
|
├── dungeon_rom_addresses.h # ROM address constants (108 lines)
|
||||||
|
|
||||||
|
src/app/editor/dungeon/
|
||||||
|
├── dungeon_editor_v2.h/cc # Main editor (card-based)
|
||||||
|
├── dungeon_room_loader.h/cc # ROM data loading
|
||||||
|
├── dungeon_room_selector.h/cc # Room selection UI
|
||||||
|
├── dungeon_canvas_viewer.h/cc # Canvas rendering
|
||||||
|
├── dungeon_object_selector.h/cc # Object palette
|
||||||
|
├── dungeon_object_interaction.h/cc # Mouse interactions
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Model
|
||||||
|
```
|
||||||
|
Dungeon
|
||||||
|
└── rooms_[296]
|
||||||
|
└── Room
|
||||||
|
├── tile_objects_[] (RoomObject instances)
|
||||||
|
├── sprites_[]
|
||||||
|
├── chests_in_room_[]
|
||||||
|
├── z3_staircases_[]
|
||||||
|
├── bg1_buffer_ (512x512 pixels)
|
||||||
|
├── bg2_buffer_ (512x512 pixels)
|
||||||
|
└── current_gfx16_[] (16KB graphics)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Room Structure
|
||||||
|
|
||||||
|
### Room Count & Organization
|
||||||
|
- **Total Rooms:** 296 (indices 0x00-0x127)
|
||||||
|
- **Canvas Size:** 512x512 pixels (64x64 tiles)
|
||||||
|
- **Layers:** BG1, BG2, BG3
|
||||||
|
|
||||||
|
### Room Properties
|
||||||
|
```cpp
|
||||||
|
// room.h
|
||||||
|
int room_id_; // Room index (0-295)
|
||||||
|
uint8_t blockset; // Graphics blockset ID
|
||||||
|
uint8_t spriteset; // Sprite set ID
|
||||||
|
uint8_t palette; // Palette ID (0-63)
|
||||||
|
uint8_t layout; // Layout template (0-7)
|
||||||
|
uint8_t floor1, floor2; // Floor graphics (nibbles)
|
||||||
|
uint16_t message_id_; // Associated message
|
||||||
|
|
||||||
|
// Behavioral
|
||||||
|
CollisionKey collision_type; // Collision enum
|
||||||
|
EffectKey effect_type; // Visual effect enum
|
||||||
|
TagKey tag1, tag2; // Special condition tags
|
||||||
|
LayerMergeType layer_merge; // BG1/BG2 blend mode
|
||||||
|
```
|
||||||
|
|
||||||
|
### Layer Merge Types
|
||||||
|
```cpp
|
||||||
|
enum LayerMergeType {
|
||||||
|
LayerMerge00 = 0x00, // Off - Layer 2 invisible
|
||||||
|
LayerMerge01 = 0x01, // Parallax scrolling
|
||||||
|
LayerMerge02 = 0x02, // Dark overlay
|
||||||
|
LayerMerge03 = 0x03, // On top (translucent)
|
||||||
|
LayerMerge04 = 0x04, // Translucent blend
|
||||||
|
LayerMerge05 = 0x05, // Addition blend
|
||||||
|
LayerMerge06 = 0x06, // Normal overlay
|
||||||
|
LayerMerge07 = 0x07, // Transparent
|
||||||
|
LayerMerge08 = 0x08, // Dark room effect
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Object Encoding System
|
||||||
|
|
||||||
|
### 3-Byte Object Format
|
||||||
|
|
||||||
|
Objects are stored as 3 bytes in ROM with three distinct encoding types:
|
||||||
|
|
||||||
|
#### Type 1: Standard Objects (ID 0x00-0xFF)
|
||||||
|
```
|
||||||
|
Byte format: xxxxxxss | yyyyyyss | iiiiiiii
|
||||||
|
b1 b2 b3
|
||||||
|
|
||||||
|
Decoding:
|
||||||
|
x = (b1 & 0xFC) >> 2 // 6 bits (0-63)
|
||||||
|
y = (b2 & 0xFC) >> 2 // 6 bits (0-63)
|
||||||
|
size = ((b1 & 0x03) << 2) | (b2 & 0x03) // 4 bits (0-15)
|
||||||
|
id = b3 // 8 bits
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Type 2: Extended Objects (ID 0x100-0x1FF)
|
||||||
|
```
|
||||||
|
Indicator: b1 >= 0xFC
|
||||||
|
|
||||||
|
Byte format: 111111xx | xxxxyyyy | yyiiiiii
|
||||||
|
b1 b2 b3
|
||||||
|
|
||||||
|
Decoding:
|
||||||
|
id = (b3 & 0x3F) | 0x100
|
||||||
|
x = ((b2 & 0xF0) >> 4) | ((b1 & 0x03) << 4)
|
||||||
|
y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6)
|
||||||
|
size = 0 // No size parameter
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Type 3: Rare Objects (ID 0xF00-0xFFF)
|
||||||
|
```
|
||||||
|
Indicator: b3 >= 0xF8
|
||||||
|
|
||||||
|
Byte format: xxxxxxii | yyyyyyii | 11111iii
|
||||||
|
b1 b2 b3
|
||||||
|
|
||||||
|
Decoding:
|
||||||
|
id = (b3 << 4) | 0x80 | ((b2 & 0x03) << 2) | (b1 & 0x03)
|
||||||
|
x = (b1 & 0xFC) >> 2
|
||||||
|
y = (b2 & 0xFC) >> 2
|
||||||
|
size = ((b1 & 0x03) << 2) | (b2 & 0x03)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Categories
|
||||||
|
|
||||||
|
| Type | ID Range | Examples |
|
||||||
|
|------|----------|----------|
|
||||||
|
| Type 1 | 0x00-0xFF | Walls, floors, decorations |
|
||||||
|
| Type 2 | 0x100-0x1FF | Corners, stairs, furniture |
|
||||||
|
| Type 3 | 0xF00-0xFFF | Chests, pipes, special objects |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. ObjectDrawer Rendering System
|
||||||
|
|
||||||
|
### Class Structure
|
||||||
|
```cpp
|
||||||
|
// object_drawer.h
|
||||||
|
class ObjectDrawer {
|
||||||
|
// Entry point
|
||||||
|
absl::Status DrawObject(const RoomObject& object,
|
||||||
|
gfx::BackgroundBuffer& bg1,
|
||||||
|
gfx::BackgroundBuffer& bg2,
|
||||||
|
const gfx::PaletteGroup& palette_group);
|
||||||
|
|
||||||
|
// Data
|
||||||
|
Rom* rom_;
|
||||||
|
const uint8_t* room_gfx_buffer_; // current_gfx16_
|
||||||
|
std::unordered_map<int16_t, int> object_to_routine_map_;
|
||||||
|
std::vector<DrawRoutine> draw_routines_;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Draw Routine Status
|
||||||
|
|
||||||
|
| # | Routine Name | Status | Lines |
|
||||||
|
|---|--------------|--------|-------|
|
||||||
|
| 0 | DrawRightwards2x2_1to15or32 | COMPLETE | 302-321 |
|
||||||
|
| 1 | DrawRightwards2x4_1to15or26 | COMPLETE | 323-348 |
|
||||||
|
| 2 | DrawRightwards2x4spaced4_1to16 | COMPLETE | 350-373 |
|
||||||
|
| 3 | DrawRightwards2x4spaced4_BothBG | **STUB** | 375-381 |
|
||||||
|
| 4 | DrawRightwards2x2_1to16 | COMPLETE | 383-399 |
|
||||||
|
| 5 | DrawDiagonalAcute_1to16 | **UNCLEAR** | 401-417 |
|
||||||
|
| 6 | DrawDiagonalGrave_1to16 | **UNCLEAR** | 419-435 |
|
||||||
|
| 7 | DrawDownwards2x2_1to15or32 | COMPLETE | 689-708 |
|
||||||
|
| 8 | DrawDownwards4x2_1to15or26 | COMPLETE | 710-753 |
|
||||||
|
| 9 | DrawDownwards4x2_BothBG | **STUB** | 755-761 |
|
||||||
|
| 10 | DrawDownwardsDecor4x2spaced4 | COMPLETE | 763-782 |
|
||||||
|
| 11 | DrawDownwards2x2_1to16 | COMPLETE | 784-799 |
|
||||||
|
| 12 | DrawDownwardsHasEdge1x1 | COMPLETE | 801-813 |
|
||||||
|
| 13 | DrawDownwardsEdge1x1 | COMPLETE | 815-827 |
|
||||||
|
| 14 | DrawDownwardsLeftCorners | COMPLETE | 829-842 |
|
||||||
|
| 15 | DrawDownwardsRightCorners | COMPLETE | 844-857 |
|
||||||
|
| 16 | DrawRightwards4x4_1to16 | COMPLETE | 534-550 |
|
||||||
|
| - | CustomDraw | **STUB** | 524-532 |
|
||||||
|
| - | DrawDoorSwitcherer | **STUB** | 566-575 |
|
||||||
|
|
||||||
|
### INCOMPLETE: BothBG Routines (4 locations)
|
||||||
|
|
||||||
|
These routines should draw to BOTH BG1 and BG2 but currently only call single-layer version:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Line 375-381: DrawRightwards2x4spaced4_1to16_BothBG
|
||||||
|
// Line 437-442: DrawDiagonalAcute_1to16_BothBG
|
||||||
|
// Line 444-449: DrawDiagonalGrave_1to16_BothBG
|
||||||
|
// Line 755-761: DrawDownwards4x2_1to16_BothBG
|
||||||
|
|
||||||
|
// Current (WRONG):
|
||||||
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||||
|
const RoomObject& obj, gfx::BackgroundBuffer& bg, // Only 1 buffer!
|
||||||
|
std::span<const gfx::TileInfo> tiles) {
|
||||||
|
// Just calls single-layer version - misses BG2
|
||||||
|
DrawRightwards2x4spaced4_1to16(obj, bg, tiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be:
|
||||||
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
||||||
|
const RoomObject& obj,
|
||||||
|
gfx::BackgroundBuffer& bg1, // Both buffers
|
||||||
|
gfx::BackgroundBuffer& bg2,
|
||||||
|
std::span<const gfx::TileInfo> tiles) {
|
||||||
|
DrawRightwards2x4spaced4_1to16(obj, bg1, tiles);
|
||||||
|
DrawRightwards2x4spaced4_1to16(obj, bg2, tiles);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### UNCLEAR: Diagonal Routines
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Lines 401-417, 419-435
|
||||||
|
// Issues:
|
||||||
|
// - Hardcoded +6 and 5 iterations (why?)
|
||||||
|
// - Coordinate formula may produce negative Y
|
||||||
|
// - Only uses 4 tiles from larger span
|
||||||
|
// - No bounds checking
|
||||||
|
|
||||||
|
for (int s = 0; s < size + 6; s++) { // Why +6?
|
||||||
|
for (int i = 0; i < 5; i++) { // Why 5?
|
||||||
|
WriteTile8(bg, obj.x_ + s, obj.y_ + (i - s), tiles[i % 4]);
|
||||||
|
// ^^ (i - s) can be negative when s > i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Tile Rendering Pipeline
|
||||||
|
|
||||||
|
### WriteTile8() - Tile to Pixel Conversion
|
||||||
|
```cpp
|
||||||
|
// object_drawer.cc lines 863-883
|
||||||
|
void WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
|
||||||
|
const gfx::TileInfo& tile_info) {
|
||||||
|
// tile coords → pixel coords: tile_x * 8, tile_y * 8
|
||||||
|
DrawTileToBitmap(bitmap, tile_info, tile_x * 8, tile_y * 8, room_gfx_buffer_);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### DrawTileToBitmap() - Pixel Rendering
|
||||||
|
```cpp
|
||||||
|
// object_drawer.cc lines 890-970
|
||||||
|
// Key steps:
|
||||||
|
// 1. Graphics sheet lookup: tile_info.id_ → (sheet_x, sheet_y)
|
||||||
|
// 2. Palette offset: (palette & 0x0F) * 8
|
||||||
|
// 3. Per-pixel with mirroring support
|
||||||
|
// 4. Color 0 = transparent (skipped)
|
||||||
|
|
||||||
|
int tile_sheet_x = (tile_info.id_ % 16) * 8; // 0-127 pixels
|
||||||
|
int tile_sheet_y = (tile_info.id_ / 16) * 8; // 0-127 pixels
|
||||||
|
uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8;
|
||||||
|
|
||||||
|
for (int py = 0; py < 8; py++) {
|
||||||
|
for (int px = 0; px < 8; px++) {
|
||||||
|
int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px;
|
||||||
|
int src_y = tile_info.vertical_mirror_ ? (7 - py) : py;
|
||||||
|
// Read pixel, apply palette, write to bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Palette Application (CRITICAL)
|
||||||
|
```cpp
|
||||||
|
// object_drawer.cc lines 71-115
|
||||||
|
// Palette must be applied AFTER drawing, BEFORE SDL sync
|
||||||
|
|
||||||
|
// 1. Draw all objects (writes palette indices 0-255)
|
||||||
|
for (auto& obj : objects) {
|
||||||
|
DrawObject(obj, bg1, bg2, palette_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Apply dungeon palette to convert indices → RGB
|
||||||
|
bg1_bmp.SetPalette(dungeon_palette);
|
||||||
|
bg2_bmp.SetPalette(dungeon_palette);
|
||||||
|
|
||||||
|
// 3. Sync to SDL surfaces
|
||||||
|
SDL_LockSurface(bg1_bmp.surface());
|
||||||
|
memcpy(bg1_bmp.surface()->pixels, bg1_bmp.mutable_data().data(), ...);
|
||||||
|
SDL_UnlockSurface(bg1_bmp.surface());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ROM Addresses
|
||||||
|
|
||||||
|
### Room Data
|
||||||
|
```cpp
|
||||||
|
kRoomObjectLayoutPointer = 0x882D // Layout pointer table
|
||||||
|
kRoomObjectPointer = 0x874C // Object data pointer
|
||||||
|
kRoomHeaderPointer = 0xB5DD // Room headers (3-byte long)
|
||||||
|
kRoomHeaderPointerBank = 0xB5E7 // Bank byte
|
||||||
|
```
|
||||||
|
|
||||||
|
### Palette & Graphics
|
||||||
|
```cpp
|
||||||
|
kDungeonsMainBgPalettePointers = 0xDEC4B
|
||||||
|
kDungeonsPalettes = 0xDD734
|
||||||
|
kGfxGroupsPointer = 0x6237
|
||||||
|
kTileAddress = 0x1B52 // Main tile graphics
|
||||||
|
kTileAddressFloor = 0x1B5A // Floor tile graphics
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Subtypes
|
||||||
|
```cpp
|
||||||
|
kRoomObjectSubtype1 = 0x0F8000 // Standard objects
|
||||||
|
kRoomObjectSubtype2 = 0x0F83F0 // Extended objects
|
||||||
|
kRoomObjectSubtype3 = 0x0F84F0 // Rare objects
|
||||||
|
kRoomObjectTileAddress = 0x091B52 // Tile data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Objects
|
||||||
|
```cpp
|
||||||
|
kBlocksPointer[1-4] = 0x15AFA-0x15B0F
|
||||||
|
kChestsDataPointer1 = 0xEBFB
|
||||||
|
kTorchData = 0x2736A
|
||||||
|
kPitPointer = 0x394AB
|
||||||
|
kDoorPointers = 0xF83C0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. TODOs in room_object.h (30+ items)
|
||||||
|
|
||||||
|
### Unknown Objects Needing Verification
|
||||||
|
|
||||||
|
| Line | ID | Description |
|
||||||
|
|------|-----|-------------|
|
||||||
|
| 234 | 0x35 | "WEIRD DOOR" - needs investigation |
|
||||||
|
| 252-255 | 0x49-0x4C | "Unknown" Type 1 objects |
|
||||||
|
| 350-353 | 0xC4-0xC7 | "Diagonal layer 2 mask B" - needs verify |
|
||||||
|
| 392-395 | 0xDE-0xE1 | "Moving wall flag" - WTF IS THIS? |
|
||||||
|
| 466-476 | Type 2 | Multiple "Unknown" objects |
|
||||||
|
| 480 | 0x30 | "Intraroom stairs north B" - verify layer |
|
||||||
|
| 486 | 0x36 | "Water ladder (south)" - needs verify |
|
||||||
|
| 512-584 | Type 3 | Multiple "Unknown" objects |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. DungeonEditorV2 Architecture
|
||||||
|
|
||||||
|
### Card-Based Component System
|
||||||
|
```cpp
|
||||||
|
DungeonEditorV2 (Coordinator)
|
||||||
|
├── DungeonRoomLoader // ROM data loading
|
||||||
|
├── DungeonRoomSelector // Room list/selection
|
||||||
|
├── DungeonCanvasViewer // 512x512 canvas
|
||||||
|
├── DungeonObjectSelector // Object palette
|
||||||
|
├── DungeonObjectInteraction // Mouse handling
|
||||||
|
├── ObjectEditorCard // Property editing
|
||||||
|
└── PaletteEditorWidget // Color editing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Card Types
|
||||||
|
```cpp
|
||||||
|
show_control_panel_ // Room/entrance selection
|
||||||
|
show_room_selector_ // Room list
|
||||||
|
show_room_matrix_ // 16x19 visual layout
|
||||||
|
show_entrances_list_ // Entrance/spawn list
|
||||||
|
show_room_graphics_ // Blockset/palette
|
||||||
|
show_object_editor_ // Object placement
|
||||||
|
show_palette_editor_ // Palette colors
|
||||||
|
show_debug_controls_ // Debug options
|
||||||
|
```
|
||||||
|
|
||||||
|
### Undo/Redo System
|
||||||
|
```cpp
|
||||||
|
// Per-room object snapshots
|
||||||
|
std::unordered_map<int, std::vector<std::vector<RoomObject>>> undo_history_;
|
||||||
|
std::unordered_map<int, std::vector<std::vector<RoomObject>>> redo_history_;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Room Loading Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
LoadRoomFromRom(room_id)
|
||||||
|
│
|
||||||
|
├── Resolve room header pointer (0xB5DD + room_id * 3)
|
||||||
|
│
|
||||||
|
├── Parse header bytes:
|
||||||
|
│ ├── BG2 type, collision, light flag
|
||||||
|
│ ├── Palette, blockset, spriteset
|
||||||
|
│ ├── Effect type, tags
|
||||||
|
│ └── Staircase data
|
||||||
|
│
|
||||||
|
├── Load graphics sheets (16 blocks)
|
||||||
|
│
|
||||||
|
└── LoadObjects()
|
||||||
|
│
|
||||||
|
├── Read floor/layout header (2 bytes)
|
||||||
|
│
|
||||||
|
├── Parse object stream:
|
||||||
|
│ ├── 3 bytes per object
|
||||||
|
│ ├── 0xFF 0xFF = layer boundary
|
||||||
|
│ └── 0xF0 0xFF = door section
|
||||||
|
│
|
||||||
|
└── Handle special objects:
|
||||||
|
├── Staircases
|
||||||
|
├── Chests
|
||||||
|
├── Doors
|
||||||
|
├── Torches
|
||||||
|
└── Blocks
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Rendering Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
1. LoadRoomGraphics()
|
||||||
|
└── Build graphics sheet list from blockset
|
||||||
|
|
||||||
|
2. CopyRoomGraphicsToBuffer()
|
||||||
|
└── Copy ROM sheets → current_gfx16_[]
|
||||||
|
|
||||||
|
3. RenderRoomGraphics()
|
||||||
|
├── Check dirty flags
|
||||||
|
├── LoadLayoutTilesToBuffer()
|
||||||
|
├── Draw floor to bg1/bg2 buffers
|
||||||
|
└── RenderObjectsToBackground()
|
||||||
|
└── ObjectDrawer::DrawObjectList()
|
||||||
|
|
||||||
|
4. Present (Canvas Viewer)
|
||||||
|
├── Process deferred texture queue
|
||||||
|
├── Create/update GPU textures
|
||||||
|
└── Render to ImGui canvas
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Known Issues Summary
|
||||||
|
|
||||||
|
### BothBG Support (4 stubs)
|
||||||
|
- Line 380: `DrawRightwards2x4spaced4_1to16_BothBG`
|
||||||
|
- Line 441: `DrawDiagonalAcute_1to16_BothBG`
|
||||||
|
- Line 448: `DrawDiagonalGrave_1to16_BothBG`
|
||||||
|
- Line 760: `DrawDownwards4x2_1to16_BothBG`
|
||||||
|
|
||||||
|
**Fix:** Change signature to accept both `bg1` and `bg2` buffers.
|
||||||
|
|
||||||
|
### Diagonal Logic (2 routines)
|
||||||
|
- Lines 401-435: Hardcoded constants, potential negative coords
|
||||||
|
- **Needs:** Game verification or ZScream reference
|
||||||
|
|
||||||
|
### Custom/Door Stubs (2 routines)
|
||||||
|
- Line 524-532: `CustomDraw` - only draws first tile
|
||||||
|
- Line 566-575: `DrawDoorSwitcherer` - only draws first tile
|
||||||
|
|
||||||
|
### Object Names (30+ unknowns)
|
||||||
|
- Multiple objects need in-game verification
|
||||||
|
- See section 7 for full list
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Testing
|
||||||
|
|
||||||
|
### Run Dungeon Tests
|
||||||
|
```bash
|
||||||
|
# Unit tests
|
||||||
|
ctest --test-dir build -R "dungeon\|Dungeon" -V
|
||||||
|
|
||||||
|
# E2E tests
|
||||||
|
ctest --test-dir build -R "DungeonEditor" -V
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E Test Files
|
||||||
|
- `test/e2e/dungeon_editor_smoke_test.cc`
|
||||||
|
- `test/e2e/dungeon_canvas_interaction_test.cc`
|
||||||
|
- `test/e2e/dungeon_layer_rendering_test.cc`
|
||||||
|
- `test/e2e/dungeon_object_drawing_test.cc`
|
||||||
|
|
||||||
|
### Test Design Doc
|
||||||
|
`docs/internal/testing/dungeon-gui-test-design.md` (1000+ lines)
|
||||||
File diff suppressed because it is too large
Load Diff
1275
docs/internal/agents/archive/large-ref-docs/gemini-zsow-ref.md
Normal file
1275
docs/internal/agents/archive/large-ref-docs/gemini-zsow-ref.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
# Initiative: Test Suite Slimdown & Gating
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Reduce test bloat, keep high-signal coverage, and gate optional AI/ROM/bench suites. Deliver lean default CI (stable + smokes) with optional nightly heavy suites.
|
||||||
|
|
||||||
|
## Scope & Owners
|
||||||
|
- **test-infrastructure-expert**: Owns harness/labels/CTests; flake triage and duplication removal.
|
||||||
|
- **ai-infra-architect**: Owns AI/experimental/ROM gating logic (skip when keys/runtime missing).
|
||||||
|
- **docs-janitor**: Updates docs (test/README, CI docs) for default vs optional suites.
|
||||||
|
- **backend-infra-engineer**: CI pipeline changes (default vs nightly matrices).
|
||||||
|
- **imgui-frontend-engineer**: Rendering/UI test pruning, keep one rendering suite.
|
||||||
|
- **snes-emulator-expert**: Consult if emulator tests are affected.
|
||||||
|
- **GEMINI_AUTOM**: Quick TODO fixes in tests (small, low-risk).
|
||||||
|
|
||||||
|
## Deliverables
|
||||||
|
1) Default test set: stable + e2e smokes (framework, dungeon editor, canvas); one rendering suite only.
|
||||||
|
2) Optional suites gated: ROM-dependent, AI experimental, benchmarks (off by default); skip cleanly when missing ROM/keys.
|
||||||
|
3) Prune duplicates: drop legacy rendering/e2e duplicates and legacy dungeon_editor_test if v2 covers it.
|
||||||
|
4) Docs: Updated test/README and CI docs with clear run commands and labels.
|
||||||
|
5) CI: PR/commit matrix runs lean set; nightly matrix runs optional suites.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- Inventory and prune
|
||||||
|
- Keep integration/dungeon_object_rendering_tests_new.cc; drop older rendering integration + e2e variants.
|
||||||
|
- Drop/retire dungeon_editor_test.cc (v1) if v2 covers current UI.
|
||||||
|
- Gating
|
||||||
|
- Ensure yaze_test_experimental and rom_dependent suites are off by default; add labels/presets for nightly.
|
||||||
|
- AI tests skip gracefully if AI runtime/key missing.
|
||||||
|
- CI changes
|
||||||
|
- PR: stable + smokes only; Nightly: add ROM + AI + bench.
|
||||||
|
- Docs
|
||||||
|
- Update test/README.md and CI docs to reflect default vs optional suites and commands/labels.
|
||||||
|
- Quick fixes
|
||||||
|
- Triage TODOs: compression header off-by-one, test_editor window/controller handling; fix or mark skipped with reason.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
- CTest/CI default runs execute only stable + smokes and one rendering suite.
|
||||||
|
- Optional suites runnable via label/preset; fail early if pre-reqs missing.
|
||||||
|
- Documentation matches actual behavior.
|
||||||
|
- No regressions in core stable tests.
|
||||||
|
|
||||||
|
## Coordination
|
||||||
|
- Post progress/hand-offs to coordination-board.md.
|
||||||
|
- Use designated agent IDs above when claiming work.
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# Gemini Pro 3 Overworld Architecture Reference
|
||||||
|
|
||||||
|
Compact reference for YAZE Overworld/Dungeon systems. Use this to quickly locate code and understand patterns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overworld Editor Architecture (~8,900 lines across 7 modules)
|
||||||
|
|
||||||
|
### Main Editor
|
||||||
|
| File | Lines | Purpose |
|
||||||
|
|------|-------|---------|
|
||||||
|
| `src/app/editor/overworld/overworld_editor.cc` | 3,204 | Main coordinator, canvas, menus |
|
||||||
|
| `src/app/editor/overworld/overworld_editor.h` | 350 | Class definition |
|
||||||
|
|
||||||
|
### Sub-Modules
|
||||||
|
| File | Lines | Purpose |
|
||||||
|
|------|-------|---------|
|
||||||
|
| `map_properties.cc` | 1,759 | `MapPropertiesSystem` - property panels |
|
||||||
|
| `tile16_editor.cc` | 2,584 | `Tile16Editor` - tile editing popup |
|
||||||
|
| `entity.cc` | 491 | `OverworldEntity` - entity containers |
|
||||||
|
| `entity_operations.cc` | 239 | Entity CRUD helpers |
|
||||||
|
| `overworld_entity_renderer.cc` | 151 | Entity drawing |
|
||||||
|
| `scratch_space.cc` | 444 | Tile16 storage utilities |
|
||||||
|
|
||||||
|
### Data Models
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `src/zelda3/overworld/overworld.cc` | Main overworld class, loading logic |
|
||||||
|
| `src/zelda3/overworld/overworld_map.cc` | Individual map data |
|
||||||
|
| `src/zelda3/overworld/overworld_item.cc` | Item entities |
|
||||||
|
| `src/zelda3/overworld/overworld_version_helper.h` | Version detection API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Completed Work (Gemini's Previous Session)
|
||||||
|
|
||||||
|
### ASM Version Check Standardization
|
||||||
|
- Replaced raw `asm_version >= 3` with `OverworldVersionHelper::SupportsAreaEnum()`
|
||||||
|
- Fixed critical bug: vanilla ROMs (0xFF) incorrectly treated as v3+
|
||||||
|
- Applied consistently across: `overworld.cc`, `overworld_item.cc`, `overworld_map.cc`
|
||||||
|
|
||||||
|
### Tile16Editor Texture Queueing
|
||||||
|
- Fixed `tile16_editor.cc` lines 37-38, 44-45, 56-57
|
||||||
|
- Pattern: `QueueTextureCommand()` instead of `RenderBitmap()` during init
|
||||||
|
|
||||||
|
### Test Infrastructure
|
||||||
|
- Created `test/unit/zelda3/overworld_regression_test.cc`
|
||||||
|
- Tests `OverworldVersionHelper` logic (passing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Remaining Work Checklist
|
||||||
|
|
||||||
|
### P0 - Must Complete
|
||||||
|
|
||||||
|
#### Texture Queueing TODOs in overworld_editor.cc
|
||||||
|
6 locations still use inline rendering instead of deferred queueing:
|
||||||
|
|
||||||
|
| Line | Current Code | Fix Pattern |
|
||||||
|
|------|--------------|-------------|
|
||||||
|
| 1392 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
| 1397 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
| 1740 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
| 1809 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
| 1819 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
| 1962 | `// TODO: Queue texture` | Use `QueueTextureCommand()` |
|
||||||
|
|
||||||
|
**Pattern to follow** (from tile16_editor.cc):
|
||||||
|
```cpp
|
||||||
|
// BEFORE (blocking)
|
||||||
|
Renderer::Get().RenderBitmap(&some_bitmap_);
|
||||||
|
|
||||||
|
// AFTER (non-blocking)
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(gfx::TextureCommand{
|
||||||
|
.operation = gfx::TextureOperation::kCreate,
|
||||||
|
.bitmap = &some_bitmap_,
|
||||||
|
.priority = gfx::TexturePriority::kHigh
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Entity Deletion Pattern (entity.cc:319) - WORKING CORRECTLY
|
||||||
|
- **Note:** The TODO comment is misleading. The `deleted` flag pattern IS CORRECT for ROM editors
|
||||||
|
- Entities live at fixed ROM offsets, so marking `deleted = true` is the proper approach
|
||||||
|
- Renderer correctly skips deleted entities (see `overworld_entity_renderer.cc`)
|
||||||
|
- `entity_operations.cc` reuses deleted slots when creating new entities
|
||||||
|
- **No fix needed** - just a cleanup of the misleading TODO comment
|
||||||
|
|
||||||
|
### P1 - Should Complete
|
||||||
|
|
||||||
|
#### Tile16Editor Undo/Redo - ALREADY COMPLETE
|
||||||
|
- Location: `tile16_editor.cc:1681-1760`
|
||||||
|
- `SaveUndoState()` called before all edit operations
|
||||||
|
- `Undo()` and `Redo()` fully implemented with `absl::Status` returns
|
||||||
|
- Ctrl+Z/Ctrl+Y handling at lines 224-231
|
||||||
|
- Stack management with `kMaxUndoStates_` limit
|
||||||
|
- **No additional work needed**
|
||||||
|
|
||||||
|
#### Overworld Regression Test Completion
|
||||||
|
- Location: `test/unit/zelda3/overworld_regression_test.cc`
|
||||||
|
- Current: Only tests version helper
|
||||||
|
- Need: Add more comprehensive mock ROM data
|
||||||
|
|
||||||
|
### P2 - Stretch Goals
|
||||||
|
|
||||||
|
#### Dungeon Downwards Draw Routines
|
||||||
|
- Location: `src/zelda3/dungeon/object_drawer.cc` lines 160-185
|
||||||
|
- Missing implementations marked with stubs
|
||||||
|
- Need: Implement based on game ROM patterns
|
||||||
|
|
||||||
|
#### E2E Cinematic Tests
|
||||||
|
- Design doc: `docs/internal/testing/dungeon-gui-test-design.md`
|
||||||
|
- Framework: ImGuiTestEngine integration ready
|
||||||
|
- Need: Screenshot capture, visual verification
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Key Patterns
|
||||||
|
|
||||||
|
### Bitmap/Surface Synchronization
|
||||||
|
```cpp
|
||||||
|
// CORRECT: Use set_data() for bulk replacement
|
||||||
|
bitmap.set_data(new_data); // Syncs both data_ and surface_->pixels
|
||||||
|
|
||||||
|
// WRONG: Direct assignment breaks sync
|
||||||
|
bitmap.mutable_data() = new_data; // NEVER DO THIS
|
||||||
|
```
|
||||||
|
|
||||||
|
### Graphics Refresh Order
|
||||||
|
```cpp
|
||||||
|
// 1. Update model
|
||||||
|
map.SetProperty(new_value);
|
||||||
|
|
||||||
|
// 2. Reload from ROM
|
||||||
|
map.LoadAreaGraphics();
|
||||||
|
|
||||||
|
// 3. Force render
|
||||||
|
Renderer::Get().RenderBitmap(&map_bitmap_); // Immediate
|
||||||
|
// OR
|
||||||
|
gfx::Arena::Get().QueueTextureCommand(...); // Deferred (preferred)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version Helper Usage
|
||||||
|
```cpp
|
||||||
|
#include "zelda3/overworld/overworld_version_helper.h"
|
||||||
|
|
||||||
|
auto version = OverworldVersionHelper::GetVersion(*rom_);
|
||||||
|
|
||||||
|
// Feature gates
|
||||||
|
if (OverworldVersionHelper::SupportsAreaEnum(version)) {
|
||||||
|
// v3+ only features
|
||||||
|
}
|
||||||
|
if (OverworldVersionHelper::SupportsExpandedSpace(version)) {
|
||||||
|
// v1+ features
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Rendering Colors (0.85f alpha)
|
||||||
|
```cpp
|
||||||
|
ImVec4 entrance_color(1.0f, 0.85f, 0.0f, 0.85f); // Bright yellow-gold
|
||||||
|
ImVec4 exit_color(0.0f, 1.0f, 1.0f, 0.85f); // Cyan
|
||||||
|
ImVec4 item_color(1.0f, 0.0f, 0.0f, 0.85f); // Bright red
|
||||||
|
ImVec4 sprite_color(1.0f, 0.0f, 1.0f, 0.85f); // Bright magenta
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. File Quick Reference
|
||||||
|
|
||||||
|
### Overworld Editor Entry Points
|
||||||
|
- `Initialize()` → overworld_editor.cc:64
|
||||||
|
- `Load()` → overworld_editor.cc:150
|
||||||
|
- `Update()` → overworld_editor.cc:203
|
||||||
|
- `DrawFullscreenCanvas()` → overworld_editor.cc:472
|
||||||
|
- `ProcessDeferredTextures()` → overworld_editor.cc:899
|
||||||
|
|
||||||
|
### Tile16Editor Entry Points
|
||||||
|
- `Initialize()` → tile16_editor.cc:30
|
||||||
|
- `Update()` → tile16_editor.cc:100
|
||||||
|
- `DrawTile16Editor()` → tile16_editor.cc:200
|
||||||
|
|
||||||
|
### Entity System Entry Points
|
||||||
|
- `OverworldEntity::Draw()` → entity.cc:50
|
||||||
|
- `DeleteSelectedEntity()` → entity.cc:319
|
||||||
|
- Entity containers: `entrances_`, `exits_`, `items_`, `sprites_`
|
||||||
|
|
||||||
|
### Dungeon System Entry Points
|
||||||
|
- `ObjectDrawer::DrawObject()` → object_drawer.cc:50
|
||||||
|
- Draw routines: lines 100-300
|
||||||
|
- Downwards stubs: lines 160-185
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Test Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run overworld regression test specifically
|
||||||
|
ctest --test-dir build_gemini -R "OverworldRegression" -V
|
||||||
|
|
||||||
|
# Run all zelda3 unit tests
|
||||||
|
ctest --test-dir build_gemini -R "zelda3" -L unit
|
||||||
|
|
||||||
|
# Run GUI E2E tests
|
||||||
|
ctest --test-dir build_gemini -L gui -V
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Validation Criteria
|
||||||
|
|
||||||
|
### For Texture Queueing Fix
|
||||||
|
- [ ] No `// TODO` comments remain at specified lines
|
||||||
|
- [ ] `QueueTextureCommand()` used consistently
|
||||||
|
- [ ] UI doesn't freeze when loading maps
|
||||||
|
- [ ] `ctest -L stable` passes
|
||||||
|
|
||||||
|
### For Entity Deletion Fix
|
||||||
|
- [ ] Items actually removed from vector
|
||||||
|
- [ ] No memory leaks (items properly destroyed)
|
||||||
|
- [ ] Undo can restore deleted items (if implementing undo)
|
||||||
|
|
||||||
|
### For Dungeon Draw Routines
|
||||||
|
- [ ] Downwards objects render correctly
|
||||||
|
- [ ] Layer ordering maintained (BG1 → BG2 → BG3)
|
||||||
|
- [ ] Palette applied correctly after render
|
||||||
@@ -0,0 +1,859 @@
|
|||||||
|
# Z3ED CLI & Agent API Enhancement Design
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This document outlines comprehensive enhancements to the z3ed CLI and agent APIs to significantly improve AI agent interaction with YAZE. The design focuses on enabling better automation, testing, and feature development through a robust command interface, programmatic editor access, and enhanced collaboration features.
|
||||||
|
|
||||||
|
## Current Architecture Analysis
|
||||||
|
|
||||||
|
### Existing Components
|
||||||
|
- **CLI Framework**: ModernCLI with CommandRegistry pattern
|
||||||
|
- **Command Handlers**: 70+ specialized handlers (hex, palette, sprite, music, dialogue, dungeon, overworld, gui, emulator)
|
||||||
|
- **Canvas Automation API**: Programmatic interface for tile operations, selection, and view control
|
||||||
|
- **Network Client**: WebSocket/HTTP fallback for collaborative editing
|
||||||
|
- **HTTP API**: REST endpoints for health, models, and basic operations
|
||||||
|
- **Model Integration**: Ollama and Gemini support through ModelRegistry
|
||||||
|
|
||||||
|
### Key Strengths
|
||||||
|
- Clean command handler abstraction with consistent execution pattern
|
||||||
|
- Canvas automation already supports tile operations and coordinate conversion
|
||||||
|
- Network infrastructure in place for collaboration
|
||||||
|
- Extensible model registry for multiple AI providers
|
||||||
|
|
||||||
|
### Gaps to Address
|
||||||
|
- Limited ROM direct manipulation commands
|
||||||
|
- No session persistence or REPL mode
|
||||||
|
- Minimal test generation capabilities
|
||||||
|
- Limited agent coordination features
|
||||||
|
- No batch operation support for complex workflows
|
||||||
|
- Missing introspection and discovery APIs
|
||||||
|
|
||||||
|
## 1. Z3ED CLI Enhancements
|
||||||
|
|
||||||
|
### 1.1 ROM Operations Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Direct ROM manipulation
|
||||||
|
z3ed rom read --address <hex> [--length <bytes>] [--format hex|ascii|binary]
|
||||||
|
z3ed rom write --address <hex> --data <hex_string> [--verify]
|
||||||
|
z3ed rom validate [--checksums] [--headers] [--regions]
|
||||||
|
z3ed rom diff --base <rom1> --compare <rom2> [--output patch]
|
||||||
|
z3ed rom patch --input <rom> --patch <ips|bps> --output <patched_rom>
|
||||||
|
z3ed rom export --region <name> --start <hex> --end <hex> --output <file>
|
||||||
|
z3ed rom import --region <name> --address <hex> --input <file>
|
||||||
|
|
||||||
|
# ROM state management
|
||||||
|
z3ed rom snapshot --name <snapshot_name> [--compress]
|
||||||
|
z3ed rom restore --snapshot <name> [--verify]
|
||||||
|
z3ed rom list-snapshots [--details]
|
||||||
|
z3ed rom compare-snapshot --current --snapshot <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Implementation Details
|
||||||
|
```cpp
|
||||||
|
class RomReadCommandHandler : public CommandHandler {
|
||||||
|
protected:
|
||||||
|
absl::Status ValidateArgs(const ArgumentParser& parser) override {
|
||||||
|
RETURN_IF_ERROR(parser.RequireArgs({"address"}));
|
||||||
|
if (auto len = parser.GetInt("length")) {
|
||||||
|
if (*len <= 0 || *len > 0x10000) {
|
||||||
|
return absl::InvalidArgumentError("Length must be 1-65536 bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status Execute(Rom* rom, const ArgumentParser& parser,
|
||||||
|
OutputFormatter& formatter) override {
|
||||||
|
uint32_t address = parser.GetHex("address").value();
|
||||||
|
int length = parser.GetInt("length").value_or(16);
|
||||||
|
std::string format = parser.GetString("format").value_or("hex");
|
||||||
|
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
data.push_back(rom->ReadByte(address + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
formatter.AddField("address", absl::StrFormat("0x%06X", address));
|
||||||
|
formatter.AddField("data", FormatData(data, format));
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Editor Automation Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dungeon editor automation
|
||||||
|
z3ed editor dungeon place-object --room <id> --type <object_id> --x <x> --y <y>
|
||||||
|
z3ed editor dungeon remove-object --room <id> --object-index <idx>
|
||||||
|
z3ed editor dungeon set-property --room <id> --property <name> --value <val>
|
||||||
|
z3ed editor dungeon list-objects --room <id> [--filter-type <type>]
|
||||||
|
z3ed editor dungeon validate-room --room <id> [--fix-issues]
|
||||||
|
|
||||||
|
# Overworld editor automation
|
||||||
|
z3ed editor overworld set-tile --map <id> --x <x> --y <y> --tile <tile_id>
|
||||||
|
z3ed editor overworld place-entrance --map <id> --x <x> --y <y> --target <room>
|
||||||
|
z3ed editor overworld modify-sprite --map <id> --sprite-index <idx> --property <prop> --value <val>
|
||||||
|
z3ed editor overworld generate-minimap --map <id> --output <file>
|
||||||
|
|
||||||
|
# Graphics editor automation
|
||||||
|
z3ed editor graphics import-sheet --sheet <id> --file <png> [--palette <id>]
|
||||||
|
z3ed editor graphics export-sheet --sheet <id> --output <png>
|
||||||
|
z3ed editor graphics modify-palette --palette <id> --color <idx> --rgb <#RRGGBB>
|
||||||
|
|
||||||
|
# Batch operations
|
||||||
|
z3ed editor batch --script <file> [--dry-run] [--parallel]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Batch Script Format (JSON)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"editor": "dungeon",
|
||||||
|
"action": "place-object",
|
||||||
|
"params": {
|
||||||
|
"room": 1,
|
||||||
|
"type": 0x22,
|
||||||
|
"x": 10,
|
||||||
|
"y": 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"editor": "overworld",
|
||||||
|
"action": "set-tile",
|
||||||
|
"params": {
|
||||||
|
"map": 0x00,
|
||||||
|
"x": 20,
|
||||||
|
"y": 30,
|
||||||
|
"tile": 0x142
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"stop_on_error": false,
|
||||||
|
"validate_after": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Testing Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test execution
|
||||||
|
z3ed test run --category <unit|integration|e2e> [--filter <pattern>]
|
||||||
|
z3ed test validate-feature --feature <name> [--rom <file>]
|
||||||
|
z3ed test generate --target <class|file> --output <test_file>
|
||||||
|
z3ed test coverage --report <html|json|text>
|
||||||
|
|
||||||
|
# Test recording
|
||||||
|
z3ed test record --name <test_name> --start
|
||||||
|
z3ed test record --stop [--save-as <file>]
|
||||||
|
z3ed test playback --file <test_file> [--speed <1-10>]
|
||||||
|
|
||||||
|
# Regression testing
|
||||||
|
z3ed test baseline --create --name <baseline>
|
||||||
|
z3ed test baseline --compare --name <baseline> [--threshold <percent>]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 Build & Deploy Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build management
|
||||||
|
z3ed build --preset <preset> [--verbose] [--parallel <jobs>]
|
||||||
|
z3ed build clean [--all]
|
||||||
|
z3ed build test [--preset <preset>]
|
||||||
|
z3ed build package --platform <win|mac|linux> --output <dir>
|
||||||
|
|
||||||
|
# CI/CD integration
|
||||||
|
z3ed ci status [--workflow <name>]
|
||||||
|
z3ed ci trigger --workflow <name> [--branch <branch>]
|
||||||
|
z3ed ci logs --run-id <id> [--follow]
|
||||||
|
z3ed ci artifacts --run-id <id> --download <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Query & Introspection Interface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# System queries
|
||||||
|
z3ed query rom-info [--detailed]
|
||||||
|
z3ed query test-status [--failures-only]
|
||||||
|
z3ed query build-status [--preset <preset>]
|
||||||
|
z3ed query available-commands [--category <cat>] [--format tree|list|json]
|
||||||
|
|
||||||
|
# Data queries
|
||||||
|
z3ed query find-tiles --pattern <hex> [--context <bytes>]
|
||||||
|
z3ed query find-sprites --type <id> [--map <id>]
|
||||||
|
z3ed query find-text --search <string> [--case-sensitive]
|
||||||
|
z3ed query dependencies --entity <type:id>
|
||||||
|
|
||||||
|
# Statistics
|
||||||
|
z3ed query stats --type <rom|dungeon|overworld|sprites>
|
||||||
|
z3ed query usage --command <name> [--since <date>]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.6 Interactive REPL Mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start REPL
|
||||||
|
z3ed repl [--rom <file>] [--history <file>]
|
||||||
|
|
||||||
|
# REPL Features:
|
||||||
|
# - Persistent ROM state across commands
|
||||||
|
# - Command history with arrow keys
|
||||||
|
# - Tab completion for commands and parameters
|
||||||
|
# - Context-aware suggestions
|
||||||
|
# - Session recording/playback
|
||||||
|
# - Variable assignment ($var = command output)
|
||||||
|
# - Pipes and filters (command1 | command2)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### REPL Implementation
|
||||||
|
```cpp
|
||||||
|
class ReplSession {
|
||||||
|
Rom* rom_;
|
||||||
|
std::map<std::string, json> variables_;
|
||||||
|
std::vector<std::string> history_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
absl::Status ProcessLine(const std::string& line) {
|
||||||
|
// Parse for variable assignment
|
||||||
|
if (auto var_match = ParseVariableAssignment(line)) {
|
||||||
|
auto result = ExecuteCommand(var_match->command);
|
||||||
|
variables_[var_match->var_name] = result;
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse for pipes
|
||||||
|
if (auto pipe_commands = ParsePipe(line)) {
|
||||||
|
json previous_output;
|
||||||
|
for (const auto& cmd : *pipe_commands) {
|
||||||
|
auto expanded = ExpandVariables(cmd, previous_output);
|
||||||
|
previous_output = ExecuteCommand(expanded);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple command
|
||||||
|
return ExecuteCommand(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Agent API Improvements
|
||||||
|
|
||||||
|
### 2.1 Enhanced Canvas Automation API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
class EnhancedCanvasAutomationAPI : public CanvasAutomationAPI {
|
||||||
|
public:
|
||||||
|
// Object selection by properties
|
||||||
|
struct ObjectQuery {
|
||||||
|
std::optional<int> type_id;
|
||||||
|
std::optional<ImVec2> position_min;
|
||||||
|
std::optional<ImVec2> position_max;
|
||||||
|
std::optional<std::string> name_pattern;
|
||||||
|
std::map<std::string, std::any> properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ObjectHandle> FindObjects(const ObjectQuery& query) const;
|
||||||
|
|
||||||
|
// Batch operations
|
||||||
|
struct BatchOperation {
|
||||||
|
enum Type { MOVE, MODIFY, DELETE, DUPLICATE };
|
||||||
|
Type type;
|
||||||
|
std::vector<ObjectHandle> objects;
|
||||||
|
std::map<std::string, std::any> parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::Status ExecuteBatch(const std::vector<BatchOperation>& ops);
|
||||||
|
|
||||||
|
// Validation queries
|
||||||
|
bool IsValidPlacement(ObjectHandle obj, ImVec2 position) const;
|
||||||
|
std::vector<std::string> GetPlacementErrors(ObjectHandle obj, ImVec2 pos) const;
|
||||||
|
|
||||||
|
// Event simulation
|
||||||
|
void SimulateClick(ImVec2 position, int button = 0);
|
||||||
|
void SimulateDrag(ImVec2 from, ImVec2 to);
|
||||||
|
void SimulateKeyPress(ImGuiKey key, bool shift = false, bool ctrl = false);
|
||||||
|
void SimulateContextMenu(ImVec2 position);
|
||||||
|
|
||||||
|
// Advanced queries
|
||||||
|
struct CanvasStatistics {
|
||||||
|
int total_objects;
|
||||||
|
std::map<int, int> objects_by_type;
|
||||||
|
float canvas_coverage_percent;
|
||||||
|
ImVec2 bounding_box_min;
|
||||||
|
ImVec2 bounding_box_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
CanvasStatistics GetStatistics() const;
|
||||||
|
|
||||||
|
// Undo/Redo support
|
||||||
|
bool CanUndo() const;
|
||||||
|
bool CanRedo() const;
|
||||||
|
void Undo();
|
||||||
|
void Redo();
|
||||||
|
std::vector<std::string> GetUndoHistory(int count = 10) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 Programmatic Editor Access
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class EditorAutomationAPI {
|
||||||
|
public:
|
||||||
|
// Editor lifecycle
|
||||||
|
absl::Status OpenEditor(EditorType type, const std::string& params = "");
|
||||||
|
absl::Status CloseEditor(EditorHandle handle);
|
||||||
|
std::vector<EditorHandle> GetOpenEditors() const;
|
||||||
|
|
||||||
|
// State snapshots
|
||||||
|
absl::StatusOr<EditorSnapshot> CaptureState(EditorHandle editor);
|
||||||
|
absl::Status RestoreState(EditorHandle editor, const EditorSnapshot& snapshot);
|
||||||
|
absl::Status CompareStates(const EditorSnapshot& s1, const EditorSnapshot& s2);
|
||||||
|
|
||||||
|
// Query current state
|
||||||
|
struct EditorState {
|
||||||
|
EditorType type;
|
||||||
|
std::string name;
|
||||||
|
bool has_unsaved_changes;
|
||||||
|
std::map<std::string, std::any> properties;
|
||||||
|
std::vector<std::string> available_actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
EditorState GetState(EditorHandle editor) const;
|
||||||
|
|
||||||
|
// Execute operations
|
||||||
|
absl::Status ExecuteAction(EditorHandle editor,
|
||||||
|
const std::string& action,
|
||||||
|
const json& parameters);
|
||||||
|
|
||||||
|
// Event subscription
|
||||||
|
using EventCallback = std::function<void(const EditorEvent&)>;
|
||||||
|
|
||||||
|
void Subscribe(EditorHandle editor, EventType type, EventCallback cb);
|
||||||
|
void Unsubscribe(EditorHandle editor, EventType type);
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
absl::Status ValidateEditor(EditorHandle editor);
|
||||||
|
std::vector<ValidationIssue> GetValidationIssues(EditorHandle editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Test Generation API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace yaze {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class TestGenerationAPI {
|
||||||
|
public:
|
||||||
|
// Record interactions
|
||||||
|
void StartRecording(const std::string& test_name);
|
||||||
|
void StopRecording();
|
||||||
|
void PauseRecording();
|
||||||
|
void ResumeRecording();
|
||||||
|
|
||||||
|
// Generate tests from recordings
|
||||||
|
absl::StatusOr<std::string> GenerateTestCode(
|
||||||
|
const std::string& test_name,
|
||||||
|
TestFramework framework = TestFramework::GTEST);
|
||||||
|
|
||||||
|
// Generate tests from specifications
|
||||||
|
struct TestSpecification {
|
||||||
|
std::string class_under_test;
|
||||||
|
std::vector<std::string> methods_to_test;
|
||||||
|
bool include_edge_cases = true;
|
||||||
|
bool include_error_cases = true;
|
||||||
|
bool generate_mocks = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::StatusOr<std::string> GenerateTests(const TestSpecification& spec);
|
||||||
|
|
||||||
|
// Test fixtures from state
|
||||||
|
absl::StatusOr<std::string> GenerateFixture(EditorHandle editor);
|
||||||
|
|
||||||
|
// Regression test generation
|
||||||
|
absl::StatusOr<std::string> GenerateRegressionTest(
|
||||||
|
const std::string& bug_description,
|
||||||
|
const std::vector<std::string>& repro_steps);
|
||||||
|
|
||||||
|
// Test execution
|
||||||
|
struct TestResult {
|
||||||
|
bool passed;
|
||||||
|
std::string output;
|
||||||
|
double execution_time_ms;
|
||||||
|
std::vector<std::string> failures;
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::StatusOr<TestResult> RunGeneratedTest(const std::string& test_code);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Agent UI Enhancements
|
||||||
|
|
||||||
|
### 3.1 Status Dashboard
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class AgentStatusDashboard : public Panel {
|
||||||
|
public:
|
||||||
|
void Draw() override {
|
||||||
|
// Real-time agent activity
|
||||||
|
DrawAgentActivity();
|
||||||
|
|
||||||
|
// Test execution progress
|
||||||
|
DrawTestProgress();
|
||||||
|
|
||||||
|
// Build/CI status
|
||||||
|
DrawBuildStatus();
|
||||||
|
|
||||||
|
// Recent changes
|
||||||
|
DrawRecentChanges();
|
||||||
|
|
||||||
|
// Performance metrics
|
||||||
|
DrawPerformanceMetrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct AgentActivity {
|
||||||
|
std::string agent_name;
|
||||||
|
std::string current_task;
|
||||||
|
float progress_percent;
|
||||||
|
std::chrono::steady_clock::time_point started_at;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<AgentActivity> active_agents_;
|
||||||
|
|
||||||
|
void DrawAgentActivity() {
|
||||||
|
ImGui::Text("Active Agents");
|
||||||
|
for (const auto& agent : active_agents_) {
|
||||||
|
ImGui::ProgressBar(agent.progress_percent / 100.0f,
|
||||||
|
ImVec2(-1, 0),
|
||||||
|
agent.current_task.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Agent Control Panel
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class AgentControlPanel : public Panel {
|
||||||
|
public:
|
||||||
|
void Draw() override {
|
||||||
|
// Agent task management
|
||||||
|
if (ImGui::Button("Start New Task")) {
|
||||||
|
ShowTaskDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active tasks
|
||||||
|
DrawActiveTasks();
|
||||||
|
|
||||||
|
// Agent logs
|
||||||
|
DrawAgentLogs();
|
||||||
|
|
||||||
|
// Manual intervention
|
||||||
|
DrawInterventionControls();
|
||||||
|
|
||||||
|
// Collaboration coordination
|
||||||
|
DrawCollaborationStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ShowTaskDialog() {
|
||||||
|
ImGui::OpenPopup("New Agent Task");
|
||||||
|
if (ImGui::BeginPopupModal("New Agent Task")) {
|
||||||
|
static char task_name[256];
|
||||||
|
ImGui::InputText("Task Name", task_name, sizeof(task_name));
|
||||||
|
|
||||||
|
static int selected_agent = 0;
|
||||||
|
ImGui::Combo("Agent", &selected_agent, available_agents_);
|
||||||
|
|
||||||
|
if (ImGui::Button("Start")) {
|
||||||
|
StartAgentTask(task_name, selected_agent);
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Network/Collaboration Features
|
||||||
|
|
||||||
|
### 4.1 Multi-Agent Coordination
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace yaze {
|
||||||
|
namespace agent {
|
||||||
|
|
||||||
|
class MultiAgentCoordinator {
|
||||||
|
public:
|
||||||
|
// Agent registration
|
||||||
|
absl::Status RegisterAgent(const AgentInfo& info);
|
||||||
|
absl::Status UnregisterAgent(const std::string& agent_id);
|
||||||
|
|
||||||
|
// Work queue management
|
||||||
|
absl::Status QueueTask(const Task& task);
|
||||||
|
absl::StatusOr<Task> ClaimTask(const std::string& agent_id);
|
||||||
|
absl::Status CompleteTask(const std::string& task_id, const TaskResult& result);
|
||||||
|
|
||||||
|
// Shared state
|
||||||
|
absl::Status UpdateSharedState(const std::string& key, const json& value);
|
||||||
|
absl::StatusOr<json> GetSharedState(const std::string& key);
|
||||||
|
absl::Status SubscribeToState(const std::string& key, StateCallback cb);
|
||||||
|
|
||||||
|
// Conflict resolution
|
||||||
|
enum ConflictStrategy {
|
||||||
|
LAST_WRITE_WINS,
|
||||||
|
MERGE,
|
||||||
|
MANUAL_RESOLUTION,
|
||||||
|
QUEUE_SEQUENTIAL
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::Status SetConflictStrategy(ConflictStrategy strategy);
|
||||||
|
absl::StatusOr<Resolution> ResolveConflict(const Conflict& conflict);
|
||||||
|
|
||||||
|
// Agent discovery
|
||||||
|
std::vector<AgentInfo> DiscoverAgents(const AgentQuery& query);
|
||||||
|
absl::StatusOr<AgentCapabilities> GetCapabilities(const std::string& agent_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace agent
|
||||||
|
} // namespace yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Remote z3ed Access
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# OpenAPI 3.0 Specification
|
||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: Z3ED Remote API
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/api/v1/command:
|
||||||
|
post:
|
||||||
|
summary: Execute z3ed command
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
type: string
|
||||||
|
example: "rom read --address 0x1000 --length 16"
|
||||||
|
session_id:
|
||||||
|
type: string
|
||||||
|
timeout_ms:
|
||||||
|
type: integer
|
||||||
|
default: 30000
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Command executed successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
result:
|
||||||
|
type: object
|
||||||
|
execution_time_ms:
|
||||||
|
type: number
|
||||||
|
|
||||||
|
/api/v1/session:
|
||||||
|
post:
|
||||||
|
summary: Create new z3ed session
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
rom_path:
|
||||||
|
type: string
|
||||||
|
persist:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Session created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
session_id:
|
||||||
|
type: string
|
||||||
|
expires_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
|
||||||
|
/api/v1/websocket:
|
||||||
|
get:
|
||||||
|
summary: WebSocket endpoint for real-time updates
|
||||||
|
responses:
|
||||||
|
101:
|
||||||
|
description: Switching Protocols
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 WebSocket Protocol
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// WebSocket message types
|
||||||
|
interface Z3edWebSocketMessage {
|
||||||
|
type: 'command' | 'event' | 'subscribe' | 'unsubscribe';
|
||||||
|
id: string;
|
||||||
|
payload: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command execution
|
||||||
|
interface CommandMessage {
|
||||||
|
type: 'command';
|
||||||
|
id: string;
|
||||||
|
payload: {
|
||||||
|
command: string;
|
||||||
|
args: string[];
|
||||||
|
stream: boolean; // Stream output as it's generated
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event subscription
|
||||||
|
interface SubscribeMessage {
|
||||||
|
type: 'subscribe';
|
||||||
|
id: string;
|
||||||
|
payload: {
|
||||||
|
events: Array<'editor.changed' | 'test.completed' | 'build.status'>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server events
|
||||||
|
interface EventMessage {
|
||||||
|
type: 'event';
|
||||||
|
id: string;
|
||||||
|
payload: {
|
||||||
|
event: string;
|
||||||
|
data: any;
|
||||||
|
timestamp: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Foundation (Weeks 1-2)
|
||||||
|
1. Implement core ROM operations commands
|
||||||
|
2. Add REPL infrastructure
|
||||||
|
3. Enhance Canvas Automation API with batch operations
|
||||||
|
4. Create command discovery/introspection system
|
||||||
|
|
||||||
|
### Phase 2: Editor Integration (Weeks 3-4)
|
||||||
|
1. Implement editor automation commands
|
||||||
|
2. Add programmatic editor access API
|
||||||
|
3. Create test recording infrastructure
|
||||||
|
4. Build event subscription system
|
||||||
|
|
||||||
|
### Phase 3: Testing & CI (Weeks 5-6)
|
||||||
|
1. Implement test generation API
|
||||||
|
2. Add test execution commands
|
||||||
|
3. Create CI/CD integration commands
|
||||||
|
4. Build regression test framework
|
||||||
|
|
||||||
|
### Phase 4: Collaboration (Weeks 7-8)
|
||||||
|
1. Implement multi-agent coordinator
|
||||||
|
2. Add REST API endpoints
|
||||||
|
3. Create WebSocket real-time protocol
|
||||||
|
4. Build conflict resolution system
|
||||||
|
|
||||||
|
### Phase 5: UI & Polish (Weeks 9-10)
|
||||||
|
1. Create Agent Status Dashboard
|
||||||
|
2. Build Agent Control Panel
|
||||||
|
3. Add comprehensive documentation
|
||||||
|
4. Create example workflows
|
||||||
|
|
||||||
|
## 6. Example Workflows
|
||||||
|
|
||||||
|
### Workflow 1: Automated Dungeon Testing
|
||||||
|
```bash
|
||||||
|
# Start REPL session
|
||||||
|
z3ed repl --rom zelda3.sfc
|
||||||
|
|
||||||
|
# Record baseline
|
||||||
|
> rom snapshot --name baseline
|
||||||
|
> editor dungeon --room 0
|
||||||
|
|
||||||
|
# Start test recording
|
||||||
|
> test record --name dungeon_placement_test --start
|
||||||
|
|
||||||
|
# Perform operations
|
||||||
|
> editor dungeon place-object --room 0 --type 0x22 --x 10 --y 15
|
||||||
|
> editor dungeon place-object --room 0 --type 0x23 --x 20 --y 15
|
||||||
|
> query stats --type dungeon
|
||||||
|
|
||||||
|
# Stop and generate test
|
||||||
|
> test record --stop
|
||||||
|
> test generate --from-recording dungeon_placement_test --output test_dungeon.cc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflow 2: Multi-Agent ROM Editing
|
||||||
|
```python
|
||||||
|
import z3ed_client
|
||||||
|
|
||||||
|
# Agent 1: Overworld specialist
|
||||||
|
agent1 = z3ed_client.Agent("overworld_agent")
|
||||||
|
agent1.connect("localhost:8080")
|
||||||
|
|
||||||
|
# Agent 2: Dungeon specialist
|
||||||
|
agent2 = z3ed_client.Agent("dungeon_agent")
|
||||||
|
agent2.connect("localhost:8080")
|
||||||
|
|
||||||
|
# Coordinator assigns tasks
|
||||||
|
coordinator = z3ed_client.Coordinator()
|
||||||
|
coordinator.queue_task({
|
||||||
|
"type": "overworld",
|
||||||
|
"action": "optimize_tilemap",
|
||||||
|
"map_id": 0x00
|
||||||
|
})
|
||||||
|
coordinator.queue_task({
|
||||||
|
"type": "dungeon",
|
||||||
|
"action": "validate_rooms",
|
||||||
|
"rooms": range(0, 296)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Agents work in parallel
|
||||||
|
results = coordinator.wait_for_completion()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflow 3: AI-Powered Test Generation
|
||||||
|
```bash
|
||||||
|
# Analyze class for test generation
|
||||||
|
z3ed test analyze --class OverworldEditor
|
||||||
|
|
||||||
|
# Generate comprehensive tests
|
||||||
|
z3ed test generate \
|
||||||
|
--target OverworldEditor \
|
||||||
|
--include-edge-cases \
|
||||||
|
--include-mocks \
|
||||||
|
--framework gtest \
|
||||||
|
--output overworld_editor_test.cc
|
||||||
|
|
||||||
|
# Run generated tests
|
||||||
|
z3ed test run --file overworld_editor_test.cc --verbose
|
||||||
|
|
||||||
|
# Create regression test from bug
|
||||||
|
z3ed test regression \
|
||||||
|
--bug "Tiles corrupt when placing entrance at map boundary" \
|
||||||
|
--repro-steps "1. Open map 0x00" "2. Place entrance at x=511,y=511" \
|
||||||
|
--output regression_boundary_test.cc
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Security Considerations
|
||||||
|
|
||||||
|
### Authentication & Authorization
|
||||||
|
- API key authentication for remote access
|
||||||
|
- Role-based permissions (read-only, editor, admin)
|
||||||
|
- Session management with expiration
|
||||||
|
- Rate limiting per API key
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
- Command injection prevention
|
||||||
|
- Path traversal protection
|
||||||
|
- Memory address validation
|
||||||
|
- File size limits for imports
|
||||||
|
|
||||||
|
### Audit Logging
|
||||||
|
- All commands logged with timestamp and user
|
||||||
|
- ROM modifications tracked
|
||||||
|
- Rollback capability for destructive operations
|
||||||
|
- Export audit trail for compliance
|
||||||
|
|
||||||
|
## 8. Performance Optimizations
|
||||||
|
|
||||||
|
### Caching
|
||||||
|
- Command result caching for repeated queries
|
||||||
|
- ROM state caching for snapshots
|
||||||
|
- Compiled test cache
|
||||||
|
- WebSocket connection pooling
|
||||||
|
|
||||||
|
### Batch Processing
|
||||||
|
- Aggregate multiple operations into transactions
|
||||||
|
- Parallel execution for independent commands
|
||||||
|
- Lazy loading for large data sets
|
||||||
|
- Progressive streaming for long operations
|
||||||
|
|
||||||
|
### Resource Management
|
||||||
|
- Connection limits per client
|
||||||
|
- Memory quotas for sessions
|
||||||
|
- CPU throttling for intensive operations
|
||||||
|
- Graceful degradation under load
|
||||||
|
|
||||||
|
## 9. Documentation Requirements
|
||||||
|
|
||||||
|
### API Reference
|
||||||
|
- Complete command reference with examples
|
||||||
|
- REST API OpenAPI specification
|
||||||
|
- WebSocket protocol documentation
|
||||||
|
- Error code reference
|
||||||
|
|
||||||
|
### Tutorials
|
||||||
|
- "Getting Started with z3ed REPL"
|
||||||
|
- "Automating ROM Testing"
|
||||||
|
- "Multi-Agent Collaboration"
|
||||||
|
- "Building Custom Commands"
|
||||||
|
|
||||||
|
### Integration Guides
|
||||||
|
- Python client library
|
||||||
|
- JavaScript/TypeScript SDK
|
||||||
|
- CI/CD integration examples
|
||||||
|
- VS Code extension
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
- Command naming conventions
|
||||||
|
- Error handling patterns
|
||||||
|
- Performance optimization tips
|
||||||
|
- Security guidelines
|
||||||
|
|
||||||
|
## 10. Success Metrics
|
||||||
|
|
||||||
|
### Functionality
|
||||||
|
- 100% coverage of editor operations via CLI
|
||||||
|
- < 100ms command execution for simple operations
|
||||||
|
- < 1s for complex batch operations
|
||||||
|
- 99.9% API availability
|
||||||
|
|
||||||
|
### Developer Experience
|
||||||
|
- Tab completion for all commands
|
||||||
|
- Comprehensive error messages
|
||||||
|
- Interactive help system
|
||||||
|
- Example for every command
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- 90% code coverage for new components
|
||||||
|
- Automated regression tests for all commands
|
||||||
|
- Performance benchmarks for critical paths
|
||||||
|
- Integration tests for multi-agent scenarios
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
These enhancements will transform z3ed from a basic CLI tool into a comprehensive automation platform for YAZE. The design prioritizes developer experience, AI agent capabilities, and robust testing infrastructure while maintaining backwards compatibility and performance.
|
||||||
|
|
||||||
|
The modular implementation plan allows for incremental delivery of value, with each phase providing immediately useful functionality. The foundation laid here will enable future innovations in ROM hacking automation and collaborative editing.
|
||||||
@@ -206,7 +206,7 @@ scripts/agents/smoke-build.sh mac-ai z3ed
|
|||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
**Last Updated**: 2025-11-19 12:05 PST
|
**Last Updated**: 2025-11-22 18:30 PST
|
||||||
|
|
||||||
### Completed:
|
### Completed:
|
||||||
- ✅ Coordination board entry posted
|
- ✅ Coordination board entry posted
|
||||||
@@ -236,8 +236,20 @@ scripts/agents/smoke-build.sh mac-ai z3ed
|
|||||||
- ✅ GET /api/v1/models: `{"count": 0, "models": []}` (empty as expected)
|
- ✅ GET /api/v1/models: `{"count": 0, "models": []}` (empty as expected)
|
||||||
- Phase 2 from AI_API_ENHANCEMENT_HANDOFF.md is COMPLETE
|
- Phase 2 from AI_API_ENHANCEMENT_HANDOFF.md is COMPLETE
|
||||||
|
|
||||||
|
- ✅ **Test Infrastructure Stabilization** - COMPLETE (2025-11-21)
|
||||||
|
- Fixed critical stack overflow crash on macOS ARM64 (increased stack from default ~8MB to 16MB)
|
||||||
|
- Resolved circular dependency issues in test configuration
|
||||||
|
- All test categories now stable: unit, integration, e2e, rom-dependent
|
||||||
|
- Verified across all platforms (macOS, Linux, Windows)
|
||||||
|
|
||||||
|
- ✅ **Milestone 2** (CLAUDE_CORE): UI unification for model configuration controls - COMPLETE
|
||||||
|
- Completed unified model configuration UI for Agent panel
|
||||||
|
- Models from all providers (Ollama, Gemini) now display in single dropdown
|
||||||
|
- Provider indicators visible for each model
|
||||||
|
- Provider filtering implemented when provider selection changes
|
||||||
|
|
||||||
### In Progress:
|
### In Progress:
|
||||||
- **Milestone 2** (CLAUDE_CORE): UI unification for model configuration controls
|
- **Milestone 4** (CLAUDE_AIINF): Enhanced Tools Phase 3 - FileSystemTool and BuildTool
|
||||||
|
|
||||||
### Helper Scripts (from CODEX):
|
### Helper Scripts (from CODEX):
|
||||||
Both personas should use these scripts for testing and validation:
|
Both personas should use these scripts for testing and validation:
|
||||||
@@ -245,7 +257,9 @@ Both personas should use these scripts for testing and validation:
|
|||||||
- `scripts/agents/run-gh-workflow.sh` - Trigger remote GitHub Actions workflows
|
- `scripts/agents/run-gh-workflow.sh` - Trigger remote GitHub Actions workflows
|
||||||
- Documentation: `scripts/agents/README.md` and `docs/internal/README.md`
|
- Documentation: `scripts/agents/README.md` and `docs/internal/README.md`
|
||||||
|
|
||||||
### Next Actions (Post Milestones 2 & 3):
|
### Next Actions (Post Milestones 2, 3, & Test Stabilization):
|
||||||
1. Add FileSystemTool and BuildTool (Phase 3)
|
1. Complete Milestone 4: Add FileSystemTool and BuildTool (Phase 3)
|
||||||
2. Begin ToolDispatcher structured output refactoring (Phase 4)
|
2. Begin ToolDispatcher structured output refactoring (Phase 4)
|
||||||
3. Comprehensive testing across all platforms using smoke-build.sh
|
3. Comprehensive testing across all platforms using smoke-build.sh
|
||||||
|
4. Release validation: Ensure all new features work in release builds
|
||||||
|
5. Performance optimization: Profile test execution time and optimize as needed
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Agent Coordination & Documentation Improvement Plan
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
1. **Persona Inconsistency**: `coordination-board.md` uses a mix of legacy IDs (`CLAUDE_AIINF`) and new canonical IDs (`ai-infra-architect`).
|
||||||
|
2. **Tool Underutilization**: The protocol in `AGENTS.md` relies entirely on manual Markdown edits, ignoring the built-in `z3ed agent` CLI tools (todo, handoff) described in `agent-architecture.md`.
|
||||||
|
3. **Fragmented Docs**: There is no central entry point (`README.md`) for agents entering the directory.
|
||||||
|
4. **Undefined Systems**: `claude-gemini-collaboration.md` references a "challenge system" and "leaderboard" that do not exist.
|
||||||
|
|
||||||
|
## Proposed Actions
|
||||||
|
|
||||||
|
### 1. Update `AGENTS.md` (The Protocol)
|
||||||
|
* **Mandate CLI Tools**: Update the "Quick tasks" and "Substantial work" sections to recommend using `z3ed agent todo` for personal task tracking.
|
||||||
|
* **Clarify Handoffs**: Explicitly mention using `z3ed agent handoff` for transferring context, with the Markdown board used for *public signaling*.
|
||||||
|
* **Strict Persona Usage**: Remove "Legacy aliases" mapping and simply link to `personas.md` as the source of truth.
|
||||||
|
|
||||||
|
### 2. Cleanup `coordination-board.md` (The Board)
|
||||||
|
* **Header Update**: Add a bold warning to use only IDs from `personas.md`.
|
||||||
|
* **Retroactive Fix**: Update recent active entries to use the correct new IDs (e.g., convert `CLAUDE_AIINF` -> `ai-infra-architect` where appropriate).
|
||||||
|
|
||||||
|
### 3. Create `docs/internal/agents/README.md` (The Hub)
|
||||||
|
* Create a simple index file that links to:
|
||||||
|
* **Protocol**: `AGENTS.md`
|
||||||
|
* **Roles**: `personas.md`
|
||||||
|
* **Status**: `coordination-board.md`
|
||||||
|
* **Tools**: `agent-architecture.md`
|
||||||
|
* Provide a 3-step "Start Here" guide for new agents.
|
||||||
|
|
||||||
|
### 4. Deprecate `claude-gemini-collaboration.md`
|
||||||
|
* Rename to `docs/internal/agents/archive/collaboration-concept-legacy.md` or remove the "Challenge System" sections if the file is still valuable for its architectural definitions.
|
||||||
|
* *Recommendation*: If the "Architecture vs. Automation" split is still relevant, update the file to use `backend-infra-engineer` (Architecture) vs `GEMINI_AUTOM` (Automation) instead of "Claude vs Gemini".
|
||||||
|
|
||||||
|
## Execution Order
|
||||||
|
1. Create `docs/internal/agents/README.md`.
|
||||||
|
2. Update `AGENTS.md`.
|
||||||
|
3. Clean up `coordination-board.md`.
|
||||||
|
4. Refactor `claude-gemini-collaboration.md`.
|
||||||
132
docs/internal/agents/archive/plans-2025-11/CLEANUP_SUMMARY.md
Normal file
132
docs/internal/agents/archive/plans-2025-11/CLEANUP_SUMMARY.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Documentation Cleanup Summary - November 2025
|
||||||
|
|
||||||
|
**Date:** 2025-11-24
|
||||||
|
**Action:** Cleanup of speculative planning documents and AI-generated bloat from `/docs/internal/plans/`
|
||||||
|
**Rationale:** Planning documents should live in GitHub issues and coordination board, not as static markdown. Only actionable, actively-tracked plans belong in the codebase.
|
||||||
|
|
||||||
|
## Files Deleted (Pure AI-Generated Bloat)
|
||||||
|
|
||||||
|
These files were entirely speculative with no corresponding implementation:
|
||||||
|
|
||||||
|
1. **asm-debug-prompt-engineering.md** (45KB)
|
||||||
|
- Extensive prompt templates for 65816 debugging
|
||||||
|
- No evidence of integration or use
|
||||||
|
- Classified as AI-generated reference material
|
||||||
|
|
||||||
|
2. **ai-assisted-development-plan.md** (17KB)
|
||||||
|
- Workflow proposal for AI-assisted development
|
||||||
|
- All features marked "Active" with "Next Review" dates but never implemented
|
||||||
|
- Generic architecture diagrams with no corresponding code
|
||||||
|
|
||||||
|
3. **app-dev-agent-tools.md** (21KB)
|
||||||
|
- 824 lines specifying 16 agent tools (build, code analysis, debug, editor integration)
|
||||||
|
- All tools in Phase 1-3 (theoretical)
|
||||||
|
- No implementation in codebase or recent commits
|
||||||
|
|
||||||
|
4. **EDITOR_ROADMAPS_2025-11.md** (25KB)
|
||||||
|
- Multi-agent analysis document referencing "imgui-frontend-engineer" agent analysis
|
||||||
|
- Generic roadmap format with estimated effort hours
|
||||||
|
- Duplicate content with dungeon_editor_ui_refactor.md
|
||||||
|
|
||||||
|
5. **message_system_improvement_plan.md** (2KB)
|
||||||
|
- Duplicate of sections in message_editor_implementation_roadmap.md
|
||||||
|
- Generic feature wishlist (JSON export, translation workspace, search & replace)
|
||||||
|
- No distinct value
|
||||||
|
|
||||||
|
6. **graphics_system_improvement_plan.md** (2.8KB)
|
||||||
|
- Feature wishlist (unified editor, palette management, sprite assembly)
|
||||||
|
- No concrete deliverables or implementation plan
|
||||||
|
- Superseded by architecture documentation
|
||||||
|
|
||||||
|
7. **ui_modernization.md** (3.3KB)
|
||||||
|
- Describes patterns already documented in CLAUDE.md
|
||||||
|
- Marked "Active" but content is obsolete (already implemented)
|
||||||
|
- Redundant with existing guidelines
|
||||||
|
|
||||||
|
## Files Archived (Partially Implemented / Historical Reference)
|
||||||
|
|
||||||
|
These files have some value as reference but are not actively tracked work:
|
||||||
|
|
||||||
|
1. **emulator-debug-api-design.md** → `archive/plans-2025-11/`
|
||||||
|
- Design document for emulator debugging API
|
||||||
|
- Some features implemented (breakpoints, memory inspection)
|
||||||
|
- Watchpoints and symbol loading still planned but deprioritized
|
||||||
|
- Value: Technical reference for future work
|
||||||
|
|
||||||
|
2. **message_editor_implementation_roadmap.md** → `archive/plans-2025-11/`
|
||||||
|
- References actual code (MessageData, MessagePreview classes)
|
||||||
|
- Documents what's completed vs. what's missing (JSON import/export)
|
||||||
|
- Some ongoing development but should be tracked in coordination board
|
||||||
|
- Value: Implementation reference
|
||||||
|
|
||||||
|
3. **hex_editor_enhancements.md** → `archive/plans-2025-11/`
|
||||||
|
- Phase 1 (Data Inspector) concept defined
|
||||||
|
- Phases 2-4 unimplemented
|
||||||
|
- Better tracked as GitHub issue than static plan
|
||||||
|
- Value: Technical spike reference
|
||||||
|
|
||||||
|
4. **dungeon_editor_ui_refactor.md** → `archive/plans-2025-11/`
|
||||||
|
- Actually referenced in git commit acab491a1f (component was removed)
|
||||||
|
- Concrete refactoring steps with clear deliverables
|
||||||
|
- Now completed/obsolete
|
||||||
|
- Value: Historical record of refactoring
|
||||||
|
|
||||||
|
## Files Retained (Actively Tracked)
|
||||||
|
|
||||||
|
1. **web_port_strategy.md**
|
||||||
|
- Strategic milestone document for WASM port
|
||||||
|
- Multiple recent commits show active ongoing work (52b1a99, 56e05bf, 206e926, etc.)
|
||||||
|
- Clear milestones with deliverables
|
||||||
|
- Actively referenced in CI/build processes
|
||||||
|
- Status: KEEP - actively developed feature
|
||||||
|
|
||||||
|
2. **ai-infra-improvements.md**
|
||||||
|
- Structured phase-based plan (gRPC server, emulator RPCs, breakpoints, symbols)
|
||||||
|
- More specific than other plans with concrete files to modify
|
||||||
|
- Tracks infrastructure gaps with file references
|
||||||
|
- Status: KEEP - tracks ongoing infrastructure work
|
||||||
|
|
||||||
|
3. **README.md**
|
||||||
|
- Directory governance and guidelines
|
||||||
|
- Actively enforces plan organization standards
|
||||||
|
- Status: KEEP - essential for directory management
|
||||||
|
|
||||||
|
## Rationale for This Cleanup
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
- 14 planning files (400KB) in `/docs/internal/plans/`
|
||||||
|
- Most marked "Active" with forward-looking dates (Nov 25 → Dec 2)
|
||||||
|
- Little correlation with actual work in recent commits
|
||||||
|
- Directory becoming a repository of speculative AI-generated content
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
- Keep only plans with active ongoing work (2 files)
|
||||||
|
- Archive reference documents with partial implementation (4 files)
|
||||||
|
- Delete pure speculation and AI-generated bloat (7 files)
|
||||||
|
- Directory size reduced from 400KB to 13KB at root level
|
||||||
|
|
||||||
|
### Principle
|
||||||
|
**Planning belongs in GitHub issues and coordination board, not in markdown files.**
|
||||||
|
|
||||||
|
Static plan documents should only exist for:
|
||||||
|
1. Strategic initiatives (like WASM web port) with active commits
|
||||||
|
2. Infrastructure work with concrete phases and file references
|
||||||
|
3. Historical reference after completion
|
||||||
|
|
||||||
|
Speculative planning should use:
|
||||||
|
- GitHub Discussions for RFCs
|
||||||
|
- Issues with labels for feature requests
|
||||||
|
- Coordination board for multi-agent work tracking
|
||||||
|
|
||||||
|
## Files in Archive
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/internal/agents/archive/plans-2025-11/
|
||||||
|
├── CLEANUP_SUMMARY.md
|
||||||
|
├── dungeon_editor_ui_refactor.md
|
||||||
|
├── emulator-debug-api-design.md
|
||||||
|
├── hex_editor_enhancements.md
|
||||||
|
└── message_editor_implementation_roadmap.md
|
||||||
|
```
|
||||||
|
|
||||||
|
These are available if needed as historical context but should not be referenced for active development.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Dungeon Editor UI Refactor Plan
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
The Dungeon Editor currently uses a primitive "colored square" representation for objects in the object selector, despite having a full-fidelity rendering component (`DungeonObjectSelector`) available. This plan outlines the refactoring steps to integrate the game-accurate object browser into the main `ObjectEditorCard`, improving UX and eliminating code duplication.
|
||||||
|
|
||||||
|
## 2. Current State Analysis
|
||||||
|
- **`ObjectEditorCard` (Active UI):** Reimplements object selection logic in `DrawObjectSelector()`. Renders objects as simple colored rectangles (`DrawObjectPreviewIcon`).
|
||||||
|
- **`DungeonObjectSelector` (Component):** Contains `DrawObjectAssetBrowser()`, which uses `ObjectDrawer` to render actual tile graphics. This component is instantiated as a member `object_selector_` in `ObjectEditorCard` but is effectively unused.
|
||||||
|
- **`DungeonEditorV2`:** Instantiates a separate, unused `DungeonObjectSelector` (`object_selector_`), adding to the confusion.
|
||||||
|
|
||||||
|
## 3. Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Component Preparation
|
||||||
|
1. **Expose UI Method:** Ensure `DungeonObjectSelector::DrawObjectAssetBrowser()` is public or accessible to `ObjectEditorCard`.
|
||||||
|
2. **State Synchronization:** Ensure `DungeonObjectSelector` has access to the same `Rom` and `PaletteGroup` data as `ObjectEditorCard` so it can render correctly.
|
||||||
|
|
||||||
|
### Phase 2: Refactor ObjectEditorCard
|
||||||
|
1. **Delegate Rendering:** Replace the body of `ObjectEditorCard::DrawObjectSelector()` with a call to `object_selector_.DrawObjectAssetBrowser()`.
|
||||||
|
2. **Callback Wiring:**
|
||||||
|
* In `ObjectEditorCard::Initialize` (or constructor), set up the callback for `object_selector_`.
|
||||||
|
* When an object is selected in `object_selector_`, it should update `ObjectEditorCard::preview_object_` and `canvas_viewer_`.
|
||||||
|
* Current logic:
|
||||||
|
```cpp
|
||||||
|
object_selector_.SetObjectSelectedCallback([this](const zelda3::RoomObject& obj) {
|
||||||
|
this->preview_object_ = obj;
|
||||||
|
this->has_preview_object_ = true;
|
||||||
|
this->canvas_viewer_->SetPreviewObject(obj);
|
||||||
|
this->interaction_mode_ = InteractionMode::Place;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
3. **Cleanup:** Remove private helper `DrawObjectPreviewIcon` and the old loop logic in `ObjectEditorCard`.
|
||||||
|
|
||||||
|
### Phase 3: Cleanup DungeonEditorV2
|
||||||
|
1. **Remove Redundancy:** Remove the top-level `DungeonObjectSelector object_selector_` from `DungeonEditorV2`. The one inside `ObjectEditorCard` is sufficient.
|
||||||
|
2. **Verify Initialization:** Ensure `DungeonEditorV2` correctly initializes `ObjectEditorCard` with the necessary dependencies.
|
||||||
|
|
||||||
|
## 4. Verification
|
||||||
|
1. **Build:** Compile `yaze`.
|
||||||
|
2. **Test:** Open Dungeon Editor -> Object Editor tab.
|
||||||
|
3. **Expectation:** The object list should now show actual graphics (walls, chests, pots) instead of colored squares.
|
||||||
|
4. **Interaction:** Clicking an object should correctly load it into the cursor for placement.
|
||||||
|
|
||||||
|
## 5. Dependencies
|
||||||
|
- `src/app/editor/dungeon/object_editor_card.cc`
|
||||||
|
- `src/app/editor/dungeon/object_editor_card.h`
|
||||||
|
- `src/app/editor/dungeon/dungeon_object_selector.cc`
|
||||||
|
- `src/app/editor/dungeon/dungeon_object_selector.h`
|
||||||
|
- `src/app/editor/dungeon/dungeon_editor_v2.h`
|
||||||
@@ -0,0 +1,516 @@
|
|||||||
|
# Emulator Debug API Design for AI Agent Integration
|
||||||
|
|
||||||
|
**Status:** Active
|
||||||
|
**Owner (Agent ID):** snes-emulator-expert
|
||||||
|
**Last Updated:** 2025-11-25
|
||||||
|
**Next Review:** 2025-12-02
|
||||||
|
**Coordination Board Entry:** link when claimed
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This document outlines the design for a comprehensive debugging API that enables AI agents to debug Zelda ROM hacks through the yaze emulator. The API provides execution control, memory inspection, disassembly, and analysis capabilities specifically tailored for 65816 and SPC700 debugging.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
||||||
|
│ AI Agent │◄────►│ Tool Dispatcher │◄────►│ gRPC Service │
|
||||||
|
│ (Claude/GPT) │ │ │ │ │
|
||||||
|
└──────────────────┘ └─────────────────┘ └──────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────────┐ ┌──────────────────┐
|
||||||
|
│ Tool Handlers │ │ Emulator Service │
|
||||||
|
│ │ │ Implementation │
|
||||||
|
└─────────────────┘ └──────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ Debug Infrastructure │
|
||||||
|
├─────────────────┬───────────────────────┤
|
||||||
|
│ Disassembler │ Step Controller │
|
||||||
|
│ Symbol Provider │ Breakpoint Manager │
|
||||||
|
│ Memory Tracker │ State Snapshots │
|
||||||
|
└─────────────────┴───────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 1 (MVP) Features
|
||||||
|
|
||||||
|
### 1. Execution Control API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Tool Dispatcher Commands
|
||||||
|
enum class EmulatorDebugTool {
|
||||||
|
// Basic execution
|
||||||
|
kDebugRun, // Run until breakpoint or pause
|
||||||
|
kDebugStep, // Single instruction step
|
||||||
|
kDebugStepOver, // Step over JSR/JSL calls
|
||||||
|
kDebugStepOut, // Step out of current subroutine
|
||||||
|
kDebugPause, // Pause execution
|
||||||
|
kDebugReset, // Reset to power-on state
|
||||||
|
|
||||||
|
// Breakpoint management
|
||||||
|
kDebugSetBreak, // Set execution breakpoint
|
||||||
|
kDebugClearBreak, // Clear breakpoint by ID
|
||||||
|
kDebugListBreaks, // List all breakpoints
|
||||||
|
kDebugToggleBreak, // Enable/disable breakpoint
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example Tool Call Structure:
|
||||||
|
```cpp
|
||||||
|
struct DebugStepRequest {
|
||||||
|
enum StepType {
|
||||||
|
SINGLE, // One instruction
|
||||||
|
OVER, // Step over calls
|
||||||
|
OUT, // Step out of routine
|
||||||
|
TO_ADDRESS // Run to specific address
|
||||||
|
};
|
||||||
|
|
||||||
|
StepType type;
|
||||||
|
uint32_t target_address; // For TO_ADDRESS
|
||||||
|
uint32_t max_steps; // Timeout protection
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DebugStepResponse {
|
||||||
|
bool success;
|
||||||
|
uint32_t pc; // New program counter
|
||||||
|
uint32_t instruction_count;
|
||||||
|
DisassembledInstruction current_instruction;
|
||||||
|
std::vector<std::string> call_stack;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Memory Inspection API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class MemoryDebugTool {
|
||||||
|
kMemoryRead, // Read memory region
|
||||||
|
kMemoryWrite, // Write memory (for patching)
|
||||||
|
kMemoryWatch, // Set memory watchpoint
|
||||||
|
kMemoryCompare, // Compare two memory regions
|
||||||
|
kMemorySearch, // Search for byte pattern
|
||||||
|
kMemorySnapshot, // Save memory state
|
||||||
|
kMemoryDiff, // Diff against snapshot
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Memory Region Types:
|
||||||
|
```cpp
|
||||||
|
enum class MemoryRegion {
|
||||||
|
WRAM, // Work RAM ($7E0000-$7FFFFF)
|
||||||
|
SRAM, // Save RAM ($700000-$77FFFF)
|
||||||
|
ROM, // ROM banks ($008000-$FFFFFF)
|
||||||
|
VRAM, // Video RAM (PPU)
|
||||||
|
OAM, // Sprite data
|
||||||
|
CGRAM, // Palette data
|
||||||
|
APU_RAM, // Audio RAM ($0000-$FFFF in SPC700 space)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemoryReadRequest {
|
||||||
|
MemoryRegion region;
|
||||||
|
uint32_t address; // 24-bit SNES address
|
||||||
|
uint32_t size; // Bytes to read
|
||||||
|
bool as_hex; // Format as hex dump
|
||||||
|
bool with_symbols; // Include symbol annotations
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemoryReadResponse {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
std::string hex_dump;
|
||||||
|
std::map<uint32_t, std::string> symbols; // Address -> symbol name
|
||||||
|
std::string interpretation; // AI-friendly interpretation
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Disassembly API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class DisassemblyTool {
|
||||||
|
kDisassemble, // Disassemble at address
|
||||||
|
kDisassembleRange, // Disassemble N instructions
|
||||||
|
kDisassembleContext, // Show surrounding code
|
||||||
|
kFindInstruction, // Search for instruction pattern
|
||||||
|
kGetCallStack, // Get current call stack
|
||||||
|
kTraceExecution, // Get execution history
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Disassembly Request/Response:
|
||||||
|
```cpp
|
||||||
|
struct DisassemblyRequest {
|
||||||
|
uint32_t address;
|
||||||
|
uint32_t instruction_count;
|
||||||
|
uint32_t context_before; // Instructions before target
|
||||||
|
uint32_t context_after; // Instructions after target
|
||||||
|
bool include_symbols;
|
||||||
|
bool include_execution_counts;
|
||||||
|
bool track_branches; // Show branch targets
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DisassemblyResponse {
|
||||||
|
struct Line {
|
||||||
|
uint32_t address;
|
||||||
|
std::string hex_bytes; // "20 34 80"
|
||||||
|
std::string mnemonic; // "JSR"
|
||||||
|
std::string operands; // "$8034"
|
||||||
|
std::string symbol; // "MainGameLoop"
|
||||||
|
std::string comment; // "; Initialize game state"
|
||||||
|
bool is_breakpoint;
|
||||||
|
bool is_current_pc;
|
||||||
|
uint32_t execution_count;
|
||||||
|
uint32_t branch_target; // For jumps/branches
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Line> lines;
|
||||||
|
std::string formatted_text; // Human-readable disassembly
|
||||||
|
std::vector<std::string> referenced_symbols;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Analysis API
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class AnalysisTool {
|
||||||
|
kAnalyzeRoutine, // Analyze subroutine behavior
|
||||||
|
kFindReferences, // Find references to address
|
||||||
|
kDetectPattern, // Detect common bug patterns
|
||||||
|
kCompareRom, // Compare with original ROM
|
||||||
|
kProfileExecution, // Performance profiling
|
||||||
|
kTrackDataFlow, // Track value propagation
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tool Dispatcher Integration
|
||||||
|
|
||||||
|
### New Tool Definitions
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In tool_dispatcher.h
|
||||||
|
enum class ToolCallType {
|
||||||
|
// ... existing tools ...
|
||||||
|
|
||||||
|
// Debugger - Execution Control
|
||||||
|
kDebugRun,
|
||||||
|
kDebugStep,
|
||||||
|
kDebugStepOver,
|
||||||
|
kDebugStepOut,
|
||||||
|
kDebugRunToAddress,
|
||||||
|
|
||||||
|
// Debugger - Breakpoints
|
||||||
|
kDebugSetBreakpoint,
|
||||||
|
kDebugSetWatchpoint,
|
||||||
|
kDebugClearBreakpoint,
|
||||||
|
kDebugListBreakpoints,
|
||||||
|
kDebugEnableBreakpoint,
|
||||||
|
|
||||||
|
// Debugger - Memory
|
||||||
|
kDebugReadMemory,
|
||||||
|
kDebugWriteMemory,
|
||||||
|
kDebugSearchMemory,
|
||||||
|
kDebugCompareMemory,
|
||||||
|
kDebugSnapshotMemory,
|
||||||
|
|
||||||
|
// Debugger - Disassembly
|
||||||
|
kDebugDisassemble,
|
||||||
|
kDebugGetCallStack,
|
||||||
|
kDebugGetExecutionTrace,
|
||||||
|
kDebugFindInstruction,
|
||||||
|
|
||||||
|
// Debugger - Analysis
|
||||||
|
kDebugAnalyzeRoutine,
|
||||||
|
kDebugFindReferences,
|
||||||
|
kDebugDetectBugs,
|
||||||
|
kDebugProfileCode,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tool Handler Implementation
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class DebuggerToolHandler {
|
||||||
|
public:
|
||||||
|
explicit DebuggerToolHandler(EmulatorServiceImpl* service);
|
||||||
|
|
||||||
|
absl::StatusOr<ToolResponse> HandleDebugStep(const nlohmann::json& params);
|
||||||
|
absl::StatusOr<ToolResponse> HandleDebugSetBreakpoint(const nlohmann::json& params);
|
||||||
|
absl::StatusOr<ToolResponse> HandleDebugDisassemble(const nlohmann::json& params);
|
||||||
|
absl::StatusOr<ToolResponse> HandleDebugReadMemory(const nlohmann::json& params);
|
||||||
|
absl::StatusOr<ToolResponse> HandleDebugAnalyze(const nlohmann::json& params);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EmulatorServiceImpl* emulator_service_;
|
||||||
|
Disassembler65816 disassembler_;
|
||||||
|
SymbolProvider symbol_provider_;
|
||||||
|
MemoryAnalyzer analyzer_;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example AI Agent Interactions
|
||||||
|
|
||||||
|
### Example 1: Debugging a Crash
|
||||||
|
|
||||||
|
**Agent Query:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "debug_analyze",
|
||||||
|
"params": {
|
||||||
|
"problem": "Game crashes when entering dungeon room 15",
|
||||||
|
"context": {
|
||||||
|
"room_id": 15,
|
||||||
|
"entrance_id": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**System Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"analysis": {
|
||||||
|
"crash_location": {
|
||||||
|
"address": "0x008A45",
|
||||||
|
"instruction": "LDA [$00],Y",
|
||||||
|
"symbol": "LoadRoomHeader+0x15"
|
||||||
|
},
|
||||||
|
"cause": "Null pointer dereference",
|
||||||
|
"explanation": "Direct page location $00 contains $0000, causing invalid long pointer access",
|
||||||
|
"registers_at_crash": {
|
||||||
|
"Y": "0x0010",
|
||||||
|
"DP": "0x1E00",
|
||||||
|
"$1E00": "0x0000"
|
||||||
|
},
|
||||||
|
"suggested_fix": "Check room header pointer before dereferencing",
|
||||||
|
"call_stack": [
|
||||||
|
"Module_Dungeon",
|
||||||
|
"Dungeon_LoadRoom",
|
||||||
|
"LoadRoomHeader"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Step-by-Step Debugging
|
||||||
|
|
||||||
|
**Agent Query:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "debug_step",
|
||||||
|
"params": {
|
||||||
|
"type": "over",
|
||||||
|
"with_context": true,
|
||||||
|
"show_memory": ["$7E0010", "$7E0012"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pc_before": "0x008034",
|
||||||
|
"pc_after": "0x008037",
|
||||||
|
"instruction_executed": {
|
||||||
|
"address": "0x008034",
|
||||||
|
"mnemonic": "JSR",
|
||||||
|
"operand": "$8520",
|
||||||
|
"symbol": "UpdateSprites",
|
||||||
|
"cycles": 6
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"before": [
|
||||||
|
{"address": "0x008031", "instruction": "LDA $10", "value": "0x07"}
|
||||||
|
],
|
||||||
|
"after": [
|
||||||
|
{"address": "0x008037", "instruction": "BEQ $8045"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"memory_values": {
|
||||||
|
"$7E0010": "0x07",
|
||||||
|
"$7E0012": "0x00"
|
||||||
|
},
|
||||||
|
"call_depth": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: Finding Bug Patterns
|
||||||
|
|
||||||
|
**Agent Query:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "debug_detect_bugs",
|
||||||
|
"params": {
|
||||||
|
"patterns": ["stack_overflow", "invalid_bank", "dma_collision"],
|
||||||
|
"range": {
|
||||||
|
"start": "0x008000",
|
||||||
|
"end": "0x00FFFF"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bugs_found": [
|
||||||
|
{
|
||||||
|
"type": "potential_stack_overflow",
|
||||||
|
"location": "0x009A23",
|
||||||
|
"description": "Recursive JSR without stack check",
|
||||||
|
"severity": "high",
|
||||||
|
"suggestion": "Add stack pointer validation before recursive call"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "invalid_bank_switch",
|
||||||
|
"location": "0x00B456",
|
||||||
|
"description": "PHB without corresponding PLB",
|
||||||
|
"severity": "medium",
|
||||||
|
"suggestion": "Ensure data bank is restored after operation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Memory Watchpoint
|
||||||
|
|
||||||
|
**Agent Query:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tool": "debug_set_watchpoint",
|
||||||
|
"params": {
|
||||||
|
"address": "0x7E0020",
|
||||||
|
"size": 2,
|
||||||
|
"type": "write",
|
||||||
|
"condition": "value > 0x00FF",
|
||||||
|
"description": "Monitor game state overflow"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"watchpoint_id": 5,
|
||||||
|
"status": "active",
|
||||||
|
"message": "Watchpoint set on $7E0020-$7E0021 for writes > 0x00FF"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 2 (Full) Features
|
||||||
|
|
||||||
|
### Advanced Analysis
|
||||||
|
- **Control Flow Graphs**: Generate CFG for routines
|
||||||
|
- **Data Flow Analysis**: Track value propagation through code
|
||||||
|
- **Symbolic Execution**: Analyze possible execution paths
|
||||||
|
- **Pattern Matching**: Detect specific code patterns (e.g., DMA setup, HDMA tables)
|
||||||
|
- **Performance Profiling**: Cycle-accurate performance analysis
|
||||||
|
|
||||||
|
### Enhanced Debugging
|
||||||
|
- **Conditional Breakpoints**: Complex expressions (e.g., "A > 0x10 && X == 0")
|
||||||
|
- **Trace Recording**: Record full execution traces to file
|
||||||
|
- **Reverse Debugging**: Step backwards through recorded execution
|
||||||
|
- **Memory Diffing**: Visual diff between memory states
|
||||||
|
- **SPC700 Debugging**: Full audio processor debugging support
|
||||||
|
|
||||||
|
### AI-Specific Features
|
||||||
|
- **Semantic Analysis**: Understanding game logic from assembly
|
||||||
|
- **Bug Pattern Database**: ML-trained bug detection
|
||||||
|
- **Automated Fix Suggestions**: Propose assembly patches for bugs
|
||||||
|
- **Test Case Generation**: Generate test scenarios for ROM hacks
|
||||||
|
- **Documentation Generation**: Auto-document assembly routines
|
||||||
|
|
||||||
|
## Implementation Priority
|
||||||
|
|
||||||
|
### Phase 1A (Immediate - Week 1-2)
|
||||||
|
1. Basic step control (single, over, out)
|
||||||
|
2. Simple breakpoints (address-based)
|
||||||
|
3. Memory read/write operations
|
||||||
|
4. Basic disassembly at address
|
||||||
|
|
||||||
|
### Phase 1B (Short-term - Week 3-4)
|
||||||
|
1. Call stack tracking
|
||||||
|
2. Symbol resolution
|
||||||
|
3. Memory watchpoints
|
||||||
|
4. Execution trace (last N instructions)
|
||||||
|
|
||||||
|
### Phase 1C (Medium-term - Week 5-6)
|
||||||
|
1. Pattern-based bug detection
|
||||||
|
2. Memory snapshots and comparison
|
||||||
|
3. Advanced breakpoint conditions
|
||||||
|
4. Performance metrics
|
||||||
|
|
||||||
|
### Phase 2 (Long-term - Month 2+)
|
||||||
|
1. Full analysis suite
|
||||||
|
2. SPC700 debugging
|
||||||
|
3. Reverse debugging
|
||||||
|
4. AI-specific enhancements
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
### Technical Metrics
|
||||||
|
- Response time < 100ms for step operations
|
||||||
|
- Support for 100+ simultaneous breakpoints without performance impact
|
||||||
|
- Accurate disassembly for 100% of valid 65816 opcodes
|
||||||
|
- Symbol resolution for all loaded ASM files
|
||||||
|
|
||||||
|
### User Experience Metrics
|
||||||
|
- AI agents can identify crash causes in < 5 interactions
|
||||||
|
- Step debugging provides sufficient context without overwhelming
|
||||||
|
- Memory inspection clearly shows relevant game state
|
||||||
|
- Bug detection has < 10% false positive rate
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### With Existing yaze Components
|
||||||
|
- **Rom Class**: Read-only access to ROM data
|
||||||
|
- **Emulator Core**: Direct CPU/PPU/APU state access
|
||||||
|
- **Symbol Files**: Integration with usdasm output
|
||||||
|
- **Canvas System**: Visual debugging overlays (Phase 2)
|
||||||
|
|
||||||
|
### With AI Infrastructure
|
||||||
|
- **Tool Dispatcher**: Seamless tool call routing
|
||||||
|
- **Prompt Builder**: Context-aware debugging prompts
|
||||||
|
- **Agent Memory**: Persistent debugging session state
|
||||||
|
- **Response Formatter**: Human-readable debug output
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Read-Only by Default**: Prevent accidental ROM corruption
|
||||||
|
2. **Sandboxed Execution**: Limit memory access to emulated space
|
||||||
|
3. **Rate Limiting**: Prevent runaway debugging loops
|
||||||
|
4. **Audit Logging**: Track all debugging operations
|
||||||
|
5. **Session Isolation**: Separate debug sessions per agent
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- Disassembler accuracy for all opcodes
|
||||||
|
- Step controller call stack tracking
|
||||||
|
- Breakpoint manager hit detection
|
||||||
|
- Symbol provider resolution
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- Full debugging session workflows
|
||||||
|
- gRPC service communication
|
||||||
|
- Tool dispatcher routing
|
||||||
|
- Memory state consistency
|
||||||
|
|
||||||
|
### End-to-End Tests
|
||||||
|
- AI agent debugging scenarios
|
||||||
|
- Bug detection accuracy
|
||||||
|
- Performance under load
|
||||||
|
- Error recovery paths
|
||||||
|
|
||||||
|
## Documentation Requirements
|
||||||
|
|
||||||
|
1. **API Reference**: Complete gRPC service documentation
|
||||||
|
2. **Tool Guide**: How to use each debugging tool
|
||||||
|
3. **Assembly Primer**: 65816 basics for AI agents
|
||||||
|
4. **Common Patterns**: Debugging patterns for Zelda3
|
||||||
|
5. **Troubleshooting**: Common issues and solutions
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This debugging API design provides a comprehensive foundation for AI agents to effectively debug SNES ROM hacks. The phased approach ensures quick delivery of core features while building toward advanced analysis capabilities. The integration with existing yaze infrastructure and focus on 65816-specific debugging makes this a powerful tool for ROM hacking assistance.
|
||||||
|
|
||||||
|
The API balances technical depth with usability, providing both low-level control for precise debugging and high-level analysis for pattern recognition. This enables AI agents to assist with everything from simple crash debugging to complex performance optimization.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Plan: Hex Editor Enhancements (Inspired by ImHex)
|
||||||
|
|
||||||
|
**Status:** Active
|
||||||
|
**Owner (Agent ID):** imgui-frontend-engineer
|
||||||
|
**Last Updated:** 2025-11-25
|
||||||
|
**Next Review:** 2025-12-02
|
||||||
|
**Coordination Board Entry:** link when claimed
|
||||||
|
|
||||||
|
This document outlines the roadmap for enhancing the `yaze` Memory/Hex Editor to provide robust analysis tools similar to ImHex.
|
||||||
|
|
||||||
|
## Phase 1: Data Inspector (High Priority)
|
||||||
|
|
||||||
|
**Goal:** Provide immediate context for the selected byte(s) in the Hex Editor without mental math.
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. **Create `DataInspector` Component:**
|
||||||
|
* A standalone ImGui widget (`src/app/editor/code/data_inspector.h/cc`).
|
||||||
|
* Accepts a `const uint8_t* data_ptr` and `size_t max_len`.
|
||||||
|
2. **Standard Types:**
|
||||||
|
* Decode and display Little Endian values:
|
||||||
|
* `int8_t` / `uint8_t`
|
||||||
|
* `int16_t` / `uint16_t`
|
||||||
|
* `int24_t` / `uint24_t` (Common SNES pointers)
|
||||||
|
* `int32_t` / `uint32_t`
|
||||||
|
* Display Binary representation (`00001111`).
|
||||||
|
3. **SNES-Specific Types (The "Yaze" Value):**
|
||||||
|
* **SNES LoROM Address:** Convert the physical offset to `$BB:AAAA` format.
|
||||||
|
* **RGB555 Color:** Interpret 2 bytes as `0bbbbbgggggrrrrr`. Show a colored rectangle preview.
|
||||||
|
* **Tile Attribute:** Interpret byte as `vhopppcc` (Vertical/Horizontal flip, Priority, Palette, Tile High bit).
|
||||||
|
4. **Integration:**
|
||||||
|
* Modify `MemoryEditorWithDiffChecker` to instantiate and render `DataInspector` in a sidebar or child window next to the main hex grid.
|
||||||
|
* Hook into the hex editor's "Selection Changed" event (or poll selection state) to update the Inspector.
|
||||||
|
|
||||||
|
## Phase 2: Entropy Navigation (Navigation)
|
||||||
|
|
||||||
|
**Goal:** Visualize the ROM's structure to quickly find free space, graphics, or code.
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. **Entropy Calculator:**
|
||||||
|
* Create a utility to calculate Shannon entropy for blocks of data (e.g., 256-byte chunks).
|
||||||
|
* Run this calculation in a background thread when a ROM is loaded to generate an `EntropyMap`.
|
||||||
|
2. **Minimap Widget:**
|
||||||
|
* Render a thin vertical bar next to the hex scrollbar.
|
||||||
|
* Map the file offset to vertical pixels.
|
||||||
|
* **Color Coding:**
|
||||||
|
* **Black:** Zeroes (`0x00` fill).
|
||||||
|
* **Dark Grey:** `0xFF` fill (common flash erase value).
|
||||||
|
* **Blue:** Low entropy (Text, Tables).
|
||||||
|
* **Red:** High entropy (Compressed Graphics, Code).
|
||||||
|
3. **Interaction:**
|
||||||
|
* Clicking the minimap jumps the Hex Editor to that offset.
|
||||||
|
|
||||||
|
## Phase 3: Structure Templates (Advanced Analysis)
|
||||||
|
|
||||||
|
**Goal:** Define and visualize complex data structures on top of the raw hex.
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. **Template Definition System:**
|
||||||
|
* Define a C++-based schema builder (e.g., `StructBuilder("Header").AddString("Title", 21).AddByte("MapMode")...`).
|
||||||
|
2. **Visualizer:**
|
||||||
|
* Render these structures as a tree view (ImGui TreeNodes).
|
||||||
|
* When a tree node is hovered, highlight the corresponding bytes in the Hex Editor grid.
|
||||||
|
3. **Standard Templates:**
|
||||||
|
* Implement templates for known ALTTP structures: `SNES Header`, `Dungeon Header`, `Sprite Properties`.
|
||||||
|
|
||||||
|
## Phase 4: Disassembly Integration
|
||||||
|
|
||||||
|
**Goal:** Seamless transition between data viewing and code analysis.
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. **Context Menu:** Add "Disassemble Here" to the Hex Editor right-click menu.
|
||||||
|
2. **Disassembly View:**
|
||||||
|
* Invoke the `disassembler` (already present in `app/emu/debug`) on the selected range.
|
||||||
|
* Display the output in a popup or switch to the Assembly Editor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Initial Work Item: Data Inspector
|
||||||
|
|
||||||
|
We will begin with Phase 1. This requires creating the `DataInspector` class and hooking it into `MemoryEditorWithDiffChecker`.
|
||||||
@@ -0,0 +1,774 @@
|
|||||||
|
# Message Editor Implementation Roadmap
|
||||||
|
|
||||||
|
**Status**: Active Development
|
||||||
|
**Owner (Agent ID)**: imgui-frontend-engineer
|
||||||
|
**Last Updated**: 2025-11-25
|
||||||
|
**Next Review**: 2025-12-02
|
||||||
|
**Coordination Board Entry**: link when claimed
|
||||||
|
**Related Docs**:
|
||||||
|
- `docs/internal/architecture/message_system.md` (Gemini's architecture vision)
|
||||||
|
- `docs/internal/plans/message_system_improvement_plan.md` (Gemini's feature proposals)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This roadmap bridges Gemini's architectural vision with practical implementation steps for completing the Message Editor. The current implementation has the **core foundation** in place (message parsing, dictionary system, preview rendering) but lacks several key features proposed in Gemini's plan, particularly around **JSON import/export**, **translation workflows**, and **theme integration**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
|
||||||
|
### What's Working (Completed Features)
|
||||||
|
|
||||||
|
#### Core Data Layer ✅
|
||||||
|
- **MessageData**: Full implementation with raw/parsed representations
|
||||||
|
- **DictionaryEntry**: Compression system with dictionary optimization
|
||||||
|
- **TextElement**: Command and special character parsing
|
||||||
|
- **Character Encoding**: Complete CharEncoder table (0x00-0x66)
|
||||||
|
- **ROM Reading**: `ReadAllTextData()` successfully loads all 396 messages
|
||||||
|
- **ROM Writing**: `Save()` handles two-bank text data with overflow detection
|
||||||
|
|
||||||
|
#### Message Preview System ✅
|
||||||
|
- **MessagePreview**: Live rendering of messages as they appear in-game
|
||||||
|
- **Font Graphics**: 2BPP font tiles loaded and displayed at 0x70000
|
||||||
|
- **Character Widths**: Proportional font support via width table at 0x74ADF
|
||||||
|
- **Preview Bitmap**: Real-time message rendering with proper palette support
|
||||||
|
|
||||||
|
#### Editor UI ✅
|
||||||
|
- **Card System**: Four dockable cards (Message List, Editor, Font Atlas, Dictionary)
|
||||||
|
- **Message List**: Table view with ID, contents, and address columns
|
||||||
|
- **Text Editor**: Multiline input with live preview updates
|
||||||
|
- **Command Insertion**: Buttons to insert text commands and special characters
|
||||||
|
- **Dictionary Display**: Read-only view of all 97 dictionary entries
|
||||||
|
- **Expanded Messages**: Basic support for loading external message bins
|
||||||
|
|
||||||
|
#### Testing Coverage ✅
|
||||||
|
- **Unit Tests**: 20+ tests covering parsing, encoding, dictionary optimization
|
||||||
|
- **Integration Tests**: ROM-dependent tests verify actual game data
|
||||||
|
- **Command Parsing**: Regression tests for argument handling bugs
|
||||||
|
|
||||||
|
#### CLI Integration ✅
|
||||||
|
- **Message List**: `z3ed message list --format json --range 0-100`
|
||||||
|
- **Message Read**: `z3ed message read --id 5 --format json`
|
||||||
|
- **Message Search**: `z3ed message search --query "Link"`
|
||||||
|
- **Message Stats**: `z3ed message stats --format json`
|
||||||
|
|
||||||
|
### What's Missing (Gaps vs. Gemini's Vision)
|
||||||
|
|
||||||
|
#### 1. JSON Import/Export ❌ (HIGH PRIORITY)
|
||||||
|
**Status**: Not implemented
|
||||||
|
**Gemini's Vision**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"address": 917504,
|
||||||
|
"text": "[W:00][SPD:00]Welcome to [D:05]...",
|
||||||
|
"context": "Uncle dying in sewers"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Current Gap**:
|
||||||
|
- No `SerializeMessages()` or `DeserializeMessages()` in `MessageData`
|
||||||
|
- No UI for export/import operations
|
||||||
|
- No context field for translator notes
|
||||||
|
- CLI has JSON output but not JSON input
|
||||||
|
|
||||||
|
**Impact**: Cannot version control text, cannot use external editors, cannot collaborate with translators
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. Translation Workspace ❌ (MEDIUM PRIORITY)
|
||||||
|
**Status**: Not implemented
|
||||||
|
**Gemini's Vision**: Side-by-side view with reference ROM/JSON and editable translation
|
||||||
|
|
||||||
|
**Current Gap**:
|
||||||
|
- No reference text display
|
||||||
|
- No side-by-side layout
|
||||||
|
- No translation progress tracking
|
||||||
|
- No language-specific dictionary optimization
|
||||||
|
|
||||||
|
**Impact**: Manual translation workflows are tedious and error-prone
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. Search & Replace ⚠️ (PARTIAL)
|
||||||
|
**Status**: Stub implementation exists
|
||||||
|
**Gemini's Vision**: Regex support, batch replace across all messages
|
||||||
|
|
||||||
|
**Current Implementation**:
|
||||||
|
- `Find()` method exists in `MessageEditor` (lines 574-600)
|
||||||
|
- Basic UI skeleton present (search input, case sensitivity toggle)
|
||||||
|
- **Missing**: Replace functionality, regex support, "Find All", multi-message operations
|
||||||
|
|
||||||
|
**Impact**: Global text edits require manual per-message changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 4. Theme Integration ❌ (LOW PRIORITY - UI POLISH)
|
||||||
|
**Status**: Not implemented
|
||||||
|
**Current Issues**:
|
||||||
|
- No hardcoded `ImVec4` colors found (GOOD!)
|
||||||
|
- Not using `AgentUITheme` system for consistency
|
||||||
|
- Missing semantic color names for message editor components
|
||||||
|
|
||||||
|
**Impact**: Message Editor UI may not match rest of application theme
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 5. Expanded ROM Support ⚠️ (PARTIAL)
|
||||||
|
**Status**: Basic implementation exists
|
||||||
|
**Gemini's Vision**: Repointing text blocks to expanded ROM space (Banks 10+), automatic bank switching
|
||||||
|
|
||||||
|
**Current Implementation**:
|
||||||
|
- Can load expanded message bins (lines 322-334)
|
||||||
|
- Can save expanded messages (lines 497-508)
|
||||||
|
- **Missing**: Repointing logic, bank management, automatic overflow handling
|
||||||
|
|
||||||
|
**Impact**: Cannot support large translation projects that exceed vanilla space
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 6. Scripting Integration ❌ (FUTURE)
|
||||||
|
**Status**: Not planned
|
||||||
|
**Gemini's Vision**: Lua/Python API for procedural text generation
|
||||||
|
|
||||||
|
**Current Gap**: No scripting hooks in message system
|
||||||
|
|
||||||
|
**Impact**: Low - nice-to-have for advanced users
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architectural Decisions Required
|
||||||
|
|
||||||
|
### Decision 1: JSON Schema Design
|
||||||
|
**Question**: What fields should the JSON export include?
|
||||||
|
|
||||||
|
**Proposal**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"rom_name": "zelda3.sfc",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"address": 917504,
|
||||||
|
"address_hex": "0xE0000",
|
||||||
|
"text": "[W:00][SPD:00]Welcome...",
|
||||||
|
"context": "Optional translator note",
|
||||||
|
"dictionary_optimized": true,
|
||||||
|
"expanded": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dictionary": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"token": "[D:00]",
|
||||||
|
"contents": "the"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Trade-offs**:
|
||||||
|
- Verbose but human-readable
|
||||||
|
- Includes metadata for validation
|
||||||
|
- Context field for translator workflow
|
||||||
|
|
||||||
|
**Status**: ✅ RECOMMENDED
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Decision 2: Translation Workspace Layout
|
||||||
|
**Question**: How should reference vs. translation be displayed?
|
||||||
|
|
||||||
|
**Option A**: Side-by-side split pane
|
||||||
|
```
|
||||||
|
┌────────────────┬────────────────┐
|
||||||
|
│ Reference │ Translation │
|
||||||
|
│ (English) │ (Spanish) │
|
||||||
|
│ [Read-only] │ [Editable] │
|
||||||
|
│ │ │
|
||||||
|
│ Message 0: │ Message 0: │
|
||||||
|
│ "Welcome to │ "Bienvenido a │
|
||||||
|
│ Hyrule" │ Hyrule" │
|
||||||
|
└────────────────┴────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B**: Top-bottom with context panel
|
||||||
|
```
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ Reference: "Welcome to Hyrule" │
|
||||||
|
│ Context: Uncle's dying words │
|
||||||
|
├────────────────────────────────┤
|
||||||
|
│ Translation: │
|
||||||
|
│ [Editable text box] │
|
||||||
|
└────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Recommendation**: Option A for large screens, Option B for narrow windows
|
||||||
|
|
||||||
|
**Status**: ⚠️ NEEDS USER FEEDBACK
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Decision 3: Dictionary Auto-Optimization
|
||||||
|
**Question**: Should we auto-generate optimal dictionary for new languages?
|
||||||
|
|
||||||
|
**Challenges**:
|
||||||
|
- Dictionary optimization is NP-hard (longest common substring problem)
|
||||||
|
- Need to preserve ROM space constraints (97 entries max)
|
||||||
|
- Different languages have different common phrases
|
||||||
|
|
||||||
|
**Proposal**:
|
||||||
|
1. Provide "Analyze Translation" button that suggests optimal dictionary
|
||||||
|
2. Let user accept/reject suggestions
|
||||||
|
3. Preserve manual dictionary entries
|
||||||
|
|
||||||
|
**Status**: ⚠️ NEEDS RESEARCH
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Priority Matrix
|
||||||
|
|
||||||
|
### Phase 1: Foundation (Sprint 1-2 weeks)
|
||||||
|
**Goal**: JSON import/export with UI integration
|
||||||
|
|
||||||
|
#### Task 1.1: Implement JSON Serialization
|
||||||
|
**Location**: `src/app/editor/message/message_data.h`, `message_data.cc`
|
||||||
|
**Priority**: P0 (Blocker for translation workflow)
|
||||||
|
**Estimated Effort**: 3 days
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```cpp
|
||||||
|
// In MessageData
|
||||||
|
nlohmann::json SerializeToJson() const;
|
||||||
|
static absl::StatusOr<MessageData> DeserializeFromJson(const nlohmann::json& j);
|
||||||
|
|
||||||
|
// Free functions
|
||||||
|
absl::Status ExportMessagesToJson(
|
||||||
|
const std::vector<MessageData>& messages,
|
||||||
|
const std::vector<DictionaryEntry>& dictionary,
|
||||||
|
const std::string& output_path);
|
||||||
|
|
||||||
|
absl::StatusOr<std::vector<MessageData>> ImportMessagesFromJson(
|
||||||
|
const std::string& input_path);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies**: nlohmann/json (already in project via CPM)
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Export all 396 messages to valid JSON
|
||||||
|
- [ ] Import JSON and verify byte-for-byte ROM equivalence
|
||||||
|
- [ ] Handle malformed JSON with clear error messages
|
||||||
|
- [ ] Preserve dictionary optimization
|
||||||
|
- [ ] Include context field in schema
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Task 1.2: Add Export/Import UI
|
||||||
|
**Location**: `src/app/editor/message/message_editor.cc`
|
||||||
|
**Priority**: P0
|
||||||
|
**Estimated Effort**: 2 days
|
||||||
|
|
||||||
|
**UI Additions**:
|
||||||
|
```cpp
|
||||||
|
void MessageEditor::DrawExportImportPanel() {
|
||||||
|
if (ImGui::Button("Export to JSON")) {
|
||||||
|
std::string path = util::FileDialogWrapper::ShowSaveFileDialog("json");
|
||||||
|
PRINT_IF_ERROR(ExportMessagesToJson(list_of_texts_,
|
||||||
|
message_preview_.all_dictionaries_,
|
||||||
|
path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Import from JSON")) {
|
||||||
|
std::string path = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||||
|
auto result = ImportMessagesFromJson(path);
|
||||||
|
if (result.ok()) {
|
||||||
|
list_of_texts_ = result.value();
|
||||||
|
RefreshMessageList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] File dialogs open with correct filters
|
||||||
|
- [ ] Progress indicator for large exports
|
||||||
|
- [ ] Confirmation dialog on import (warns about overwriting)
|
||||||
|
- [ ] Error popup on import failure with details
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Task 1.3: CLI JSON Import Support
|
||||||
|
**Location**: `src/cli/handlers/game/message.cc`
|
||||||
|
**Priority**: P1
|
||||||
|
**Estimated Effort**: 1 day
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```bash
|
||||||
|
z3ed message import --json messages.json --rom zelda3.sfc --output zelda3_translated.sfc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Import JSON and write to ROM
|
||||||
|
- [ ] Validate JSON schema before import
|
||||||
|
- [ ] Verify ROM size constraints
|
||||||
|
- [ ] Dry-run mode (validate without writing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Translation Workflow (Sprint 2-3 weeks)
|
||||||
|
**Goal**: Side-by-side translation UI
|
||||||
|
|
||||||
|
#### Task 2.1: Add Translation Mode Card
|
||||||
|
**Location**: `src/app/editor/message/message_editor.h`, `message_editor.cc`
|
||||||
|
**Priority**: P1
|
||||||
|
**Estimated Effort**: 5 days
|
||||||
|
|
||||||
|
**New Components**:
|
||||||
|
```cpp
|
||||||
|
class TranslationWorkspace {
|
||||||
|
public:
|
||||||
|
void Initialize(Rom* reference_rom, Rom* translation_rom);
|
||||||
|
void DrawUI();
|
||||||
|
void LoadReferenceFromJson(const std::string& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DrawSideBySideView();
|
||||||
|
void DrawProgressTracker();
|
||||||
|
void UpdateTranslationProgress();
|
||||||
|
|
||||||
|
std::vector<MessageData> reference_messages_;
|
||||||
|
std::vector<MessageData> translation_messages_;
|
||||||
|
std::map<int, bool> translation_complete_flags_;
|
||||||
|
Rom* reference_rom_ = nullptr;
|
||||||
|
Rom* translation_rom_ = nullptr;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**UI Mockup**:
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Translation Progress: 42/396 (10.6%) │
|
||||||
|
├──────────────────────┬──────────────────────────┤
|
||||||
|
│ Reference (EN) │ Translation (ES) │
|
||||||
|
├──────────────────────┼──────────────────────────┤
|
||||||
|
│ Message 0: │ Message 0: │
|
||||||
|
│ "Welcome to Hyrule" │ [Editable input box] │
|
||||||
|
│ │ │
|
||||||
|
│ Dictionary: [D:05] │ Dictionary: [D:05] │
|
||||||
|
├──────────────────────┴──────────────────────────┤
|
||||||
|
│ [Previous] [Next] [Mark Complete] [Skip] │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Load reference ROM or JSON
|
||||||
|
- [ ] Display messages side-by-side
|
||||||
|
- [ ] Track translation progress (per-message completion)
|
||||||
|
- [ ] Keyboard shortcuts for navigation (Ctrl+N, Ctrl+P)
|
||||||
|
- [ ] Auto-save translated ROM on completion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Task 2.2: Context/Notes System
|
||||||
|
**Location**: `src/app/editor/message/message_data.h`
|
||||||
|
**Priority**: P2
|
||||||
|
**Estimated Effort**: 2 days
|
||||||
|
|
||||||
|
**Schema Addition**:
|
||||||
|
```cpp
|
||||||
|
struct MessageData {
|
||||||
|
// ... existing fields ...
|
||||||
|
std::string context; // Translator notes, scene context
|
||||||
|
std::string screenshot_path; // Optional screenshot reference
|
||||||
|
|
||||||
|
nlohmann::json SerializeToJson() const {
|
||||||
|
return {
|
||||||
|
{"id", ID},
|
||||||
|
{"address", Address},
|
||||||
|
{"text", RawString},
|
||||||
|
{"context", context},
|
||||||
|
{"screenshot", screenshot_path}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**UI Addition**:
|
||||||
|
```cpp
|
||||||
|
void MessageEditor::DrawContextPanel() {
|
||||||
|
ImGui::InputTextMultiline("Context Notes", ¤t_message_.context);
|
||||||
|
if (!current_message_.screenshot_path.empty()) {
|
||||||
|
ImGui::Image(LoadScreenshot(current_message_.screenshot_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Context field persists in JSON export/import
|
||||||
|
- [ ] Context displayed in translation workspace
|
||||||
|
- [ ] Optional screenshot attachment (stored as relative path)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Search & Replace (Sprint 3-1 week)
|
||||||
|
**Goal**: Complete Find/Replace implementation
|
||||||
|
|
||||||
|
#### Task 3.1: Implement Replace Functionality
|
||||||
|
**Location**: `src/app/editor/message/message_editor.cc`
|
||||||
|
**Priority**: P2
|
||||||
|
**Estimated Effort**: 2 days
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```cpp
|
||||||
|
absl::Status MessageEditor::Replace(const std::string& find_text,
|
||||||
|
const std::string& replace_text,
|
||||||
|
bool case_sensitive,
|
||||||
|
bool whole_word,
|
||||||
|
bool all_messages) {
|
||||||
|
int replaced_count = 0;
|
||||||
|
|
||||||
|
if (all_messages) {
|
||||||
|
for (auto& message : list_of_texts_) {
|
||||||
|
replaced_count += ReplaceInMessage(message, find_text, replace_text,
|
||||||
|
case_sensitive, whole_word);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replaced_count += ReplaceInMessage(current_message_, find_text,
|
||||||
|
replace_text, case_sensitive, whole_word);
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**UI Updates**:
|
||||||
|
```cpp
|
||||||
|
void MessageEditor::DrawFindReplacePanel() {
|
||||||
|
static char find_text[256] = "";
|
||||||
|
static char replace_text[256] = "";
|
||||||
|
|
||||||
|
ImGui::InputText("Find", find_text, IM_ARRAYSIZE(find_text));
|
||||||
|
ImGui::InputText("Replace", replace_text, IM_ARRAYSIZE(replace_text));
|
||||||
|
|
||||||
|
ImGui::Checkbox("Case Sensitive", &case_sensitive_);
|
||||||
|
ImGui::Checkbox("Whole Word", &match_whole_word_);
|
||||||
|
ImGui::Checkbox("All Messages", &replace_all_messages_);
|
||||||
|
|
||||||
|
if (ImGui::Button("Replace")) {
|
||||||
|
PRINT_IF_ERROR(Replace(find_text, replace_text, case_sensitive_,
|
||||||
|
match_whole_word_, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Replace All")) {
|
||||||
|
PRINT_IF_ERROR(Replace(find_text, replace_text, case_sensitive_,
|
||||||
|
match_whole_word_, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Replace in current message
|
||||||
|
- [ ] Replace in all messages
|
||||||
|
- [ ] Case-sensitive/insensitive options
|
||||||
|
- [ ] Whole word matching
|
||||||
|
- [ ] Undo support (requires history stack)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Task 3.2: Add Regex Support
|
||||||
|
**Location**: `src/app/editor/message/message_editor.cc`
|
||||||
|
**Priority**: P3 (Nice-to-have)
|
||||||
|
**Estimated Effort**: 2 days
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```cpp
|
||||||
|
absl::Status MessageEditor::ReplaceRegex(const std::string& pattern,
|
||||||
|
const std::string& replacement,
|
||||||
|
bool all_messages) {
|
||||||
|
std::regex regex_pattern;
|
||||||
|
try {
|
||||||
|
regex_pattern = std::regex(pattern);
|
||||||
|
} catch (const std::regex_error& e) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
absl::StrFormat("Invalid regex: %s", e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform replacement...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Regex validation with error messages
|
||||||
|
- [ ] Capture group support ($1, $2, etc.)
|
||||||
|
- [ ] Preview matches before replacement
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4: UI Polish (Sprint 4-1 week)
|
||||||
|
**Goal**: Theme integration and UX improvements
|
||||||
|
|
||||||
|
#### Task 4.1: Integrate AgentUITheme
|
||||||
|
**Location**: `src/app/editor/message/message_editor.cc`
|
||||||
|
**Priority**: P3
|
||||||
|
**Estimated Effort**: 1 day
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```cpp
|
||||||
|
void MessageEditor::DrawMessageList() {
|
||||||
|
const auto& theme = AgentUI::GetTheme();
|
||||||
|
|
||||||
|
AgentUI::PushPanelStyle();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Header, theme.panel_bg_darker);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, theme.accent_color);
|
||||||
|
|
||||||
|
// ... table rendering ...
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
AgentUI::PopPanelStyle();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] All panels use `AgentUI::PushPanelStyle()`
|
||||||
|
- [ ] Section headers use `AgentUI::RenderSectionHeader()`
|
||||||
|
- [ ] Buttons use `AgentUI::StyledButton()` where appropriate
|
||||||
|
- [ ] Color scheme matches rest of editor
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Task 4.2: Add Keyboard Shortcuts
|
||||||
|
**Location**: `src/app/editor/message/message_editor.cc`
|
||||||
|
**Priority**: P3
|
||||||
|
**Estimated Effort**: 1 day
|
||||||
|
|
||||||
|
**Shortcuts**:
|
||||||
|
- `Ctrl+F`: Open Find/Replace
|
||||||
|
- `Ctrl+E`: Export to JSON
|
||||||
|
- `Ctrl+I`: Import from JSON
|
||||||
|
- `Ctrl+S`: Save ROM
|
||||||
|
- `Ctrl+N`: Next message (in translation mode)
|
||||||
|
- `Ctrl+P`: Previous message (in translation mode)
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
```cpp
|
||||||
|
void MessageEditor::HandleKeyboardShortcuts() {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_F) &&
|
||||||
|
ImGui::GetIO().KeyCtrl) {
|
||||||
|
show_find_replace_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... other shortcuts ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
- [ ] Shortcuts don't conflict with global shortcuts
|
||||||
|
- [ ] Shortcuts displayed in tooltips
|
||||||
|
- [ ] Configurable shortcuts (future enhancement)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
**Location**: `test/unit/message_data_test.cc` (new file)
|
||||||
|
```cpp
|
||||||
|
TEST(MessageDataTest, SerializeToJson_BasicMessage) {
|
||||||
|
MessageData msg;
|
||||||
|
msg.ID = 0;
|
||||||
|
msg.Address = 0xE0000;
|
||||||
|
msg.RawString = "Hello World";
|
||||||
|
msg.context = "Test message";
|
||||||
|
|
||||||
|
auto json = msg.SerializeToJson();
|
||||||
|
|
||||||
|
EXPECT_EQ(json["id"], 0);
|
||||||
|
EXPECT_EQ(json["text"], "Hello World");
|
||||||
|
EXPECT_EQ(json["context"], "Test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MessageDataTest, DeserializeFromJson_RoundTrip) {
|
||||||
|
MessageData original;
|
||||||
|
original.ID = 5;
|
||||||
|
original.RawString = "[W:00][K]Test";
|
||||||
|
|
||||||
|
auto json = original.SerializeToJson();
|
||||||
|
auto result = MessageData::DeserializeFromJson(json);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.ok());
|
||||||
|
EXPECT_EQ(result.value().ID, 5);
|
||||||
|
EXPECT_EQ(result.value().RawString, "[W:00][K]Test");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
**Location**: `test/integration/message_export_test.cc` (new file)
|
||||||
|
```cpp
|
||||||
|
TEST_F(MessageRomTest, ExportImport_RoundTrip) {
|
||||||
|
// Export all messages to JSON
|
||||||
|
std::string json_path = "/tmp/messages.json";
|
||||||
|
EXPECT_OK(ExportMessagesToJson(list_of_texts_, dictionary_, json_path));
|
||||||
|
|
||||||
|
// Import back
|
||||||
|
auto imported = ImportMessagesFromJson(json_path);
|
||||||
|
ASSERT_TRUE(imported.ok());
|
||||||
|
|
||||||
|
// Verify identical
|
||||||
|
EXPECT_EQ(imported.value().size(), list_of_texts_.size());
|
||||||
|
for (size_t i = 0; i < list_of_texts_.size(); ++i) {
|
||||||
|
EXPECT_EQ(imported.value()[i].RawString, list_of_texts_[i].RawString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E Tests
|
||||||
|
**Location**: `test/e2e/message_editor_workflow_test.cc` (new file)
|
||||||
|
```cpp
|
||||||
|
TEST_F(MessageEditorE2ETest, TranslationWorkflow) {
|
||||||
|
// Open translation workspace
|
||||||
|
EXPECT_OK(ClickButton("Translation Mode"));
|
||||||
|
|
||||||
|
// Load reference ROM
|
||||||
|
EXPECT_OK(OpenFileDialog("reference_rom.sfc"));
|
||||||
|
|
||||||
|
// Navigate to message 0
|
||||||
|
EXPECT_EQ(GetCurrentMessageID(), 0);
|
||||||
|
|
||||||
|
// Edit translation
|
||||||
|
EXPECT_OK(SetTextBoxValue("Bienvenido a Hyrule"));
|
||||||
|
|
||||||
|
// Mark complete
|
||||||
|
EXPECT_OK(ClickButton("Mark Complete"));
|
||||||
|
|
||||||
|
// Verify progress
|
||||||
|
EXPECT_EQ(GetTranslationProgress(), "1/396");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies & Risks
|
||||||
|
|
||||||
|
### External Dependencies
|
||||||
|
1. **nlohmann/json**: Already integrated via CPM ✅
|
||||||
|
2. **ImGui Test Engine**: Available for E2E tests ✅
|
||||||
|
3. **File Dialog**: `util::FileDialogWrapper` already exists ✅
|
||||||
|
|
||||||
|
### Technical Risks
|
||||||
|
|
||||||
|
#### Risk 1: JSON Schema Evolution
|
||||||
|
**Impact**: Breaking changes to JSON format
|
||||||
|
**Mitigation**:
|
||||||
|
- Include version number in schema
|
||||||
|
- Implement forward/backward compatibility
|
||||||
|
- Provide migration tool for old exports
|
||||||
|
|
||||||
|
#### Risk 2: Dictionary Auto-Optimization Complexity
|
||||||
|
**Impact**: Algorithm may be too slow for real-time use
|
||||||
|
**Mitigation**:
|
||||||
|
- Run optimization in background thread
|
||||||
|
- Provide progress indicator
|
||||||
|
- Allow cancellation
|
||||||
|
|
||||||
|
#### Risk 3: Large ROM Size with Expanded Messages
|
||||||
|
**Impact**: May exceed bank boundaries
|
||||||
|
**Mitigation**:
|
||||||
|
- Implement repointing logic early (Phase 5)
|
||||||
|
- Warn user when approaching limits
|
||||||
|
- Suggest dictionary optimization
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
### Quantitative Metrics
|
||||||
|
- [ ] 100% message export/import success rate (no data loss)
|
||||||
|
- [ ] JSON schema supports all 396 vanilla messages
|
||||||
|
- [ ] Translation workspace reduces edit time by 50% vs. current workflow
|
||||||
|
- [ ] Search/Replace handles batch operations in <1 second
|
||||||
|
- [ ] 90%+ test coverage for new code
|
||||||
|
|
||||||
|
### Qualitative Metrics
|
||||||
|
- [ ] Translator feedback: "Translation workflow is intuitive"
|
||||||
|
- [ ] No hardcoded colors in Message Editor
|
||||||
|
- [ ] UI matches yaze style guide
|
||||||
|
- [ ] Documentation complete for all new features
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Timeline Estimate
|
||||||
|
|
||||||
|
| Phase | Duration | Effort (Dev Days) |
|
||||||
|
|-------|----------|-------------------|
|
||||||
|
| Phase 1: JSON Export/Import | 2 weeks | 6 days |
|
||||||
|
| Phase 2: Translation Workspace | 3 weeks | 9 days |
|
||||||
|
| Phase 3: Search & Replace | 1 week | 4 days |
|
||||||
|
| Phase 4: UI Polish | 1 week | 2 days |
|
||||||
|
| **Total** | **7 weeks** | **21 dev days** |
|
||||||
|
|
||||||
|
**Note**: Timeline assumes single developer working full-time. Adjust for part-time work or team collaboration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Enhancements (Post-MVP)
|
||||||
|
|
||||||
|
1. **Scripting API** (Gemini's vision)
|
||||||
|
- Expose MessageData to Lua/Python
|
||||||
|
- Allow procedural text generation
|
||||||
|
- Useful for randomizers
|
||||||
|
|
||||||
|
2. **Cloud Translation Integration**
|
||||||
|
- Google Translate API for quick drafts
|
||||||
|
- DeepL API for quality translations
|
||||||
|
- Requires API key management
|
||||||
|
|
||||||
|
3. **Collaborative Editing**
|
||||||
|
- Real-time multi-user translation
|
||||||
|
- Conflict resolution for concurrent edits
|
||||||
|
- Requires network infrastructure
|
||||||
|
|
||||||
|
4. **ROM Patch Generation**
|
||||||
|
- Export as `.ips` or `.bps` patch files
|
||||||
|
- Useful for distribution without ROM sharing
|
||||||
|
- Requires patch generation library
|
||||||
|
|
||||||
|
5. **Message Validation**
|
||||||
|
- Check for overlong messages (exceeds textbox width)
|
||||||
|
- Verify all messages have terminators
|
||||||
|
- Flag unused dictionary entries
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. **Q**: Should we support multiple translation languages simultaneously?
|
||||||
|
**A**: TBD - May require multi-ROM workspace UI
|
||||||
|
|
||||||
|
2. **Q**: How should we handle custom dictionary entries for expanded ROMs?
|
||||||
|
**A**: TBD - Need research into ROM space allocation
|
||||||
|
|
||||||
|
3. **Q**: Should translation progress be persisted?
|
||||||
|
**A**: TBD - Could store in `.yaze` project file
|
||||||
|
|
||||||
|
4. **Q**: Do we need undo/redo for message editing?
|
||||||
|
**A**: TBD - ImGui InputTextMultiline has built-in undo, may be sufficient
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Message Editor has a **solid foundation** with core parsing, preview, and UI systems in place. The main gaps are **JSON export/import** (P0), **translation workspace** (P1), and **search/replace** (P2).
|
||||||
|
|
||||||
|
**Recommended Next Steps**:
|
||||||
|
1. Start with Phase 1 (JSON export/import) - this unblocks all translation workflows
|
||||||
|
2. Get user feedback on translation workspace mockups before Phase 2
|
||||||
|
3. Defer theme integration to Phase 4 - not blocking functionality
|
||||||
|
|
||||||
|
**Estimated Effort**: ~7 weeks to MVP, ~21 dev days total.
|
||||||
|
|
||||||
|
**Success Criteria**: Translator can export messages to JSON, edit in external tool, and re-import without data loss. Side-by-side translation workspace reduces manual comparison time by 50%.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user