backend-infra-engineer: Release v0.3.1 snapshot
This commit is contained in:
5
.clangd
5
.clangd
@@ -28,3 +28,8 @@ Diagnostics:
|
|||||||
Remove:
|
Remove:
|
||||||
- modernize-use-trailing-return-type
|
- modernize-use-trailing-return-type
|
||||||
- readability-braces-around-statements
|
- readability-braces-around-statements
|
||||||
|
- readability-magic-numbers
|
||||||
|
- readability-implicit-bool-conversion
|
||||||
|
- readability-identifier-naming
|
||||||
|
- readability-function-cognitive-complexity
|
||||||
|
- readability-function-size
|
||||||
|
|||||||
151
.github/workflows/build-windows-fallback.yml
vendored
Normal file
151
.github/workflows/build-windows-fallback.yml
vendored
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
name: Windows Build Fallback
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
build_type:
|
||||||
|
description: 'Build type (Debug/Release)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
default: 'Release'
|
||||||
|
platform:
|
||||||
|
description: 'Platform (x64/x86)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
default: 'x64'
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUILD_TYPE: ${{ inputs.build_type }}
|
||||||
|
PLATFORM: ${{ inputs.platform }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows-fallback:
|
||||||
|
name: Windows ${{ inputs.platform }} ${{ inputs.build_type }} (Fallback)
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
# Try vcpkg first
|
||||||
|
- name: Set up vcpkg (Primary)
|
||||||
|
id: vcpkg_primary
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
VCPKG_DEFAULT_TRIPLET: '${{ inputs.platform }}-windows'
|
||||||
|
VCPKG_INSTALL_OPTIONS: '--x-install-root="${{ github.workspace }}/vcpkg_installed"'
|
||||||
|
|
||||||
|
# Fallback to newer baseline if primary fails
|
||||||
|
- name: Set up vcpkg (Fallback)
|
||||||
|
if: steps.vcpkg_primary.outcome == 'failure'
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: '2024.01.12'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json.backup'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
VCPKG_DEFAULT_TRIPLET: '${{ inputs.platform }}-windows'
|
||||||
|
VCPKG_INSTALL_OPTIONS: '--x-install-root="${{ github.workspace }}/vcpkg_installed"'
|
||||||
|
|
||||||
|
# Fallback to manual dependency installation if both vcpkg attempts fail
|
||||||
|
- name: Install dependencies manually
|
||||||
|
if: steps.vcpkg_primary.outcome == 'failure'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "Installing dependencies manually using Chocolatey and pre-built libraries..."
|
||||||
|
|
||||||
|
# Install Chocolatey if not present
|
||||||
|
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
||||||
|
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||||
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||||
|
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install basic dependencies
|
||||||
|
choco install -y cmake ninja
|
||||||
|
|
||||||
|
# Create a minimal build configuration
|
||||||
|
Write-Host "Creating minimal build configuration for CI..."
|
||||||
|
|
||||||
|
# Set up environment variables for minimal build
|
||||||
|
echo "YAZE_MINIMAL_BUILD=ON" >> $env:GITHUB_ENV
|
||||||
|
echo "CMAKE_PREFIX_PATH=$env:GITHUB_WORKSPACE\vcpkg_installed\${{ inputs.platform }}-windows" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
if exist "${{ github.workspace }}\vcpkg_installed" (
|
||||||
|
echo "Using vcpkg installation..."
|
||||||
|
cmake -B build ^
|
||||||
|
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 ^
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" ^
|
||||||
|
-DYAZE_BUILD_TESTS=OFF ^
|
||||||
|
-DYAZE_BUILD_EMU=OFF ^
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF ^
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF ^
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF ^
|
||||||
|
-DYAZE_INSTALL_LIB=OFF ^
|
||||||
|
-DYAZE_MINIMAL_BUILD=ON ^
|
||||||
|
-G "Visual Studio 17 2022" ^
|
||||||
|
-A %PLATFORM%
|
||||||
|
) else (
|
||||||
|
echo "Using minimal build configuration..."
|
||||||
|
cmake -B build ^
|
||||||
|
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 ^
|
||||||
|
-DYAZE_BUILD_TESTS=OFF ^
|
||||||
|
-DYAZE_BUILD_EMU=OFF ^
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF ^
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF ^
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF ^
|
||||||
|
-DYAZE_INSTALL_LIB=OFF ^
|
||||||
|
-DYAZE_MINIMAL_BUILD=ON ^
|
||||||
|
-G "Visual Studio 17 2022" ^
|
||||||
|
-A %PLATFORM%
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --config %BUILD_TYPE% --parallel
|
||||||
|
|
||||||
|
- name: Test executable
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$exePath = "build\bin\$env:BUILD_TYPE\yaze.exe"
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
Write-Host "✓ Executable created: $exePath" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
$testResult = & $exePath --help 2>&1
|
||||||
|
if ($testResult -match "Google Test" -or $testResult -match "gtest") {
|
||||||
|
Write-Error "Executable is running test main instead of app main!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable runs correctly" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Error "Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: yaze-${{ inputs.platform }}-${{ inputs.build_type }}-fallback
|
||||||
|
path: |
|
||||||
|
build/bin/${{ inputs.build_type }}/
|
||||||
|
retention-days: 7
|
||||||
712
.github/workflows/release-complex.yml
vendored
Normal file
712
.github/workflows/release-complex.yml
vendored
Normal file
@@ -0,0 +1,712 @@
|
|||||||
|
name: Release-Complex
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+-*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag (must start with v and follow semantic versioning)'
|
||||||
|
required: true
|
||||||
|
default: 'v0.3.0'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-and-prepare:
|
||||||
|
name: Validate Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
tag_name: ${{ steps.validate.outputs.tag_name }}
|
||||||
|
release_notes: ${{ steps.notes.outputs.content }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Validate tag format
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
# Debug information
|
||||||
|
echo "Event name: ${{ github.event_name }}"
|
||||||
|
echo "Ref: ${{ github.ref }}"
|
||||||
|
echo "Ref name: ${{ github.ref_name }}"
|
||||||
|
echo "Ref type: ${{ github.ref_type }}"
|
||||||
|
|
||||||
|
# Determine the tag based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "push" ]]; then
|
||||||
|
if [[ "${{ github.ref_type }}" != "tag" ]]; then
|
||||||
|
echo "❌ Error: Release workflow triggered by push to ${{ github.ref_type }} '${{ github.ref_name }}'"
|
||||||
|
echo "This workflow should only be triggered by pushing version tags (v1.2.3)"
|
||||||
|
echo "Use: git tag v0.3.0 && git push origin v0.3.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TAG="${{ github.ref_name }}"
|
||||||
|
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
TAG="${{ github.event.inputs.tag }}"
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "❌ Error: No tag specified for manual workflow dispatch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "❌ Error: Unsupported event type: ${{ github.event_name }}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Validating tag: $TAG"
|
||||||
|
|
||||||
|
# Check if tag follows semantic versioning pattern
|
||||||
|
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]]; then
|
||||||
|
echo "❌ Error: Tag '$TAG' does not follow semantic versioning format (v1.2.3 or v1.2.3-beta)"
|
||||||
|
echo "Valid examples: v0.3.0, v1.0.0, v2.1.3-beta, v1.0.0-rc1"
|
||||||
|
echo ""
|
||||||
|
echo "To create a proper release:"
|
||||||
|
echo "1. Use the helper script: ./scripts/create_release.sh 0.3.0"
|
||||||
|
echo "2. Or manually: git tag v0.3.0 && git push origin v0.3.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Tag format is valid: $TAG"
|
||||||
|
echo "VALIDATED_TAG=$TAG" >> $GITHUB_ENV
|
||||||
|
echo "tag_name=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
id: release_notes
|
||||||
|
run: |
|
||||||
|
# Extract release version from validated tag
|
||||||
|
VERSION="${VALIDATED_TAG}"
|
||||||
|
VERSION_NUM=$(echo "$VERSION" | sed 's/^v//')
|
||||||
|
|
||||||
|
# Generate release notes using the dedicated script
|
||||||
|
echo "Extracting changelog for version: $VERSION_NUM"
|
||||||
|
if python3 scripts/extract_changelog.py "$VERSION_NUM" > release_notes.md; then
|
||||||
|
echo "Changelog extracted successfully"
|
||||||
|
echo "Release notes content:"
|
||||||
|
cat release_notes.md
|
||||||
|
else
|
||||||
|
echo "Failed to extract changelog, creating default release notes"
|
||||||
|
echo "# Yaze $VERSION Release Notes\n\nPlease see the full changelog at docs/C1-changelog.md" > release_notes.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Store release notes
|
||||||
|
id: notes
|
||||||
|
run: |
|
||||||
|
# Store release notes content for later use
|
||||||
|
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
cat release_notes.md >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build-release:
|
||||||
|
name: Build Release
|
||||||
|
needs: validate-and-prepare
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: "Windows x64"
|
||||||
|
os: windows-2022
|
||||||
|
vcpkg_triplet: x64-windows
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: x64
|
||||||
|
artifact_name: "yaze-windows-x64"
|
||||||
|
artifact_path: "build/bin/Release/"
|
||||||
|
package_cmd: |
|
||||||
|
mkdir -p package
|
||||||
|
cp -r build/bin/Release/* package/ 2>/dev/null || echo "No Release binaries found, trying Debug..."
|
||||||
|
cp -r build/bin/Debug/* package/ 2>/dev/null || echo "No Debug binaries found"
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cd package && zip -r ../yaze-windows-x64.zip *
|
||||||
|
|
||||||
|
- name: "Windows ARM64"
|
||||||
|
os: windows-2022
|
||||||
|
vcpkg_triplet: arm64-windows
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: ARM64
|
||||||
|
artifact_name: "yaze-windows-arm64"
|
||||||
|
artifact_path: "build/bin/Release/"
|
||||||
|
package_cmd: |
|
||||||
|
mkdir -p package
|
||||||
|
cp -r build/bin/Release/* package/ 2>/dev/null || echo "No Release binaries found, trying Debug..."
|
||||||
|
cp -r build/bin/Debug/* package/ 2>/dev/null || echo "No Debug binaries found"
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cd package && zip -r ../yaze-windows-arm64.zip *
|
||||||
|
|
||||||
|
- name: "macOS Universal"
|
||||||
|
os: macos-14
|
||||||
|
vcpkg_triplet: arm64-osx
|
||||||
|
artifact_name: "yaze-macos"
|
||||||
|
artifact_path: "build/bin/"
|
||||||
|
package_cmd: |
|
||||||
|
# Debug: List what was actually built
|
||||||
|
echo "Contents of build/bin/:"
|
||||||
|
ls -la build/bin/ || echo "build/bin/ does not exist"
|
||||||
|
|
||||||
|
# Check if we have a bundle or standalone executable
|
||||||
|
if [ -d "build/bin/yaze.app" ]; then
|
||||||
|
echo "Found macOS bundle, using it directly"
|
||||||
|
# Use the existing bundle and just update it
|
||||||
|
cp -r build/bin/yaze.app ./Yaze.app
|
||||||
|
# Add additional resources to the bundle
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Update Info.plist with correct version
|
||||||
|
if [ -f "cmake/yaze.plist.in" ]; then
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
sed "s/@yaze_VERSION@/$VERSION_NUM/g" cmake/yaze.plist.in > "Yaze.app/Contents/Info.plist"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No bundle found, creating manual bundle"
|
||||||
|
# Create bundle structure manually
|
||||||
|
mkdir -p "Yaze.app/Contents/MacOS"
|
||||||
|
mkdir -p "Yaze.app/Contents/Resources"
|
||||||
|
cp build/bin/yaze "Yaze.app/Contents/MacOS/"
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Create Info.plist with correct version
|
||||||
|
if [ -f "cmake/yaze.plist.in" ]; then
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
sed "s/@yaze_VERSION@/$VERSION_NUM/g" cmake/yaze.plist.in > "Yaze.app/Contents/Info.plist"
|
||||||
|
else
|
||||||
|
# Create a basic Info.plist
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
echo '<?xml version="1.0" encoding="UTF-8"?>' > "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<plist version="1.0">' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<dict>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleExecutable</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>yaze</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleIdentifier</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>com.yaze.editor</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleName</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>Yaze</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleVersion</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo "<string>$VERSION_NUM</string>" >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleShortVersionString</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo "<string>$VERSION_NUM</string>" >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundlePackageType</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>APPL</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '</dict>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '</plist>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create DMG
|
||||||
|
mkdir dmg_staging
|
||||||
|
cp -r Yaze.app dmg_staging/
|
||||||
|
cp LICENSE dmg_staging/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md dmg_staging/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cp -r docs dmg_staging/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
hdiutil create -srcfolder dmg_staging -format UDZO -volname "Yaze ${{ needs.validate-and-prepare.outputs.tag_name }}" yaze-macos.dmg
|
||||||
|
|
||||||
|
- name: "Linux x64"
|
||||||
|
os: ubuntu-22.04
|
||||||
|
artifact_name: "yaze-linux-x64"
|
||||||
|
artifact_path: "build/bin/"
|
||||||
|
package_cmd: |
|
||||||
|
mkdir package
|
||||||
|
cp build/bin/yaze package/
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp -r docs package/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
tar -czf yaze-linux-x64.tar.gz -C package .
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
# Clean up any potential vcpkg issues (Windows only)
|
||||||
|
- name: Clean vcpkg state (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "Cleaning up any existing vcpkg state..."
|
||||||
|
if (Test-Path "vcpkg") {
|
||||||
|
Remove-Item -Recurse -Force "vcpkg" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if (Test-Path "vcpkg_installed") {
|
||||||
|
Remove-Item -Recurse -Force "vcpkg_installed" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
Write-Host "Cleanup completed"
|
||||||
|
|
||||||
|
# Platform-specific dependency installation
|
||||||
|
- name: Install Linux dependencies
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
libglew-dev \
|
||||||
|
libxext-dev \
|
||||||
|
libwavpack-dev \
|
||||||
|
libabsl-dev \
|
||||||
|
libboost-all-dev \
|
||||||
|
libpng-dev \
|
||||||
|
python3-dev \
|
||||||
|
libpython3-dev \
|
||||||
|
libasound2-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libxrandr-dev \
|
||||||
|
libxcursor-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libxi-dev
|
||||||
|
|
||||||
|
- name: Install macOS dependencies
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
# Install Homebrew dependencies needed for UI tests and full builds
|
||||||
|
brew install pkg-config libpng boost abseil ninja gtk+3
|
||||||
|
|
||||||
|
- name: Setup build environment
|
||||||
|
run: |
|
||||||
|
echo "Using streamlined release build configuration for all platforms"
|
||||||
|
echo "Linux/macOS: UI tests enabled with full dependency support"
|
||||||
|
echo "Windows: Full build with vcpkg integration for proper releases"
|
||||||
|
echo "All platforms: Emulator and developer tools disabled for clean releases"
|
||||||
|
|
||||||
|
# Configure CMake
|
||||||
|
- name: Configure CMake (Linux/macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
cmake -B build \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 \
|
||||||
|
-DYAZE_BUILD_TESTS=OFF \
|
||||||
|
-DYAZE_BUILD_EMU=OFF \
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF \
|
||||||
|
-DYAZE_ENABLE_UI_TESTS=ON \
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF \
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
|
||||||
|
-DYAZE_INSTALL_LIB=OFF \
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF \
|
||||||
|
-GNinja
|
||||||
|
|
||||||
|
# Set up vcpkg for Windows builds with fallback
|
||||||
|
- name: Set up vcpkg (Windows)
|
||||||
|
id: vcpkg_setup
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
VCPKG_DISABLE_METRICS: 1
|
||||||
|
VCPKG_DEFAULT_TRIPLET: ${{ matrix.vcpkg_triplet }}
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
|
||||||
|
# Debug vcpkg failure (Windows only)
|
||||||
|
- name: Debug vcpkg failure (Windows)
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'failure'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "=== vcpkg Setup Failed - Debug Information ===" -ForegroundColor Red
|
||||||
|
Write-Host "vcpkg directory exists: $(Test-Path 'vcpkg')"
|
||||||
|
Write-Host "vcpkg_installed directory exists: $(Test-Path 'vcpkg_installed')"
|
||||||
|
if (Test-Path "vcpkg") {
|
||||||
|
Write-Host "vcpkg directory contents:"
|
||||||
|
Get-ChildItem "vcpkg" | Select-Object -First 10
|
||||||
|
}
|
||||||
|
Write-Host "Git status:"
|
||||||
|
git status --porcelain
|
||||||
|
Write-Host "=============================================" -ForegroundColor Red
|
||||||
|
|
||||||
|
# Fallback: Install dependencies manually if vcpkg fails
|
||||||
|
- name: Install dependencies manually (Windows fallback)
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'failure'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "vcpkg setup failed, installing dependencies manually..."
|
||||||
|
|
||||||
|
# Install Chocolatey if not present
|
||||||
|
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Host "Installing Chocolatey..."
|
||||||
|
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||||
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||||
|
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install basic build tools and dependencies
|
||||||
|
Write-Host "Installing build tools and dependencies..."
|
||||||
|
try {
|
||||||
|
choco install -y cmake ninja git python3
|
||||||
|
Write-Host "Successfully installed build tools via Chocolatey"
|
||||||
|
} catch {
|
||||||
|
Write-Host "Chocolatey installation failed, trying individual packages..."
|
||||||
|
choco install -y cmake || Write-Host "CMake installation failed"
|
||||||
|
choco install -y ninja || Write-Host "Ninja installation failed"
|
||||||
|
choco install -y git || Write-Host "Git installation failed"
|
||||||
|
choco install -y python3 || Write-Host "Python3 installation failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up basic development environment
|
||||||
|
Write-Host "Setting up basic development environment..."
|
||||||
|
$env:Path += ";C:\Program Files\Git\bin"
|
||||||
|
|
||||||
|
# Verify installations
|
||||||
|
Write-Host "Verifying installations..."
|
||||||
|
cmake --version
|
||||||
|
ninja --version
|
||||||
|
git --version
|
||||||
|
|
||||||
|
# Set environment variable to indicate minimal build
|
||||||
|
echo "YAZE_MINIMAL_BUILD=ON" >> $env:GITHUB_ENV
|
||||||
|
echo "VCPKG_AVAILABLE=false" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
Write-Host "Manual dependency installation completed"
|
||||||
|
Write-Host "Build will proceed with minimal configuration (no vcpkg dependencies)"
|
||||||
|
|
||||||
|
# Set vcpkg availability flag when vcpkg succeeds
|
||||||
|
- name: Set vcpkg availability flag
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'success'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
echo "VCPKG_AVAILABLE=true" >> $env:GITHUB_ENV
|
||||||
|
Write-Host "vcpkg setup successful"
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "Configuring CMake for Windows build..."
|
||||||
|
|
||||||
|
# Check if vcpkg is available
|
||||||
|
if ((Test-Path "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake") -and ($env:VCPKG_AVAILABLE -ne "false")) {
|
||||||
|
Write-Host "Using vcpkg toolchain..."
|
||||||
|
cmake -B build `
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" `
|
||||||
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF `
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
} else {
|
||||||
|
Write-Host "Using minimal build configuration (vcpkg not available)..."
|
||||||
|
cmake -B build `
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF `
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=ON `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "CMake configuration completed successfully"
|
||||||
|
|
||||||
|
# Verify CMake configuration (Windows only)
|
||||||
|
- name: Verify CMake configuration (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "Verifying CMake configuration..."
|
||||||
|
if (Test-Path "build/CMakeCache.txt") {
|
||||||
|
Write-Host "✓ CMakeCache.txt found"
|
||||||
|
Write-Host "Build type: $(Select-String -Path 'build/CMakeCache.txt' -Pattern 'CMAKE_BUILD_TYPE' | ForEach-Object { $_.Line.Split('=')[1] })"
|
||||||
|
Write-Host "Minimal build: $(Select-String -Path 'build/CMakeCache.txt' -Pattern 'YAZE_MINIMAL_BUILD' | ForEach-Object { $_.Line.Split('=')[1] })"
|
||||||
|
} else {
|
||||||
|
Write-Error "CMakeCache.txt not found - CMake configuration failed!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
echo "Building YAZE for ${{ matrix.name }}..."
|
||||||
|
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
||||||
|
echo "Build completed successfully!"
|
||||||
|
|
||||||
|
# Validate Visual Studio project builds (Windows only)
|
||||||
|
# Generate yaze_config.h for Visual Studio builds
|
||||||
|
- name: Generate yaze_config.h
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$version = "${{ needs.validate-and-prepare.outputs.tag_name }}" -replace '^v', ''
|
||||||
|
$versionParts = $version -split '\.'
|
||||||
|
$major = if ($versionParts.Length -gt 0) { $versionParts[0] } else { "0" }
|
||||||
|
$minor = if ($versionParts.Length -gt 1) { $versionParts[1] } else { "0" }
|
||||||
|
$patch = if ($versionParts.Length -gt 2) { $versionParts[2] -split '-' | Select-Object -First 1 } else { "0" }
|
||||||
|
|
||||||
|
Write-Host "Generating yaze_config.h with version: $major.$minor.$patch"
|
||||||
|
Copy-Item "src\yaze_config.h.in" "yaze_config.h"
|
||||||
|
(Get-Content 'yaze_config.h') -replace '@yaze_VERSION_MAJOR@', $major -replace '@yaze_VERSION_MINOR@', $minor -replace '@yaze_VERSION_PATCH@', $patch | Set-Content 'yaze_config.h'
|
||||||
|
Write-Host "Generated yaze_config.h:"
|
||||||
|
Get-Content 'yaze_config.h'
|
||||||
|
|
||||||
|
- name: Validate Visual Studio Project Build
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "Validating Visual Studio Project Build" -ForegroundColor Cyan
|
||||||
|
Write-Host "Platform: ${{ matrix.cmake_generator_platform }}" -ForegroundColor Yellow
|
||||||
|
Write-Host "Configuration: ${{ env.BUILD_TYPE }}" -ForegroundColor Yellow
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if (-not (Test-Path "yaze.sln")) {
|
||||||
|
Write-Error "yaze.sln not found. Please run this script from the project root directory."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ yaze.sln found" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Build using MSBuild
|
||||||
|
Write-Host "Building with MSBuild..." -ForegroundColor Yellow
|
||||||
|
$msbuildArgs = @(
|
||||||
|
"yaze.sln"
|
||||||
|
"/p:Configuration=${{ env.BUILD_TYPE }}"
|
||||||
|
"/p:Platform=${{ matrix.cmake_generator_platform }}"
|
||||||
|
"/p:VcpkgEnabled=true"
|
||||||
|
"/p:VcpkgManifestInstall=true"
|
||||||
|
"/m" # Multi-processor build
|
||||||
|
"/verbosity:minimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host "MSBuild command: msbuild $($msbuildArgs -join ' ')" -ForegroundColor Gray
|
||||||
|
& msbuild @msbuildArgs
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "MSBuild failed with exit code $LASTEXITCODE"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Visual Studio build completed successfully" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Verify executable was created
|
||||||
|
$exePath = "build\bin\${{ env.BUILD_TYPE }}\yaze.exe"
|
||||||
|
if (-not (Test-Path $exePath)) {
|
||||||
|
Write-Error "Executable not found at expected path: $exePath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable created: $exePath" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Test that the executable runs (basic test)
|
||||||
|
Write-Host "Testing executable startup..." -ForegroundColor Yellow
|
||||||
|
$testResult = & $exePath --help 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
# Check if it's the test main or app main
|
||||||
|
if ($testResult -match "Google Test" -or $testResult -match "gtest") {
|
||||||
|
Write-Error "Executable is running test main instead of app main!"
|
||||||
|
Write-Host "Output: $testResult" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable runs correctly (exit code: $exitCode)" -ForegroundColor Green
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "✓ Visual Studio build validation PASSED" -ForegroundColor Green
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Test executable functionality (Windows)
|
||||||
|
- name: Test executable functionality (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Debug: List build directory contents
|
||||||
|
Write-Host "Build directory contents:" -ForegroundColor Cyan
|
||||||
|
if (Test-Path "build") {
|
||||||
|
Get-ChildItem -Recurse build -Name | Select-Object -First 20
|
||||||
|
} else {
|
||||||
|
Write-Host "build directory does not exist" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine executable path for Windows
|
||||||
|
$exePath = "build\bin\${{ env.BUILD_TYPE }}\yaze.exe"
|
||||||
|
|
||||||
|
if (Test-Path $exePath) {
|
||||||
|
Write-Host "✓ Executable found: $exePath" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
$testResult = & $exePath --help 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
if ($testResult -match "Google Test" -or $testResult -match "gtest") {
|
||||||
|
Write-Error "Executable is running test main instead of app main!"
|
||||||
|
Write-Host "Output: $testResult" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable runs correctly (exit code: $exitCode)" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
$exeInfo = Get-Item $exePath
|
||||||
|
Write-Host "Executable size: $([math]::Round($exeInfo.Length / 1MB, 2)) MB" -ForegroundColor Cyan
|
||||||
|
} else {
|
||||||
|
Write-Error "Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test executable functionality (macOS)
|
||||||
|
- name: Test executable functionality (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Build directory contents:"
|
||||||
|
if [ -d "build" ]; then
|
||||||
|
find build -name "*.app" -o -name "yaze" | head -10
|
||||||
|
else
|
||||||
|
echo "build directory does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine executable path for macOS
|
||||||
|
exePath="build/bin/yaze.app/Contents/MacOS/yaze"
|
||||||
|
|
||||||
|
if [ -f "$exePath" ]; then
|
||||||
|
echo "✓ Executable found: $exePath"
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
testResult=$($exePath --help 2>&1 || true)
|
||||||
|
exitCode=$?
|
||||||
|
|
||||||
|
if echo "$testResult" | grep -q "Google Test\|gtest"; then
|
||||||
|
echo "ERROR: Executable is running test main instead of app main!"
|
||||||
|
echo "Output: $testResult"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Executable runs correctly (exit code: $exitCode)"
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
fileSize=$(stat -f%z "$exePath")
|
||||||
|
fileSizeMB=$(echo "scale=2; $fileSize / 1024 / 1024" | bc -l)
|
||||||
|
echo "Executable size: ${fileSizeMB} MB"
|
||||||
|
else
|
||||||
|
echo "ERROR: Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test executable functionality (Linux)
|
||||||
|
- name: Test executable functionality (Linux)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Build directory contents:"
|
||||||
|
if [ -d "build" ]; then
|
||||||
|
find build -type f -name "yaze" | head -10
|
||||||
|
else
|
||||||
|
echo "build directory does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine executable path for Linux
|
||||||
|
exePath="build/bin/yaze"
|
||||||
|
|
||||||
|
if [ -f "$exePath" ]; then
|
||||||
|
echo "✓ Executable found: $exePath"
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
testResult=$($exePath --help 2>&1 || true)
|
||||||
|
exitCode=$?
|
||||||
|
|
||||||
|
if echo "$testResult" | grep -q "Google Test\|gtest"; then
|
||||||
|
echo "ERROR: Executable is running test main instead of app main!"
|
||||||
|
echo "Output: $testResult"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Executable runs correctly (exit code: $exitCode)"
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
fileSize=$(stat -c%s "$exePath")
|
||||||
|
fileSizeMB=$(echo "scale=2; $fileSize / 1024 / 1024" | bc -l)
|
||||||
|
echo "Executable size: ${fileSizeMB} MB"
|
||||||
|
else
|
||||||
|
echo "ERROR: Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify build artifacts
|
||||||
|
- name: Verify build artifacts
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Verifying build artifacts for ${{ matrix.name }}..."
|
||||||
|
if [ -d "build/bin" ]; then
|
||||||
|
echo "Build directory contents:"
|
||||||
|
find build/bin -type f -name "yaze*" -o -name "*.exe" -o -name "*.app" | head -10
|
||||||
|
else
|
||||||
|
echo "ERROR: build/bin directory not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Package
|
||||||
|
- name: Package
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Packaging for ${{ matrix.name }}..."
|
||||||
|
${{ matrix.package_cmd }}
|
||||||
|
echo "Packaging completed successfully!"
|
||||||
|
|
||||||
|
# Create release with artifacts (will create release if it doesn't exist)
|
||||||
|
- name: Upload to Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.validate-and-prepare.outputs.tag_name }}
|
||||||
|
name: Yaze ${{ needs.validate-and-prepare.outputs.tag_name }}
|
||||||
|
body: ${{ needs.validate-and-prepare.outputs.release_notes }}
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(needs.validate-and-prepare.outputs.tag_name, 'beta') || contains(needs.validate-and-prepare.outputs.tag_name, 'alpha') || contains(needs.validate-and-prepare.outputs.tag_name, 'rc') }}
|
||||||
|
files: |
|
||||||
|
${{ matrix.artifact_name }}.*
|
||||||
|
fail_on_unmatched_files: false
|
||||||
|
|
||||||
|
publish-packages:
|
||||||
|
name: Publish Packages
|
||||||
|
needs: [validate-and-prepare, build-release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: success()
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Update release status
|
||||||
|
run: |
|
||||||
|
echo "Release has been published successfully"
|
||||||
|
echo "All build artifacts have been uploaded"
|
||||||
|
|
||||||
|
- name: Announce release
|
||||||
|
run: |
|
||||||
|
echo "🎉 Yaze ${{ needs.validate-and-prepare.outputs.tag_name }} has been released!"
|
||||||
|
echo "📦 Packages are now available for download"
|
||||||
|
echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate-and-prepare.outputs.tag_name }}"
|
||||||
414
.github/workflows/release-simplified.yml
vendored
Normal file
414
.github/workflows/release-simplified.yml
vendored
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
name: Release-Simplified
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+-*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag (must start with v and follow semantic versioning)'
|
||||||
|
required: true
|
||||||
|
default: 'v0.3.0'
|
||||||
|
type: string
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-and-prepare:
|
||||||
|
name: Validate Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
tag_name: ${{ steps.validate.outputs.tag_name }}
|
||||||
|
release_notes: ${{ steps.notes.outputs.content }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Validate tag format
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
# Determine the tag based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "push" ]]; then
|
||||||
|
if [[ "${{ github.ref_type }}" != "tag" ]]; then
|
||||||
|
echo "❌ Error: Release workflow triggered by push to ${{ github.ref_type }} '${{ github.ref_name }}'"
|
||||||
|
echo "This workflow should only be triggered by pushing version tags (v1.2.3)"
|
||||||
|
echo "Use: git tag v0.3.0 && git push origin v0.3.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TAG="${{ github.ref_name }}"
|
||||||
|
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
TAG="${{ github.event.inputs.tag }}"
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "❌ Error: No tag specified for manual workflow dispatch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "❌ Error: Unsupported event type: ${{ github.event_name }}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Validating tag: $TAG"
|
||||||
|
|
||||||
|
# Check if tag follows semantic versioning pattern
|
||||||
|
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]]; then
|
||||||
|
echo "❌ Error: Tag '$TAG' does not follow semantic versioning format (v1.2.3 or v1.2.3-beta)"
|
||||||
|
echo "Valid examples: v0.3.0, v1.0.0, v2.1.3-beta, v1.0.0-rc1"
|
||||||
|
echo ""
|
||||||
|
echo "To create a proper release:"
|
||||||
|
echo "1. Use the helper script: ./scripts/create_release.sh 0.3.0"
|
||||||
|
echo "2. Or manually: git tag v0.3.0 && git push origin v0.3.0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Tag format is valid: $TAG"
|
||||||
|
echo "VALIDATED_TAG=$TAG" >> $GITHUB_ENV
|
||||||
|
echo "tag_name=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
id: release_notes
|
||||||
|
run: |
|
||||||
|
# Extract release version from validated tag
|
||||||
|
VERSION="${VALIDATED_TAG}"
|
||||||
|
VERSION_NUM=$(echo "$VERSION" | sed 's/^v//')
|
||||||
|
|
||||||
|
# Generate release notes using the dedicated script
|
||||||
|
echo "Extracting changelog for version: $VERSION_NUM"
|
||||||
|
if python3 scripts/extract_changelog.py "$VERSION_NUM" > release_notes.md; then
|
||||||
|
echo "Changelog extracted successfully"
|
||||||
|
echo "Release notes content:"
|
||||||
|
cat release_notes.md
|
||||||
|
else
|
||||||
|
echo "Failed to extract changelog, creating default release notes"
|
||||||
|
echo "# Yaze $VERSION Release Notes\n\nPlease see the full changelog at docs/C1-changelog.md" > release_notes.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Store release notes
|
||||||
|
id: notes
|
||||||
|
run: |
|
||||||
|
# Store release notes content for later use
|
||||||
|
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
cat release_notes.md >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build-release:
|
||||||
|
name: Build Release
|
||||||
|
needs: validate-and-prepare
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: "Windows x64"
|
||||||
|
os: windows-2022
|
||||||
|
vcpkg_triplet: x64-windows
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: x64
|
||||||
|
artifact_name: "yaze-windows-x64"
|
||||||
|
|
||||||
|
- name: "Windows ARM64"
|
||||||
|
os: windows-2022
|
||||||
|
vcpkg_triplet: arm64-windows
|
||||||
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
|
cmake_generator_platform: ARM64
|
||||||
|
artifact_name: "yaze-windows-arm64"
|
||||||
|
|
||||||
|
- name: "macOS Universal"
|
||||||
|
os: macos-14
|
||||||
|
vcpkg_triplet: arm64-osx
|
||||||
|
artifact_name: "yaze-macos"
|
||||||
|
|
||||||
|
- name: "Linux x64"
|
||||||
|
os: ubuntu-22.04
|
||||||
|
artifact_name: "yaze-linux-x64"
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
# Platform-specific dependency installation
|
||||||
|
- name: Install Linux dependencies
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
libglew-dev \
|
||||||
|
libxext-dev \
|
||||||
|
libwavpack-dev \
|
||||||
|
libabsl-dev \
|
||||||
|
libboost-all-dev \
|
||||||
|
libpng-dev \
|
||||||
|
python3-dev \
|
||||||
|
libpython3-dev \
|
||||||
|
libasound2-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libxrandr-dev \
|
||||||
|
libxcursor-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libxi-dev
|
||||||
|
|
||||||
|
- name: Install macOS dependencies
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
# Install Homebrew dependencies needed for UI tests and full builds
|
||||||
|
brew install pkg-config libpng boost abseil ninja gtk+3
|
||||||
|
|
||||||
|
# Set up vcpkg for Windows builds with fallback
|
||||||
|
- name: Set up vcpkg (Windows)
|
||||||
|
id: vcpkg_setup
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
VCPKG_DISABLE_METRICS: 1
|
||||||
|
VCPKG_DEFAULT_TRIPLET: ${{ matrix.vcpkg_triplet }}
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
|
||||||
|
# Set vcpkg availability flag when vcpkg succeeds
|
||||||
|
- name: Set vcpkg availability flag
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'success'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
echo "VCPKG_AVAILABLE=true" >> $env:GITHUB_ENV
|
||||||
|
Write-Host "vcpkg setup successful"
|
||||||
|
|
||||||
|
# Fallback: Set minimal build flag when vcpkg fails
|
||||||
|
- name: Set minimal build flag (Windows fallback)
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'failure'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
echo "VCPKG_AVAILABLE=false" >> $env:GITHUB_ENV
|
||||||
|
echo "YAZE_MINIMAL_BUILD=ON" >> $env:GITHUB_ENV
|
||||||
|
Write-Host "vcpkg setup failed, using minimal build configuration"
|
||||||
|
|
||||||
|
# Configure CMake
|
||||||
|
- name: Configure CMake (Linux/macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
cmake -B build \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 \
|
||||||
|
-DYAZE_BUILD_TESTS=OFF \
|
||||||
|
-DYAZE_BUILD_EMU=OFF \
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF \
|
||||||
|
-DYAZE_ENABLE_UI_TESTS=ON \
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF \
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
|
||||||
|
-DYAZE_INSTALL_LIB=OFF \
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF \
|
||||||
|
-GNinja
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Write-Host "Configuring CMake for Windows build..."
|
||||||
|
|
||||||
|
# Check if vcpkg is available
|
||||||
|
if ((Test-Path "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake") -and ($env:VCPKG_AVAILABLE -ne "false")) {
|
||||||
|
Write-Host "Using vcpkg toolchain..."
|
||||||
|
cmake -B build `
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" `
|
||||||
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF `
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
} else {
|
||||||
|
Write-Host "Using minimal build configuration (vcpkg not available)..."
|
||||||
|
cmake -B build `
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
|
-DYAZE_BUILD_Z3ED=OFF `
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=ON `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "CMake configuration completed successfully"
|
||||||
|
|
||||||
|
# Build
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
echo "Building YAZE for ${{ matrix.name }}..."
|
||||||
|
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
||||||
|
echo "Build completed successfully!"
|
||||||
|
|
||||||
|
# Test executable functionality
|
||||||
|
- name: Test executable functionality
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Testing executable for ${{ matrix.name }}..."
|
||||||
|
|
||||||
|
# Determine executable path based on platform
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
exePath="build/bin/${{ env.BUILD_TYPE }}/yaze.exe"
|
||||||
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||||
|
exePath="build/bin/yaze.app/Contents/MacOS/yaze"
|
||||||
|
else
|
||||||
|
exePath="build/bin/yaze"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$exePath" ]; then
|
||||||
|
echo "✓ Executable found: $exePath"
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
testResult=$($exePath --help 2>&1 || true)
|
||||||
|
exitCode=$?
|
||||||
|
|
||||||
|
if echo "$testResult" | grep -q "Google Test\|gtest"; then
|
||||||
|
echo "ERROR: Executable is running test main instead of app main!"
|
||||||
|
echo "Output: $testResult"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Executable runs correctly (exit code: $exitCode)"
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
fileSize=$(stat -c%s "$exePath" 2>/dev/null || echo "0")
|
||||||
|
else
|
||||||
|
fileSize=$(stat -f%z "$exePath" 2>/dev/null || stat -c%s "$exePath" 2>/dev/null || echo "0")
|
||||||
|
fi
|
||||||
|
fileSizeMB=$(echo "scale=2; $fileSize / 1024 / 1024" | bc -l 2>/dev/null || echo "0")
|
||||||
|
echo "Executable size: ${fileSizeMB} MB"
|
||||||
|
else
|
||||||
|
echo "ERROR: Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Package
|
||||||
|
- name: Package
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Packaging for ${{ matrix.name }}..."
|
||||||
|
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
# Windows packaging
|
||||||
|
mkdir -p package
|
||||||
|
cp -r build/bin/${{ env.BUILD_TYPE }}/* package/ 2>/dev/null || echo "No Release binaries found, trying Debug..."
|
||||||
|
cp -r build/bin/Debug/* package/ 2>/dev/null || echo "No Debug binaries found"
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cd package && zip -r ../${{ matrix.artifact_name }}.zip *
|
||||||
|
|
||||||
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||||
|
# macOS packaging
|
||||||
|
if [ -d "build/bin/yaze.app" ]; then
|
||||||
|
echo "Found macOS bundle, using it directly"
|
||||||
|
cp -r build/bin/yaze.app ./Yaze.app
|
||||||
|
# Add additional resources to the bundle
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Update Info.plist with correct version
|
||||||
|
if [ -f "cmake/yaze.plist.in" ]; then
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
sed "s/@yaze_VERSION@/$VERSION_NUM/g" cmake/yaze.plist.in > "Yaze.app/Contents/Info.plist"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No bundle found, creating manual bundle"
|
||||||
|
mkdir -p "Yaze.app/Contents/MacOS"
|
||||||
|
mkdir -p "Yaze.app/Contents/Resources"
|
||||||
|
cp build/bin/yaze "Yaze.app/Contents/MacOS/"
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Create Info.plist with correct version
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
echo '<?xml version="1.0" encoding="UTF-8"?>' > "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<plist version="1.0">' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<dict>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleExecutable</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>yaze</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleIdentifier</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>com.yaze.editor</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleName</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>Yaze</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleVersion</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo "<string>$VERSION_NUM</string>" >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundleShortVersionString</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo "<string>$VERSION_NUM</string>" >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<key>CFBundlePackageType</key>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '<string>APPL</string>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '</dict>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
echo '</plist>' >> "Yaze.app/Contents/Info.plist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create DMG
|
||||||
|
mkdir dmg_staging
|
||||||
|
cp -r Yaze.app dmg_staging/
|
||||||
|
cp LICENSE dmg_staging/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md dmg_staging/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cp -r docs dmg_staging/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
hdiutil create -srcfolder dmg_staging -format UDZO -volname "Yaze ${{ needs.validate-and-prepare.outputs.tag_name }}" ${{ matrix.artifact_name }}.dmg
|
||||||
|
|
||||||
|
else
|
||||||
|
# Linux packaging
|
||||||
|
mkdir package
|
||||||
|
cp build/bin/yaze package/
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp -r docs package/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
tar -czf ${{ matrix.artifact_name }}.tar.gz -C package .
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Packaging completed successfully!"
|
||||||
|
|
||||||
|
# Create release with artifacts
|
||||||
|
- name: Upload to Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.validate-and-prepare.outputs.tag_name }}
|
||||||
|
name: Yaze ${{ needs.validate-and-prepare.outputs.tag_name }}
|
||||||
|
body: ${{ needs.validate-and-prepare.outputs.release_notes }}
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(needs.validate-and-prepare.outputs.tag_name, 'beta') || contains(needs.validate-and-prepare.outputs.tag_name, 'alpha') || contains(needs.validate-and-prepare.outputs.tag_name, 'rc') }}
|
||||||
|
files: |
|
||||||
|
${{ matrix.artifact_name }}.*
|
||||||
|
|
||||||
|
publish-packages:
|
||||||
|
name: Publish Packages
|
||||||
|
needs: [validate-and-prepare, build-release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: success()
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Announce release
|
||||||
|
run: |
|
||||||
|
echo "🎉 Yaze ${{ needs.validate-and-prepare.outputs.tag_name }} has been released!"
|
||||||
|
echo "📦 Packages are now available for download"
|
||||||
|
echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ needs.validate-and-prepare.outputs.tag_name }}"
|
||||||
255
.github/workflows/release.yml
vendored
255
.github/workflows/release.yml
vendored
@@ -21,18 +21,13 @@ jobs:
|
|||||||
name: Validate Release
|
name: Validate Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
tag_name: ${{ env.VALIDATED_TAG }}
|
tag_name: ${{ steps.validate.outputs.tag_name }}
|
||||||
release_notes: ${{ steps.notes.outputs.content }}
|
release_notes: ${{ steps.notes.outputs.content }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Validate tag format
|
- name: Validate tag format
|
||||||
|
id: validate
|
||||||
run: |
|
run: |
|
||||||
# Debug information
|
|
||||||
echo "Event name: ${{ github.event_name }}"
|
|
||||||
echo "Ref: ${{ github.ref }}"
|
|
||||||
echo "Ref name: ${{ github.ref_name }}"
|
|
||||||
echo "Ref type: ${{ github.ref_type }}"
|
|
||||||
|
|
||||||
# Determine the tag based on trigger type
|
# Determine the tag based on trigger type
|
||||||
if [[ "${{ github.event_name }}" == "push" ]]; then
|
if [[ "${{ github.event_name }}" == "push" ]]; then
|
||||||
if [[ "${{ github.ref_type }}" != "tag" ]]; then
|
if [[ "${{ github.ref_type }}" != "tag" ]]; then
|
||||||
@@ -68,6 +63,7 @@ jobs:
|
|||||||
|
|
||||||
echo "✅ Tag format is valid: $TAG"
|
echo "✅ Tag format is valid: $TAG"
|
||||||
echo "VALIDATED_TAG=$TAG" >> $GITHUB_ENV
|
echo "VALIDATED_TAG=$TAG" >> $GITHUB_ENV
|
||||||
|
echo "tag_name=$TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -112,81 +108,22 @@ jobs:
|
|||||||
cmake_generator: "Visual Studio 17 2022"
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
cmake_generator_platform: x64
|
cmake_generator_platform: x64
|
||||||
artifact_name: "yaze-windows-x64"
|
artifact_name: "yaze-windows-x64"
|
||||||
artifact_path: "build/bin/Release/"
|
|
||||||
package_cmd: |
|
|
||||||
mkdir package
|
|
||||||
cp -r build/bin/Release/* package/
|
|
||||||
cp -r assets package/
|
|
||||||
cp LICENSE package/
|
|
||||||
cp README.md package/
|
|
||||||
cd package && 7z a ../yaze-windows-x64.zip *
|
|
||||||
|
|
||||||
- name: "Windows x86"
|
- name: "Windows ARM64"
|
||||||
os: windows-2022
|
os: windows-2022
|
||||||
vcpkg_triplet: x86-windows
|
vcpkg_triplet: arm64-windows
|
||||||
cmake_generator: "Visual Studio 17 2022"
|
cmake_generator: "Visual Studio 17 2022"
|
||||||
cmake_generator_platform: Win32
|
cmake_generator_platform: ARM64
|
||||||
artifact_name: "yaze-windows-x86"
|
artifact_name: "yaze-windows-arm64"
|
||||||
artifact_path: "build/bin/Release/"
|
|
||||||
package_cmd: |
|
|
||||||
mkdir package
|
|
||||||
cp -r build/bin/Release/* package/
|
|
||||||
cp -r assets package/
|
|
||||||
cp LICENSE package/
|
|
||||||
cp README.md package/
|
|
||||||
cd package && 7z a ../yaze-windows-x86.zip *
|
|
||||||
|
|
||||||
- name: "macOS Universal"
|
- name: "macOS Universal"
|
||||||
os: macos-14
|
os: macos-14
|
||||||
vcpkg_triplet: arm64-osx
|
vcpkg_triplet: arm64-osx
|
||||||
artifact_name: "yaze-macos"
|
artifact_name: "yaze-macos"
|
||||||
artifact_path: "build/bin/"
|
|
||||||
package_cmd: |
|
|
||||||
# Debug: List what was actually built
|
|
||||||
echo "Contents of build/bin/:"
|
|
||||||
ls -la build/bin/ || echo "build/bin/ does not exist"
|
|
||||||
|
|
||||||
# Check if we have a bundle or standalone executable
|
|
||||||
if [ -d "build/bin/yaze.app" ]; then
|
|
||||||
echo "Found macOS bundle, using it directly"
|
|
||||||
# Use the existing bundle and just update it
|
|
||||||
cp -r build/bin/yaze.app ./Yaze.app
|
|
||||||
# Add additional resources to the bundle
|
|
||||||
cp -r assets "Yaze.app/Contents/Resources/"
|
|
||||||
# Update Info.plist if needed
|
|
||||||
if [ -f "cmake/yaze.plist.in" ]; then
|
|
||||||
cp cmake/yaze.plist.in "Yaze.app/Contents/Info.plist"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "No bundle found, creating manual bundle"
|
|
||||||
# Create bundle structure manually
|
|
||||||
mkdir -p "Yaze.app/Contents/MacOS"
|
|
||||||
mkdir -p "Yaze.app/Contents/Resources"
|
|
||||||
cp build/bin/yaze "Yaze.app/Contents/MacOS/"
|
|
||||||
cp -r assets "Yaze.app/Contents/Resources/"
|
|
||||||
cp cmake/yaze.plist.in "Yaze.app/Contents/Info.plist"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create DMG
|
|
||||||
mkdir dmg_staging
|
|
||||||
cp -r Yaze.app dmg_staging/
|
|
||||||
cp LICENSE dmg_staging/
|
|
||||||
cp README.md dmg_staging/
|
|
||||||
cp -r docs dmg_staging/
|
|
||||||
hdiutil create -srcfolder dmg_staging -format UDZO -volname "Yaze ${{ needs.validate-and-prepare.outputs.tag_name }}" yaze-macos.dmg
|
|
||||||
|
|
||||||
- name: "Linux x64"
|
- name: "Linux x64"
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
artifact_name: "yaze-linux-x64"
|
artifact_name: "yaze-linux-x64"
|
||||||
artifact_path: "build/bin/"
|
|
||||||
package_cmd: |
|
|
||||||
mkdir package
|
|
||||||
cp build/bin/yaze package/
|
|
||||||
cp -r assets package/
|
|
||||||
cp -r docs package/
|
|
||||||
cp LICENSE package/
|
|
||||||
cp README.md package/
|
|
||||||
tar -czf yaze-linux-x64.tar.gz -C package .
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@@ -225,14 +162,42 @@ jobs:
|
|||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
# Install Homebrew dependencies needed for UI tests and full builds
|
# Install Homebrew dependencies needed for UI tests and full builds
|
||||||
brew install pkg-config libpng boost abseil ninja
|
brew install pkg-config libpng boost abseil ninja gtk+3
|
||||||
|
|
||||||
- name: Setup build environment
|
# Set up vcpkg for Windows builds with fallback
|
||||||
|
- name: Set up vcpkg (Windows)
|
||||||
|
id: vcpkg_setup
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
VCPKG_DISABLE_METRICS: 1
|
||||||
|
VCPKG_DEFAULT_TRIPLET: ${{ matrix.vcpkg_triplet }}
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
|
||||||
|
# Set vcpkg availability flag when vcpkg succeeds
|
||||||
|
- name: Set vcpkg availability flag
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'success'
|
||||||
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
echo "Using streamlined release build configuration for all platforms"
|
echo "VCPKG_AVAILABLE=true" >> $env:GITHUB_ENV
|
||||||
echo "Linux/macOS: UI tests enabled with full dependency support"
|
Write-Host "vcpkg setup successful"
|
||||||
echo "Windows: Minimal build to avoid vcpkg issues, UI tests disabled"
|
|
||||||
echo "All platforms: Emulator and developer tools disabled for clean releases"
|
# Fallback: Set minimal build flag when vcpkg fails
|
||||||
|
- name: Set minimal build flag (Windows fallback)
|
||||||
|
if: runner.os == 'Windows' && steps.vcpkg_setup.outcome == 'failure'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
echo "VCPKG_AVAILABLE=false" >> $env:GITHUB_ENV
|
||||||
|
echo "YAZE_MINIMAL_BUILD=ON" >> $env:GITHUB_ENV
|
||||||
|
Write-Host "vcpkg setup failed, using minimal build configuration"
|
||||||
|
|
||||||
# Configure CMake
|
# Configure CMake
|
||||||
- name: Configure CMake (Linux/macOS)
|
- name: Configure CMake (Linux/macOS)
|
||||||
@@ -248,35 +213,135 @@ jobs:
|
|||||||
-DYAZE_ENABLE_ROM_TESTS=OFF \
|
-DYAZE_ENABLE_ROM_TESTS=OFF \
|
||||||
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF \
|
||||||
-DYAZE_INSTALL_LIB=OFF \
|
-DYAZE_INSTALL_LIB=OFF \
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF \
|
||||||
-GNinja
|
-GNinja
|
||||||
|
|
||||||
- name: Configure CMake (Windows)
|
- name: Configure CMake (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: cmd
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
cmake -B build ^
|
Write-Host "Configuring CMake for Windows build..."
|
||||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} ^
|
|
||||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 ^
|
# Check if vcpkg is available
|
||||||
-DYAZE_BUILD_TESTS=OFF ^
|
if ((Test-Path "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake") -and ($env:VCPKG_AVAILABLE -ne "false")) {
|
||||||
-DYAZE_BUILD_EMU=OFF ^
|
Write-Host "Using vcpkg toolchain..."
|
||||||
-DYAZE_BUILD_Z3ED=OFF ^
|
cmake -B build `
|
||||||
-DYAZE_ENABLE_ROM_TESTS=OFF ^
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF ^
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
-DYAZE_INSTALL_LIB=OFF ^
|
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" `
|
||||||
-DYAZE_MINIMAL_BUILD=ON ^
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
-G "${{ matrix.cmake_generator }}" ^
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
-A ${{ matrix.cmake_generator_platform }}
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=OFF `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
} else {
|
||||||
|
Write-Host "Using minimal build configuration (vcpkg not available)..."
|
||||||
|
cmake -B build `
|
||||||
|
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} `
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.16 `
|
||||||
|
-DYAZE_BUILD_TESTS=OFF `
|
||||||
|
-DYAZE_BUILD_EMU=OFF `
|
||||||
|
-DYAZE_ENABLE_ROM_TESTS=OFF `
|
||||||
|
-DYAZE_ENABLE_EXPERIMENTAL_TESTS=OFF `
|
||||||
|
-DYAZE_INSTALL_LIB=OFF `
|
||||||
|
-DYAZE_MINIMAL_BUILD=ON `
|
||||||
|
-G "${{ matrix.cmake_generator }}" `
|
||||||
|
-A ${{ matrix.cmake_generator_platform }}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "CMake configuration completed successfully"
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
run: |
|
||||||
|
echo "Building YAZE for ${{ matrix.name }}..."
|
||||||
|
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
||||||
|
echo "Build completed successfully!"
|
||||||
|
|
||||||
|
# Test executable functionality
|
||||||
|
- name: Test executable functionality
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Testing executable for ${{ matrix.name }}..."
|
||||||
|
|
||||||
|
# Determine executable path based on platform
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
exePath="build/bin/${{ env.BUILD_TYPE }}/yaze.exe"
|
||||||
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||||
|
exePath="build/bin/yaze.app/Contents/MacOS/yaze"
|
||||||
|
else
|
||||||
|
exePath="build/bin/yaze"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$exePath" ]; then
|
||||||
|
echo "✓ Executable found: $exePath"
|
||||||
|
|
||||||
|
# Test that it's not the test main
|
||||||
|
testResult=$($exePath --help 2>&1 || true)
|
||||||
|
exitCode=$?
|
||||||
|
|
||||||
|
if echo "$testResult" | grep -q "Google Test\|gtest"; then
|
||||||
|
echo "ERROR: Executable is running test main instead of app main!"
|
||||||
|
echo "Output: $testResult"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Executable runs correctly (exit code: $exitCode)"
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
fileSize=$(stat -c%s "$exePath" 2>/dev/null || echo "0")
|
||||||
|
else
|
||||||
|
fileSize=$(stat -f%z "$exePath" 2>/dev/null || stat -c%s "$exePath" 2>/dev/null || echo "0")
|
||||||
|
fi
|
||||||
|
fileSizeMB=$(echo "scale=2; $fileSize / 1024 / 1024" | bc -l 2>/dev/null || echo "0")
|
||||||
|
echo "Executable size: ${fileSizeMB} MB"
|
||||||
|
else
|
||||||
|
echo "ERROR: Executable not found at: $exePath"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Package
|
# Package
|
||||||
- name: Package
|
- name: Package
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ${{ matrix.package_cmd }}
|
run: |
|
||||||
|
set -e
|
||||||
|
echo "Packaging for ${{ matrix.name }}..."
|
||||||
|
|
||||||
|
if [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
# Windows packaging
|
||||||
|
mkdir -p package
|
||||||
|
cp -r build/bin/${{ env.BUILD_TYPE }}/* package/ 2>/dev/null || echo "No Release binaries found, trying Debug..."
|
||||||
|
cp -r build/bin/Debug/* package/ 2>/dev/null || echo "No Debug binaries found"
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cd package && zip -r ../${{ matrix.artifact_name }}.zip *
|
||||||
|
|
||||||
|
elif [[ "${{ runner.os }}" == "macOS" ]]; then
|
||||||
|
# macOS packaging using dedicated script
|
||||||
|
VERSION_NUM=$(echo "${{ needs.validate-and-prepare.outputs.tag_name }}" | sed 's/^v//')
|
||||||
|
./scripts/create-macos-bundle.sh "$VERSION_NUM" "${{ matrix.artifact_name }}"
|
||||||
|
|
||||||
|
else
|
||||||
|
# Linux packaging
|
||||||
|
mkdir package
|
||||||
|
cp build/bin/yaze package/
|
||||||
|
cp -r assets package/ 2>/dev/null || echo "assets directory not found"
|
||||||
|
cp -r docs package/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
cp LICENSE package/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md package/ 2>/dev/null || echo "README.md not found"
|
||||||
|
tar -czf ${{ matrix.artifact_name }}.tar.gz -C package .
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Packaging completed successfully!"
|
||||||
|
|
||||||
# Create release with artifacts (will create release if it doesn't exist)
|
# Create release with artifacts
|
||||||
- name: Upload to Release
|
- name: Upload to Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
env:
|
env:
|
||||||
@@ -289,7 +354,6 @@ jobs:
|
|||||||
prerelease: ${{ contains(needs.validate-and-prepare.outputs.tag_name, 'beta') || contains(needs.validate-and-prepare.outputs.tag_name, 'alpha') || contains(needs.validate-and-prepare.outputs.tag_name, 'rc') }}
|
prerelease: ${{ contains(needs.validate-and-prepare.outputs.tag_name, 'beta') || contains(needs.validate-and-prepare.outputs.tag_name, 'alpha') || contains(needs.validate-and-prepare.outputs.tag_name, 'rc') }}
|
||||||
files: |
|
files: |
|
||||||
${{ matrix.artifact_name }}.*
|
${{ matrix.artifact_name }}.*
|
||||||
fail_on_unmatched_files: true
|
|
||||||
|
|
||||||
publish-packages:
|
publish-packages:
|
||||||
name: Publish Packages
|
name: Publish Packages
|
||||||
@@ -298,11 +362,6 @@ jobs:
|
|||||||
if: success()
|
if: success()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Update release status
|
|
||||||
run: |
|
|
||||||
echo "Release has been published successfully"
|
|
||||||
echo "All build artifacts have been uploaded"
|
|
||||||
|
|
||||||
- name: Announce release
|
- name: Announce release
|
||||||
run: |
|
run: |
|
||||||
echo "🎉 Yaze ${{ needs.validate-and-prepare.outputs.tag_name }} has been released!"
|
echo "🎉 Yaze ${{ needs.validate-and-prepare.outputs.tag_name }} has been released!"
|
||||||
|
|||||||
157
.github/workflows/validate-vs-build.yml
vendored
Normal file
157
.github/workflows/validate-vs-build.yml
vendored
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
name: Validate Visual Studio Builds
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master", "develop" ]
|
||||||
|
paths:
|
||||||
|
- 'vcpkg.json'
|
||||||
|
- 'src/**'
|
||||||
|
- 'scripts/generate-vs-projects.py'
|
||||||
|
- 'scripts/validate-vs-build.ps1'
|
||||||
|
- '.github/workflows/validate-vs-build.yml'
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master", "develop" ]
|
||||||
|
paths:
|
||||||
|
- 'vcpkg.json'
|
||||||
|
- 'src/**'
|
||||||
|
- 'scripts/generate-vs-projects.py'
|
||||||
|
- 'scripts/validate-vs-build.ps1'
|
||||||
|
- '.github/workflows/validate-vs-build.yml'
|
||||||
|
|
||||||
|
env:
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-vs-builds:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: "Windows x64 Debug"
|
||||||
|
platform: x64
|
||||||
|
configuration: Debug
|
||||||
|
|
||||||
|
- name: "Windows x64 Release"
|
||||||
|
platform: x64
|
||||||
|
configuration: Release
|
||||||
|
|
||||||
|
|
||||||
|
name: ${{ matrix.name }}
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up vcpkg
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
runVcpkgInstall: true
|
||||||
|
vcpkgJsonGlob: '**/vcpkg.json'
|
||||||
|
vcpkgDirectory: '${{ github.workspace }}/vcpkg'
|
||||||
|
env:
|
||||||
|
VCPKG_FORCE_SYSTEM_BINARIES: 1
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
|
||||||
|
- name: Validate Visual Studio Build
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "YAZE Visual Studio Build Validation" -ForegroundColor Cyan
|
||||||
|
Write-Host "Platform: ${{ matrix.platform }}" -ForegroundColor Yellow
|
||||||
|
Write-Host "Configuration: ${{ matrix.configuration }}" -ForegroundColor Yellow
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if (-not (Test-Path "yaze.sln")) {
|
||||||
|
Write-Error "yaze.sln not found. Please run this script from the project root directory."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ yaze.sln found" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Ensure build directory exists
|
||||||
|
if (-not (Test-Path "build")) {
|
||||||
|
New-Item -ItemType Directory -Path "build" | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build using MSBuild
|
||||||
|
Write-Host "Building with MSBuild..." -ForegroundColor Yellow
|
||||||
|
$msbuildArgs = @(
|
||||||
|
"yaze.sln"
|
||||||
|
"/p:Configuration=${{ matrix.configuration }}"
|
||||||
|
"/p:Platform=${{ matrix.platform }}"
|
||||||
|
"/p:VcpkgEnabled=true"
|
||||||
|
"/p:VcpkgManifestInstall=true"
|
||||||
|
"/m" # Multi-processor build
|
||||||
|
"/verbosity:minimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host "MSBuild command: msbuild $($msbuildArgs -join ' ')" -ForegroundColor Gray
|
||||||
|
& msbuild @msbuildArgs
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "MSBuild failed with exit code $LASTEXITCODE"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Build completed successfully" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Verify executable was created
|
||||||
|
$exePath = "build\bin\${{ matrix.configuration }}\yaze.exe"
|
||||||
|
if (-not (Test-Path $exePath)) {
|
||||||
|
Write-Error "Executable not found at expected path: $exePath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable created: $exePath" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Verify assets were copied
|
||||||
|
$assetsPath = "build\bin\${{ matrix.configuration }}\assets"
|
||||||
|
if (-not (Test-Path $assetsPath)) {
|
||||||
|
Write-Error "Assets directory not found at expected path: $assetsPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Assets copied to: $assetsPath" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Test that the executable runs (basic test)
|
||||||
|
Write-Host "Testing executable startup..." -ForegroundColor Yellow
|
||||||
|
$testResult = & $exePath --help 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
# Check if it's the test main or app main
|
||||||
|
if ($testResult -match "Google Test" -or $testResult -match "gtest") {
|
||||||
|
Write-Error "Executable is running test main instead of app main!"
|
||||||
|
Write-Host "Output: $testResult" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Executable runs correctly (exit code: $exitCode)" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
$exeInfo = Get-Item $exePath
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Build Summary:" -ForegroundColor Cyan
|
||||||
|
Write-Host " Executable: $($exeInfo.FullName)" -ForegroundColor White
|
||||||
|
Write-Host " Size: $([math]::Round($exeInfo.Length / 1MB, 2)) MB" -ForegroundColor White
|
||||||
|
Write-Host " Created: $($exeInfo.CreationTime)" -ForegroundColor White
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "✓ Visual Studio build validation PASSED" -ForegroundColor Green
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: yaze-${{ matrix.platform }}-${{ matrix.configuration }}
|
||||||
|
path: |
|
||||||
|
build/bin/${{ matrix.configuration }}/
|
||||||
|
retention-days: 7
|
||||||
@@ -7,14 +7,33 @@ if(POLICY CMP0091)
|
|||||||
cmake_policy(SET CMP0091 NEW)
|
cmake_policy(SET CMP0091 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(yaze VERSION 0.3.0
|
# Set additional policies to handle submodule compatibility
|
||||||
|
if(POLICY CMP0048)
|
||||||
|
cmake_policy(SET CMP0048 NEW)
|
||||||
|
endif()
|
||||||
|
if(POLICY CMP0077)
|
||||||
|
cmake_policy(SET CMP0077 NEW)
|
||||||
|
endif()
|
||||||
|
if(POLICY CMP0091)
|
||||||
|
cmake_policy(SET CMP0091 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Suppress deprecation warnings from submodules
|
||||||
|
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Suppress deprecation warnings")
|
||||||
|
|
||||||
|
# Handle pthread issues on Windows
|
||||||
|
if(WIN32)
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(yaze VERSION 0.3.1
|
||||||
DESCRIPTION "Yet Another Zelda3 Editor"
|
DESCRIPTION "Yet Another Zelda3 Editor"
|
||||||
LANGUAGES CXX C)
|
LANGUAGES CXX C)
|
||||||
|
|
||||||
# Set project metadata
|
# Set project metadata
|
||||||
set(YAZE_VERSION_MAJOR 0)
|
set(YAZE_VERSION_MAJOR 0)
|
||||||
set(YAZE_VERSION_MINOR 3)
|
set(YAZE_VERSION_MINOR 3)
|
||||||
set(YAZE_VERSION_PATCH 0)
|
set(YAZE_VERSION_PATCH 1)
|
||||||
|
|
||||||
configure_file(src/yaze_config.h.in yaze_config.h @ONLY)
|
configure_file(src/yaze_config.h.in yaze_config.h @ONLY)
|
||||||
|
|
||||||
@@ -75,6 +94,14 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|||||||
set(YAZE_PLATFORM_LINUX ON)
|
set(YAZE_PLATFORM_LINUX ON)
|
||||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||||
set(YAZE_PLATFORM_WINDOWS ON)
|
set(YAZE_PLATFORM_WINDOWS ON)
|
||||||
|
# Enable vcpkg integration for Windows builds
|
||||||
|
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
CACHE STRING "Vcpkg toolchain file")
|
||||||
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
CACHE STRING "Vcpkg toolchain file")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Create a common interface target for shared settings
|
# Create a common interface target for shared settings
|
||||||
@@ -90,19 +117,52 @@ elseif(YAZE_PLATFORM_MACOS)
|
|||||||
elseif(YAZE_PLATFORM_WINDOWS)
|
elseif(YAZE_PLATFORM_WINDOWS)
|
||||||
include(cmake/vcpkg.cmake)
|
include(cmake/vcpkg.cmake)
|
||||||
target_compile_definitions(yaze_common INTERFACE WINDOWS)
|
target_compile_definitions(yaze_common INTERFACE WINDOWS)
|
||||||
|
|
||||||
|
# Windows-specific architecture detection and configuration
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
|
||||||
|
target_compile_definitions(yaze_common INTERFACE YAZE_ARCH_ARM64)
|
||||||
|
message(STATUS "Building for Windows ARM64")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
|
target_compile_definitions(yaze_common INTERFACE YAZE_ARCH_X64)
|
||||||
|
message(STATUS "Building for Windows x64")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686|x86")
|
||||||
|
target_compile_definitions(yaze_common INTERFACE YAZE_ARCH_X86)
|
||||||
|
message(STATUS "Building for Windows x86")
|
||||||
|
else()
|
||||||
|
message(WARNING "Unknown Windows architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Compiler-specific settings
|
# Compiler-specific settings
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(yaze_common INTERFACE /W4 /permissive-)
|
target_compile_options(yaze_common INTERFACE
|
||||||
|
/W4 /permissive-
|
||||||
|
/bigobj # Support large object files
|
||||||
|
/utf-8 # Use UTF-8 encoding
|
||||||
|
)
|
||||||
target_compile_definitions(yaze_common INTERFACE
|
target_compile_definitions(yaze_common INTERFACE
|
||||||
_CRT_SECURE_NO_WARNINGS
|
_CRT_SECURE_NO_WARNINGS
|
||||||
_CRT_NONSTDC_NO_WARNINGS
|
_CRT_NONSTDC_NO_WARNINGS
|
||||||
|
SILENCE_CXX23_DEPRECATIONS
|
||||||
|
_SILENCE_CXX23_DEPRECATION_WARNING
|
||||||
|
_SILENCE_ALL_CXX23_DEPRECATION_WARNINGS
|
||||||
|
NOMINMAX # Disable min/max macros
|
||||||
|
WIN32_LEAN_AND_MEAN # Reduce Windows header bloat
|
||||||
strncasecmp=_strnicmp
|
strncasecmp=_strnicmp
|
||||||
strcasecmp=_stricmp
|
strcasecmp=_stricmp
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
target_compile_options(yaze_common INTERFACE -Wall -Wextra -Wpedantic)
|
target_compile_options(yaze_common INTERFACE
|
||||||
|
-Wall -Wextra -Wpedantic
|
||||||
|
-Wno-deprecated-declarations # Silence deprecation warnings
|
||||||
|
-Wno-c++23-compat # Silence C++23 compatibility warnings
|
||||||
|
)
|
||||||
|
# Add C++23 deprecation silencing for GCC/Clang
|
||||||
|
target_compile_definitions(yaze_common INTERFACE
|
||||||
|
_SILENCE_CXX23_DEPRECATION_WARNING
|
||||||
|
_SILENCE_ALL_CXX23_DEPRECATION_WARNINGS
|
||||||
|
ABSL_HAVE_INTRINSIC_INT128=1 # Enable intrinsic int128 support
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Abseil Standard Specifications
|
# Abseil Standard Specifications
|
||||||
|
|||||||
@@ -166,7 +166,7 @@
|
|||||||
{
|
{
|
||||||
"name": "windows-debug",
|
"name": "windows-debug",
|
||||||
"displayName": "Windows Debug",
|
"displayName": "Windows Debug",
|
||||||
"description": "Windows-specific debug configuration",
|
"description": "Windows-specific debug configuration with vcpkg",
|
||||||
"inherits": "debug",
|
"inherits": "debug",
|
||||||
"condition": {
|
"condition": {
|
||||||
"type": "equals",
|
"type": "equals",
|
||||||
@@ -177,7 +177,46 @@
|
|||||||
"architecture": "x64",
|
"architecture": "x64",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||||
"VCPKG_TARGET_TRIPLET": "x64-windows"
|
"VCPKG_TARGET_TRIPLET": "x64-windows",
|
||||||
|
"VCPKG_MANIFEST_MODE": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-release",
|
||||||
|
"displayName": "Windows Release",
|
||||||
|
"description": "Windows-specific release configuration with vcpkg",
|
||||||
|
"inherits": "release",
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Windows"
|
||||||
|
},
|
||||||
|
"generator": "Visual Studio 17 2022",
|
||||||
|
"architecture": "x64",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"VCPKG_TARGET_TRIPLET": "x64-windows",
|
||||||
|
"VCPKG_MANIFEST_MODE": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-dev",
|
||||||
|
"displayName": "Windows Development",
|
||||||
|
"description": "Windows development build with vcpkg and testing enabled",
|
||||||
|
"inherits": "debug",
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Windows"
|
||||||
|
},
|
||||||
|
"generator": "Visual Studio 17 2022",
|
||||||
|
"architecture": "x64",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"VCPKG_TARGET_TRIPLET": "x64-windows",
|
||||||
|
"VCPKG_MANIFEST_MODE": "ON",
|
||||||
|
"YAZE_BUILD_TESTS": "ON",
|
||||||
|
"YAZE_ENABLE_ROM_TESTS": "ON"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -260,6 +299,21 @@
|
|||||||
"configurePreset": "debug",
|
"configurePreset": "debug",
|
||||||
"displayName": "Fast Debug Build",
|
"displayName": "Fast Debug Build",
|
||||||
"jobs": 0
|
"jobs": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-debug",
|
||||||
|
"configurePreset": "windows-debug",
|
||||||
|
"displayName": "Windows Debug Build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-release",
|
||||||
|
"configurePreset": "windows-release",
|
||||||
|
"displayName": "Windows Release Build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-dev",
|
||||||
|
"configurePreset": "windows-dev",
|
||||||
|
"displayName": "Windows Development Build"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"testPresets": [
|
"testPresets": [
|
||||||
|
|||||||
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = "yaze"
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = "0.3.0"
|
PROJECT_NUMBER = "0.3.1"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ A modern, cross-platform editor for The Legend of Zelda: A Link to the Past ROM
|
|||||||
[](https://github.com/scawful/yaze/actions)
|
[](https://github.com/scawful/yaze/actions)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
|
||||||
## Version 0.3.0 - Stable Release
|
## Version 0.3.1 - Release
|
||||||
|
|
||||||
#### Asar 65816 Assembler Integration
|
#### Asar 65816 Assembler Integration
|
||||||
- **Cross-platform ROM patching** with assembly code support
|
- **Cross-platform ROM patching** with assembly code support
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
BeginTabBar title="##OwEditorTabBar" {
|
|
||||||
BeginTabItem title="Map Editor" {
|
|
||||||
Function id="owToolset",
|
|
||||||
|
|
||||||
Table id="##owEditTable" count="2" flags="Resizable|Reorderable|Hideable|BordersOuter|BordersV" {
|
|
||||||
TableSetupColumn title="Canvas" flags="WidthStretch",
|
|
||||||
TableSetupColumn title="Tile Selector" flags="WidthFixed" width="256",
|
|
||||||
TableHeadersRow
|
|
||||||
TableNextRow,
|
|
||||||
TableNextColumn,
|
|
||||||
Function id="OverworldCanvas",
|
|
||||||
TableNextColumn,
|
|
||||||
Function id="OverworldTileSelector",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BeginTabItem title="Tile16 Editor" {
|
|
||||||
Function id="OwTile16Editor"
|
|
||||||
}
|
|
||||||
BeginTabItem title "Graphics Group Editor" {
|
|
||||||
Function id="OwGfxGroupEditor"
|
|
||||||
}
|
|
||||||
BeginTabItem title="Usage Statistics" {
|
|
||||||
Function id="OwUsageStats"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -82,6 +82,26 @@ table_row_bg_alt=255,255,255,25 # Slightly more visible alternating rows
|
|||||||
# Link colors (high contrast for better visibility)
|
# Link colors (high contrast for better visibility)
|
||||||
text_link=120,200,255,255 # Bright blue for links - high contrast against dark backgrounds
|
text_link=120,200,255,255 # Bright blue for links - high contrast against dark backgrounds
|
||||||
|
|
||||||
|
# Navigation and special elements
|
||||||
|
input_text_cursor=245,245,245,255
|
||||||
|
nav_cursor=110,145,110,255
|
||||||
|
nav_windowing_highlight=110,145,110,255
|
||||||
|
nav_windowing_dim_bg=0,0,0,128
|
||||||
|
modal_window_dim_bg=0,0,0,89
|
||||||
|
text_selected_bg=89,119,89,89
|
||||||
|
drag_drop_target=110,145,110,255
|
||||||
|
docking_preview=92,115,92,180
|
||||||
|
docking_empty_bg=46,66,46,255
|
||||||
|
|
||||||
|
# Tree lines
|
||||||
|
tree_lines=127,127,127,153
|
||||||
|
|
||||||
|
# Tab variations for unfocused windows
|
||||||
|
tab_dimmed=37,52,37,255
|
||||||
|
tab_dimmed_selected=62,83,62,255
|
||||||
|
tab_dimmed_selected_overline=110,145,110,255
|
||||||
|
tab_selected_overline=110,145,110,255
|
||||||
|
|
||||||
[style]
|
[style]
|
||||||
window_rounding=0.0
|
window_rounding=0.0
|
||||||
frame_rounding=5.0
|
frame_rounding=5.0
|
||||||
|
|||||||
@@ -15,11 +15,27 @@ set(ABSL_PROPAGATE_CXX_STD ON)
|
|||||||
set(ABSL_CXX_STANDARD 23)
|
set(ABSL_CXX_STANDARD 23)
|
||||||
set(ABSL_USE_GOOGLETEST_HEAD ON)
|
set(ABSL_USE_GOOGLETEST_HEAD ON)
|
||||||
set(ABSL_ENABLE_INSTALL ON)
|
set(ABSL_ENABLE_INSTALL ON)
|
||||||
|
|
||||||
|
# Silence C++23 deprecation warnings for Abseil int128
|
||||||
|
if(MSVC)
|
||||||
|
add_definitions(-DSILENCE_CXX23_DEPRECATIONS)
|
||||||
|
else()
|
||||||
|
add_definitions(-D_SILENCE_CXX23_DEPRECATION_WARNING)
|
||||||
|
endif()
|
||||||
|
# Define base Abseil targets
|
||||||
set(
|
set(
|
||||||
ABSL_TARGETS
|
ABSL_TARGETS
|
||||||
absl::strings
|
absl::strings
|
||||||
absl::str_format
|
absl::str_format
|
||||||
absl::flags
|
absl::flags
|
||||||
|
absl::flags_parse
|
||||||
|
absl::flags_usage
|
||||||
|
absl::flags_commandlineflag
|
||||||
|
absl::flags_marshalling
|
||||||
|
absl::flags_private_handle_accessor
|
||||||
|
absl::flags_program_name
|
||||||
|
absl::flags_config
|
||||||
|
absl::flags_reflection
|
||||||
absl::status
|
absl::status
|
||||||
absl::statusor
|
absl::statusor
|
||||||
absl::examine_stack
|
absl::examine_stack
|
||||||
@@ -34,13 +50,15 @@ set(
|
|||||||
absl::synchronization
|
absl::synchronization
|
||||||
absl::time
|
absl::time
|
||||||
absl::symbolize
|
absl::symbolize
|
||||||
absl::flags_commandlineflag
|
|
||||||
absl::flags_marshalling
|
|
||||||
absl::flags_private_handle_accessor
|
|
||||||
absl::flags_program_name
|
|
||||||
absl::flags_config
|
|
||||||
absl::flags_reflection
|
|
||||||
absl::container_memory
|
absl::container_memory
|
||||||
absl::memory
|
absl::memory
|
||||||
absl::utility
|
absl::utility
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add int128 only on non-Windows platforms to avoid C++23 deprecation issues
|
||||||
|
if(NOT WIN32)
|
||||||
|
list(APPEND ABSL_TARGETS absl::int128)
|
||||||
|
message(STATUS "Including absl::int128 (non-Windows platform)")
|
||||||
|
else()
|
||||||
|
message(STATUS "Excluding absl::int128 on Windows to avoid C++23 deprecation issues")
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets/
|
|||||||
PATTERN "*.png"
|
PATTERN "*.png"
|
||||||
PATTERN "*.ttf"
|
PATTERN "*.ttf"
|
||||||
PATTERN "*.asm"
|
PATTERN "*.asm"
|
||||||
PATTERN "*.zeml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install documentation
|
# Install documentation
|
||||||
|
|||||||
@@ -21,12 +21,24 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libpng and ZLIB dependencies
|
# libpng and ZLIB dependencies
|
||||||
if(WIN32 AND NOT YAZE_MINIMAL_BUILD)
|
if(WIN32)
|
||||||
# Use vcpkg on Windows
|
# Windows builds with vcpkg (OpenGL/GLEW removed to avoid MSYS2 issues)
|
||||||
find_package(ZLIB REQUIRED)
|
if(NOT YAZE_MINIMAL_BUILD)
|
||||||
find_package(PNG REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
else()
|
||||||
|
# For CI/minimal builds, try to find but don't require
|
||||||
|
find_package(ZLIB QUIET)
|
||||||
|
find_package(PNG QUIET)
|
||||||
|
if(NOT ZLIB_FOUND OR NOT PNG_FOUND)
|
||||||
|
message(STATUS "PNG/ZLIB not found in minimal build, some features may be disabled")
|
||||||
|
set(PNG_FOUND FALSE)
|
||||||
|
set(PNG_LIBRARIES "")
|
||||||
|
set(PNG_INCLUDE_DIRS "")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
elseif(YAZE_MINIMAL_BUILD)
|
elseif(YAZE_MINIMAL_BUILD)
|
||||||
# For CI builds, try to find but don't require
|
# For CI builds on other platforms, try to find but don't require
|
||||||
find_package(ZLIB QUIET)
|
find_package(ZLIB QUIET)
|
||||||
find_package(PNG QUIET)
|
find_package(PNG QUIET)
|
||||||
if(NOT ZLIB_FOUND OR NOT PNG_FOUND)
|
if(NOT ZLIB_FOUND OR NOT PNG_FOUND)
|
||||||
|
|||||||
@@ -1,6 +1,40 @@
|
|||||||
|
|
||||||
|
# vcpkg configuration for Windows builds
|
||||||
add_definitions("-DMICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS=0")
|
add_definitions("-DMICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS=0")
|
||||||
|
|
||||||
set(VCPKG_TARGET_ARCHITECTURE x64)
|
# vcpkg settings
|
||||||
set(VCPKG_CRT_LINKAGE dynamic)
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||||
|
|
||||||
|
# Enable vcpkg manifest mode for automatic dependency management
|
||||||
|
set(VCPKG_MANIFEST_MODE ON)
|
||||||
|
|
||||||
|
# Auto-detect target architecture and set vcpkg triplet
|
||||||
|
if(NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
|
||||||
|
set(VCPKG_TARGET_TRIPLET "arm64-windows" CACHE STRING "vcpkg target triplet")
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
|
set(VCPKG_TARGET_TRIPLET "x64-windows" CACHE STRING "vcpkg target triplet")
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686|x86")
|
||||||
|
set(VCPKG_TARGET_TRIPLET "x86-windows" CACHE STRING "vcpkg target triplet")
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x86)
|
||||||
|
else()
|
||||||
|
# Fallback to x64 if architecture detection fails
|
||||||
|
set(VCPKG_TARGET_TRIPLET "x64-windows" CACHE STRING "vcpkg target triplet")
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
message(WARNING "Could not detect target architecture, defaulting to x64")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set vcpkg installation directory if not already set
|
||||||
|
if(NOT DEFINED VCPKG_INSTALLED_DIR)
|
||||||
|
set(VCPKG_INSTALLED_DIR "${CMAKE_BINARY_DIR}/vcpkg_installed" CACHE PATH "vcpkg installed directory")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "vcpkg configuration:")
|
||||||
|
message(STATUS " Target architecture: ${VCPKG_TARGET_ARCHITECTURE}")
|
||||||
|
message(STATUS " Target triplet: ${VCPKG_TARGET_TRIPLET}")
|
||||||
|
message(STATUS " Installed directory: ${VCPKG_INSTALLED_DIR}")
|
||||||
|
message(STATUS " Manifest mode: ${VCPKG_MANIFEST_MODE}")
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
YAZE uses CMake 3.16+ with modern target-based configuration. For VSCode users, install the CMake extensions:
|
YAZE uses CMake 3.16+ with modern target-based configuration. The project includes comprehensive Windows support with Visual Studio integration, vcpkg package management, and automated setup scripts.
|
||||||
- https://marketplace.visualstudio.com/items?itemName=twxs.cmake
|
|
||||||
- https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -12,13 +10,26 @@ cmake --preset debug
|
|||||||
cmake --build build
|
cmake --build build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linux / Windows
|
### Linux
|
||||||
```bash
|
```bash
|
||||||
cmake -B build -DCMAKE_BUILD_TYPE=Debug
|
cmake -B build -DCMAKE_BUILD_TYPE=Debug
|
||||||
cmake --build build
|
cmake --build build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minimal Build
|
### Windows (Recommended)
|
||||||
|
```powershell
|
||||||
|
# Automated setup (first time only)
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
|
||||||
|
# Generate Visual Studio projects (with proper vcpkg integration)
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Or use CMake directly
|
||||||
|
cmake --preset windows-debug
|
||||||
|
cmake --build build --preset windows-debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Minimal Build (CI/Fast)
|
||||||
```bash
|
```bash
|
||||||
cmake -B build -DYAZE_MINIMAL_BUILD=ON
|
cmake -B build -DYAZE_MINIMAL_BUILD=ON
|
||||||
cmake --build build
|
cmake --build build
|
||||||
@@ -55,12 +66,95 @@ sudo apt-get install -y build-essential cmake ninja-build pkg-config \
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
**Option 1 - Minimal (Recommended for CI):**
|
|
||||||
|
#### Automated Setup (Recommended)
|
||||||
|
The project includes comprehensive setup scripts for Windows development:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Complete development environment setup
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
|
||||||
|
# Generate Visual Studio project files (with proper vcpkg integration)
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Test CMake configuration
|
||||||
|
.\scripts\test-cmake-config.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
**What the setup script installs:**
|
||||||
|
- Chocolatey package manager
|
||||||
|
- CMake 3.16+
|
||||||
|
- Git, Ninja, Python 3
|
||||||
|
- Visual Studio 2022 detection and verification
|
||||||
|
|
||||||
|
#### Manual Setup Options
|
||||||
|
|
||||||
|
**Option 1 - Minimal (CI/Fast Builds):**
|
||||||
- Visual Studio 2019+ with C++ CMake tools
|
- Visual Studio 2019+ with C++ CMake tools
|
||||||
- No additional dependencies needed (all bundled)
|
- No additional dependencies needed (all bundled)
|
||||||
|
|
||||||
**Option 2 - Full Development:**
|
**Option 2 - Full Development with vcpkg:**
|
||||||
- Install vcpkg and dependencies from `vcpkg.json`
|
- Visual Studio 2019+ with C++ CMake tools
|
||||||
|
- vcpkg package manager for dependency management
|
||||||
|
|
||||||
|
#### vcpkg Integration
|
||||||
|
|
||||||
|
**Automatic Setup:**
|
||||||
|
```powershell
|
||||||
|
# PowerShell
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
|
||||||
|
# Command Prompt
|
||||||
|
scripts\setup-windows-dev.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual vcpkg Setup:**
|
||||||
|
```cmd
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
.\bootstrap-vcpkg.bat
|
||||||
|
.\vcpkg.exe integrate install
|
||||||
|
set VCPKG_ROOT=%CD%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies (vcpkg.json):**
|
||||||
|
- zlib (compression)
|
||||||
|
- libpng (PNG support)
|
||||||
|
- sdl2 (graphics/input with Vulkan support)
|
||||||
|
|
||||||
|
**Note**: Abseil and gtest are built from source via CMake rather than through vcpkg to avoid compatibility issues.
|
||||||
|
|
||||||
|
#### Windows Build Commands
|
||||||
|
|
||||||
|
**Using CMake Presets:**
|
||||||
|
```cmd
|
||||||
|
# Debug build (minimal, no tests)
|
||||||
|
cmake --preset windows-debug
|
||||||
|
cmake --build build --preset windows-debug
|
||||||
|
|
||||||
|
# Development build (includes Google Test)
|
||||||
|
cmake --preset windows-dev
|
||||||
|
cmake --build build --preset windows-dev
|
||||||
|
|
||||||
|
# Release build (optimized, no tests)
|
||||||
|
cmake --preset windows-release
|
||||||
|
cmake --build build --preset windows-release
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using Visual Studio Projects:**
|
||||||
|
```powershell
|
||||||
|
# Generate project files (with proper vcpkg integration)
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Open YAZE.sln in Visual Studio
|
||||||
|
# Select configuration (Debug/Release) and platform (x64/x86/ARM64)
|
||||||
|
# Press F5 to build and run
|
||||||
|
```
|
||||||
|
|
||||||
|
**Build Types:**
|
||||||
|
- **windows-debug**: Minimal debug build, no Google Test
|
||||||
|
- **windows-dev**: Development build with Google Test and ROM testing
|
||||||
|
- **windows-release**: Optimized release build, no Google Test
|
||||||
|
|
||||||
## Build Targets
|
## Build Targets
|
||||||
|
|
||||||
@@ -101,6 +195,28 @@ cmake -B build -DCMAKE_BUILD_TYPE=Release # All platforms
|
|||||||
|
|
||||||
## IDE Integration
|
## IDE Integration
|
||||||
|
|
||||||
|
### Visual Studio (Windows)
|
||||||
|
**Recommended approach:**
|
||||||
|
```powershell
|
||||||
|
# Setup development environment
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
|
||||||
|
# Generate Visual Studio project files (with proper vcpkg integration)
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Open YAZE.sln in Visual Studio 2022
|
||||||
|
# Select configuration (Debug/Release) and platform (x64/x86/ARM64)
|
||||||
|
# Press F5 to build and run
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Full IntelliSense support
|
||||||
|
- Integrated debugging
|
||||||
|
- Automatic vcpkg dependency management (zlib, libpng, SDL2)
|
||||||
|
- Multi-platform support (x64, ARM64)
|
||||||
|
- Automatic asset copying
|
||||||
|
- Generated project files stay in sync with CMake configuration
|
||||||
|
|
||||||
### VS Code
|
### VS Code
|
||||||
1. Install CMake Tools extension
|
1. Install CMake Tools extension
|
||||||
2. Open project, select "Debug" preset
|
2. Open project, select "Debug" preset
|
||||||
@@ -116,6 +232,46 @@ cmake --preset debug -G Xcode
|
|||||||
open build/yaze.xcodeproj
|
open build/yaze.xcodeproj
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Windows Development Scripts
|
||||||
|
|
||||||
|
The project includes several PowerShell and Batch scripts to streamline Windows development:
|
||||||
|
|
||||||
|
### Setup Scripts
|
||||||
|
- **`setup-windows-dev.ps1`**: Complete development environment setup
|
||||||
|
- **`setup-windows-dev.bat`**: Batch version of setup script
|
||||||
|
|
||||||
|
**What they install:**
|
||||||
|
- Chocolatey package manager
|
||||||
|
- CMake 3.16+
|
||||||
|
- Git, Ninja, Python 3
|
||||||
|
- Visual Studio 2022 detection
|
||||||
|
|
||||||
|
### Project Generation Scripts
|
||||||
|
- **`generate-vs-projects.py`**: Generate Visual Studio project files with proper vcpkg integration
|
||||||
|
- **`generate-vs-projects.bat`**: Batch version of project generation
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Automatic CMake detection and installation
|
||||||
|
- Visual Studio 2022 detection
|
||||||
|
- Multi-architecture support (x64, ARM64)
|
||||||
|
- vcpkg integration
|
||||||
|
- CMake compatibility fixes
|
||||||
|
|
||||||
|
### Testing Scripts
|
||||||
|
- **`test-cmake-config.ps1`**: Test CMake configuration without full build
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```powershell
|
||||||
|
# Test configuration
|
||||||
|
.\scripts\test-cmake-config.ps1
|
||||||
|
|
||||||
|
# Test with specific architecture
|
||||||
|
.\scripts\test-cmake-config.ps1 -Architecture x86
|
||||||
|
|
||||||
|
# Clean test build
|
||||||
|
.\scripts\test-cmake-config.ps1 -Clean
|
||||||
|
```
|
||||||
|
|
||||||
## Features by Build Type
|
## Features by Build Type
|
||||||
|
|
||||||
| Feature | Debug | Release | Minimal (CI) |
|
| Feature | Debug | Release | Minimal (CI) |
|
||||||
@@ -128,8 +284,135 @@ open build/yaze.xcodeproj
|
|||||||
| Test Suite | ✅ | ❌ | ✅ (limited) |
|
| Test Suite | ✅ | ❌ | ✅ (limited) |
|
||||||
| UI Testing | ✅ | ❌ | ❌ |
|
| UI Testing | ✅ | ❌ | ❌ |
|
||||||
|
|
||||||
|
## CMake Compatibility
|
||||||
|
|
||||||
|
### Submodule Compatibility Issues
|
||||||
|
YAZE includes several submodules (abseil-cpp, SDL) that may have CMake compatibility issues. The project automatically handles these with:
|
||||||
|
|
||||||
|
**Automatic Policy Management:**
|
||||||
|
- `CMAKE_POLICY_VERSION_MINIMUM=3.5` (handles SDL requirements)
|
||||||
|
- `CMAKE_POLICY_VERSION_MAXIMUM=3.28` (prevents future issues)
|
||||||
|
- `CMAKE_WARN_DEPRECATED=OFF` (suppresses submodule warnings)
|
||||||
|
- `ABSL_PROPAGATE_CXX_STD=ON` (handles Abseil C++ standard propagation)
|
||||||
|
- `THREADS_PREFER_PTHREAD_FLAG=OFF` (fixes Windows pthread issues)
|
||||||
|
|
||||||
|
**Manual Configuration (if needed):**
|
||||||
|
```bash
|
||||||
|
cmake -B build \
|
||||||
|
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||||
|
-DCMAKE_POLICY_VERSION_MAXIMUM=3.28 \
|
||||||
|
-DCMAKE_WARN_DEPRECATED=OFF \
|
||||||
|
-DABSL_PROPAGATE_CXX_STD=ON \
|
||||||
|
-DTHREADS_PREFER_PTHREAD_FLAG=OFF \
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD and Release Builds
|
||||||
|
|
||||||
|
### GitHub Actions Workflows
|
||||||
|
|
||||||
|
The project includes three release workflows with different levels of complexity:
|
||||||
|
|
||||||
|
- **`release-simplified.yml`**: Fast, basic release builds
|
||||||
|
- **`release.yml`**: Standard release builds with fallback mechanisms
|
||||||
|
- **`release-complex.yml`**: Comprehensive release builds with multiple fallback strategies
|
||||||
|
|
||||||
|
### vcpkg Fallback Mechanisms
|
||||||
|
|
||||||
|
All Windows CI/CD builds include automatic fallback mechanisms:
|
||||||
|
|
||||||
|
**When vcpkg succeeds:**
|
||||||
|
- Full build with all dependencies (zlib, libpng, SDL2)
|
||||||
|
- Complete feature set available
|
||||||
|
|
||||||
|
**When vcpkg fails (network issues):**
|
||||||
|
- Automatic fallback to minimal build configuration
|
||||||
|
- Uses source-built dependencies (Abseil, etc.)
|
||||||
|
- Still produces functional executables
|
||||||
|
|
||||||
|
### Supported Architectures
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
- x64 (64-bit) - Primary target for modern systems
|
||||||
|
- ARM64 - For ARM-based Windows devices (Surface Pro X, etc.)
|
||||||
|
|
||||||
|
**macOS:**
|
||||||
|
- Universal binary (Apple Silicon + Intel)
|
||||||
|
|
||||||
|
**Linux:**
|
||||||
|
- x64 (64-bit)
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Windows CMake Issues
|
||||||
|
|
||||||
|
**CMake Not Found:**
|
||||||
|
```powershell
|
||||||
|
# Run the setup script
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
|
||||||
|
# Or install manually via Chocolatey
|
||||||
|
choco install cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
**Submodule Compatibility Errors:**
|
||||||
|
```powershell
|
||||||
|
# Test CMake configuration
|
||||||
|
.\scripts\test-cmake-config.ps1
|
||||||
|
|
||||||
|
# Clean build with compatibility flags
|
||||||
|
Remove-Item -Recurse -Force build
|
||||||
|
cmake -B build -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_WARN_DEPRECATED=OFF
|
||||||
|
```
|
||||||
|
|
||||||
|
**Visual Studio Project Issues:**
|
||||||
|
```powershell
|
||||||
|
# Regenerate project files
|
||||||
|
.\scripts\generate-vs-projects.ps1
|
||||||
|
|
||||||
|
# Clean and rebuild
|
||||||
|
Remove-Item -Recurse -Force build
|
||||||
|
cmake --preset windows-debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### vcpkg Issues
|
||||||
|
|
||||||
|
**Dependencies Not Installing:**
|
||||||
|
```cmd
|
||||||
|
# Check vcpkg installation
|
||||||
|
vcpkg version
|
||||||
|
|
||||||
|
# Reinstall dependencies
|
||||||
|
vcpkg install --triplet x64-windows zlib libpng sdl2
|
||||||
|
|
||||||
|
# Check installed packages
|
||||||
|
vcpkg list
|
||||||
|
```
|
||||||
|
|
||||||
|
**Network/Download Failures:**
|
||||||
|
- The CI/CD workflows automatically fall back to minimal builds
|
||||||
|
- For local development, ensure stable internet connection
|
||||||
|
- If vcpkg consistently fails, use minimal build mode:
|
||||||
|
```bash
|
||||||
|
cmake -B build -DYAZE_MINIMAL_BUILD=ON
|
||||||
|
```
|
||||||
|
|
||||||
|
**Visual Studio Integration:**
|
||||||
|
```cmd
|
||||||
|
# Re-integrate vcpkg
|
||||||
|
cd C:\vcpkg
|
||||||
|
.\vcpkg.exe integrate install
|
||||||
|
```
|
||||||
|
|
||||||
|
**ZLIB or Other Dependencies Not Found:**
|
||||||
|
```bash
|
||||||
|
# Regenerate project files with proper vcpkg integration
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Ensure vcpkg is properly set up
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
```
|
||||||
|
|
||||||
### Architecture Errors (macOS)
|
### Architecture Errors (macOS)
|
||||||
```bash
|
```bash
|
||||||
# Clean and use ARM64-only preset
|
# Clean and use ARM64-only preset
|
||||||
@@ -151,3 +434,26 @@ Use minimal build configuration that matches CI:
|
|||||||
cmake -B build -DYAZE_MINIMAL_BUILD=ON -DYAZE_ENABLE_UI_TESTS=OFF
|
cmake -B build -DYAZE_MINIMAL_BUILD=ON -DYAZE_ENABLE_UI_TESTS=OFF
|
||||||
cmake --build build
|
cmake --build build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Common Error Solutions
|
||||||
|
|
||||||
|
**"CMake Deprecation Warning" from submodules:**
|
||||||
|
- This is automatically handled by the project's CMake configuration
|
||||||
|
- If you see these warnings, they can be safely ignored
|
||||||
|
|
||||||
|
**"pthread_create not found" on Windows:**
|
||||||
|
- The project automatically sets `THREADS_PREFER_PTHREAD_FLAG=OFF`
|
||||||
|
- This is normal for Windows builds
|
||||||
|
|
||||||
|
**"Abseil C++ std propagation" warnings:**
|
||||||
|
- Automatically handled with `ABSL_PROPAGATE_CXX_STD=ON`
|
||||||
|
- Ensures proper C++ standard handling
|
||||||
|
|
||||||
|
**Visual Studio "file not found" errors:**
|
||||||
|
- Run `python scripts/generate-vs-projects.py` to regenerate project files
|
||||||
|
- Ensure CMake configuration completed successfully first
|
||||||
|
|
||||||
|
**CI/CD Build Failures:**
|
||||||
|
- Check if vcpkg download failed (network issues)
|
||||||
|
- The workflows automatically fall back to minimal builds
|
||||||
|
- For persistent issues, check the workflow logs for specific error messages
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ absl::StatusOr<AsarPatchResult> ApplyPatch(
|
|||||||
|
|
||||||
### Version Numbering
|
### Version Numbering
|
||||||
- **Semantic Versioning**: MAJOR.MINOR.PATCH
|
- **Semantic Versioning**: MAJOR.MINOR.PATCH
|
||||||
- **v0.3.0**: Current stable release
|
- **v0.3.1**: Current stable release
|
||||||
- **Pre-release**: v0.4.0-alpha, v0.4.0-beta
|
- **Pre-release**: v0.4.0-alpha, v0.4.0-beta
|
||||||
|
|
||||||
### Release Checklist
|
### Release Checklist
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
# Platform Compatibility Improvements
|
|
||||||
|
|
||||||
Recent improvements to ensure YAZE works reliably across all supported platforms.
|
|
||||||
|
|
||||||
## Native File Dialog Support
|
|
||||||
|
|
||||||
YAZE now features native file dialogs on all platforms:
|
|
||||||
- **macOS**: Cocoa-based file selection with proper sandboxing support
|
|
||||||
- **Windows**: Windows Explorer integration with COM APIs
|
|
||||||
- **Linux**: GTK3 dialogs that match system appearance
|
|
||||||
- **Fallback**: Bespoke implementation when native dialogs unavailable
|
|
||||||
|
|
||||||
## Cross-Platform Build Reliability
|
|
||||||
|
|
||||||
Enhanced build system ensures consistent compilation:
|
|
||||||
- **Windows**: Resolved MSVC compatibility issues and dependency conflicts
|
|
||||||
- **Linux**: Fixed standard library compatibility for older distributions
|
|
||||||
- **macOS**: Proper support for both Intel and Apple Silicon architectures
|
|
||||||
- **All Platforms**: Bundled dependencies eliminate external package requirements
|
|
||||||
|
|
||||||
## Build Configuration Options
|
|
||||||
|
|
||||||
YAZE supports different build configurations for various use cases:
|
|
||||||
|
|
||||||
### Full Build (Development)
|
|
||||||
Includes all features: emulator, CLI tools, UI testing framework, and optional libraries.
|
|
||||||
|
|
||||||
### Minimal Build
|
|
||||||
Streamlined build excluding complex components, optimized for automated testing and CI environments.
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
The build system automatically detects platform capabilities and adjusts feature sets accordingly:
|
|
||||||
|
|
||||||
- **File Dialogs**: Uses native platform dialogs when available, with cross-platform fallbacks
|
|
||||||
- **Dependencies**: Bundles all required libraries to eliminate external package requirements
|
|
||||||
- **Testing**: Separates ROM-dependent tests from unit tests for CI compatibility
|
|
||||||
- **Architecture**: Supports both Intel and Apple Silicon on macOS without conflicts
|
|
||||||
|
|
||||||
## Platform-Specific Adaptations
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
- Complete COM-based file dialog implementation
|
|
||||||
- MSVC compatibility improvements for modern C++ features
|
|
||||||
- Resource file handling for proper application integration
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
- Cocoa-based native file dialogs with sandboxing support
|
|
||||||
- Universal binary support for Intel and Apple Silicon
|
|
||||||
- Proper bundle configuration for macOS applications
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
- GTK3 integration for native file dialogs
|
|
||||||
- Package manager integration for system dependencies
|
|
||||||
- Support for multiple compiler toolchains (GCC, Clang)
|
|
||||||
308
docs/B4-release-workflows.md
Normal file
308
docs/B4-release-workflows.md
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
# Release Workflows Documentation
|
||||||
|
|
||||||
|
YAZE uses three different GitHub Actions workflows for creating releases, each designed for specific use cases and reliability levels. This document explains the differences, use cases, and when to use each workflow.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
| Workflow | Complexity | Reliability | Use Case |
|
||||||
|
|----------|------------|-------------|----------|
|
||||||
|
| **release-simplified.yml** | Low | Basic | Quick releases, testing |
|
||||||
|
| **release.yml** | Medium | High | Standard releases |
|
||||||
|
| **release-complex.yml** | High | Maximum | Production releases, fallbacks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Release-Simplified (`release-simplified.yml`)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
A streamlined workflow for quick releases and testing scenarios.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Minimal Configuration**: Basic build setup with standard dependencies
|
||||||
|
- **No Fallback Mechanisms**: Direct dependency installation without error handling
|
||||||
|
- **Standard vcpkg**: Uses fixed vcpkg commit without fallback options
|
||||||
|
- **Basic Testing**: Simple executable verification
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
- **Development Testing**: Testing release process during development
|
||||||
|
- **Beta Releases**: Quick beta or alpha releases
|
||||||
|
- **Hotfixes**: Emergency releases that need to be deployed quickly
|
||||||
|
- **CI/CD Validation**: Ensuring the basic release process works
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
```yaml
|
||||||
|
# Standard vcpkg setup
|
||||||
|
vcpkgGitCommitId: 'c8696863d371ab7f46e213d8f5ca923c4aef2a00'
|
||||||
|
# No fallback mechanisms
|
||||||
|
# Basic dependency installation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Platforms Supported
|
||||||
|
- Windows (x64, x86, ARM64)
|
||||||
|
- macOS Universal
|
||||||
|
- Linux x64
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Release (`release.yml`)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
The standard production release workflow with enhanced reliability.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Enhanced vcpkg**: Updated baseline and improved dependency management
|
||||||
|
- **Better Error Handling**: More robust error reporting and debugging
|
||||||
|
- **Comprehensive Testing**: Extended executable validation and artifact verification
|
||||||
|
- **Production Ready**: Designed for stable releases
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
- **Stable Releases**: Official stable version releases
|
||||||
|
- **Feature Releases**: Major feature releases with full testing
|
||||||
|
- **Release Candidates**: Pre-release candidates for testing
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
```yaml
|
||||||
|
# Updated vcpkg baseline
|
||||||
|
builtin-baseline: "2024.12.12"
|
||||||
|
# Enhanced error handling
|
||||||
|
# Comprehensive testing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advantages over Simplified
|
||||||
|
- More reliable dependency resolution
|
||||||
|
- Better error reporting
|
||||||
|
- Enhanced artifact validation
|
||||||
|
- Production-grade stability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Release-Complex (`release-complex.yml`)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Maximum reliability release workflow with comprehensive fallback mechanisms.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Advanced Fallback System**: Multiple dependency installation strategies
|
||||||
|
- **vcpkg Failure Handling**: Automatic fallback to manual dependency installation
|
||||||
|
- **Chocolatey Integration**: Windows package manager fallback
|
||||||
|
- **Comprehensive Debugging**: Extensive logging and error analysis
|
||||||
|
- **Multiple Build Strategies**: CMake configuration fallbacks
|
||||||
|
- **Enhanced Validation**: Multi-stage build verification
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
- **Production Releases**: Critical production releases requiring maximum reliability
|
||||||
|
- **Enterprise Deployments**: Releases for enterprise customers
|
||||||
|
- **Major Version Releases**: Significant version releases (v1.0, v2.0, etc.)
|
||||||
|
- **Problem Resolution**: When other workflows fail due to dependency issues
|
||||||
|
|
||||||
|
### Fallback Mechanisms
|
||||||
|
|
||||||
|
#### vcpkg Fallback
|
||||||
|
```yaml
|
||||||
|
# Primary: vcpkg installation
|
||||||
|
- name: Set up vcpkg (Windows)
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
# Fallback: Manual dependency installation
|
||||||
|
- name: Install dependencies manually (Windows fallback)
|
||||||
|
if: steps.vcpkg_setup.outcome == 'failure'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Chocolatey Integration
|
||||||
|
```yaml
|
||||||
|
# Install Chocolatey if not present
|
||||||
|
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
||||||
|
# Install Chocolatey
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install dependencies via Chocolatey
|
||||||
|
choco install -y cmake ninja git python3
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Build Configuration Fallback
|
||||||
|
```yaml
|
||||||
|
# Primary: Full build with vcpkg
|
||||||
|
cmake -DCMAKE_TOOLCHAIN_FILE="vcpkg.cmake" -DYAZE_MINIMAL_BUILD=OFF
|
||||||
|
|
||||||
|
# Fallback: Minimal build without vcpkg
|
||||||
|
cmake -DYAZE_MINIMAL_BUILD=ON
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Features
|
||||||
|
- **Multi-stage Validation**: Visual Studio project validation
|
||||||
|
- **Artifact Verification**: Comprehensive build artifact checking
|
||||||
|
- **Debug Information**: Extensive logging for troubleshooting
|
||||||
|
- **Environment Detection**: Automatic environment configuration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow Comparison Matrix
|
||||||
|
|
||||||
|
| Feature | Simplified | Release | Complex |
|
||||||
|
|---------|------------|---------|---------|
|
||||||
|
| **vcpkg Integration** | Basic | Enhanced | Advanced + Fallback |
|
||||||
|
| **Error Handling** | Minimal | Standard | Comprehensive |
|
||||||
|
| **Fallback Mechanisms** | None | Limited | Multiple |
|
||||||
|
| **Debugging** | Basic | Standard | Extensive |
|
||||||
|
| **Dependency Management** | Fixed | Updated | Adaptive |
|
||||||
|
| **Build Validation** | Simple | Enhanced | Multi-stage |
|
||||||
|
| **Failure Recovery** | None | Limited | Automatic |
|
||||||
|
| **Production Ready** | No | Yes | Yes |
|
||||||
|
| **Build Time** | Fast | Medium | Slow |
|
||||||
|
| **Reliability** | Low | High | Maximum |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use Each Workflow
|
||||||
|
|
||||||
|
### Use Simplified When:
|
||||||
|
- ✅ Testing release process during development
|
||||||
|
- ✅ Creating beta or alpha releases
|
||||||
|
- ✅ Quick hotfix releases
|
||||||
|
- ✅ Validating basic CI/CD functionality
|
||||||
|
- ✅ Development team testing
|
||||||
|
|
||||||
|
### Use Release When:
|
||||||
|
- ✅ Creating stable production releases
|
||||||
|
- ✅ Feature releases with full testing
|
||||||
|
- ✅ Release candidates
|
||||||
|
- ✅ Standard version releases
|
||||||
|
- ✅ Most production scenarios
|
||||||
|
|
||||||
|
### Use Complex When:
|
||||||
|
- ✅ Critical production releases
|
||||||
|
- ✅ Major version releases (v1.0, v2.0)
|
||||||
|
- ✅ Enterprise customer releases
|
||||||
|
- ✅ When other workflows fail
|
||||||
|
- ✅ Maximum reliability requirements
|
||||||
|
- ✅ Complex dependency scenarios
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow Selection Guide
|
||||||
|
|
||||||
|
### For Development Team
|
||||||
|
```
|
||||||
|
Development → Simplified
|
||||||
|
Testing → Release
|
||||||
|
Production → Complex
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Release Manager
|
||||||
|
```
|
||||||
|
Hotfix → Simplified
|
||||||
|
Feature Release → Release
|
||||||
|
Major Release → Complex
|
||||||
|
```
|
||||||
|
|
||||||
|
### For CI/CD Pipeline
|
||||||
|
```
|
||||||
|
PR Validation → Simplified
|
||||||
|
Nightly Builds → Release
|
||||||
|
Release Pipeline → Complex
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
### Triggering a Release
|
||||||
|
|
||||||
|
#### Manual Release (All Workflows)
|
||||||
|
```bash
|
||||||
|
# Using workflow_dispatch
|
||||||
|
gh workflow run release.yml -f tag=v0.3.0
|
||||||
|
gh workflow run release-simplified.yml -f tag=v0.3.0-beta
|
||||||
|
gh workflow run release-complex.yml -f tag=v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Automatic Release (Tag Push)
|
||||||
|
```bash
|
||||||
|
# Creates release automatically
|
||||||
|
git tag v0.3.0
|
||||||
|
git push origin v0.3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customizing Release Notes
|
||||||
|
All workflows support automatic changelog extraction:
|
||||||
|
```bash
|
||||||
|
# Extract changelog for version
|
||||||
|
python3 scripts/extract_changelog.py "0.3.0" > release_notes.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### vcpkg Failures (Windows)
|
||||||
|
- **Simplified**: Fails completely
|
||||||
|
- **Release**: Basic error reporting
|
||||||
|
- **Complex**: Automatic fallback to manual installation
|
||||||
|
|
||||||
|
#### Dependency Conflicts
|
||||||
|
- **Simplified**: Manual intervention required
|
||||||
|
- **Release**: Enhanced error reporting
|
||||||
|
- **Complex**: Multiple resolution strategies
|
||||||
|
|
||||||
|
#### Build Failures
|
||||||
|
- **Simplified**: Basic error output
|
||||||
|
- **Release**: Enhanced debugging
|
||||||
|
- **Complex**: Comprehensive failure analysis
|
||||||
|
|
||||||
|
### Debug Information
|
||||||
|
|
||||||
|
#### Simplified Workflow
|
||||||
|
- Basic build output
|
||||||
|
- Simple error messages
|
||||||
|
- Minimal logging
|
||||||
|
|
||||||
|
#### Release Workflow
|
||||||
|
- Enhanced error reporting
|
||||||
|
- Artifact verification
|
||||||
|
- Build validation
|
||||||
|
|
||||||
|
#### Complex Workflow
|
||||||
|
- Extensive debug output
|
||||||
|
- Multi-stage validation
|
||||||
|
- Comprehensive error analysis
|
||||||
|
- Automatic fallback execution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Workflow Selection
|
||||||
|
1. **Start with Simplified** for development and testing
|
||||||
|
2. **Use Release** for standard production releases
|
||||||
|
3. **Use Complex** only when maximum reliability is required
|
||||||
|
|
||||||
|
### Release Process
|
||||||
|
1. **Test with Simplified** first
|
||||||
|
2. **Validate with Release** for production readiness
|
||||||
|
3. **Use Complex** for critical releases
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
1. **Keep all workflows updated** with latest dependency versions
|
||||||
|
2. **Monitor workflow performance** and adjust as needed
|
||||||
|
3. **Document any custom modifications** for team knowledge
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Improvements
|
||||||
|
|
||||||
|
### Planned Enhancements
|
||||||
|
- **Automated Workflow Selection**: Based on release type and criticality
|
||||||
|
- **Enhanced Fallback Strategies**: Additional dependency resolution methods
|
||||||
|
- **Performance Optimization**: Reduced build times while maintaining reliability
|
||||||
|
- **Cross-Platform Consistency**: Unified behavior across all platforms
|
||||||
|
|
||||||
|
### Integration Opportunities
|
||||||
|
- **Release Automation**: Integration with semantic versioning
|
||||||
|
- **Quality Gates**: Automated quality checks before release
|
||||||
|
- **Distribution**: Integration with package managers and app stores
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This documentation is maintained alongside the YAZE project. For updates or corrections, please refer to the project repository.*
|
||||||
@@ -1,5 +1,64 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
### Major Features
|
||||||
|
- **Complete Tile16 Editor Overhaul**: Professional-grade tile editing with modern UI and advanced capabilities
|
||||||
|
- **Advanced Palette Management**: Full access to all SNES palette groups with configurable normalization
|
||||||
|
- **Comprehensive Undo/Redo System**: 50-state history with intelligent time-based throttling
|
||||||
|
- **ZSCustomOverworld v3 Full Support**: Complete implementation of ZScream Save.cs functionality with complex transition calculations
|
||||||
|
- **ZEML System Removal**: Converted overworld editor from markup to pure ImGui for better performance and maintainability
|
||||||
|
- **OverworldEditorManager**: New management system to handle complex v3 overworld features
|
||||||
|
|
||||||
|
### Tile16 Editor Enhancements
|
||||||
|
- **Modern UI Layout**: Fully resizable 3-column interface (Tile8 Source, Editor, Preview & Controls)
|
||||||
|
- **Multi-Palette Group Support**: Access to Overworld Main/Aux1/Aux2, Dungeon Main, Global Sprites, Armors, and Swords palettes
|
||||||
|
- **Advanced Transform Operations**: Flip horizontal/vertical, rotate 90°, fill with tile8, clear operations
|
||||||
|
- **Professional Workflow**: Copy/paste, 4-slot scratch space, live preview with auto-commit
|
||||||
|
- **Pixel Normalization Settings**: Configurable pixel value masks (0x01-0xFF) for handling corrupted graphics sheets
|
||||||
|
|
||||||
|
### ZSCustomOverworld v3 Implementation
|
||||||
|
- **SaveLargeMapsExpanded()**: Complex neighbor-aware transition calculations for all area sizes (Small, Large, Wide, Tall)
|
||||||
|
- **Interactive Overlay System**: Full `SaveMapOverlays()` with ASM code generation for revealing holes and changing map elements
|
||||||
|
- **SaveCustomOverworldASM()**: Complete custom overworld ASM application with feature toggles and data tables
|
||||||
|
- **Expanded Memory Support**: Automatic detection and use of v3 expanded memory locations (0x140xxx)
|
||||||
|
- **Area-Specific Features**: Background colors, main palettes, mosaic transitions, GFX groups, subscreen overlays, animated tiles
|
||||||
|
- **Transition Logic**: Sophisticated camera transition calculations based on neighboring area types and quadrants
|
||||||
|
- **Version Compatibility**: Maintains vanilla/v2 compatibility while adding full v3+ feature support
|
||||||
|
|
||||||
|
### Technical Improvements
|
||||||
|
- **SNES Data Accuracy**: Proper 4-bit palette index handling with configurable normalization
|
||||||
|
- **Bitmap Pipeline Fixes**: Corrected tile16 extraction using `GetTilemapData()` with manual fallback
|
||||||
|
- **Real-time Updates**: Immediate visual feedback for all editing operations
|
||||||
|
- **Memory Safety**: Enhanced bounds checking and error handling throughout
|
||||||
|
- **ASM Version Detection**: Automatic detection of custom overworld ASM version for feature availability
|
||||||
|
- **Conditional Save Logic**: Different save paths for vanilla, v2, and v3+ ROMs
|
||||||
|
|
||||||
|
### User Interface
|
||||||
|
- **Keyboard Shortcuts**: Comprehensive shortcuts for all operations (H/V/R for transforms, Q/E for palette cycling, 1-8 for direct palette selection)
|
||||||
|
- **Visual Feedback**: Hover preview restoration, current palette highlighting, texture status indicators
|
||||||
|
- **Compact Controls**: Streamlined property panel with essential tools easily accessible
|
||||||
|
- **Settings Dialog**: Advanced palette normalization controls with real-time application
|
||||||
|
- **Pure ImGui Layout**: Removed ZEML markup system in favor of native ImGui tabs and tables for better performance
|
||||||
|
- **v3 Settings Panel**: Dedicated UI for ZSCustomOverworld v3 features with ASM version detection and feature toggles
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- **Tile16 Bitmap Display**: Fixed blank/white tile issue caused by unnormalized pixel values
|
||||||
|
- **Hover Preview**: Restored tile8 preview when hovering over tile16 canvas
|
||||||
|
- **Canvas Scaling**: Corrected coordinate scaling for 8x magnification factor
|
||||||
|
- **Palette Corruption**: Fixed high-bit contamination in graphics sheets
|
||||||
|
- **UI Layout**: Proper column sizing and resizing behavior
|
||||||
|
- **Linux CI/CD Build**: Fixed undefined reference errors for `ShowSaveFileDialog` method
|
||||||
|
- **ZSCustomOverworld v3**: Fixed complex area transition calculations and neighbor-aware tilemap adjustments
|
||||||
|
- **ZEML Performance**: Eliminated markup parsing overhead by converting to native ImGui components
|
||||||
|
|
||||||
|
### ZScream Compatibility Improvements
|
||||||
|
- **Complete Save.cs Implementation**: All major methods from ZScream's Save.cs now implemented in YAZE
|
||||||
|
- **Area Size Support**: Full support for Small, Large, Wide, and Tall area types with proper transitions
|
||||||
|
- **Interactive Overlays**: Complete overlay save system matching ZScream's functionality
|
||||||
|
- **Custom ASM Integration**: Proper handling of ZSCustomOverworld ASM versions 1-3+
|
||||||
|
- **Memory Layout**: Correct usage of expanded vs vanilla memory locations based on ROM type
|
||||||
|
|
||||||
## 0.3.0 (September 2025)
|
## 0.3.0 (September 2025)
|
||||||
|
|
||||||
### Major Features
|
### Major Features
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Yet Another Zelda3 Editor - A comprehensive ROM editor for The Legend of Zelda:
|
|||||||
- [Contributing](B1-contributing.md) - Development guidelines and standards
|
- [Contributing](B1-contributing.md) - Development guidelines and standards
|
||||||
- [Platform Compatibility](B2-platform-compatibility.md) - Cross-platform support details
|
- [Platform Compatibility](B2-platform-compatibility.md) - Cross-platform support details
|
||||||
- [Build Presets](B3-build-presets.md) - CMake preset usage guide
|
- [Build Presets](B3-build-presets.md) - CMake preset usage guide
|
||||||
|
- [Release Workflows](B4-release-workflows.md) - GitHub Actions release pipeline documentation
|
||||||
|
|
||||||
## Technical Documentation
|
## Technical Documentation
|
||||||
|
|
||||||
@@ -37,6 +38,8 @@ Yet Another Zelda3 Editor - A comprehensive ROM editor for The Legend of Zelda:
|
|||||||
- ZSCustomOverworld v3 support for enhanced overworld editing
|
- ZSCustomOverworld v3 support for enhanced overworld editing
|
||||||
- Cross-platform support (Windows, macOS, Linux)
|
- Cross-platform support (Windows, macOS, Linux)
|
||||||
- Modern C++23 codebase with comprehensive testing
|
- Modern C++23 codebase with comprehensive testing
|
||||||
|
- **Windows Development**: Automated setup scripts, Visual Studio integration, vcpkg package management
|
||||||
|
- **CMake Compatibility**: Automatic handling of submodule compatibility issues (abseil-cpp, SDL)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
165
docs/vcpkg-integration.md
Normal file
165
docs/vcpkg-integration.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# vcpkg Integration for Windows Builds
|
||||||
|
|
||||||
|
> **Note**: This document provides detailed vcpkg information. For the most up-to-date build instructions, see [Build Instructions](02-build-instructions.md).
|
||||||
|
|
||||||
|
This document describes how to use vcpkg for Windows builds in Visual Studio with YAZE.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
vcpkg is Microsoft's C++ package manager that simplifies dependency management for Windows builds. YAZE now includes full vcpkg integration with manifest mode support for automatic dependency resolution.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Manifest Mode**: Dependencies are automatically managed via `vcpkg.json`
|
||||||
|
- **Visual Studio Integration**: Seamless integration with Visual Studio 2022
|
||||||
|
- **Generated Project Files**: Visual Studio project files with proper vcpkg integration
|
||||||
|
- **CMake Presets**: Pre-configured build presets for Windows
|
||||||
|
- **Automatic Setup**: Setup scripts for easy vcpkg installation
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Setup vcpkg
|
||||||
|
|
||||||
|
Run the automated setup script:
|
||||||
|
```powershell
|
||||||
|
# PowerShell (recommended)
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Set up vcpkg
|
||||||
|
- Install dependencies (zlib, libpng, SDL2)
|
||||||
|
- Generate Visual Studio project files with proper vcpkg integration
|
||||||
|
|
||||||
|
### 2. Build with Visual Studio
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Generate project files (if not already done)
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# Open YAZE.sln in Visual Studio 2022
|
||||||
|
# Select configuration and platform, then build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Alternative: Build with CMake
|
||||||
|
|
||||||
|
Use the Windows presets in CMakePresets.json:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
# Debug build
|
||||||
|
cmake --preset windows-debug
|
||||||
|
cmake --build build --preset windows-debug
|
||||||
|
|
||||||
|
# Release build
|
||||||
|
cmake --preset windows-release
|
||||||
|
cmake --build build --preset windows-release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Details
|
||||||
|
|
||||||
|
### vcpkg.json Manifest
|
||||||
|
|
||||||
|
The `vcpkg.json` file defines all dependencies:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "yaze",
|
||||||
|
"version": "0.3.1",
|
||||||
|
"description": "Yet Another Zelda3 Editor",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"name": "zlib",
|
||||||
|
"platform": "!uwp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libpng",
|
||||||
|
"platform": "!uwp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sdl2",
|
||||||
|
"platform": "!uwp",
|
||||||
|
"features": ["vulkan"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"builtin-baseline": "2024.12.12"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CMake Configuration
|
||||||
|
|
||||||
|
vcpkg integration is handled in several files:
|
||||||
|
|
||||||
|
- **CMakeLists.txt**: Automatic toolchain detection
|
||||||
|
- **cmake/vcpkg.cmake**: vcpkg-specific settings
|
||||||
|
- **CMakePresets.json**: Windows build presets
|
||||||
|
|
||||||
|
### Build Presets
|
||||||
|
|
||||||
|
Available Windows presets:
|
||||||
|
|
||||||
|
- `windows-debug`: Debug build with vcpkg
|
||||||
|
- `windows-release`: Release build with vcpkg
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
vcpkg automatically installs these dependencies:
|
||||||
|
|
||||||
|
- **zlib**: Compression library
|
||||||
|
- **libpng**: PNG image support
|
||||||
|
- **sdl2**: Graphics and input handling (with Vulkan support)
|
||||||
|
|
||||||
|
**Note**: Abseil and gtest are now built from source via CMake rather than through vcpkg to avoid compatibility issues.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Set `VCPKG_ROOT` to point to your vcpkg installation:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
set VCPKG_ROOT=C:\path\to\vcpkg
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **vcpkg not found**: Ensure `VCPKG_ROOT` is set or vcpkg is in the project directory
|
||||||
|
2. **Dependencies not installing**: Check internet connection and vcpkg bootstrap
|
||||||
|
3. **Visual Studio integration**: Run `vcpkg integrate install` from vcpkg directory
|
||||||
|
|
||||||
|
### Manual Setup
|
||||||
|
|
||||||
|
If automated setup fails:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
.\bootstrap-vcpkg.bat
|
||||||
|
.\vcpkg.exe integrate install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- **Consistent Dependencies**: Same versions across development environments
|
||||||
|
- **Easy Updates**: Update dependencies via vcpkg.json
|
||||||
|
- **CI/CD Friendly**: Reproducible builds
|
||||||
|
- **Visual Studio Integration**: Native IntelliSense support
|
||||||
|
- **No Manual Downloads**: Automatic dependency resolution
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Custom Triplets
|
||||||
|
|
||||||
|
Override the default x64-windows triplet:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
cmake --preset windows-debug -DVCPKG_TARGET_TRIPLET=x86-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Linking
|
||||||
|
|
||||||
|
For static builds, modify `cmake/vcpkg.cmake`:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE static)
|
||||||
|
set(VCPKG_CRT_LINKAGE static)
|
||||||
|
```
|
||||||
117
docs/vcpkg-triplet-setup.md
Normal file
117
docs/vcpkg-triplet-setup.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# Installing vcpkg Triplets for Windows
|
||||||
|
|
||||||
|
This guide explains how to install the `x64-windows` triplet that's required for building YAZE on Windows.
|
||||||
|
|
||||||
|
## What is a vcpkg Triplet?
|
||||||
|
|
||||||
|
A triplet defines the target platform, architecture, and linking configuration for vcpkg packages. The `x64-windows` triplet is the most common one for 64-bit Windows development.
|
||||||
|
|
||||||
|
## Method 1: Install via Package (Recommended)
|
||||||
|
|
||||||
|
The easiest way to ensure the triplet is available is to install any package with that triplet:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
# Navigate to your vcpkg directory
|
||||||
|
cd C:\path\to\your\vcpkg
|
||||||
|
|
||||||
|
# Install a package with the x64-windows triplet
|
||||||
|
vcpkg install sdl2:x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically create the triplet configuration if it doesn't exist.
|
||||||
|
|
||||||
|
## Method 2: Create Triplet File Manually
|
||||||
|
|
||||||
|
If you need to create the triplet configuration manually:
|
||||||
|
|
||||||
|
1. **Navigate to vcpkg triplets directory:**
|
||||||
|
```cmd
|
||||||
|
cd C:\path\to\your\vcpkg\triplets
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create or verify `x64-windows.cmake` exists:**
|
||||||
|
```cmd
|
||||||
|
dir x64-windows.cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **If it doesn't exist, create it with this content:**
|
||||||
|
```cmake
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||||
|
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_NAME Windows)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Method 3: Check Available Triplets
|
||||||
|
|
||||||
|
To see what triplets are currently available on your system:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
vcpkg help triplet
|
||||||
|
```
|
||||||
|
|
||||||
|
Or list all available triplet files:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
vcpkg help triplet | findstr "Available"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Method 4: Install YAZE Dependencies
|
||||||
|
|
||||||
|
Since YAZE uses several vcpkg packages, installing them will ensure the triplet is properly set up:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
# From the YAZE project root
|
||||||
|
vcpkg install --triplet x64-windows sdl2 zlib libpng abseil
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues and Solutions
|
||||||
|
|
||||||
|
### Issue: "Invalid triplet"
|
||||||
|
**Solution:** Make sure vcpkg is properly installed and in your PATH:
|
||||||
|
```cmd
|
||||||
|
vcpkg version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Triplet not found"
|
||||||
|
**Solution:** Install a package with that triplet first:
|
||||||
|
```cmd
|
||||||
|
vcpkg install zlib:x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Permission denied"
|
||||||
|
**Solution:** Run Command Prompt as Administrator, or install vcpkg in a user-writable location.
|
||||||
|
|
||||||
|
## Alternative Triplets
|
||||||
|
|
||||||
|
If `x64-windows` doesn't work, you can try these alternatives:
|
||||||
|
|
||||||
|
- `x64-windows-static` - Static linking
|
||||||
|
- `x86-windows` - 32-bit Windows
|
||||||
|
- `x64-windows-static-md` - Static runtime, dynamic CRT
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
To verify the triplet is working:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
vcpkg list --triplet x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
This should show installed packages for that triplet.
|
||||||
|
|
||||||
|
## For YAZE Build
|
||||||
|
|
||||||
|
Once the triplet is installed, you can build YAZE using CMake presets:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
cmake --preset=windows-release
|
||||||
|
cmake --build build --config Release
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with the Visual Studio solution:
|
||||||
|
```cmd
|
||||||
|
# Open yaze.sln in Visual Studio
|
||||||
|
# Build normally (F5 or Ctrl+Shift+B)
|
||||||
|
```
|
||||||
284
docs/visual-studio-setup.md
Normal file
284
docs/visual-studio-setup.md
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
# Visual Studio Setup Guide
|
||||||
|
|
||||||
|
> **Note**: This document provides detailed Visual Studio setup information. For the most up-to-date build instructions, see [Build Instructions](02-build-instructions.md).
|
||||||
|
|
||||||
|
This guide will help Visual Studio users set up and build the yaze project on Windows.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Required Software
|
||||||
|
1. **Visual Studio 2022** (Community, Professional, or Enterprise)
|
||||||
|
- Install with "Desktop development with C++" workload
|
||||||
|
- Ensure CMake tools are included
|
||||||
|
- Install Git for Windows (or use built-in Git support)
|
||||||
|
|
||||||
|
2. **vcpkg** (Package Manager)
|
||||||
|
- Download from: https://github.com/Microsoft/vcpkg
|
||||||
|
- Follow installation instructions to integrate with Visual Studio
|
||||||
|
|
||||||
|
3. **CMake** (3.16 or later)
|
||||||
|
- Usually included with Visual Studio 2022
|
||||||
|
- Verify with: `cmake --version`
|
||||||
|
|
||||||
|
### Environment Setup
|
||||||
|
|
||||||
|
1. **Set up vcpkg environment variable:**
|
||||||
|
```cmd
|
||||||
|
set VCPKG_ROOT=C:\vcpkg
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Integrate vcpkg with Visual Studio:**
|
||||||
|
```cmd
|
||||||
|
cd C:\vcpkg
|
||||||
|
.\vcpkg integrate install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
### 1. Clone the Repository
|
||||||
|
```cmd
|
||||||
|
git clone --recursive https://github.com/your-username/yaze.git
|
||||||
|
cd yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Install Dependencies via vcpkg
|
||||||
|
The project uses `vcpkg.json` for automatic dependency management. Dependencies will be installed automatically during CMake configuration.
|
||||||
|
|
||||||
|
Manual installation (if needed):
|
||||||
|
```cmd
|
||||||
|
vcpkg install zlib:x64-windows
|
||||||
|
vcpkg install libpng:x64-windows
|
||||||
|
vcpkg install sdl2[vulkan]:x64-windows
|
||||||
|
vcpkg install abseil:x64-windows
|
||||||
|
vcpkg install gtest:x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configure Build System
|
||||||
|
|
||||||
|
#### Option A: Using Visual Studio Project File (Easiest)
|
||||||
|
1. Open Visual Studio 2022
|
||||||
|
2. Select "Open a project or solution"
|
||||||
|
3. Navigate to the yaze project folder and open `yaze.sln`
|
||||||
|
4. The project is pre-configured with vcpkg integration and proper dependencies
|
||||||
|
5. Select your desired build configuration (Debug/Release) and platform (x64/x86)
|
||||||
|
6. Press F5 to build and run, or Ctrl+Shift+B to build only
|
||||||
|
|
||||||
|
#### Option B: Using CMake with Visual Studio (Recommended for developers)
|
||||||
|
1. Open Visual Studio 2022
|
||||||
|
2. Select "Open a local folder" and navigate to the yaze project folder
|
||||||
|
3. Visual Studio will automatically detect the CMake project
|
||||||
|
4. Wait for CMake configuration to complete (check Output window)
|
||||||
|
|
||||||
|
#### Option C: Using Command Line
|
||||||
|
```cmd
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Build Configuration
|
||||||
|
|
||||||
|
#### Using Visual Studio Project File (.vcxproj)
|
||||||
|
- **Debug Build:** Select "Debug" configuration and press F5 or Ctrl+Shift+B
|
||||||
|
- **Release Build:** Select "Release" configuration and press F5 or Ctrl+Shift+B
|
||||||
|
- **Platform:** Choose x64 (recommended) or x86 from the platform dropdown
|
||||||
|
|
||||||
|
#### Using CMake (Command Line)
|
||||||
|
```cmd
|
||||||
|
# For Development (Debug Build)
|
||||||
|
cmake --build . --config Debug --target yaze
|
||||||
|
|
||||||
|
# For Release Build
|
||||||
|
cmake --build . --config Release --target yaze
|
||||||
|
|
||||||
|
# For Testing (Optional)
|
||||||
|
cmake --build . --config Debug --target yaze_test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues and Solutions
|
||||||
|
|
||||||
|
### Issue 1: zlib Import Errors
|
||||||
|
**Problem:** `fatal error C1083: Cannot open include file: 'zlib.h'`
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Ensure vcpkg is properly integrated with Visual Studio
|
||||||
|
2. Verify the vcpkg toolchain file is set:
|
||||||
|
```cmd
|
||||||
|
cmake .. -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||||
|
```
|
||||||
|
3. Check that zlib is installed:
|
||||||
|
```cmd
|
||||||
|
vcpkg list zlib
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 2: Executable Runs Tests Instead of Main App
|
||||||
|
**Problem:** Running `yaze.exe` starts the test framework instead of the application
|
||||||
|
|
||||||
|
**Solution:** This has been fixed in the latest version. The issue was caused by linking `gtest_main` to the main executable. The fix removes `gtest_main` from the main application while keeping `gtest` for testing capabilities.
|
||||||
|
|
||||||
|
### Issue 3: SDL2 Configuration Issues
|
||||||
|
**Problem:** SDL2 not found or linking errors
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Install SDL2 with vcpkg:
|
||||||
|
```cmd
|
||||||
|
vcpkg install sdl2[vulkan]:x64-windows
|
||||||
|
```
|
||||||
|
2. Ensure the project uses the vcpkg toolchain file
|
||||||
|
|
||||||
|
### Issue 4: Build Errors with Abseil
|
||||||
|
**Problem:** Missing Abseil symbols or linking issues
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Install Abseil via vcpkg:
|
||||||
|
```cmd
|
||||||
|
vcpkg install abseil:x64-windows
|
||||||
|
```
|
||||||
|
2. The project is configured to use Abseil 20240116.2 (see vcpkg.json overrides)
|
||||||
|
|
||||||
|
## Visual Studio Configuration
|
||||||
|
|
||||||
|
### CMake Settings
|
||||||
|
Create or modify `.vscode/settings.json` or use Visual Studio's CMake settings:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cmake.configureArgs": [
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"-Dyaze_BUILD_TESTS=ON",
|
||||||
|
"-Dyaze_BUILD_APP=ON",
|
||||||
|
"-Dyaze_BUILD_LIB=ON"
|
||||||
|
],
|
||||||
|
"cmake.buildDirectory": "${workspaceFolder}/build"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Presets
|
||||||
|
The project includes CMake presets in `CMakePresets.json`. Use these in Visual Studio:
|
||||||
|
|
||||||
|
1. **Debug Build:** `debug` preset
|
||||||
|
2. **Release Build:** `release` preset
|
||||||
|
3. **Development Build:** `dev` preset (includes ROM testing)
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
### Using Visual Studio Project File
|
||||||
|
1. Open `yaze.sln` in Visual Studio
|
||||||
|
2. Set `yaze` as the startup project (should be default)
|
||||||
|
3. Configure command line arguments in Project Properties > Debugging > Command Arguments
|
||||||
|
- Example: `--rom_file=C:\path\to\your\zelda3.sfc`
|
||||||
|
4. Press F5 to build and run, or Ctrl+F5 to run without debugging
|
||||||
|
|
||||||
|
### Command Line
|
||||||
|
```cmd
|
||||||
|
cd build/bin/Debug # or Release
|
||||||
|
yaze.exe --rom_file=path/to/your/zelda3.sfc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Visual Studio (CMake)
|
||||||
|
1. Set `yaze` as the startup project
|
||||||
|
2. Configure command line arguments in Project Properties > Debugging
|
||||||
|
3. Press F5 to run
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Run Unit Tests
|
||||||
|
```cmd
|
||||||
|
cd build
|
||||||
|
ctest --build-config Debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Specific Test Suite
|
||||||
|
```cmd
|
||||||
|
cd build/bin/Debug
|
||||||
|
yaze_test.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Clean Build
|
||||||
|
If you encounter persistent issues:
|
||||||
|
```cmd
|
||||||
|
rmdir /s build
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||||
|
cmake --build . --config Debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Dependencies
|
||||||
|
Verify all dependencies are properly installed:
|
||||||
|
```cmd
|
||||||
|
vcpkg list
|
||||||
|
```
|
||||||
|
|
||||||
|
### CMake Cache Issues
|
||||||
|
Clear CMake cache:
|
||||||
|
```cmd
|
||||||
|
del CMakeCache.txt
|
||||||
|
cmake .. -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
## Visual Studio Project File Features
|
||||||
|
|
||||||
|
The included `yaze.vcxproj` and `yaze.sln` files provide:
|
||||||
|
|
||||||
|
### **Automatic Dependency Management**
|
||||||
|
- **vcpkg Integration:** Automatically installs and links dependencies from `vcpkg.json`
|
||||||
|
- **Platform Support:** Pre-configured for both x64 and x86 builds
|
||||||
|
- **Library Linking:** Automatically links SDL2, zlib, libpng, and system libraries
|
||||||
|
|
||||||
|
### **Build Configuration**
|
||||||
|
- **Debug Configuration:** Includes debugging symbols and runtime checks
|
||||||
|
- **Release Configuration:** Optimized for performance with full optimizations
|
||||||
|
- **C++23 Standard:** Uses modern C++ features and standard library
|
||||||
|
|
||||||
|
### **Asset Management**
|
||||||
|
- **Automatic Asset Copying:** Post-build events copy themes and assets to output directory
|
||||||
|
- **ROM File Handling:** Automatically copies `zelda3.sfc` if present in project root
|
||||||
|
- **Resource Organization:** Properly structures output directory for distribution
|
||||||
|
|
||||||
|
### **Development Features**
|
||||||
|
- **IntelliSense Support:** Full code completion and error detection
|
||||||
|
- **Debugging Integration:** Native Visual Studio debugging support
|
||||||
|
- **Project Properties:** Easy access to compiler and linker settings
|
||||||
|
|
||||||
|
## CI/CD Integration
|
||||||
|
|
||||||
|
The Visual Studio project files are fully integrated into the CI/CD pipeline:
|
||||||
|
|
||||||
|
### **Automated Validation**
|
||||||
|
- **Pre-commit checks:** Visual Studio builds are validated on every pull request
|
||||||
|
- **Release validation:** Both CMake and Visual Studio builds are tested before release
|
||||||
|
- **Multi-platform testing:** x64 and x86 builds are validated on Windows
|
||||||
|
- **Dependency verification:** vcpkg integration is tested automatically
|
||||||
|
|
||||||
|
### **Build Matrix**
|
||||||
|
The CI/CD pipeline tests:
|
||||||
|
- **Windows x64 Debug/Release** using Visual Studio 2022
|
||||||
|
- **Windows x86 Debug/Release** using Visual Studio 2022
|
||||||
|
- **CMake builds** alongside Visual Studio builds for compatibility
|
||||||
|
- **Asset copying** and executable functionality
|
||||||
|
|
||||||
|
### **Quality Assurance**
|
||||||
|
- **Test main detection:** Prevents the test framework from hijacking the main application
|
||||||
|
- **Asset validation:** Ensures themes and resources are properly copied
|
||||||
|
- **Executable testing:** Verifies the application starts correctly
|
||||||
|
- **Dependency checking:** Validates all required libraries are properly linked
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
- The project supports both x64 and x86 builds (use appropriate vcpkg triplets)
|
||||||
|
- For ARM64 Windows builds, use `arm64-windows` triplet
|
||||||
|
- The CI/CD pipeline validates both CMake and Visual Studio builds
|
||||||
|
- Development builds include additional debugging features and ROM testing capabilities
|
||||||
|
- The `.vcxproj` file provides the easiest setup for Visual Studio users who prefer traditional project files over CMake
|
||||||
|
- All builds are automatically validated to ensure they produce working executables
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If you encounter issues not covered in this guide:
|
||||||
|
1. Check the project's GitHub issues
|
||||||
|
2. Verify your Visual Studio and vcpkg installations
|
||||||
|
3. Ensure all dependencies are properly installed via vcpkg
|
||||||
|
4. Try a clean build following the troubleshooting steps above
|
||||||
346
docs/windows-development-guide.md
Normal file
346
docs/windows-development-guide.md
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
# Windows Development Guide for YAZE
|
||||||
|
|
||||||
|
This guide will help you set up a Windows development environment for YAZE and build the project successfully.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Required Software
|
||||||
|
|
||||||
|
1. **Visual Studio 2022** (Community, Professional, or Enterprise)
|
||||||
|
- Download from: https://visualstudio.microsoft.com/downloads/
|
||||||
|
- Required workloads:
|
||||||
|
- Desktop development with C++
|
||||||
|
- Game development with C++ (optional, for additional tools)
|
||||||
|
|
||||||
|
2. **Git for Windows**
|
||||||
|
- Download from: https://git-scm.com/download/win
|
||||||
|
- Use default installation options
|
||||||
|
|
||||||
|
3. **Python 3.8 or later**
|
||||||
|
- Download from: https://www.python.org/downloads/
|
||||||
|
- Make sure to check "Add Python to PATH" during installation
|
||||||
|
|
||||||
|
### Optional Software
|
||||||
|
|
||||||
|
- **PowerShell 7** (recommended for better script support)
|
||||||
|
- **Windows Terminal** (for better terminal experience)
|
||||||
|
|
||||||
|
## Quick Setup
|
||||||
|
|
||||||
|
### Automated Setup
|
||||||
|
|
||||||
|
The easiest way to get started is to use our automated setup script:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Run from the YAZE project root directory
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will:
|
||||||
|
- Check for required software (Visual Studio 2022, Git, Python)
|
||||||
|
- Set up vcpkg and install dependencies (zlib, libpng, SDL2)
|
||||||
|
- Generate Visual Studio project files with proper vcpkg integration
|
||||||
|
- Perform a test build to verify everything works
|
||||||
|
|
||||||
|
### Manual Setup
|
||||||
|
|
||||||
|
If you prefer to set up manually or the automated script fails:
|
||||||
|
|
||||||
|
#### 1. Clone the Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/your-username/yaze.git
|
||||||
|
cd yaze
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Set up vcpkg
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone vcpkg
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git vcpkg
|
||||||
|
|
||||||
|
# Bootstrap vcpkg
|
||||||
|
cd vcpkg
|
||||||
|
.\bootstrap-vcpkg.bat
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
.\vcpkg\vcpkg.exe install --triplet x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Generate Visual Studio Project Files
|
||||||
|
|
||||||
|
The generation script creates project files with proper vcpkg integration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates:
|
||||||
|
- `YAZE.sln` - Visual Studio solution file
|
||||||
|
- `YAZE.vcxproj` - Visual Studio project file with vcpkg integration
|
||||||
|
- Proper vcpkg triplet settings for all platforms (x86, x64, ARM64)
|
||||||
|
|
||||||
|
#### 4. Build the Project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using PowerShell script (recommended)
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Release -Platform x64
|
||||||
|
|
||||||
|
# Or using batch script
|
||||||
|
.\scripts\build-windows.bat Release x64
|
||||||
|
|
||||||
|
# Or using Visual Studio
|
||||||
|
# Open YAZE.sln in Visual Studio 2022 and build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building the Project
|
||||||
|
|
||||||
|
### Using Visual Studio
|
||||||
|
|
||||||
|
1. Open `YAZE.sln` in Visual Studio 2022
|
||||||
|
2. Select your desired configuration:
|
||||||
|
- **Debug**: For development and debugging
|
||||||
|
- **Release**: For optimized builds
|
||||||
|
- **RelWithDebInfo**: Release with debug information
|
||||||
|
- **MinSizeRel**: Minimal size release
|
||||||
|
3. Select your platform:
|
||||||
|
- **x64**: 64-bit (recommended)
|
||||||
|
- **x86**: 32-bit
|
||||||
|
- **ARM64**: ARM64 (if supported)
|
||||||
|
4. Build the solution (Ctrl+Shift+B)
|
||||||
|
|
||||||
|
### Using Command Line
|
||||||
|
|
||||||
|
#### PowerShell Script (Recommended)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Build Release x64 (default)
|
||||||
|
.\scripts\build-windows.ps1
|
||||||
|
|
||||||
|
# Build Debug x64
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Debug -Platform x64
|
||||||
|
|
||||||
|
# Build Release x86
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Release -Platform x86
|
||||||
|
|
||||||
|
# Clean build
|
||||||
|
.\scripts\build-windows.ps1 -Clean
|
||||||
|
|
||||||
|
# Verbose output
|
||||||
|
.\scripts\build-windows.ps1 -Verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Batch Script
|
||||||
|
|
||||||
|
```batch
|
||||||
|
REM Build Release x64 (default)
|
||||||
|
.\scripts\build-windows.bat
|
||||||
|
|
||||||
|
REM Build Debug x64
|
||||||
|
.\scripts\build-windows.bat Debug x64
|
||||||
|
|
||||||
|
REM Build Release x86
|
||||||
|
.\scripts\build-windows.bat Release x86
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Direct MSBuild
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build Release x64
|
||||||
|
msbuild YAZE.sln /p:Configuration=Release /p:Platform=x64 /p:VcpkgEnabled=true /p:VcpkgManifestInstall=true /m
|
||||||
|
|
||||||
|
# Build Debug x64
|
||||||
|
msbuild YAZE.sln /p:Configuration=Debug /p:Platform=x64 /p:VcpkgEnabled=true /p:VcpkgManifestInstall=true /m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
yaze/
|
||||||
|
├── YAZE.sln # Visual Studio solution file (generated)
|
||||||
|
├── YAZE.vcxproj # Visual Studio project file (generated)
|
||||||
|
├── vcpkg.json # vcpkg dependencies
|
||||||
|
├── scripts/ # Build and setup scripts
|
||||||
|
│ ├── build-windows.ps1 # PowerShell build script
|
||||||
|
│ ├── build-windows.bat # Batch build script
|
||||||
|
│ ├── setup-windows-dev.ps1 # Automated setup script
|
||||||
|
│ └── generate-vs-projects.py # Project file generator
|
||||||
|
├── src/ # Source code
|
||||||
|
├── incl/ # Public headers
|
||||||
|
├── assets/ # Game assets
|
||||||
|
└── docs/ # Documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The Visual Studio project files (`YAZE.sln`, `YAZE.vcxproj`) are generated automatically and should not be edited manually. If you need to modify project settings, update the generation script instead.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### 1. MSBuild Not Found
|
||||||
|
|
||||||
|
**Error**: `'msbuild' is not recognized as an internal or external command`
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Install Visual Studio 2022 with C++ workload
|
||||||
|
- Or add MSBuild to your PATH:
|
||||||
|
```bash
|
||||||
|
# Add to PATH (adjust path as needed)
|
||||||
|
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. vcpkg Integration Issues
|
||||||
|
|
||||||
|
**Error**: `vcpkg.json not found` or dependency resolution fails
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Ensure vcpkg is properly set up:
|
||||||
|
```bash
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
```
|
||||||
|
- Or manually set up vcpkg as described in the manual setup section
|
||||||
|
|
||||||
|
#### 3. ZLIB or Other Dependencies Not Found
|
||||||
|
|
||||||
|
**Error**: `Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)`
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- This usually means vcpkg integration isn't working properly
|
||||||
|
- Regenerate project files with proper vcpkg integration:
|
||||||
|
```bash
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
```
|
||||||
|
- Ensure vcpkg is installed and dependencies are available:
|
||||||
|
```bash
|
||||||
|
.\vcpkg\vcpkg.exe install --triplet x64-windows
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Python Script Execution Policy
|
||||||
|
|
||||||
|
**Error**: `execution of scripts is disabled on this system`
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```powershell
|
||||||
|
# Run as Administrator
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. Missing Dependencies
|
||||||
|
|
||||||
|
**Error**: Linker errors about missing libraries
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Ensure all dependencies are installed via vcpkg:
|
||||||
|
```bash
|
||||||
|
.\vcpkg\vcpkg.exe install --triplet x64-windows
|
||||||
|
```
|
||||||
|
- Regenerate project files:
|
||||||
|
```bash
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. Build Failures
|
||||||
|
|
||||||
|
**Error**: Compilation or linking errors
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Clean and rebuild:
|
||||||
|
```powershell
|
||||||
|
.\scripts\build-windows.ps1 -Clean
|
||||||
|
```
|
||||||
|
- Check that all source files are included in the project
|
||||||
|
- Verify that include paths are correct
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
If you encounter issues not covered here:
|
||||||
|
|
||||||
|
1. Check the [main build instructions](02-build-instructions.md)
|
||||||
|
2. Review the [troubleshooting section](02-build-instructions.md#troubleshooting)
|
||||||
|
3. Check the [GitHub Issues](https://github.com/your-username/yaze/issues)
|
||||||
|
4. Create a new issue with:
|
||||||
|
- Your Windows version
|
||||||
|
- Visual Studio version
|
||||||
|
- Complete error message
|
||||||
|
- Steps to reproduce
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Making Changes
|
||||||
|
|
||||||
|
1. Make your changes to the source code
|
||||||
|
2. If you added new source files, regenerate project files:
|
||||||
|
```bash
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
```
|
||||||
|
3. Build the project:
|
||||||
|
```powershell
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Debug -Platform x64
|
||||||
|
```
|
||||||
|
4. Test your changes
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
1. Set breakpoints in Visual Studio
|
||||||
|
2. Build in Debug configuration
|
||||||
|
3. Run with debugger (F5)
|
||||||
|
4. Use Visual Studio's debugging tools
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
1. Build the project
|
||||||
|
2. Run the executable:
|
||||||
|
```bash
|
||||||
|
.\build\bin\Debug\yaze.exe
|
||||||
|
```
|
||||||
|
3. Test with a ROM file:
|
||||||
|
```bash
|
||||||
|
.\build\bin\Debug\yaze.exe --rom_file=path\to\your\rom.sfc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
### Build Performance
|
||||||
|
|
||||||
|
- Use the `/m` flag for parallel builds
|
||||||
|
- Use SSD storage for better I/O performance
|
||||||
|
- Exclude build directories from antivirus scanning
|
||||||
|
- Use Release configuration for final builds
|
||||||
|
|
||||||
|
### Development Performance
|
||||||
|
|
||||||
|
- Use Debug configuration for development
|
||||||
|
- Use incremental builds (default in Visual Studio)
|
||||||
|
- Use RelWithDebInfo for performance testing with debug info
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Custom Build Configurations
|
||||||
|
|
||||||
|
You can create custom build configurations by modifying the Visual Studio project file or using CMake directly.
|
||||||
|
|
||||||
|
### Cross-Platform Development
|
||||||
|
|
||||||
|
While this guide focuses on Windows, YAZE also supports:
|
||||||
|
- Linux (Ubuntu/Debian)
|
||||||
|
- macOS
|
||||||
|
|
||||||
|
See the main build instructions for other platforms.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
When contributing to YAZE on Windows:
|
||||||
|
|
||||||
|
1. Follow the [coding standards](B1-contributing.md)
|
||||||
|
2. Test your changes on Windows
|
||||||
|
3. Ensure the build scripts still work
|
||||||
|
4. Update documentation if needed
|
||||||
|
5. Submit a pull request
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
- [Visual Studio Documentation](https://docs.microsoft.com/en-us/visualstudio/)
|
||||||
|
- [vcpkg Documentation](https://vcpkg.readthedocs.io/)
|
||||||
|
- [CMake Documentation](https://cmake.org/documentation/)
|
||||||
|
- [YAZE API Reference](04-api-reference.md)
|
||||||
10
incl/yaze.h
10
incl/yaze.h
@@ -9,7 +9,7 @@
|
|||||||
* The Legend of Zelda: A Link to the Past. This API allows external
|
* The Legend of Zelda: A Link to the Past. This API allows external
|
||||||
* applications to interact with YAZE's functionality.
|
* applications to interact with YAZE's functionality.
|
||||||
*
|
*
|
||||||
* @version 0.3.0
|
* @version 0.3.1
|
||||||
* @author YAZE Team
|
* @author YAZE Team
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ extern "C" {
|
|||||||
/** Minor version number */
|
/** Minor version number */
|
||||||
#define YAZE_VERSION_MINOR 3
|
#define YAZE_VERSION_MINOR 3
|
||||||
/** Patch version number */
|
/** Patch version number */
|
||||||
#define YAZE_VERSION_PATCH 0
|
#define YAZE_VERSION_PATCH 1
|
||||||
|
|
||||||
/** Combined version as a string */
|
/** Combined version as a string */
|
||||||
#define YAZE_VERSION_STRING "0.3.0"
|
#define YAZE_VERSION_STRING "0.3.1"
|
||||||
|
|
||||||
/** Combined version as a number (major * 10000 + minor * 100 + patch) */
|
/** Combined version as a number (major * 10000 + minor * 100 + patch) */
|
||||||
#define YAZE_VERSION_NUMBER 300
|
#define YAZE_VERSION_NUMBER 301
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ int yaze_app_main(int argc, char** argv);
|
|||||||
/**
|
/**
|
||||||
* @brief Check if the current YAZE version is compatible with the expected version
|
* @brief Check if the current YAZE version is compatible with the expected version
|
||||||
*
|
*
|
||||||
* @param expected_version Expected version string (e.g., "0.3.0")
|
* @param expected_version Expected version string (e.g., "0.3.1")
|
||||||
* @return true if compatible, false otherwise
|
* @return true if compatible, false otherwise
|
||||||
*/
|
*/
|
||||||
bool yaze_check_version_compatibility(const char* expected_version);
|
bool yaze_check_version_compatibility(const char* expected_version);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* This header defines data structures and constants specific to
|
* This header defines data structures and constants specific to
|
||||||
* The Legend of Zelda: A Link to the Past ROM format and game data.
|
* The Legend of Zelda: A Link to the Past ROM format and game data.
|
||||||
*
|
*
|
||||||
* @version 0.3.0
|
* @version 0.3.1
|
||||||
* @author YAZE Team
|
* @author YAZE Team
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
143
scripts/README.md
Normal file
143
scripts/README.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# YAZE Build Scripts
|
||||||
|
|
||||||
|
This directory contains build and setup scripts for YAZE development on different platforms.
|
||||||
|
|
||||||
|
## Windows Scripts
|
||||||
|
|
||||||
|
### Setup Scripts
|
||||||
|
- **`setup-windows-dev.ps1`** - Complete Windows development environment setup (PowerShell)
|
||||||
|
- **`setup-vcpkg-windows.ps1`** - vcpkg setup only (PowerShell)
|
||||||
|
- **`setup-vcpkg-windows.bat`** - vcpkg setup only (Batch)
|
||||||
|
|
||||||
|
### Build Scripts
|
||||||
|
- **`build-windows.ps1`** - Build YAZE on Windows (PowerShell)
|
||||||
|
- **`build-windows.bat`** - Build YAZE on Windows (Batch)
|
||||||
|
|
||||||
|
### Validation Scripts
|
||||||
|
- **`validate-windows-build.ps1`** - Validate Windows build environment
|
||||||
|
|
||||||
|
### Project Generation
|
||||||
|
- **`generate-vs-projects.py`** - Generate Visual Studio project files (Cross-platform Python)
|
||||||
|
- **`generate-vs-projects.ps1`** - Generate Visual Studio project files (PowerShell)
|
||||||
|
- **`generate-vs-projects.bat`** - Generate Visual Studio project files (Batch)
|
||||||
|
|
||||||
|
## Quick Start (Windows)
|
||||||
|
|
||||||
|
### Option 1: Automated Setup (Recommended)
|
||||||
|
```powershell
|
||||||
|
.\scripts\setup-windows-dev.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Manual Setup
|
||||||
|
```powershell
|
||||||
|
# 1. Setup vcpkg
|
||||||
|
.\scripts\setup-vcpkg-windows.ps1
|
||||||
|
|
||||||
|
# 2. Generate project files
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
# 3. Build
|
||||||
|
.\scripts\build-windows.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Using Batch Scripts
|
||||||
|
```batch
|
||||||
|
REM Setup vcpkg
|
||||||
|
.\scripts\setup-vcpkg-windows.bat
|
||||||
|
|
||||||
|
REM Generate project files
|
||||||
|
python scripts/generate-vs-projects.py
|
||||||
|
|
||||||
|
REM Build
|
||||||
|
.\scripts\build-windows.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Script Options
|
||||||
|
|
||||||
|
### setup-windows-dev.ps1
|
||||||
|
- `-SkipVcpkg` - Skip vcpkg setup
|
||||||
|
- `-SkipVS` - Skip Visual Studio check
|
||||||
|
- `-SkipBuild` - Skip test build
|
||||||
|
|
||||||
|
### build-windows.ps1
|
||||||
|
- `-Configuration` - Build configuration (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
|
- `-Platform` - Target platform (x64, x86, ARM64)
|
||||||
|
- `-Clean` - Clean build directories before building
|
||||||
|
- `-Verbose` - Verbose build output
|
||||||
|
|
||||||
|
### build-windows.bat
|
||||||
|
- First argument: Configuration (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
|
- Second argument: Platform (x64, x86, ARM64)
|
||||||
|
- `clean` - Clean build directories
|
||||||
|
- `verbose` - Verbose build output
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Build Release x64 (default)
|
||||||
|
.\scripts\build-windows.ps1
|
||||||
|
|
||||||
|
# Build Debug x64
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Debug -Platform x64
|
||||||
|
|
||||||
|
# Build Release x86
|
||||||
|
.\scripts\build-windows.ps1 -Configuration Release -Platform x86
|
||||||
|
|
||||||
|
# Clean build
|
||||||
|
.\scripts\build-windows.ps1 -Clean
|
||||||
|
|
||||||
|
# Verbose build
|
||||||
|
.\scripts\build-windows.ps1 -Verbose
|
||||||
|
|
||||||
|
# Validate environment
|
||||||
|
.\scripts\validate-windows-build.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
```batch
|
||||||
|
REM Build Release x64 (default)
|
||||||
|
.\scripts\build-windows.bat
|
||||||
|
|
||||||
|
REM Build Debug x64
|
||||||
|
.\scripts\build-windows.bat Debug x64
|
||||||
|
|
||||||
|
REM Build Release x86
|
||||||
|
.\scripts\build-windows.bat Release x86
|
||||||
|
|
||||||
|
REM Clean build
|
||||||
|
.\scripts\build-windows.bat clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **PowerShell Execution Policy**
|
||||||
|
```powershell
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **MSBuild Not Found**
|
||||||
|
- Install Visual Studio 2022 with C++ workload
|
||||||
|
- Or add MSBuild to PATH
|
||||||
|
|
||||||
|
3. **vcpkg Issues**
|
||||||
|
- Run `.\scripts\setup-vcpkg-windows.ps1` to reinstall
|
||||||
|
- Check internet connection for dependency downloads
|
||||||
|
|
||||||
|
4. **Python Not Found**
|
||||||
|
- Install Python 3.8+ from python.org
|
||||||
|
- Make sure Python is in PATH
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
1. Run validation script: `.\scripts\validate-windows-build.ps1`
|
||||||
|
2. Check the [Windows Development Guide](../docs/windows-development-guide.md)
|
||||||
|
3. Review build output for specific error messages
|
||||||
|
|
||||||
|
## Other Scripts
|
||||||
|
|
||||||
|
- **`create_release.sh`** - Create GitHub releases (Linux/macOS)
|
||||||
|
- **`extract_changelog.py`** - Extract changelog for releases
|
||||||
|
- **`quality_check.sh`** - Code quality checks (Linux/macOS)
|
||||||
|
- **`test_asar_integration.py`** - Test Asar integration
|
||||||
|
- **`agent.sh`** - AI agent helper script (Linux/macOS)
|
||||||
164
scripts/build-windows.bat
Normal file
164
scripts/build-windows.bat
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
@echo off
|
||||||
|
REM YAZE Windows Build Script (Batch Version)
|
||||||
|
REM This script builds the YAZE project on Windows using MSBuild
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
REM Parse command line arguments
|
||||||
|
set BUILD_CONFIG=Release
|
||||||
|
set BUILD_PLATFORM=x64
|
||||||
|
set CLEAN_BUILD=0
|
||||||
|
set VERBOSE=0
|
||||||
|
|
||||||
|
:parse_args
|
||||||
|
if "%~1"=="" goto :args_done
|
||||||
|
if /i "%~1"=="Debug" set BUILD_CONFIG=Debug
|
||||||
|
if /i "%~1"=="Release" set BUILD_CONFIG=Release
|
||||||
|
if /i "%~1"=="RelWithDebInfo" set BUILD_CONFIG=RelWithDebInfo
|
||||||
|
if /i "%~1"=="MinSizeRel" set BUILD_CONFIG=MinSizeRel
|
||||||
|
if /i "%~1"=="x64" set BUILD_PLATFORM=x64
|
||||||
|
if /i "%~1"=="x86" set BUILD_PLATFORM=x86
|
||||||
|
if /i "%~1"=="ARM64" set BUILD_PLATFORM=ARM64
|
||||||
|
if /i "%~1"=="clean" set CLEAN_BUILD=1
|
||||||
|
if /i "%~1"=="verbose" set VERBOSE=1
|
||||||
|
shift
|
||||||
|
goto :parse_args
|
||||||
|
|
||||||
|
:args_done
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo YAZE Windows Build Script
|
||||||
|
echo ========================================
|
||||||
|
|
||||||
|
REM Check if we're in the right directory
|
||||||
|
if not exist "YAZE.sln" (
|
||||||
|
echo ERROR: YAZE.sln not found. Please run this script from the project root directory.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ YAZE.sln found
|
||||||
|
|
||||||
|
REM Check for MSBuild
|
||||||
|
where msbuild >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: MSBuild not found. Please install Visual Studio 2022 or later.
|
||||||
|
echo Make sure to install the C++ development workload.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ MSBuild found
|
||||||
|
|
||||||
|
REM Check for vcpkg
|
||||||
|
if not exist "vcpkg.json" (
|
||||||
|
echo WARNING: vcpkg.json not found. vcpkg integration may not work properly.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Build Configuration: %BUILD_CONFIG%
|
||||||
|
echo Build Platform: %BUILD_PLATFORM%
|
||||||
|
|
||||||
|
REM Create build directories
|
||||||
|
echo Creating build directories...
|
||||||
|
if not exist "build" mkdir build
|
||||||
|
if not exist "build\bin" mkdir build\bin
|
||||||
|
if not exist "build\obj" mkdir build\obj
|
||||||
|
|
||||||
|
REM Clean build if requested
|
||||||
|
if %CLEAN_BUILD%==1 (
|
||||||
|
echo Cleaning build directories...
|
||||||
|
if exist "build\bin" rmdir /s /q "build\bin" 2>nul
|
||||||
|
if exist "build\obj" rmdir /s /q "build\obj" 2>nul
|
||||||
|
if not exist "build\bin" mkdir build\bin
|
||||||
|
if not exist "build\obj" mkdir build\obj
|
||||||
|
echo ✓ Build directories cleaned
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Generate yaze_config.h if it doesn't exist
|
||||||
|
if not exist "yaze_config.h" (
|
||||||
|
echo Generating yaze_config.h...
|
||||||
|
if exist "src\yaze_config.h.in" (
|
||||||
|
copy "src\yaze_config.h.in" "yaze_config.h" >nul
|
||||||
|
powershell -Command "(Get-Content 'yaze_config.h') -replace '@yaze_VERSION_MAJOR@', '0' -replace '@yaze_VERSION_MINOR@', '3' -replace '@yaze_VERSION_PATCH@', '1' | Set-Content 'yaze_config.h'"
|
||||||
|
echo ✓ Generated yaze_config.h
|
||||||
|
) else (
|
||||||
|
echo WARNING: yaze_config.h.in not found, creating basic config
|
||||||
|
echo // yaze config file > yaze_config.h
|
||||||
|
echo #define YAZE_VERSION_MAJOR 0 >> yaze_config.h
|
||||||
|
echo #define YAZE_VERSION_MINOR 3 >> yaze_config.h
|
||||||
|
echo #define YAZE_VERSION_PATCH 1 >> yaze_config.h
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Build using MSBuild
|
||||||
|
echo Building with MSBuild...
|
||||||
|
|
||||||
|
set MSBUILD_ARGS=YAZE.sln /p:Configuration=%BUILD_CONFIG% /p:Platform=%BUILD_PLATFORM% /p:VcpkgEnabled=true /p:VcpkgManifestInstall=true /m
|
||||||
|
|
||||||
|
if %VERBOSE%==1 (
|
||||||
|
set MSBUILD_ARGS=%MSBUILD_ARGS% /verbosity:detailed
|
||||||
|
) else (
|
||||||
|
set MSBUILD_ARGS=%MSBUILD_ARGS% /verbosity:minimal
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Command: msbuild %MSBUILD_ARGS%
|
||||||
|
|
||||||
|
msbuild %MSBUILD_ARGS%
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Build failed with exit code %errorlevel%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ Build completed successfully
|
||||||
|
|
||||||
|
REM Verify executable was created
|
||||||
|
set EXE_PATH=build\bin\%BUILD_CONFIG%\yaze.exe
|
||||||
|
if not exist "%EXE_PATH%" (
|
||||||
|
echo ERROR: Executable not found at expected path: %EXE_PATH%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ Executable created: %EXE_PATH%
|
||||||
|
|
||||||
|
REM Test that the executable runs (basic test)
|
||||||
|
echo Testing executable startup...
|
||||||
|
"%EXE_PATH%" --help >nul 2>&1
|
||||||
|
set EXIT_CODE=%errorlevel%
|
||||||
|
|
||||||
|
REM Check if it's the test main or app main
|
||||||
|
"%EXE_PATH%" --help 2>&1 | findstr /i "Google Test" >nul
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo ERROR: Executable is running test main instead of app main!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ Executable runs correctly (exit code: %EXIT_CODE%)
|
||||||
|
|
||||||
|
REM Display file info
|
||||||
|
for %%A in ("%EXE_PATH%") do set FILE_SIZE=%%~zA
|
||||||
|
set /a FILE_SIZE_MB=%FILE_SIZE% / 1024 / 1024
|
||||||
|
echo Executable size: %FILE_SIZE_MB% MB
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo ✓ YAZE Windows build completed successfully!
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo Build Configuration: %BUILD_CONFIG%
|
||||||
|
echo Build Platform: %BUILD_PLATFORM%
|
||||||
|
echo Executable: %EXE_PATH%
|
||||||
|
echo.
|
||||||
|
echo To run YAZE:
|
||||||
|
echo %EXE_PATH%
|
||||||
|
echo.
|
||||||
|
echo To build other configurations:
|
||||||
|
echo %~nx0 Debug x64
|
||||||
|
echo %~nx0 Release x86
|
||||||
|
echo %~nx0 RelWithDebInfo ARM64
|
||||||
|
echo %~nx0 clean
|
||||||
|
echo.
|
||||||
|
|
||||||
|
pause
|
||||||
220
scripts/build-windows.ps1
Normal file
220
scripts/build-windows.ps1
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
# YAZE Windows Build Script
|
||||||
|
# This script builds the YAZE project on Windows using MSBuild
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Configuration = "Release",
|
||||||
|
[string]$Platform = "x64",
|
||||||
|
[switch]$Clean,
|
||||||
|
[switch]$Verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set error handling
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
$Colors = @{
|
||||||
|
Success = "Green"
|
||||||
|
Warning = "Yellow"
|
||||||
|
Error = "Red"
|
||||||
|
Info = "Cyan"
|
||||||
|
White = "White"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Status {
|
||||||
|
param([string]$Message, [string]$Color = "White")
|
||||||
|
Write-Host $Message -ForegroundColor $Colors[$Color]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-Command {
|
||||||
|
param([string]$Command)
|
||||||
|
try {
|
||||||
|
$null = Get-Command $Command -ErrorAction Stop
|
||||||
|
return $true
|
||||||
|
} catch {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MSBuildPath {
|
||||||
|
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
if (Test-Path $vsWhere) {
|
||||||
|
$vsInstall = & $vsWhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
if ($vsInstall) {
|
||||||
|
$msbuildPath = Join-Path $vsInstall "MSBuild\Current\Bin\MSBuild.exe"
|
||||||
|
if (Test-Path $msbuildPath) {
|
||||||
|
return $msbuildPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status "YAZE Windows Build Script" "Info"
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
|
||||||
|
# Validate parameters
|
||||||
|
$ValidConfigs = @("Debug", "Release", "RelWithDebInfo", "MinSizeRel")
|
||||||
|
$ValidPlatforms = @("x64", "x86", "ARM64")
|
||||||
|
|
||||||
|
if ($ValidConfigs -notcontains $Configuration) {
|
||||||
|
Write-Status "ERROR: Invalid configuration '$Configuration'. Valid options: $($ValidConfigs -join ', ')" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ValidPlatforms -notcontains $Platform) {
|
||||||
|
Write-Status "ERROR: Invalid platform '$Platform'. Valid options: $($ValidPlatforms -join ', ')" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "Build Configuration: $Configuration" "Warning"
|
||||||
|
Write-Status "Build Platform: $Platform" "Warning"
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if (-not (Test-Path "YAZE.sln")) {
|
||||||
|
Write-Status "ERROR: YAZE.sln not found. Please run this script from the project root directory." "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ Found YAZE.sln" "Success"
|
||||||
|
|
||||||
|
# Check for MSBuild
|
||||||
|
$msbuildPath = Get-MSBuildPath
|
||||||
|
if (-not $msbuildPath) {
|
||||||
|
Write-Status "ERROR: MSBuild not found. Please install Visual Studio 2022 with C++ workload." "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ MSBuild found at: $msbuildPath" "Success"
|
||||||
|
|
||||||
|
# Check for vcpkg
|
||||||
|
if (-not (Test-Path "vcpkg.json")) {
|
||||||
|
Write-Status "WARNING: vcpkg.json not found. vcpkg integration may not work properly." "Warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create build directories
|
||||||
|
Write-Status "Creating build directories..." "Warning"
|
||||||
|
$directories = @("build", "build\bin", "build\obj")
|
||||||
|
foreach ($dir in $directories) {
|
||||||
|
if (-not (Test-Path $dir)) {
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||||
|
Write-Status "✓ Created directory: $dir" "Success"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean build if requested
|
||||||
|
if ($Clean) {
|
||||||
|
Write-Status "Cleaning build directories..." "Warning"
|
||||||
|
if (Test-Path "build\bin") {
|
||||||
|
Remove-Item -Recurse -Force "build\bin\*" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if (Test-Path "build\obj") {
|
||||||
|
Remove-Item -Recurse -Force "build\obj\*" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
Write-Status "✓ Build directories cleaned" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate yaze_config.h if it doesn't exist
|
||||||
|
if (-not (Test-Path "yaze_config.h")) {
|
||||||
|
Write-Status "Generating yaze_config.h..." "Warning"
|
||||||
|
if (Test-Path "src\yaze_config.h.in") {
|
||||||
|
Copy-Item "src\yaze_config.h.in" "yaze_config.h"
|
||||||
|
$content = Get-Content "yaze_config.h" -Raw
|
||||||
|
$content = $content -replace '@yaze_VERSION_MAJOR@', '0'
|
||||||
|
$content = $content -replace '@yaze_VERSION_MINOR@', '3'
|
||||||
|
$content = $content -replace '@yaze_VERSION_PATCH@', '1'
|
||||||
|
Set-Content "yaze_config.h" $content
|
||||||
|
Write-Status "✓ Generated yaze_config.h" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "WARNING: yaze_config.h.in not found, creating basic config" "Warning"
|
||||||
|
@"
|
||||||
|
// yaze config file
|
||||||
|
#define YAZE_VERSION_MAJOR 0
|
||||||
|
#define YAZE_VERSION_MINOR 3
|
||||||
|
#define YAZE_VERSION_PATCH 1
|
||||||
|
"@ | Out-File -FilePath "yaze_config.h" -Encoding UTF8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build using MSBuild
|
||||||
|
Write-Status "Building with MSBuild..." "Warning"
|
||||||
|
|
||||||
|
$msbuildArgs = @(
|
||||||
|
"YAZE.sln"
|
||||||
|
"/p:Configuration=$Configuration"
|
||||||
|
"/p:Platform=$Platform"
|
||||||
|
"/p:VcpkgEnabled=true"
|
||||||
|
"/p:VcpkgManifestInstall=true"
|
||||||
|
"/m"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Verbose) {
|
||||||
|
$msbuildArgs += "/verbosity:detailed"
|
||||||
|
} else {
|
||||||
|
$msbuildArgs += "/verbosity:minimal"
|
||||||
|
}
|
||||||
|
|
||||||
|
$msbuildCommand = "& `"$msbuildPath`" $($msbuildArgs -join ' ')"
|
||||||
|
Write-Status "Command: $msbuildCommand" "Info"
|
||||||
|
|
||||||
|
try {
|
||||||
|
& $msbuildPath @msbuildArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "MSBuild failed with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
Write-Status "✓ Build completed successfully" "Success"
|
||||||
|
} catch {
|
||||||
|
Write-Status "✗ Build failed: $_" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify executable was created
|
||||||
|
$exePath = "build\bin\$Configuration\yaze.exe"
|
||||||
|
if (-not (Test-Path $exePath)) {
|
||||||
|
Write-Status "ERROR: Executable not found at expected path: $exePath" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ Executable created: $exePath" "Success"
|
||||||
|
|
||||||
|
# Test that the executable runs
|
||||||
|
Write-Status "Testing executable..." "Warning"
|
||||||
|
try {
|
||||||
|
$testResult = & $exePath --help 2>&1
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
# Check if it's the test main or app main
|
||||||
|
if ($testResult -match "Google Test|gtest") {
|
||||||
|
Write-Status "ERROR: Executable is running test main instead of app main!" "Error"
|
||||||
|
Write-Status "Output: $testResult" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ Executable runs correctly (exit code: $exitCode)" "Success"
|
||||||
|
} catch {
|
||||||
|
Write-Status "WARNING: Could not test executable: $_" "Warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display file info
|
||||||
|
$exeInfo = Get-Item $exePath
|
||||||
|
$fileSizeMB = [math]::Round($exeInfo.Length / 1MB, 2)
|
||||||
|
Write-Status "Executable size: $fileSizeMB MB" "Info"
|
||||||
|
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status "✓ YAZE Windows build completed successfully!" "Success"
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "Build Configuration: $Configuration" "White"
|
||||||
|
Write-Status "Build Platform: $Platform" "White"
|
||||||
|
Write-Status "Executable: $exePath" "White"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "To run YAZE:" "Warning"
|
||||||
|
Write-Status " $exePath" "White"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "To build other configurations:" "Warning"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1 -Configuration Debug -Platform x64" "White"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1 -Configuration Release -Platform x86" "White"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1 -Configuration RelWithDebInfo -Platform ARM64" "White"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1 -Clean" "White"
|
||||||
|
Write-Status ""
|
||||||
64
scripts/create-macos-bundle.sh
Executable file
64
scripts/create-macos-bundle.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Create macOS bundle script
|
||||||
|
# Usage: create-macos-bundle.sh <version> <artifact_name>
|
||||||
|
|
||||||
|
VERSION_NUM="$1"
|
||||||
|
ARTIFACT_NAME="$2"
|
||||||
|
|
||||||
|
if [ -z "$VERSION_NUM" ] || [ -z "$ARTIFACT_NAME" ]; then
|
||||||
|
echo "Usage: $0 <version> <artifact_name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating macOS bundle for version: $VERSION_NUM"
|
||||||
|
|
||||||
|
# macOS packaging
|
||||||
|
if [ -d "build/bin/yaze.app" ]; then
|
||||||
|
echo "Found macOS bundle, using it directly"
|
||||||
|
cp -r build/bin/yaze.app ./Yaze.app
|
||||||
|
# Add additional resources to the bundle
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Update Info.plist with correct version
|
||||||
|
if [ -f "cmake/yaze.plist.in" ]; then
|
||||||
|
sed "s/@yaze_VERSION@/$VERSION_NUM/g" cmake/yaze.plist.in > "Yaze.app/Contents/Info.plist"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No bundle found, creating manual bundle"
|
||||||
|
mkdir -p "Yaze.app/Contents/MacOS"
|
||||||
|
mkdir -p "Yaze.app/Contents/Resources"
|
||||||
|
cp build/bin/yaze "Yaze.app/Contents/MacOS/"
|
||||||
|
cp -r assets "Yaze.app/Contents/Resources/" 2>/dev/null || echo "assets directory not found"
|
||||||
|
# Create Info.plist with correct version
|
||||||
|
cat > "Yaze.app/Contents/Info.plist" <<EOF
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>yaze</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.yaze.editor</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>Yaze</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$VERSION_NUM</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$VERSION_NUM</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create DMG
|
||||||
|
mkdir dmg_staging
|
||||||
|
cp -r Yaze.app dmg_staging/
|
||||||
|
cp LICENSE dmg_staging/ 2>/dev/null || echo "LICENSE not found"
|
||||||
|
cp README.md dmg_staging/ 2>/dev/null || echo "README.md not found"
|
||||||
|
cp -r docs dmg_staging/ 2>/dev/null || echo "docs directory not found"
|
||||||
|
hdiutil create -srcfolder dmg_staging -format UDZO -volname "Yaze v$VERSION_NUM" "$ARTIFACT_NAME.dmg"
|
||||||
|
|
||||||
|
echo "macOS bundle creation completed successfully!"
|
||||||
328
scripts/generate-vs-projects-simple.py
Normal file
328
scripts/generate-vs-projects-simple.py
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Simple Visual Studio project generator for YAZE
|
||||||
|
This script creates Visual Studio project files without complex CMake dependencies
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def generate_vcxproj():
|
||||||
|
"""Generate the YAZE.vcxproj file with all source files"""
|
||||||
|
|
||||||
|
# Source file lists (from CMake files)
|
||||||
|
app_core_src = [
|
||||||
|
"app/core/controller.cc",
|
||||||
|
"app/emu/emulator.cc",
|
||||||
|
"app/core/project.cc",
|
||||||
|
"app/core/window.cc",
|
||||||
|
"app/core/asar_wrapper.cc",
|
||||||
|
"app/core/platform/font_loader.cc",
|
||||||
|
"app/core/platform/clipboard.cc",
|
||||||
|
"app/core/platform/file_dialog.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_emu_src = [
|
||||||
|
"app/emu/audio/apu.cc",
|
||||||
|
"app/emu/audio/spc700.cc",
|
||||||
|
"app/emu/audio/dsp.cc",
|
||||||
|
"app/emu/audio/internal/addressing.cc",
|
||||||
|
"app/emu/audio/internal/instructions.cc",
|
||||||
|
"app/emu/cpu/internal/addressing.cc",
|
||||||
|
"app/emu/cpu/internal/instructions.cc",
|
||||||
|
"app/emu/cpu/cpu.cc",
|
||||||
|
"app/emu/video/ppu.cc",
|
||||||
|
"app/emu/memory/dma.cc",
|
||||||
|
"app/emu/memory/memory.cc",
|
||||||
|
"app/emu/snes.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_editor_src = [
|
||||||
|
"app/editor/editor_manager.cc",
|
||||||
|
"app/editor/dungeon/dungeon_editor.cc",
|
||||||
|
"app/editor/dungeon/dungeon_room_selector.cc",
|
||||||
|
"app/editor/dungeon/dungeon_canvas_viewer.cc",
|
||||||
|
"app/editor/dungeon/dungeon_object_selector.cc",
|
||||||
|
"app/editor/dungeon/dungeon_toolset.cc",
|
||||||
|
"app/editor/dungeon/dungeon_object_interaction.cc",
|
||||||
|
"app/editor/dungeon/dungeon_renderer.cc",
|
||||||
|
"app/editor/dungeon/dungeon_room_loader.cc",
|
||||||
|
"app/editor/dungeon/dungeon_usage_tracker.cc",
|
||||||
|
"app/editor/overworld/overworld_editor.cc",
|
||||||
|
"app/editor/overworld/overworld_editor_manager.cc",
|
||||||
|
"app/editor/sprite/sprite_editor.cc",
|
||||||
|
"app/editor/music/music_editor.cc",
|
||||||
|
"app/editor/message/message_editor.cc",
|
||||||
|
"app/editor/message/message_data.cc",
|
||||||
|
"app/editor/message/message_preview.cc",
|
||||||
|
"app/editor/code/assembly_editor.cc",
|
||||||
|
"app/editor/graphics/screen_editor.cc",
|
||||||
|
"app/editor/graphics/graphics_editor.cc",
|
||||||
|
"app/editor/graphics/palette_editor.cc",
|
||||||
|
"app/editor/overworld/tile16_editor.cc",
|
||||||
|
"app/editor/overworld/map_properties.cc",
|
||||||
|
"app/editor/graphics/gfx_group_editor.cc",
|
||||||
|
"app/editor/overworld/entity.cc",
|
||||||
|
"app/editor/system/settings_editor.cc",
|
||||||
|
"app/editor/system/command_manager.cc",
|
||||||
|
"app/editor/system/extension_manager.cc",
|
||||||
|
"app/editor/system/shortcut_manager.cc",
|
||||||
|
"app/editor/system/popup_manager.cc",
|
||||||
|
"app/test/test_manager.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_gfx_src = [
|
||||||
|
"app/gfx/arena.cc",
|
||||||
|
"app/gfx/background_buffer.cc",
|
||||||
|
"app/gfx/bitmap.cc",
|
||||||
|
"app/gfx/compression.cc",
|
||||||
|
"app/gfx/scad_format.cc",
|
||||||
|
"app/gfx/snes_palette.cc",
|
||||||
|
"app/gfx/snes_tile.cc",
|
||||||
|
"app/gfx/snes_color.cc",
|
||||||
|
"app/gfx/tilemap.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_zelda3_src = [
|
||||||
|
"app/zelda3/hyrule_magic.cc",
|
||||||
|
"app/zelda3/overworld/overworld_map.cc",
|
||||||
|
"app/zelda3/overworld/overworld.cc",
|
||||||
|
"app/zelda3/screen/inventory.cc",
|
||||||
|
"app/zelda3/screen/title_screen.cc",
|
||||||
|
"app/zelda3/screen/dungeon_map.cc",
|
||||||
|
"app/zelda3/sprite/sprite.cc",
|
||||||
|
"app/zelda3/sprite/sprite_builder.cc",
|
||||||
|
"app/zelda3/music/tracker.cc",
|
||||||
|
"app/zelda3/dungeon/room.cc",
|
||||||
|
"app/zelda3/dungeon/room_object.cc",
|
||||||
|
"app/zelda3/dungeon/object_parser.cc",
|
||||||
|
"app/zelda3/dungeon/object_renderer.cc",
|
||||||
|
"app/zelda3/dungeon/room_layout.cc",
|
||||||
|
"app/zelda3/dungeon/dungeon_editor_system.cc",
|
||||||
|
"app/zelda3/dungeon/dungeon_object_editor.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
gui_src = [
|
||||||
|
"app/gui/modules/asset_browser.cc",
|
||||||
|
"app/gui/modules/text_editor.cc",
|
||||||
|
"app/gui/canvas.cc",
|
||||||
|
"app/gui/canvas_utils.cc",
|
||||||
|
"app/gui/enhanced_palette_editor.cc",
|
||||||
|
"app/gui/input.cc",
|
||||||
|
"app/gui/style.cc",
|
||||||
|
"app/gui/color.cc",
|
||||||
|
"app/gui/zeml.cc",
|
||||||
|
"app/gui/theme_manager.cc",
|
||||||
|
"app/gui/background_renderer.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
util_src = [
|
||||||
|
"util/bps.cc",
|
||||||
|
"util/flag.cc",
|
||||||
|
"util/hex.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Combine all source files
|
||||||
|
all_source_files = (
|
||||||
|
["yaze.cc", "app/main.cc", "app/rom.cc"] +
|
||||||
|
app_core_src + app_emu_src + app_editor_src +
|
||||||
|
app_gfx_src + app_zelda3_src + gui_src + util_src
|
||||||
|
)
|
||||||
|
|
||||||
|
# Header files
|
||||||
|
header_files = [
|
||||||
|
"incl/yaze.h",
|
||||||
|
"incl/zelda.h",
|
||||||
|
"src/yaze_config.h.in"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Generate the .vcxproj file content
|
||||||
|
vcxproj_content = '''<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{B2C3D4E5-F6G7-8901-BCDE-F23456789012}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>YAZE</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
<ProjectName>YAZE</ProjectName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>$(ProjectDir)src;$(ProjectDir)incl;$(ProjectDir)src\\lib;$(ProjectDir)vcpkg\\installed\\x64-windows\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||||
|
<BigObj>true</BigObj>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)vcpkg\\installed\\x64-windows\\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>SDL2.lib;SDL2main.lib;libpng16.lib;zlib.lib;absl_base.lib;absl_strings.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>$(ProjectDir)src;$(ProjectDir)incl;$(ProjectDir)src\\lib;$(ProjectDir)vcpkg\\installed\\x64-windows\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||||
|
<BigObj>true</BigObj>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(ProjectDir)vcpkg\\installed\\x64-windows\\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>SDL2.lib;SDL2main.lib;libpng16.lib;zlib.lib;absl_base.lib;absl_strings.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
'''
|
||||||
|
|
||||||
|
for header in header_files:
|
||||||
|
vcxproj_content += f' <ClInclude Include="{header}" />\n'
|
||||||
|
|
||||||
|
vcxproj_content += ''' </ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
'''
|
||||||
|
|
||||||
|
for source in all_source_files:
|
||||||
|
vcxproj_content += f' <ClCompile Include="src\\{source}" />\n'
|
||||||
|
|
||||||
|
vcxproj_content += ''' </ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="CMakeLists.txt" />
|
||||||
|
<None Include="vcpkg.json" />
|
||||||
|
<None Include="README.md" />
|
||||||
|
<None Include="LICENSE" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>'''
|
||||||
|
|
||||||
|
return vcxproj_content
|
||||||
|
|
||||||
|
def generate_solution():
|
||||||
|
"""Generate the YAZE.sln file"""
|
||||||
|
return '''Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YAZE", "YAZE.vcxproj", "{B2C3D4E5-F6G7-8901-BCDE-F23456789012}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal'''
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to generate Visual Studio project files"""
|
||||||
|
print("Generating simple Visual Studio project files for YAZE...")
|
||||||
|
|
||||||
|
# Get the project root directory
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
project_root = script_dir.parent
|
||||||
|
|
||||||
|
# Generate .vcxproj file
|
||||||
|
vcxproj_content = generate_vcxproj()
|
||||||
|
vcxproj_path = project_root / "YAZE.vcxproj"
|
||||||
|
|
||||||
|
with open(vcxproj_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(vcxproj_content)
|
||||||
|
|
||||||
|
print(f"Generated: {vcxproj_path}")
|
||||||
|
|
||||||
|
# Generate .sln file
|
||||||
|
solution_content = generate_solution()
|
||||||
|
solution_path = project_root / "YAZE.sln"
|
||||||
|
|
||||||
|
with open(solution_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(solution_content)
|
||||||
|
|
||||||
|
print(f"Generated: {solution_path}")
|
||||||
|
|
||||||
|
print("Visual Studio project files generated successfully!")
|
||||||
|
print("")
|
||||||
|
print("IMPORTANT: Before building in Visual Studio:")
|
||||||
|
print("1. Make sure vcpkg is set up: .\\scripts\\setup-vcpkg-windows.ps1")
|
||||||
|
print("2. Install dependencies: .\\vcpkg\\vcpkg.exe install --triplet x64-windows")
|
||||||
|
print("3. Open YAZE.sln in Visual Studio 2022")
|
||||||
|
print("4. Build the solution (Ctrl+Shift+B)")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
200
scripts/generate-vs-projects.bat
Normal file
200
scripts/generate-vs-projects.bat
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
@echo off
|
||||||
|
REM Configure Visual Studio project files for YAZE
|
||||||
|
REM This script configures CMake build system to work with existing Visual Studio project files
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
REM Default values
|
||||||
|
set CONFIGURATION=Debug
|
||||||
|
set ARCHITECTURE=x64
|
||||||
|
set CLEAN=false
|
||||||
|
|
||||||
|
REM Parse command line arguments
|
||||||
|
:parse_args
|
||||||
|
if "%~1"=="" goto :args_done
|
||||||
|
if "%~1"=="--clean" set CLEAN=true
|
||||||
|
if "%~1"=="--release" set CONFIGURATION=Release
|
||||||
|
if "%~1"=="--x86" set ARCHITECTURE=Win32
|
||||||
|
if "%~1"=="--x64" set ARCHITECTURE=x64
|
||||||
|
if "%~1"=="--arm64" set ARCHITECTURE=ARM64
|
||||||
|
shift
|
||||||
|
goto :parse_args
|
||||||
|
|
||||||
|
:args_done
|
||||||
|
|
||||||
|
REM Validate architecture
|
||||||
|
if not "%ARCHITECTURE%"=="x64" if not "%ARCHITECTURE%"=="Win32" if not "%ARCHITECTURE%"=="ARM64" (
|
||||||
|
echo Invalid architecture: %ARCHITECTURE%
|
||||||
|
echo Valid architectures: x64, Win32, ARM64
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Generating Visual Studio project files for YAZE...
|
||||||
|
|
||||||
|
REM Check if we're on Windows
|
||||||
|
if not "%OS%"=="Windows_NT" (
|
||||||
|
echo This script is designed for Windows. Use CMake presets on other platforms.
|
||||||
|
echo Available presets:
|
||||||
|
echo - windows-debug
|
||||||
|
echo - windows-release
|
||||||
|
echo - windows-dev
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check if CMake is available
|
||||||
|
where cmake >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
REM Try common CMake installation paths
|
||||||
|
if exist "C:\Program Files\CMake\bin\cmake.exe" (
|
||||||
|
echo Found CMake at: C:\Program Files\CMake\bin\cmake.exe
|
||||||
|
set "PATH=%PATH%;C:\Program Files\CMake\bin"
|
||||||
|
goto :cmake_found
|
||||||
|
)
|
||||||
|
if exist "C:\Program Files (x86)\CMake\bin\cmake.exe" (
|
||||||
|
echo Found CMake at: C:\Program Files (x86)\CMake\bin\cmake.exe
|
||||||
|
set "PATH=%PATH%;C:\Program Files (x86)\CMake\bin"
|
||||||
|
goto :cmake_found
|
||||||
|
)
|
||||||
|
if exist "C:\cmake\bin\cmake.exe" (
|
||||||
|
echo Found CMake at: C:\cmake\bin\cmake.exe
|
||||||
|
set "PATH=%PATH%;C:\cmake\bin"
|
||||||
|
goto :cmake_found
|
||||||
|
)
|
||||||
|
|
||||||
|
REM If we get here, CMake is not found
|
||||||
|
echo CMake not found in PATH. Attempting to install...
|
||||||
|
|
||||||
|
REM Try to install CMake via Chocolatey
|
||||||
|
where choco >nul 2>&1
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo Installing CMake via Chocolatey...
|
||||||
|
choco install -y cmake
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to install CMake via Chocolatey
|
||||||
|
) else (
|
||||||
|
echo CMake installed successfully
|
||||||
|
REM Refresh PATH
|
||||||
|
call refreshenv
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo Chocolatey not found. Please install CMake manually:
|
||||||
|
echo 1. Download from: https://cmake.org/download/
|
||||||
|
echo 2. Or install Chocolatey first: https://chocolatey.org/install
|
||||||
|
echo 3. Then run: choco install cmake
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check again after installation
|
||||||
|
where cmake >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo CMake still not found after installation. Please restart your terminal or add CMake to PATH manually.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
:cmake_found
|
||||||
|
echo CMake found and ready to use
|
||||||
|
|
||||||
|
REM Set up paths
|
||||||
|
set SOURCE_DIR=%~dp0..
|
||||||
|
set BUILD_DIR=%SOURCE_DIR%\build-vs
|
||||||
|
|
||||||
|
echo Source directory: %SOURCE_DIR%
|
||||||
|
echo Build directory: %BUILD_DIR%
|
||||||
|
|
||||||
|
REM Clean build directory if requested
|
||||||
|
if "%CLEAN%"=="true" (
|
||||||
|
if exist "%BUILD_DIR%" (
|
||||||
|
echo Cleaning build directory...
|
||||||
|
rmdir /s /q "%BUILD_DIR%"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Create build directory
|
||||||
|
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
|
||||||
|
|
||||||
|
REM Check if vcpkg is available
|
||||||
|
set VCPKG_PATH=%SOURCE_DIR%\vcpkg\scripts\buildsystems\vcpkg.cmake
|
||||||
|
if exist "%VCPKG_PATH%" (
|
||||||
|
echo Using vcpkg toolchain: %VCPKG_PATH%
|
||||||
|
set USE_VCPKG=true
|
||||||
|
) else (
|
||||||
|
echo vcpkg not found, using system libraries
|
||||||
|
set USE_VCPKG=false
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Build CMake command
|
||||||
|
set CMAKE_ARGS=-B "%BUILD_DIR%" -G "Visual Studio 17 2022" -A %ARCHITECTURE% -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_POLICY_VERSION_MAXIMUM=3.28 -DCMAKE_WARN_DEPRECATED=OFF -DABSL_PROPAGATE_CXX_STD=ON -DTHREADS_PREFER_PTHREAD_FLAG=OFF -DYAZE_BUILD_TESTS=ON -DYAZE_BUILD_APP=ON -DYAZE_BUILD_LIB=ON -DYAZE_BUILD_EMU=ON -DYAZE_BUILD_Z3ED=ON -DYAZE_ENABLE_ROM_TESTS=OFF -DYAZE_ENABLE_EXPERIMENTAL_TESTS=ON -DYAZE_ENABLE_UI_TESTS=ON -DYAZE_INSTALL_LIB=OFF
|
||||||
|
|
||||||
|
if "%USE_VCPKG%"=="true" (
|
||||||
|
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_TOOLCHAIN_FILE="%VCPKG_PATH%" -DVCPKG_TARGET_TRIPLET=%ARCHITECTURE%-windows -DVCPKG_MANIFEST_MODE=ON
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Run CMake configuration
|
||||||
|
echo Configuring CMake...
|
||||||
|
echo Command: cmake %CMAKE_ARGS% "%SOURCE_DIR%"
|
||||||
|
|
||||||
|
cmake %CMAKE_ARGS% "%SOURCE_DIR%"
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo CMake configuration failed!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check if the existing solution file is present and valid
|
||||||
|
set EXISTING_SOLUTION_FILE=%SOURCE_DIR%\YAZE.sln
|
||||||
|
if exist "%EXISTING_SOLUTION_FILE%" (
|
||||||
|
echo ✅ Using existing Visual Studio solution: %EXISTING_SOLUTION_FILE%
|
||||||
|
|
||||||
|
REM Verify the solution file references the project file
|
||||||
|
findstr /C:"YAZE.vcxproj" "%EXISTING_SOLUTION_FILE%" >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo ✅ Solution file references YAZE.vcxproj correctly
|
||||||
|
|
||||||
|
REM Check if project configurations are set up
|
||||||
|
findstr /C:"ProjectConfigurationPlatforms" "%EXISTING_SOLUTION_FILE%" >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo ✅ Project configurations are properly set up
|
||||||
|
) else (
|
||||||
|
echo ⚠️ Warning: Project configurations may not be set up
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo ❌ Solution file does not reference YAZE.vcxproj
|
||||||
|
echo Please ensure the solution file includes the YAZE project
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Try to open solution in Visual Studio
|
||||||
|
where devenv >nul 2>&1
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo Opening solution in Visual Studio...
|
||||||
|
start "" devenv "%EXISTING_SOLUTION_FILE%"
|
||||||
|
) else (
|
||||||
|
echo Visual Studio solution ready: %EXISTING_SOLUTION_FILE%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo ❌ Existing solution file not found: %EXISTING_SOLUTION_FILE%
|
||||||
|
echo Please ensure YAZE.sln exists in the project root
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo 🎉 Visual Studio project configuration complete!
|
||||||
|
echo.
|
||||||
|
echo Next steps:
|
||||||
|
echo 1. Open YAZE.sln in Visual Studio
|
||||||
|
echo 2. Select configuration: %CONFIGURATION%
|
||||||
|
echo 3. Select platform: %ARCHITECTURE%
|
||||||
|
echo 4. Build the solution (Ctrl+Shift+B)
|
||||||
|
echo.
|
||||||
|
echo Available configurations:
|
||||||
|
echo - Debug (with debugging symbols)
|
||||||
|
echo - Release (optimized)
|
||||||
|
echo - RelWithDebInfo (optimized with debug info)
|
||||||
|
echo - MinSizeRel (minimum size)
|
||||||
|
echo.
|
||||||
|
echo Available architectures:
|
||||||
|
echo - x64 (64-bit Intel/AMD)
|
||||||
|
echo - x86 (32-bit Intel/AMD)
|
||||||
|
echo - ARM64 (64-bit ARM)
|
||||||
|
|
||||||
|
pause
|
||||||
251
scripts/generate-vs-projects.ps1
Normal file
251
scripts/generate-vs-projects.ps1
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
# Configure Visual Studio project files for YAZE
|
||||||
|
# This script configures CMake build system to work with existing Visual Studio project files
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Configuration = "Debug",
|
||||||
|
[string]$Architecture = "x64",
|
||||||
|
[switch]$Clean = $false,
|
||||||
|
[switch]$UseVcpkg = $false,
|
||||||
|
[switch]$Help = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show help if requested
|
||||||
|
if ($Help) {
|
||||||
|
Write-Host "Usage: .\generate-vs-projects.ps1 [options]"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Options:"
|
||||||
|
Write-Host " -Configuration <config> Build configuration (Debug, Release, RelWithDebInfo, MinSizeRel)"
|
||||||
|
Write-Host " -Architecture <arch> Target architecture (x64, x86, ARM64)"
|
||||||
|
Write-Host " -Clean Clean build directory before configuring"
|
||||||
|
Write-Host " -UseVcpkg Use vcpkg for dependency management"
|
||||||
|
Write-Host " -Help Show this help message"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Examples:"
|
||||||
|
Write-Host " .\generate-vs-projects.ps1 # Default: Debug x64"
|
||||||
|
Write-Host " .\generate-vs-projects.ps1 -Configuration Release -Architecture x86"
|
||||||
|
Write-Host " .\generate-vs-projects.ps1 -Clean -UseVcpkg"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Note: This script configures CMake to work with existing YAZE.sln and YAZE.vcxproj files"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Validate architecture parameter
|
||||||
|
$ValidArchitectures = @("x64", "x86", "ARM64")
|
||||||
|
if ($Architecture -notin $ValidArchitectures) {
|
||||||
|
Write-Host "Invalid architecture: $Architecture" -ForegroundColor Red
|
||||||
|
Write-Host "Valid architectures: $($ValidArchitectures -join ', ')" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Generating Visual Studio project files for YAZE..." -ForegroundColor Green
|
||||||
|
|
||||||
|
# Check if we're on Windows
|
||||||
|
if ($env:OS -ne "Windows_NT") {
|
||||||
|
Write-Host "This script is designed for Windows. Use CMake presets on other platforms." -ForegroundColor Yellow
|
||||||
|
Write-Host "Available presets:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - windows-debug" -ForegroundColor Gray
|
||||||
|
Write-Host " - windows-release" -ForegroundColor Gray
|
||||||
|
Write-Host " - windows-dev" -ForegroundColor Gray
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if CMake is available
|
||||||
|
$cmakePath = Get-Command cmake -ErrorAction SilentlyContinue
|
||||||
|
if (-not $cmakePath) {
|
||||||
|
# Try common CMake installation paths
|
||||||
|
$commonPaths = @(
|
||||||
|
"C:\Program Files\CMake\bin\cmake.exe",
|
||||||
|
"C:\Program Files (x86)\CMake\bin\cmake.exe",
|
||||||
|
"C:\cmake\bin\cmake.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($path in $commonPaths) {
|
||||||
|
if (Test-Path $path) {
|
||||||
|
Write-Host "Found CMake at: $path" -ForegroundColor Green
|
||||||
|
$env:Path += ";$(Split-Path $path)"
|
||||||
|
$cmakePath = Get-Command cmake -ErrorAction SilentlyContinue
|
||||||
|
if ($cmakePath) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $cmakePath) {
|
||||||
|
Write-Host "CMake not found in PATH. Attempting to install..." -ForegroundColor Yellow
|
||||||
|
|
||||||
|
# Try to install CMake via Chocolatey
|
||||||
|
if (Get-Command choco -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "Installing CMake via Chocolatey..." -ForegroundColor Yellow
|
||||||
|
choco install -y cmake
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "CMake installed successfully" -ForegroundColor Green
|
||||||
|
# Refresh PATH
|
||||||
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||||
|
} else {
|
||||||
|
Write-Host "Failed to install CMake via Chocolatey" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "Chocolatey not found. Please install CMake manually:" -ForegroundColor Red
|
||||||
|
Write-Host "1. Download from: https://cmake.org/download/" -ForegroundColor Yellow
|
||||||
|
Write-Host "2. Or install Chocolatey first: https://chocolatey.org/install" -ForegroundColor Yellow
|
||||||
|
Write-Host "3. Then run: choco install cmake" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check again after installation
|
||||||
|
$cmakePath = Get-Command cmake -ErrorAction SilentlyContinue
|
||||||
|
if (-not $cmakePath) {
|
||||||
|
Write-Host "CMake still not found after installation. Please restart your terminal or add CMake to PATH manually." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "CMake found: $($cmakePath.Source)" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Check if Visual Studio is available
|
||||||
|
$vsWhere = Get-Command "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue
|
||||||
|
if (-not $vsWhere) {
|
||||||
|
$vsWhere = Get-Command vswhere -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($vsWhere) {
|
||||||
|
$vsInstallPath = & $vsWhere.Source -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
if ($vsInstallPath) {
|
||||||
|
Write-Host "Visual Studio found at: $vsInstallPath" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "Visual Studio 2022 not found. Please install Visual Studio 2022 with C++ workload." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "vswhere not found. Assuming Visual Studio is available." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up paths
|
||||||
|
$SourceDir = Split-Path -Parent $PSScriptRoot
|
||||||
|
$BuildDir = Join-Path $SourceDir "build-vs"
|
||||||
|
|
||||||
|
Write-Host "Source directory: $SourceDir" -ForegroundColor Cyan
|
||||||
|
Write-Host "Build directory: $BuildDir" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Clean build directory if requested
|
||||||
|
if ($Clean -and (Test-Path $BuildDir)) {
|
||||||
|
Write-Host "Cleaning build directory..." -ForegroundColor Yellow
|
||||||
|
Remove-Item -Recurse -Force $BuildDir
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create build directory
|
||||||
|
if (-not (Test-Path $BuildDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $BuildDir | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if vcpkg is available
|
||||||
|
$VcpkgPath = Join-Path $SourceDir "vcpkg\scripts\buildsystems\vcpkg.cmake"
|
||||||
|
$UseVcpkg = Test-Path $VcpkgPath
|
||||||
|
|
||||||
|
if ($UseVcpkg) {
|
||||||
|
Write-Host "Using vcpkg toolchain: $VcpkgPath" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "vcpkg not found, using system libraries" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine generator and architecture
|
||||||
|
$Generator = "Visual Studio 17 2022"
|
||||||
|
$ArchFlag = if ($Architecture -eq "x64") { "-A x64" } else { "-A Win32" }
|
||||||
|
|
||||||
|
# Build CMake command
|
||||||
|
$CmakeArgs = @(
|
||||||
|
"-B", $BuildDir,
|
||||||
|
"-G", "`"$Generator`"",
|
||||||
|
$ArchFlag,
|
||||||
|
"-DCMAKE_BUILD_TYPE=$Configuration",
|
||||||
|
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
|
"-DCMAKE_POLICY_VERSION_MAXIMUM=3.28",
|
||||||
|
"-DCMAKE_WARN_DEPRECATED=OFF",
|
||||||
|
"-DABSL_PROPAGATE_CXX_STD=ON",
|
||||||
|
"-DTHREADS_PREFER_PTHREAD_FLAG=OFF",
|
||||||
|
"-DYAZE_BUILD_TESTS=ON",
|
||||||
|
"-DYAZE_BUILD_APP=ON",
|
||||||
|
"-DYAZE_BUILD_LIB=ON",
|
||||||
|
"-DYAZE_BUILD_EMU=ON",
|
||||||
|
"-DYAZE_BUILD_Z3ED=ON",
|
||||||
|
"-DYAZE_ENABLE_ROM_TESTS=OFF",
|
||||||
|
"-DYAZE_ENABLE_EXPERIMENTAL_TESTS=ON",
|
||||||
|
"-DYAZE_ENABLE_UI_TESTS=ON",
|
||||||
|
"-DYAZE_INSTALL_LIB=OFF"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($UseVcpkg) {
|
||||||
|
$CmakeArgs += @(
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=`"$VcpkgPath`"",
|
||||||
|
"-DVCPKG_TARGET_TRIPLET=$Architecture-windows",
|
||||||
|
"-DVCPKG_MANIFEST_MODE=ON"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure CMake to generate build files (but don't overwrite existing project files)
|
||||||
|
Write-Host "Configuring CMake for build system..." -ForegroundColor Yellow
|
||||||
|
Write-Host "Command: cmake $($CmakeArgs -join ' ')" -ForegroundColor Gray
|
||||||
|
|
||||||
|
& cmake @CmakeArgs $SourceDir
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "CMake configuration failed!" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the existing solution file is present and valid
|
||||||
|
$ExistingSolutionFile = Join-Path $SourceDir "YAZE.sln"
|
||||||
|
if (Test-Path $ExistingSolutionFile) {
|
||||||
|
Write-Host "✅ Using existing Visual Studio solution: $ExistingSolutionFile" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Verify the solution file is properly structured
|
||||||
|
$SolutionContent = Get-Content $ExistingSolutionFile -Raw
|
||||||
|
if ($SolutionContent -match "YAZE\.vcxproj") {
|
||||||
|
Write-Host "✅ Solution file references YAZE.vcxproj correctly" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Check if project configurations are set up
|
||||||
|
if ($SolutionContent -match "ProjectConfigurationPlatforms") {
|
||||||
|
Write-Host "✅ Project configurations are properly set up" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "⚠️ Warning: Project configurations may not be set up" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "❌ Solution file does not reference YAZE.vcxproj" -ForegroundColor Red
|
||||||
|
Write-Host "Please ensure the solution file includes the YAZE project" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open solution in Visual Studio if available
|
||||||
|
if (Get-Command "devenv" -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "Opening solution in Visual Studio..." -ForegroundColor Yellow
|
||||||
|
& devenv $ExistingSolutionFile
|
||||||
|
} elseif (Get-Command "code" -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "Opening solution in VS Code..." -ForegroundColor Yellow
|
||||||
|
& code $ExistingSolutionFile
|
||||||
|
} else {
|
||||||
|
Write-Host "Visual Studio solution ready: $ExistingSolutionFile" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "❌ Existing solution file not found: $ExistingSolutionFile" -ForegroundColor Red
|
||||||
|
Write-Host "Please ensure YAZE.sln exists in the project root" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "🎉 Visual Studio project configuration complete!" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Next steps:" -ForegroundColor Cyan
|
||||||
|
Write-Host "1. Open YAZE.sln in Visual Studio" -ForegroundColor White
|
||||||
|
Write-Host "2. Select configuration: $Configuration" -ForegroundColor White
|
||||||
|
Write-Host "3. Select platform: $Architecture" -ForegroundColor White
|
||||||
|
Write-Host "4. Build the solution (Ctrl+Shift+B)" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Available configurations:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Debug (with debugging symbols)" -ForegroundColor Gray
|
||||||
|
Write-Host " - Release (optimized)" -ForegroundColor Gray
|
||||||
|
Write-Host " - RelWithDebInfo (optimized with debug info)" -ForegroundColor Gray
|
||||||
|
Write-Host " - MinSizeRel (minimum size)" -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Available architectures:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - x64 (64-bit Intel/AMD)" -ForegroundColor Gray
|
||||||
|
Write-Host " - x86 (32-bit Intel/AMD)" -ForegroundColor Gray
|
||||||
|
Write-Host " - ARM64 (64-bit ARM)" -ForegroundColor Gray
|
||||||
543
scripts/generate-vs-projects.py
Normal file
543
scripts/generate-vs-projects.py
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Python script to generate proper Visual Studio project files for YAZE
|
||||||
|
This script creates a comprehensive .vcxproj file with all necessary source files
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def generate_vcxproj():
|
||||||
|
"""Generate the YAZE.vcxproj file with all source files"""
|
||||||
|
|
||||||
|
# Source file lists (from CMake files)
|
||||||
|
app_core_src = [
|
||||||
|
"app/core/controller.cc",
|
||||||
|
"app/emu/emulator.cc",
|
||||||
|
"app/core/project.cc",
|
||||||
|
"app/core/window.cc",
|
||||||
|
"app/core/asar_wrapper.cc",
|
||||||
|
"app/core/platform/font_loader.cc",
|
||||||
|
"app/core/platform/clipboard.cc",
|
||||||
|
"app/core/platform/file_dialog.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_emu_src = [
|
||||||
|
"app/emu/audio/apu.cc",
|
||||||
|
"app/emu/audio/spc700.cc",
|
||||||
|
"app/emu/audio/dsp.cc",
|
||||||
|
"app/emu/audio/internal/addressing.cc",
|
||||||
|
"app/emu/audio/internal/instructions.cc",
|
||||||
|
"app/emu/cpu/internal/addressing.cc",
|
||||||
|
"app/emu/cpu/internal/instructions.cc",
|
||||||
|
"app/emu/cpu/cpu.cc",
|
||||||
|
"app/emu/video/ppu.cc",
|
||||||
|
"app/emu/memory/dma.cc",
|
||||||
|
"app/emu/memory/memory.cc",
|
||||||
|
"app/emu/snes.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_editor_src = [
|
||||||
|
"app/editor/editor_manager.cc",
|
||||||
|
"app/editor/dungeon/dungeon_editor.cc",
|
||||||
|
"app/editor/dungeon/dungeon_room_selector.cc",
|
||||||
|
"app/editor/dungeon/dungeon_canvas_viewer.cc",
|
||||||
|
"app/editor/dungeon/dungeon_object_selector.cc",
|
||||||
|
"app/editor/dungeon/dungeon_toolset.cc",
|
||||||
|
"app/editor/dungeon/dungeon_object_interaction.cc",
|
||||||
|
"app/editor/dungeon/dungeon_renderer.cc",
|
||||||
|
"app/editor/dungeon/dungeon_room_loader.cc",
|
||||||
|
"app/editor/dungeon/dungeon_usage_tracker.cc",
|
||||||
|
"app/editor/overworld/overworld_editor.cc",
|
||||||
|
"app/editor/overworld/overworld_editor_manager.cc",
|
||||||
|
"app/editor/sprite/sprite_editor.cc",
|
||||||
|
"app/editor/music/music_editor.cc",
|
||||||
|
"app/editor/message/message_editor.cc",
|
||||||
|
"app/editor/message/message_data.cc",
|
||||||
|
"app/editor/message/message_preview.cc",
|
||||||
|
"app/editor/code/assembly_editor.cc",
|
||||||
|
"app/editor/graphics/screen_editor.cc",
|
||||||
|
"app/editor/graphics/graphics_editor.cc",
|
||||||
|
"app/editor/graphics/palette_editor.cc",
|
||||||
|
"app/editor/overworld/tile16_editor.cc",
|
||||||
|
"app/editor/overworld/map_properties.cc",
|
||||||
|
"app/editor/graphics/gfx_group_editor.cc",
|
||||||
|
"app/editor/overworld/entity.cc",
|
||||||
|
"app/editor/system/settings_editor.cc",
|
||||||
|
"app/editor/system/command_manager.cc",
|
||||||
|
"app/editor/system/extension_manager.cc",
|
||||||
|
"app/editor/system/shortcut_manager.cc",
|
||||||
|
"app/editor/system/popup_manager.cc",
|
||||||
|
"app/test/test_manager.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_gfx_src = [
|
||||||
|
"app/gfx/arena.cc",
|
||||||
|
"app/gfx/background_buffer.cc",
|
||||||
|
"app/gfx/bitmap.cc",
|
||||||
|
"app/gfx/compression.cc",
|
||||||
|
"app/gfx/scad_format.cc",
|
||||||
|
"app/gfx/snes_palette.cc",
|
||||||
|
"app/gfx/snes_tile.cc",
|
||||||
|
"app/gfx/snes_color.cc",
|
||||||
|
"app/gfx/tilemap.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
app_zelda3_src = [
|
||||||
|
"app/zelda3/hyrule_magic.cc",
|
||||||
|
"app/zelda3/overworld/overworld_map.cc",
|
||||||
|
"app/zelda3/overworld/overworld.cc",
|
||||||
|
"app/zelda3/screen/inventory.cc",
|
||||||
|
"app/zelda3/screen/title_screen.cc",
|
||||||
|
"app/zelda3/screen/dungeon_map.cc",
|
||||||
|
"app/zelda3/sprite/sprite.cc",
|
||||||
|
"app/zelda3/sprite/sprite_builder.cc",
|
||||||
|
"app/zelda3/music/tracker.cc",
|
||||||
|
"app/zelda3/dungeon/room.cc",
|
||||||
|
"app/zelda3/dungeon/room_object.cc",
|
||||||
|
"app/zelda3/dungeon/object_parser.cc",
|
||||||
|
"app/zelda3/dungeon/object_renderer.cc",
|
||||||
|
"app/zelda3/dungeon/room_layout.cc",
|
||||||
|
"app/zelda3/dungeon/dungeon_editor_system.cc",
|
||||||
|
"app/zelda3/dungeon/dungeon_object_editor.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
gui_src = [
|
||||||
|
"app/gui/modules/asset_browser.cc",
|
||||||
|
"app/gui/modules/text_editor.cc",
|
||||||
|
"app/gui/canvas.cc",
|
||||||
|
"app/gui/canvas_utils.cc",
|
||||||
|
"app/gui/enhanced_palette_editor.cc",
|
||||||
|
"app/gui/input.cc",
|
||||||
|
"app/gui/style.cc",
|
||||||
|
"app/gui/color.cc",
|
||||||
|
"app/gui/zeml.cc",
|
||||||
|
"app/gui/theme_manager.cc",
|
||||||
|
"app/gui/background_renderer.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
util_src = [
|
||||||
|
"util/bps.cc",
|
||||||
|
"util/flag.cc",
|
||||||
|
"util/hex.cc"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Combine all source files
|
||||||
|
all_source_files = (
|
||||||
|
["yaze.cc", "app/main.cc", "app/rom.cc"] +
|
||||||
|
app_core_src + app_emu_src + app_editor_src +
|
||||||
|
app_gfx_src + app_zelda3_src + gui_src + util_src
|
||||||
|
)
|
||||||
|
|
||||||
|
# Header files
|
||||||
|
header_files = [
|
||||||
|
"incl/yaze.h",
|
||||||
|
"incl/zelda.h",
|
||||||
|
"src/yaze_config.h.in"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Generate the .vcxproj file content
|
||||||
|
vcxproj_content = '''<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x86">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x86</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|ARM64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x86">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x86</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="RelWithDebInfo|x64">
|
||||||
|
<Configuration>RelWithDebInfo</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="RelWithDebInfo|x86">
|
||||||
|
<Configuration>RelWithDebInfo</Configuration>
|
||||||
|
<Platform>x86</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="RelWithDebInfo|ARM64">
|
||||||
|
<Configuration>RelWithDebInfo</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="MinSizeRel|x64">
|
||||||
|
<Configuration>MinSizeRel</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="MinSizeRel|x86">
|
||||||
|
<Configuration>MinSizeRel</Configuration>
|
||||||
|
<Platform>x86</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="MinSizeRel|ARM64">
|
||||||
|
<Configuration>MinSizeRel</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{B2C3D4E5-F6G7-8901-BCDE-F23456789012}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>YAZE</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
<ProjectName>YAZE</ProjectName>
|
||||||
|
<VcpkgEnabled>true</VcpkgEnabled>
|
||||||
|
<VcpkgManifestInstall>true</VcpkgManifestInstall>
|
||||||
|
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows</VcpkgTriplet>
|
||||||
|
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows</VcpkgTriplet>
|
||||||
|
<VcpkgTriplet Condition="'$(Platform)'=='ARM64'">arm64-windows</VcpkgTriplet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x86'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|ARM64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x86'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|ARM64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x86'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|ARM64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x86'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|ARM64'">
|
||||||
|
<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x86'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|ARM64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x86'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|ARM64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)build\\bin\\$(Configuration)\\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\\obj\\$(Configuration)\\</IntDir>
|
||||||
|
</PropertyGroup>'''
|
||||||
|
|
||||||
|
# Add compiler and linker settings for all configurations
|
||||||
|
configurations = ["Debug", "Release", "RelWithDebInfo", "MinSizeRel"]
|
||||||
|
platforms = ["x64", "x86", "ARM64"]
|
||||||
|
|
||||||
|
for config in configurations:
|
||||||
|
for platform in platforms:
|
||||||
|
is_debug = (config == "Debug")
|
||||||
|
debug_flags = "_DEBUG;_CONSOLE;%(PreprocessorDefinitions)" if is_debug else "NDEBUG;_CONSOLE;%(PreprocessorDefinitions)"
|
||||||
|
link_incremental = "true" if is_debug else "false"
|
||||||
|
generate_debug_info = "false" if config == "MinSizeRel" else "true"
|
||||||
|
|
||||||
|
vcxproj_content += f'''
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='{config}|{platform}'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>{debug_flags}</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>$(ProjectDir)src;$(ProjectDir)incl;$(ProjectDir)src\\lib;$(ProjectDir)src\\lib\\asar\\src;$(ProjectDir)src\\lib\\asar\\src\\asar;$(ProjectDir)src\\lib\\asar\\src\\asar-dll-bindings\\c;$(ProjectDir)src\\lib\\imgui;$(ProjectDir)src\\lib\\imgui_test_engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||||
|
<BigObj>true</BigObj>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<RuntimeLibrary>MultiThreaded{"Debug" if is_debug else ""}DLL</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>{generate_debug_info}</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>{"false" if is_debug else "true"}</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>{"false" if is_debug else "true"}</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>'''
|
||||||
|
|
||||||
|
# Add source files
|
||||||
|
vcxproj_content += '''
|
||||||
|
<ItemGroup>
|
||||||
|
'''
|
||||||
|
for header in header_files:
|
||||||
|
vcxproj_content += f' <ClInclude Include="{header}" />\n'
|
||||||
|
|
||||||
|
vcxproj_content += ''' </ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
'''
|
||||||
|
for source in all_source_files:
|
||||||
|
vcxproj_content += f' <ClCompile Include="src\\{source}" />\n'
|
||||||
|
|
||||||
|
vcxproj_content += ''' </ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="CMakeLists.txt" />
|
||||||
|
<None Include="CMakePresets.json" />
|
||||||
|
<None Include="vcpkg.json" />
|
||||||
|
<None Include="README.md" />
|
||||||
|
<None Include="LICENSE" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>'''
|
||||||
|
|
||||||
|
return vcxproj_content
|
||||||
|
|
||||||
|
def generate_solution():
|
||||||
|
"""Generate the YAZE.sln file"""
|
||||||
|
return '''Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YAZE", "YAZE.vcxproj", "{B2C3D4E5-F6G7-8901-BCDE-F23456789012}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Debug|ARM64 = Debug|ARM64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
Release|ARM64 = Release|ARM64
|
||||||
|
RelWithDebInfo|x64 = RelWithDebInfo|x64
|
||||||
|
RelWithDebInfo|x86 = RelWithDebInfo|x86
|
||||||
|
RelWithDebInfo|ARM64 = RelWithDebInfo|ARM64
|
||||||
|
MinSizeRel|x64 = MinSizeRel|x64
|
||||||
|
MinSizeRel|x86 = MinSizeRel|x86
|
||||||
|
MinSizeRel|ARM64 = MinSizeRel|ARM64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x64.Build.0 = Release|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|x86.Build.0 = Release|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|x86.Build.0 = RelWithDebInfo|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|x86.Build.0 = MinSizeRel|x86
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|ARM64.ActiveCfg = MinSizeRel|ARM64
|
||||||
|
{B2C3D4E5-F6G7-8901-BCDE-F23456789012}.MinSizeRel|ARM64.Build.0 = MinSizeRel|ARM64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal'''
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to generate Visual Studio project files"""
|
||||||
|
print("Generating Visual Studio project files for YAZE...")
|
||||||
|
|
||||||
|
# Get the project root directory
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
project_root = script_dir.parent
|
||||||
|
|
||||||
|
# Generate .vcxproj file
|
||||||
|
vcxproj_content = generate_vcxproj()
|
||||||
|
vcxproj_path = project_root / "YAZE.vcxproj"
|
||||||
|
|
||||||
|
with open(vcxproj_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(vcxproj_content)
|
||||||
|
|
||||||
|
print(f"Generated: {vcxproj_path}")
|
||||||
|
|
||||||
|
# Generate .sln file
|
||||||
|
solution_content = generate_solution()
|
||||||
|
solution_path = project_root / "YAZE.sln"
|
||||||
|
|
||||||
|
with open(solution_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(solution_content)
|
||||||
|
|
||||||
|
print(f"Generated: {solution_path}")
|
||||||
|
|
||||||
|
print("Visual Studio project files generated successfully!")
|
||||||
|
print("")
|
||||||
|
print("To build:")
|
||||||
|
print("1. Open YAZE.sln in Visual Studio 2022")
|
||||||
|
print("2. Ensure vcpkg is installed and configured")
|
||||||
|
print("3. Select your desired configuration (Debug/Release) and platform (x64/x86/ARM64)")
|
||||||
|
print("4. Build the solution (Ctrl+Shift+B)")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
81
scripts/setup-vcpkg-windows.bat
Normal file
81
scripts/setup-vcpkg-windows.bat
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
@echo off
|
||||||
|
REM YAZE vcpkg Setup Script (Batch Version)
|
||||||
|
REM This script sets up vcpkg for YAZE development on Windows
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo YAZE vcpkg Setup Script
|
||||||
|
echo ========================================
|
||||||
|
|
||||||
|
REM Check if we're in the right directory
|
||||||
|
if not exist "vcpkg.json" (
|
||||||
|
echo ERROR: vcpkg.json not found. Please run this script from the project root directory.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ vcpkg.json found
|
||||||
|
|
||||||
|
REM Check for Git
|
||||||
|
where git >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Git not found. Please install Git for Windows.
|
||||||
|
echo Download from: https://git-scm.com/download/win
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ✓ Git found
|
||||||
|
|
||||||
|
REM Clone vcpkg if needed
|
||||||
|
if not exist "vcpkg" (
|
||||||
|
echo Cloning vcpkg...
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git vcpkg
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Failed to clone vcpkg
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo ✓ vcpkg cloned successfully
|
||||||
|
) else (
|
||||||
|
echo ✓ vcpkg directory already exists
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Bootstrap vcpkg
|
||||||
|
if not exist "vcpkg\vcpkg.exe" (
|
||||||
|
echo Bootstrapping vcpkg...
|
||||||
|
cd vcpkg
|
||||||
|
call bootstrap-vcpkg.bat
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: Failed to bootstrap vcpkg
|
||||||
|
cd ..
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
cd ..
|
||||||
|
echo ✓ vcpkg bootstrapped successfully
|
||||||
|
) else (
|
||||||
|
echo ✓ vcpkg already bootstrapped
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Install dependencies
|
||||||
|
echo Installing dependencies...
|
||||||
|
vcpkg\vcpkg.exe install --triplet x64-windows
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo WARNING: Some dependencies may not have installed correctly
|
||||||
|
) else (
|
||||||
|
echo ✓ Dependencies installed successfully
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo ✓ vcpkg setup complete!
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo You can now build YAZE using:
|
||||||
|
echo .\scripts\build-windows.ps1
|
||||||
|
echo or
|
||||||
|
echo .\scripts\build-windows.bat
|
||||||
|
echo.
|
||||||
|
|
||||||
|
pause
|
||||||
106
scripts/setup-vcpkg-windows.ps1
Normal file
106
scripts/setup-vcpkg-windows.ps1
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# YAZE vcpkg Setup Script
|
||||||
|
# This script sets up vcpkg for YAZE development on Windows
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Triplet = "x64-windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set error handling
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
$Colors = @{
|
||||||
|
Success = "Green"
|
||||||
|
Warning = "Yellow"
|
||||||
|
Error = "Red"
|
||||||
|
Info = "Cyan"
|
||||||
|
White = "White"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Status {
|
||||||
|
param([string]$Message, [string]$Color = "White")
|
||||||
|
Write-Host $Message -ForegroundColor $Colors[$Color]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-Command {
|
||||||
|
param([string]$Command)
|
||||||
|
try {
|
||||||
|
$null = Get-Command $Command -ErrorAction Stop
|
||||||
|
return $true
|
||||||
|
} catch {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status "YAZE vcpkg Setup Script" "Info"
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
|
||||||
|
Write-Status "Target triplet: $Triplet" "Warning"
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if (-not (Test-Path "vcpkg.json")) {
|
||||||
|
Write-Status "ERROR: vcpkg.json not found. Please run this script from the project root directory." "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ Found vcpkg.json" "Success"
|
||||||
|
|
||||||
|
# Check for Git
|
||||||
|
if (-not (Test-Command "git")) {
|
||||||
|
Write-Status "ERROR: Git not found. Please install Git for Windows." "Error"
|
||||||
|
Write-Status "Download from: https://git-scm.com/download/win" "Info"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "✓ Git found" "Success"
|
||||||
|
|
||||||
|
# Clone vcpkg if needed
|
||||||
|
if (-not (Test-Path "vcpkg")) {
|
||||||
|
Write-Status "Cloning vcpkg..." "Warning"
|
||||||
|
& git clone https://github.com/Microsoft/vcpkg.git vcpkg
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Status "✓ vcpkg cloned successfully" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Failed to clone vcpkg" "Error"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Status "✓ vcpkg directory already exists" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bootstrap vcpkg
|
||||||
|
$vcpkgExe = "vcpkg\vcpkg.exe"
|
||||||
|
if (-not (Test-Path $vcpkgExe)) {
|
||||||
|
Write-Status "Bootstrapping vcpkg..." "Warning"
|
||||||
|
Push-Location vcpkg
|
||||||
|
& .\bootstrap-vcpkg.bat
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Status "✓ vcpkg bootstrapped successfully" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Failed to bootstrap vcpkg" "Error"
|
||||||
|
Pop-Location
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Pop-Location
|
||||||
|
} else {
|
||||||
|
Write-Status "✓ vcpkg already bootstrapped" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
Write-Status "Installing dependencies for triplet: $Triplet" "Warning"
|
||||||
|
& $vcpkgExe install --triplet $Triplet
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Status "✓ Dependencies installed successfully" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "⚠ Some dependencies may not have installed correctly" "Warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status "✓ vcpkg setup complete!" "Success"
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "You can now build YAZE using:" "Warning"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1" "White"
|
||||||
|
Write-Status ""
|
||||||
219
scripts/setup-windows-dev.ps1
Normal file
219
scripts/setup-windows-dev.ps1
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# YAZE Windows Development Setup Script
|
||||||
|
# Sequential approach with no functions and minimal conditionals
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$SkipVcpkg,
|
||||||
|
[switch]$SkipVS,
|
||||||
|
[switch]$SkipBuild
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "YAZE Windows Development Setup" -ForegroundColor Cyan
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Step 1: Check project directory
|
||||||
|
Write-Host "Step 1: Checking project directory..." -ForegroundColor Yellow
|
||||||
|
$projectValid = Test-Path "CMakeLists.txt"
|
||||||
|
switch ($projectValid) {
|
||||||
|
$true { Write-Host "✓ CMakeLists.txt found" -ForegroundColor Green }
|
||||||
|
$false {
|
||||||
|
Write-Host "✗ CMakeLists.txt not found" -ForegroundColor Red
|
||||||
|
Write-Host "Please run this script from the YAZE project root directory" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 2: Check Visual Studio
|
||||||
|
Write-Host "Step 2: Checking Visual Studio..." -ForegroundColor Yellow
|
||||||
|
switch ($SkipVS) {
|
||||||
|
$true { Write-Host "Skipping Visual Studio check" -ForegroundColor Yellow }
|
||||||
|
$false {
|
||||||
|
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
$vsFound = $false
|
||||||
|
$vsExists = Test-Path $vsWhere
|
||||||
|
switch ($vsExists) {
|
||||||
|
$true {
|
||||||
|
$vsInstall = & $vsWhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
switch ($null -ne $vsInstall) {
|
||||||
|
$true {
|
||||||
|
$msbuildPath = Join-Path $vsInstall "MSBuild\Current\Bin\MSBuild.exe"
|
||||||
|
$vsFound = Test-Path $msbuildPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($vsFound) {
|
||||||
|
$true { Write-Host "✓ Visual Studio 2022 with C++ workload found" -ForegroundColor Green }
|
||||||
|
$false {
|
||||||
|
Write-Host "⚠ Visual Studio 2022 with C++ workload not found" -ForegroundColor Yellow
|
||||||
|
Write-Host "Please install Visual Studio 2022 with 'Desktop development with C++' workload" -ForegroundColor White
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 3: Check Git
|
||||||
|
Write-Host "Step 3: Checking Git..." -ForegroundColor Yellow
|
||||||
|
$gitFound = $false
|
||||||
|
try {
|
||||||
|
$null = Get-Command git -ErrorAction Stop
|
||||||
|
$gitFound = $true
|
||||||
|
} catch {
|
||||||
|
$gitFound = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($gitFound) {
|
||||||
|
$true {
|
||||||
|
$gitVersion = & git --version
|
||||||
|
Write-Host "✓ Git found: $gitVersion" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
$false {
|
||||||
|
Write-Host "⚠ Git not found" -ForegroundColor Yellow
|
||||||
|
Write-Host "Please install Git for Windows from: https://git-scm.com/download/win" -ForegroundColor White
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 4: Check Python
|
||||||
|
Write-Host "Step 4: Checking Python..." -ForegroundColor Yellow
|
||||||
|
$pythonFound = $false
|
||||||
|
try {
|
||||||
|
$null = Get-Command python -ErrorAction Stop
|
||||||
|
$pythonFound = $true
|
||||||
|
} catch {
|
||||||
|
$pythonFound = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($pythonFound) {
|
||||||
|
$true {
|
||||||
|
$pythonVersion = & python --version
|
||||||
|
Write-Host "✓ Python found: $pythonVersion" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
$false {
|
||||||
|
Write-Host "⚠ Python not found" -ForegroundColor Yellow
|
||||||
|
Write-Host "Please install Python 3.8+ from: https://www.python.org/downloads/" -ForegroundColor White
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 5: Setup vcpkg
|
||||||
|
Write-Host "Step 5: Setting up vcpkg..." -ForegroundColor Yellow
|
||||||
|
switch ($SkipVcpkg) {
|
||||||
|
$true { Write-Host "Skipping vcpkg setup" -ForegroundColor Yellow }
|
||||||
|
$false {
|
||||||
|
# Clone vcpkg
|
||||||
|
$vcpkgExists = Test-Path "vcpkg"
|
||||||
|
switch ($vcpkgExists) {
|
||||||
|
$false {
|
||||||
|
Write-Host "Cloning vcpkg..." -ForegroundColor Yellow
|
||||||
|
switch ($gitFound) {
|
||||||
|
$true {
|
||||||
|
& git clone https://github.com/Microsoft/vcpkg.git vcpkg
|
||||||
|
$cloneSuccess = ($LASTEXITCODE -eq 0)
|
||||||
|
switch ($cloneSuccess) {
|
||||||
|
$true { Write-Host "✓ vcpkg cloned successfully" -ForegroundColor Green }
|
||||||
|
$false {
|
||||||
|
Write-Host "✗ Failed to clone vcpkg" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$false {
|
||||||
|
Write-Host "✗ Git is required to clone vcpkg" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$true { Write-Host "✓ vcpkg directory already exists" -ForegroundColor Green }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bootstrap vcpkg
|
||||||
|
$vcpkgExe = "vcpkg\vcpkg.exe"
|
||||||
|
$vcpkgBootstrapped = Test-Path $vcpkgExe
|
||||||
|
switch ($vcpkgBootstrapped) {
|
||||||
|
$false {
|
||||||
|
Write-Host "Bootstrapping vcpkg..." -ForegroundColor Yellow
|
||||||
|
Push-Location vcpkg
|
||||||
|
& .\bootstrap-vcpkg.bat
|
||||||
|
$bootstrapSuccess = ($LASTEXITCODE -eq 0)
|
||||||
|
Pop-Location
|
||||||
|
switch ($bootstrapSuccess) {
|
||||||
|
$true { Write-Host "✓ vcpkg bootstrapped successfully" -ForegroundColor Green }
|
||||||
|
$false {
|
||||||
|
Write-Host "✗ Failed to bootstrap vcpkg" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$true { Write-Host "✓ vcpkg already bootstrapped" -ForegroundColor Green }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
Write-Host "Installing dependencies..." -ForegroundColor Yellow
|
||||||
|
& $vcpkgExe install --triplet x64-windows
|
||||||
|
$installSuccess = ($LASTEXITCODE -eq 0)
|
||||||
|
switch ($installSuccess) {
|
||||||
|
$true { Write-Host "✓ Dependencies installed successfully" -ForegroundColor Green }
|
||||||
|
$false { Write-Host "⚠ Some dependencies may not have installed correctly" -ForegroundColor Yellow }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 6: Generate project files
|
||||||
|
Write-Host "Step 6: Generating Visual Studio project files..." -ForegroundColor Yellow
|
||||||
|
switch ($pythonFound) {
|
||||||
|
$true {
|
||||||
|
& python scripts/generate-vs-projects-simple.py
|
||||||
|
$generateSuccess = ($LASTEXITCODE -eq 0)
|
||||||
|
switch ($generateSuccess) {
|
||||||
|
$true { Write-Host "✓ Project files generated successfully" -ForegroundColor Green }
|
||||||
|
$false {
|
||||||
|
Write-Host "⚠ Failed to generate project files with simple generator, trying original..." -ForegroundColor Yellow
|
||||||
|
& python scripts/generate-vs-projects.py
|
||||||
|
$generateSuccess2 = ($LASTEXITCODE -eq 0)
|
||||||
|
switch ($generateSuccess2) {
|
||||||
|
$true { Write-Host "✓ Project files generated successfully" -ForegroundColor Green }
|
||||||
|
$false { Write-Host "⚠ Failed to generate project files" -ForegroundColor Yellow }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$false { Write-Host "⚠ Python required to generate project files" -ForegroundColor Yellow }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 7: Test build
|
||||||
|
Write-Host "Step 7: Testing build..." -ForegroundColor Yellow
|
||||||
|
switch ($SkipBuild) {
|
||||||
|
$true { Write-Host "Skipping test build" -ForegroundColor Yellow }
|
||||||
|
$false {
|
||||||
|
$buildScriptExists = Test-Path "scripts\build-windows.ps1"
|
||||||
|
switch ($buildScriptExists) {
|
||||||
|
$true {
|
||||||
|
& .\scripts\build-windows.ps1 -Configuration Release -Platform x64
|
||||||
|
$buildSuccess = ($LASTEXITCODE -eq 0)
|
||||||
|
switch ($buildSuccess) {
|
||||||
|
$true { Write-Host "✓ Test build successful" -ForegroundColor Green }
|
||||||
|
$false { Write-Host "⚠ Test build failed, but setup is complete" -ForegroundColor Yellow }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$false { Write-Host "⚠ Build script not found" -ForegroundColor Yellow }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Final instructions
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "✓ YAZE Windows development setup complete!" -ForegroundColor Green
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Next steps:" -ForegroundColor Yellow
|
||||||
|
Write-Host "1. Visual Studio project files have been generated" -ForegroundColor White
|
||||||
|
Write-Host "2. Open YAZE.sln in Visual Studio 2022" -ForegroundColor White
|
||||||
|
Write-Host "3. Select configuration (Debug/Release) and platform (x64/x86/ARM64)" -ForegroundColor White
|
||||||
|
Write-Host "4. Build the solution (Ctrl+Shift+B)" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Or use command line:" -ForegroundColor Yellow
|
||||||
|
Write-Host " .\scripts\build-windows.ps1 -Configuration Release -Platform x64" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "For more information, see docs/windows-development-guide.md" -ForegroundColor Cyan
|
||||||
132
scripts/validate-windows-build.ps1
Normal file
132
scripts/validate-windows-build.ps1
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# YAZE Windows Build Validation Script
|
||||||
|
# This script validates that the Windows build environment is properly set up
|
||||||
|
|
||||||
|
# Set error handling
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
$Colors = @{
|
||||||
|
Success = "Green"
|
||||||
|
Warning = "Yellow"
|
||||||
|
Error = "Red"
|
||||||
|
Info = "Cyan"
|
||||||
|
White = "White"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-Status {
|
||||||
|
param([string]$Message, [string]$Color = "White")
|
||||||
|
Write-Host $Message -ForegroundColor $Colors[$Color]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-Command {
|
||||||
|
param([string]$Command)
|
||||||
|
try {
|
||||||
|
$null = Get-Command $Command -ErrorAction Stop
|
||||||
|
return $true
|
||||||
|
} catch {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-VisualStudio {
|
||||||
|
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||||
|
if (Test-Path $vsWhere) {
|
||||||
|
$vsInstall = & $vsWhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
if ($vsInstall) {
|
||||||
|
$msbuildPath = Join-Path $vsInstall "MSBuild\Current\Bin\MSBuild.exe"
|
||||||
|
if (Test-Path $msbuildPath) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
Write-Status "YAZE Windows Build Validation" "Info"
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
|
||||||
|
$allGood = $true
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if (-not (Test-Path "YAZE.sln")) {
|
||||||
|
Write-Status "✗ YAZE.sln not found" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
} else {
|
||||||
|
Write-Status "✓ YAZE.sln found" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for vcpkg.json
|
||||||
|
if (-not (Test-Path "vcpkg.json")) {
|
||||||
|
Write-Status "✗ vcpkg.json not found" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
} else {
|
||||||
|
Write-Status "✓ vcpkg.json found" "Success"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for Visual Studio
|
||||||
|
if (Test-VisualStudio) {
|
||||||
|
Write-Status "✓ Visual Studio 2022 with C++ workload found" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Visual Studio 2022 with C++ workload not found" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for Git
|
||||||
|
if (Test-Command "git") {
|
||||||
|
$gitVersion = & git --version
|
||||||
|
Write-Status "✓ Git found: $gitVersion" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Git not found" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for Python
|
||||||
|
if (Test-Command "python") {
|
||||||
|
$pythonVersion = & python --version
|
||||||
|
Write-Status "✓ Python found: $pythonVersion" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Python not found" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for vcpkg
|
||||||
|
if (Test-Path "vcpkg\vcpkg.exe") {
|
||||||
|
Write-Status "✓ vcpkg found and bootstrapped" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ vcpkg not found or not bootstrapped" "Error"
|
||||||
|
$allGood = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for generated project files
|
||||||
|
if (Test-Path "YAZE.vcxproj") {
|
||||||
|
Write-Status "✓ Visual Studio project file found" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Visual Studio project file not found" "Error"
|
||||||
|
Write-Status " Run: python scripts/generate-vs-projects.py" "Info"
|
||||||
|
$allGood = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for config file
|
||||||
|
if (Test-Path "yaze_config.h") {
|
||||||
|
Write-Status "✓ yaze_config.h found" "Success"
|
||||||
|
} else {
|
||||||
|
Write-Status "⚠ yaze_config.h not found (will be generated during build)" "Warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
|
if ($allGood) {
|
||||||
|
Write-Status "✓ All checks passed! Build environment is ready." "Success"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "You can now build YAZE using:" "Warning"
|
||||||
|
Write-Status " .\scripts\build-windows.ps1" "White"
|
||||||
|
Write-Status " or" "White"
|
||||||
|
Write-Status " .\scripts\build-windows.bat" "White"
|
||||||
|
} else {
|
||||||
|
Write-Status "✗ Some checks failed. Please fix the issues above." "Error"
|
||||||
|
Write-Status ""
|
||||||
|
Write-Status "Run the setup script to fix issues:" "Warning"
|
||||||
|
Write-Status " .\scripts\setup-windows-dev.ps1" "White"
|
||||||
|
}
|
||||||
|
Write-Status "========================================" "Info"
|
||||||
@@ -22,7 +22,6 @@ set(
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(YAZE_RESOURCE_FILES
|
set(YAZE_RESOURCE_FILES
|
||||||
${CMAKE_SOURCE_DIR}/assets/layouts/overworld.zeml
|
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Roboto-Medium.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Roboto-Medium.ttf
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Cousine-Regular.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Cousine-Regular.ttf
|
||||||
|
|||||||
@@ -106,12 +106,14 @@ else()
|
|||||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=0)
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_IMGUI_TEST_ENGINE=0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Link Google Test if available for integrated testing
|
# Link Google Test if available for integrated testing (but NOT gtest_main to avoid main() conflicts)
|
||||||
if(YAZE_BUILD_TESTS AND TARGET gtest AND TARGET gtest_main)
|
if(YAZE_BUILD_TESTS AND TARGET gtest)
|
||||||
target_link_libraries(yaze PRIVATE gtest gtest_main)
|
target_link_libraries(yaze PRIVATE gtest)
|
||||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=1)
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=1)
|
||||||
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_TESTING=1)
|
||||||
else()
|
else()
|
||||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=0)
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_GTEST=0)
|
||||||
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_TESTING=0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Conditionally link PNG if available
|
# Conditionally link PNG if available
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ class FeatureFlags {
|
|||||||
// Save overworld properties to the Rom.
|
// Save overworld properties to the Rom.
|
||||||
bool kSaveOverworldProperties = true;
|
bool kSaveOverworldProperties = true;
|
||||||
|
|
||||||
// Load custom overworld data from the ROM and enable UI.
|
// Enable custom overworld features for vanilla ROMs or override detection.
|
||||||
|
// If ZSCustomOverworld ASM is already applied, features are auto-enabled.
|
||||||
bool kLoadCustomOverworld = false;
|
bool kLoadCustomOverworld = false;
|
||||||
|
|
||||||
// Apply ZSCustomOverworld ASM patches when upgrading ROM versions.
|
// Apply ZSCustomOverworld ASM patches when upgrading ROM versions.
|
||||||
@@ -134,8 +135,19 @@ struct FlagsMenu {
|
|||||||
&FeatureFlags::get().overworld.kSaveOverworldItems);
|
&FeatureFlags::get().overworld.kSaveOverworldItems);
|
||||||
Checkbox("Save Overworld Properties",
|
Checkbox("Save Overworld Properties",
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldProperties);
|
&FeatureFlags::get().overworld.kSaveOverworldProperties);
|
||||||
Checkbox("Load Custom Overworld",
|
Checkbox("Enable Custom Overworld Features",
|
||||||
&FeatureFlags::get().overworld.kLoadCustomOverworld);
|
&FeatureFlags::get().overworld.kLoadCustomOverworld);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("?")) {
|
||||||
|
ImGui::OpenPopup("CustomOverworldHelp");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("CustomOverworldHelp")) {
|
||||||
|
ImGui::Text("This flag enables ZSCustomOverworld features.");
|
||||||
|
ImGui::Text("If ZSCustomOverworld ASM is already applied to the ROM,");
|
||||||
|
ImGui::Text("features are auto-enabled regardless of this flag.");
|
||||||
|
ImGui::Text("For vanilla ROMs, enable this to use custom features.");
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
Checkbox("Apply ZSCustomOverworld ASM",
|
Checkbox("Apply ZSCustomOverworld ASM",
|
||||||
&FeatureFlags::get().overworld.kApplyZSCustomOverworldASM);
|
&FeatureFlags::get().overworld.kApplyZSCustomOverworldASM);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace yaze {
|
|||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
std::string GetFileExtension(const std::string &filename) {
|
std::string GetFileExtension(const std::string &filename) {
|
||||||
size_t dot = filename.find_last_of(".");
|
size_t dot = filename.find_last_of('.');
|
||||||
if (dot == std::string::npos) {
|
if (dot == std::string::npos) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ std::string GetFileExtension(const std::string &filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string GetFileName(const std::string &filename) {
|
std::string GetFileName(const std::string &filename) {
|
||||||
size_t slash = filename.find_last_of("/");
|
size_t slash = filename.find_last_of('/');
|
||||||
if (slash == std::string::npos) {
|
if (slash == std::string::npos) {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
@@ -51,13 +51,6 @@ std::string LoadFile(const std::string &filename) {
|
|||||||
|
|
||||||
std::string LoadConfigFile(const std::string &filename) {
|
std::string LoadConfigFile(const std::string &filename) {
|
||||||
std::string contents;
|
std::string contents;
|
||||||
#if defined(_WIN32)
|
|
||||||
Platform platform = Platform::kWindows;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
Platform platform = Platform::kMacOS;
|
|
||||||
#else
|
|
||||||
Platform platform = Platform::kLinux;
|
|
||||||
#endif
|
|
||||||
std::string filepath = GetConfigDirectory() + "/" + filename;
|
std::string filepath = GetConfigDirectory() + "/" + filename;
|
||||||
std::ifstream file(filepath);
|
std::ifstream file(filepath);
|
||||||
if (file.is_open()) {
|
if (file.is_open()) {
|
||||||
@@ -125,107 +118,152 @@ std::string GetConfigDirectory() {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
// Forward declaration for the main implementation
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
std::string ShowOpenFileDialogImpl();
|
#include <nfd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFileDialog() {
|
std::string FileDialogWrapper::ShowOpenFileDialog() {
|
||||||
return ShowOpenFileDialogImpl();
|
// Use global feature flag to choose implementation
|
||||||
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
|
return ShowOpenFileDialogNFD();
|
||||||
|
} else {
|
||||||
|
return ShowOpenFileDialogBespoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFileDialogNFD() {
|
std::string FileDialogWrapper::ShowOpenFileDialogNFD() {
|
||||||
// Windows doesn't use NFD in this implementation, fallback to bespoke
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
|
NFD_Init();
|
||||||
|
nfdu8char_t *out_path = NULL;
|
||||||
|
nfdu8filteritem_t filters[1] = {{"ROM Files", "sfc,smc"}};
|
||||||
|
nfdopendialogu8args_t args = {0};
|
||||||
|
args.filterList = filters;
|
||||||
|
args.filterCount = 1;
|
||||||
|
nfdresult_t result = NFD_OpenDialogU8_With(&out_path, &args);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
std::string file_path(out_path);
|
||||||
|
NFD_FreePath(out_path);
|
||||||
|
NFD_Quit();
|
||||||
|
return file_path;
|
||||||
|
} else if (result == NFD_CANCEL) {
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
// NFD not available - fallback to bespoke
|
||||||
return ShowOpenFileDialogBespoke();
|
return ShowOpenFileDialogBespoke();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
std::string FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
||||||
return ShowOpenFileDialogImpl();
|
// For CI/CD, just return a placeholder path
|
||||||
|
return ""; // Placeholder for bespoke implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ShowOpenFileDialogImpl() {
|
std::string FileDialogWrapper::ShowSaveFileDialog(const std::string& default_name,
|
||||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
const std::string& default_extension) {
|
||||||
|
// Use global feature flag to choose implementation
|
||||||
IFileDialog *pfd = NULL;
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
HRESULT hr =
|
return ShowSaveFileDialogNFD(default_name, default_extension);
|
||||||
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
|
} else {
|
||||||
reinterpret_cast<void **>(&pfd));
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
std::string file_path_windows;
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
// Show the dialog
|
|
||||||
hr = pfd->Show(NULL);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
IShellItem *psiResult;
|
|
||||||
hr = pfd->GetResult(&psiResult);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
// Get the file path
|
|
||||||
PWSTR pszFilePath;
|
|
||||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
|
||||||
char str[128];
|
|
||||||
wcstombs(str, pszFilePath, 128);
|
|
||||||
file_path_windows = str;
|
|
||||||
psiResult->Release();
|
|
||||||
CoTaskMemFree(pszFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pfd->Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CoUninitialize();
|
|
||||||
return file_path_windows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward declaration for folder dialog implementation
|
std::string FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||||
std::string ShowOpenFolderDialogImpl();
|
const std::string& default_extension) {
|
||||||
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
|
NFD_Init();
|
||||||
|
nfdu8char_t *out_path = NULL;
|
||||||
|
|
||||||
|
nfdsavedialogu8args_t args = {0};
|
||||||
|
if (!default_extension.empty()) {
|
||||||
|
// Create filter for the save dialog
|
||||||
|
static nfdu8filteritem_t filters[3] = {
|
||||||
|
{"Theme Files", "theme"},
|
||||||
|
{"YAZE Project Files", "yaze"},
|
||||||
|
{"ROM Files", "sfc,smc"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (default_extension == "theme") {
|
||||||
|
args.filterList = &filters[0];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "yaze") {
|
||||||
|
args.filterList = &filters[1];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||||
|
args.filterList = &filters[2];
|
||||||
|
args.filterCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_name.empty()) {
|
||||||
|
args.defaultName = default_name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
nfdresult_t result = NFD_SaveDialogU8_With(&out_path, &args);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
std::string file_path(out_path);
|
||||||
|
NFD_FreePath(out_path);
|
||||||
|
NFD_Quit();
|
||||||
|
return file_path;
|
||||||
|
} else if (result == NFD_CANCEL) {
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
// NFD not available - fallback to bespoke
|
||||||
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
// For CI/CD, just return a placeholder path
|
||||||
|
if (!default_name.empty() && !default_extension.empty()) {
|
||||||
|
return default_name + "." + default_extension;
|
||||||
|
}
|
||||||
|
return ""; // Placeholder for bespoke implementation
|
||||||
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||||
return ShowOpenFolderDialogImpl();
|
// Use global feature flag to choose implementation
|
||||||
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
|
return ShowOpenFolderDialogNFD();
|
||||||
|
} else {
|
||||||
|
return ShowOpenFolderDialogBespoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFolderDialogNFD() {
|
std::string FileDialogWrapper::ShowOpenFolderDialogNFD() {
|
||||||
// Windows doesn't use NFD in this implementation, fallback to bespoke
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
|
NFD_Init();
|
||||||
|
nfdu8char_t *out_path = NULL;
|
||||||
|
nfdresult_t result = NFD_PickFolderU8(&out_path, NULL);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
std::string folder_path(out_path);
|
||||||
|
NFD_FreePath(out_path);
|
||||||
|
NFD_Quit();
|
||||||
|
return folder_path;
|
||||||
|
} else if (result == NFD_CANCEL) {
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
// NFD not available - fallback to bespoke
|
||||||
return ShowOpenFolderDialogBespoke();
|
return ShowOpenFolderDialogBespoke();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
std::string FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
||||||
return ShowOpenFolderDialogImpl();
|
// For CI/CD, just return a placeholder path
|
||||||
}
|
return ""; // Placeholder for bespoke implementation
|
||||||
|
|
||||||
std::string ShowOpenFolderDialogImpl() {
|
|
||||||
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
||||||
|
|
||||||
IFileDialog *pfd = NULL;
|
|
||||||
HRESULT hr =
|
|
||||||
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
|
|
||||||
reinterpret_cast<void **>(&pfd));
|
|
||||||
std::string folder_path_windows;
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
// Show the dialog
|
|
||||||
DWORD dwOptions;
|
|
||||||
hr = pfd->GetOptions(&dwOptions);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
hr = pfd->Show(NULL);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
IShellItem *psiResult;
|
|
||||||
hr = pfd->GetResult(&psiResult);
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
// Get the folder path
|
|
||||||
PWSTR pszFolderPath;
|
|
||||||
psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);
|
|
||||||
char str[128];
|
|
||||||
wcstombs(str, pszFolderPath, 128);
|
|
||||||
folder_path_windows = str;
|
|
||||||
psiResult->Release();
|
|
||||||
CoTaskMemFree(pszFolderPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pfd->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
CoUninitialize();
|
|
||||||
return folder_path_windows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(
|
std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(
|
||||||
@@ -238,7 +276,7 @@ std::vector<std::string> FileDialogWrapper::GetSubdirectoriesInFolder(
|
|||||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
if (strcmp(findFileData.cFileName, ".") != 0 &&
|
if (strcmp(findFileData.cFileName, ".") != 0 &&
|
||||||
strcmp(findFileData.cFileName, "..") != 0) {
|
strcmp(findFileData.cFileName, "..") != 0) {
|
||||||
subdirectories.push_back(findFileData.cFileName);
|
subdirectories.emplace_back(findFileData.cFileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (FindNextFile(hFind, &findFileData) != 0);
|
} while (FindNextFile(hFind, &findFileData) != 0);
|
||||||
@@ -255,7 +293,7 @@ std::vector<std::string> FileDialogWrapper::GetFilesInFolder(
|
|||||||
if (hFind != INVALID_HANDLE_VALUE) {
|
if (hFind != INVALID_HANDLE_VALUE) {
|
||||||
do {
|
do {
|
||||||
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
files.push_back(findFileData.cFileName);
|
files.emplace_back(findFileData.cFileName);
|
||||||
}
|
}
|
||||||
} while (FindNextFile(hFind, &findFileData) != 0);
|
} while (FindNextFile(hFind, &findFileData) != 0);
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
@@ -306,10 +344,79 @@ std::string FileDialogWrapper::ShowOpenFileDialogNFD() {
|
|||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
std::string FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
||||||
// Implement bespoke file dialog or return placeholder
|
// Implement bespoke file dialog or return placeholder
|
||||||
// This would contain the custom macOS implementation
|
// This would contain the custom Linux implementation
|
||||||
return ""; // Placeholder for bespoke implementation
|
return ""; // Placeholder for bespoke implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
|
NFD_Init();
|
||||||
|
nfdu8char_t *out_path = NULL;
|
||||||
|
|
||||||
|
nfdsavedialogu8args_t args = {0};
|
||||||
|
if (!default_extension.empty()) {
|
||||||
|
// Create filter for the save dialog
|
||||||
|
static nfdu8filteritem_t filters[3] = {
|
||||||
|
{"Theme File", "theme"},
|
||||||
|
{"Project File", "yaze"},
|
||||||
|
{"ROM File", "sfc,smc"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (default_extension == "theme") {
|
||||||
|
args.filterList = &filters[0];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "yaze") {
|
||||||
|
args.filterList = &filters[1];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||||
|
args.filterList = &filters[2];
|
||||||
|
args.filterCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_name.empty()) {
|
||||||
|
args.defaultName = default_name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
nfdresult_t result = NFD_SaveDialogU8_With(&out_path, &args);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
std::string file_path(out_path);
|
||||||
|
NFD_FreePath(out_path);
|
||||||
|
NFD_Quit();
|
||||||
|
return file_path;
|
||||||
|
} else if (result == NFD_CANCEL) {
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
// NFD not available - fallback to bespoke
|
||||||
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
// Basic Linux implementation using system command
|
||||||
|
// For CI/CD, just return a placeholder path
|
||||||
|
if (!default_name.empty() && !default_extension.empty()) {
|
||||||
|
return default_name + "." + default_extension;
|
||||||
|
}
|
||||||
|
return ""; // For now return empty - full implementation can be added later
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileDialogWrapper::ShowSaveFileDialog(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
// Use global feature flag to choose implementation
|
||||||
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
|
return ShowSaveFileDialogNFD(default_name, default_extension);
|
||||||
|
} else {
|
||||||
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
std::string FileDialogWrapper::ShowOpenFolderDialog() {
|
||||||
// Use global feature flag to choose implementation
|
// Use global feature flag to choose implementation
|
||||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
|
|||||||
@@ -20,10 +20,21 @@ class FileDialogWrapper {
|
|||||||
* folder path. Uses global feature flag to choose implementation.
|
* folder path. Uses global feature flag to choose implementation.
|
||||||
*/
|
*/
|
||||||
static std::string ShowOpenFolderDialog();
|
static std::string ShowOpenFolderDialog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ShowSaveFileDialog opens a save file dialog and returns the selected
|
||||||
|
* filepath. Uses global feature flag to choose implementation.
|
||||||
|
*/
|
||||||
|
static std::string ShowSaveFileDialog(const std::string& default_name = "",
|
||||||
|
const std::string& default_extension = "");
|
||||||
|
|
||||||
// Specific implementations for testing
|
// Specific implementations for testing
|
||||||
static std::string ShowOpenFileDialogNFD();
|
static std::string ShowOpenFileDialogNFD();
|
||||||
static std::string ShowOpenFileDialogBespoke();
|
static std::string ShowOpenFileDialogBespoke();
|
||||||
|
static std::string ShowSaveFileDialogNFD(const std::string& default_name = "",
|
||||||
|
const std::string& default_extension = "");
|
||||||
|
static std::string ShowSaveFileDialogBespoke(const std::string& default_name = "",
|
||||||
|
const std::string& default_extension = "");
|
||||||
static std::string ShowOpenFolderDialogNFD();
|
static std::string ShowOpenFolderDialogNFD();
|
||||||
static std::string ShowOpenFolderDialogBespoke();
|
static std::string ShowOpenFolderDialogBespoke();
|
||||||
static std::vector<std::string> GetSubdirectoriesInFolder(
|
static std::vector<std::string> GetSubdirectoriesInFolder(
|
||||||
|
|||||||
@@ -88,6 +88,28 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFileDialogBespoke() {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialogBespoke(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
NSSavePanel* savePanel = [NSSavePanel savePanel];
|
||||||
|
|
||||||
|
if (!default_name.empty()) {
|
||||||
|
[savePanel setNameFieldStringValue:[NSString stringWithUTF8String:default_name.c_str()]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_extension.empty()) {
|
||||||
|
NSString* ext = [NSString stringWithUTF8String:default_extension.c_str()];
|
||||||
|
[savePanel setAllowedFileTypes:@[ext]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([savePanel runModal] == NSModalResponseOK) {
|
||||||
|
NSURL* url = [savePanel URL];
|
||||||
|
NSString* path = [url path];
|
||||||
|
return std::string([path UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
// Global feature flag-based dispatch methods
|
// Global feature flag-based dispatch methods
|
||||||
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialog() {
|
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialog() {
|
||||||
if (FeatureFlags::get().kUseNativeFileDialog) {
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
@@ -105,6 +127,15 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialog(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
if (FeatureFlags::get().kUseNativeFileDialog) {
|
||||||
|
return ShowSaveFileDialogNFD(default_name, default_extension);
|
||||||
|
} else {
|
||||||
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NFD implementation for macOS (fallback to bespoke if NFD not available)
|
// NFD implementation for macOS (fallback to bespoke if NFD not available)
|
||||||
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialogNFD() {
|
std::string yaze::core::FileDialogWrapper::ShowOpenFileDialogNFD() {
|
||||||
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
@@ -156,6 +187,55 @@ std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialogNFD() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string yaze::core::FileDialogWrapper::ShowSaveFileDialogNFD(const std::string& default_name,
|
||||||
|
const std::string& default_extension) {
|
||||||
|
#if defined(YAZE_ENABLE_NFD) && YAZE_ENABLE_NFD
|
||||||
|
NFD_Init();
|
||||||
|
nfdu8char_t *out_path = NULL;
|
||||||
|
|
||||||
|
nfdsavedialogu8args_t args = {0};
|
||||||
|
if (!default_extension.empty()) {
|
||||||
|
// Create filter for the save dialog
|
||||||
|
static nfdu8filteritem_t filters[3] = {
|
||||||
|
{"Theme File", "theme"},
|
||||||
|
{"Project File", "yaze"},
|
||||||
|
{"ROM File", "sfc,smc"}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (default_extension == "theme") {
|
||||||
|
args.filterList = &filters[0];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "yaze") {
|
||||||
|
args.filterList = &filters[1];
|
||||||
|
args.filterCount = 1;
|
||||||
|
} else if (default_extension == "sfc" || default_extension == "smc") {
|
||||||
|
args.filterList = &filters[2];
|
||||||
|
args.filterCount = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_name.empty()) {
|
||||||
|
args.defaultName = default_name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
nfdresult_t result = NFD_SaveDialogU8_With(&out_path, &args);
|
||||||
|
if (result == NFD_OKAY) {
|
||||||
|
std::string file_path(out_path);
|
||||||
|
NFD_FreePath(out_path);
|
||||||
|
NFD_Quit();
|
||||||
|
return file_path;
|
||||||
|
} else if (result == NFD_CANCEL) {
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
NFD_Quit();
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
// NFD not compiled in, use bespoke
|
||||||
|
return ShowSaveFileDialogBespoke(default_name, default_extension);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
std::string yaze::core::FileDialogWrapper::ShowOpenFolderDialogBespoke() {
|
||||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||||
[openPanel setCanChooseFiles:NO];
|
[openPanel setCanChooseFiles:NO];
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
@@ -23,9 +24,9 @@ static const char* DROID_SANS = "DroidSans.ttf";
|
|||||||
static const char* NOTO_SANS_JP = "NotoSansJP.ttf";
|
static const char* NOTO_SANS_JP = "NotoSansJP.ttf";
|
||||||
static const char* IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
|
static const char* IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
|
||||||
|
|
||||||
static const float FONT_SIZE_DEFAULT = 16.0f;
|
static const float FONT_SIZE_DEFAULT = 16.0F;
|
||||||
static const float FONT_SIZE_DROID_SANS = 18.0f;
|
static const float FONT_SIZE_DROID_SANS = 18.0F;
|
||||||
static const float ICON_FONT_SIZE = 18.0f;
|
static const float ICON_FONT_SIZE = 18.0F;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ std::string SetFontPath(const std::string& font_path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status LoadFont(const FontConfig& font_config) {
|
absl::Status LoadFont(const FontConfig& font_config) {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& imgui_io = ImGui::GetIO();
|
||||||
std::string actual_font_path = SetFontPath(font_config.font_path);
|
std::string actual_font_path = SetFontPath(font_config.font_path);
|
||||||
// Check if the file exists with std library first, since ImGui IO will assert
|
// Check if the file exists with std library first, since ImGui IO will assert
|
||||||
// if the file does not exist
|
// if the file does not exist
|
||||||
@@ -53,7 +54,7 @@ absl::Status LoadFont(const FontConfig& font_config) {
|
|||||||
absl::StrFormat("Font file %s does not exist", actual_font_path));
|
absl::StrFormat("Font file %s does not exist", actual_font_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
|
if (!imgui_io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
|
||||||
font_config.font_size)) {
|
font_config.font_size)) {
|
||||||
return absl::InternalError(
|
return absl::InternalError(
|
||||||
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
||||||
@@ -61,33 +62,33 @@ absl::Status LoadFont(const FontConfig& font_config) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status AddIconFont(const FontConfig& config) {
|
absl::Status AddIconFont(const FontConfig& /*config*/) {
|
||||||
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
||||||
ImFontConfig icons_config;
|
ImFontConfig icons_config{};
|
||||||
icons_config.MergeMode = true;
|
icons_config.MergeMode = true;
|
||||||
icons_config.GlyphOffset.y = 5.0f;
|
icons_config.GlyphOffset.y = 5.0F;
|
||||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
icons_config.GlyphMinAdvanceX = 13.0F;
|
||||||
icons_config.PixelSnapH = true;
|
icons_config.PixelSnapH = true;
|
||||||
std::string icon_font_path = SetFontPath(FONT_ICON_FILE_NAME_MD);
|
std::string icon_font_path = SetFontPath(FONT_ICON_FILE_NAME_MD);
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& imgui_io = ImGui::GetIO();
|
||||||
if (!io.Fonts->AddFontFromFileTTF(icon_font_path.c_str(), ICON_FONT_SIZE,
|
if (!imgui_io.Fonts->AddFontFromFileTTF(icon_font_path.c_str(), ICON_FONT_SIZE,
|
||||||
&icons_config, icons_ranges)) {
|
&icons_config, icons_ranges)) {
|
||||||
return absl::InternalError("Failed to add icon fonts");
|
return absl::InternalError("Failed to add icon fonts");
|
||||||
}
|
}
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status AddJapaneseFont(const FontConfig& config) {
|
absl::Status AddJapaneseFont(const FontConfig& /*config*/) {
|
||||||
ImFontConfig japanese_font_config;
|
ImFontConfig japanese_font_config{};
|
||||||
japanese_font_config.MergeMode = true;
|
japanese_font_config.MergeMode = true;
|
||||||
japanese_font_config.GlyphOffset.y = 5.0f;
|
japanese_font_config.GlyphOffset.y = 5.0F;
|
||||||
japanese_font_config.GlyphMinAdvanceX = 13.0f;
|
japanese_font_config.GlyphMinAdvanceX = 13.0F;
|
||||||
japanese_font_config.PixelSnapH = true;
|
japanese_font_config.PixelSnapH = true;
|
||||||
std::string japanese_font_path = SetFontPath(NOTO_SANS_JP);
|
std::string japanese_font_path = SetFontPath(NOTO_SANS_JP);
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& imgui_io = ImGui::GetIO();
|
||||||
if (!io.Fonts->AddFontFromFileTTF(japanese_font_path.data(), ICON_FONT_SIZE,
|
if (!imgui_io.Fonts->AddFontFromFileTTF(japanese_font_path.data(), ICON_FONT_SIZE,
|
||||||
&japanese_font_config,
|
&japanese_font_config,
|
||||||
io.Fonts->GetGlyphRangesJapanese())) {
|
imgui_io.Fonts->GetGlyphRangesJapanese())) {
|
||||||
return absl::InternalError("Failed to add Japanese fonts");
|
return absl::InternalError("Failed to add Japanese fonts");
|
||||||
}
|
}
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -97,13 +98,13 @@ absl::Status AddJapaneseFont(const FontConfig& config) {
|
|||||||
|
|
||||||
absl::Status LoadPackageFonts() {
|
absl::Status LoadPackageFonts() {
|
||||||
if (font_registry.fonts.empty()) {
|
if (font_registry.fonts.empty()) {
|
||||||
// Initialize the font names and sizes
|
// Initialize the font names and sizes with proper ImFontConfig initialization
|
||||||
font_registry.fonts = {
|
font_registry.fonts = {
|
||||||
{KARLA_REGULAR, FONT_SIZE_DEFAULT},
|
FontConfig{KARLA_REGULAR, FONT_SIZE_DEFAULT, {}, {}},
|
||||||
{ROBOTO_MEDIUM, FONT_SIZE_DEFAULT},
|
FontConfig{ROBOTO_MEDIUM, FONT_SIZE_DEFAULT, {}, {}},
|
||||||
{COUSINE_REGULAR, FONT_SIZE_DEFAULT},
|
FontConfig{COUSINE_REGULAR, FONT_SIZE_DEFAULT, {}, {}},
|
||||||
{IBM_PLEX_JP, FONT_SIZE_DEFAULT},
|
FontConfig{IBM_PLEX_JP, FONT_SIZE_DEFAULT, {}, {}},
|
||||||
{DROID_SANS, FONT_SIZE_DROID_SANS},
|
FontConfig{DROID_SANS, FONT_SIZE_DROID_SANS, {}, {}},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +118,9 @@ absl::Status LoadPackageFonts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ReloadPackageFont(const FontConfig& config) {
|
absl::Status ReloadPackageFont(const FontConfig& config) {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& imgui_io = ImGui::GetIO();
|
||||||
std::string actual_font_path = SetFontPath(config.font_path);
|
std::string actual_font_path = SetFontPath(config.font_path);
|
||||||
if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
|
if (!imgui_io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
|
||||||
config.font_size)) {
|
config.font_size)) {
|
||||||
return absl::InternalError(
|
return absl::InternalError(
|
||||||
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
absl::StrFormat("Failed to load font from %s", actual_font_path));
|
||||||
@@ -129,127 +130,12 @@ absl::Status ReloadPackageFont(const FontConfig& config) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef __linux__
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe, const TEXTMETRIC* lpntme,
|
|
||||||
DWORD FontType, LPARAM lParam) {
|
|
||||||
// Step 3: Load the font into ImGui
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
io.Fonts->AddFontFromFileTTF(lpelfe->lfFaceName, 16.0f);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadSystemFonts() {
|
|
||||||
HKEY hKey;
|
|
||||||
std::vector<std::string> fontPaths;
|
|
||||||
|
|
||||||
// Open the registry key where fonts are listed
|
|
||||||
if (RegOpenKeyEx(
|
|
||||||
HKEY_LOCAL_MACHINE,
|
|
||||||
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"), 0,
|
|
||||||
KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
||||||
DWORD valueCount;
|
|
||||||
DWORD maxValueNameSize;
|
|
||||||
DWORD maxValueDataSize;
|
|
||||||
|
|
||||||
// Query the number of entries and the maximum size of the names and values
|
|
||||||
RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount,
|
|
||||||
&maxValueNameSize, &maxValueDataSize, NULL, NULL);
|
|
||||||
|
|
||||||
char* valueName = new char[maxValueNameSize + 1]; // +1 for null terminator
|
|
||||||
BYTE* valueData = new BYTE[maxValueDataSize + 1]; // +1 for null terminator
|
|
||||||
|
|
||||||
// Enumerate all font entries
|
|
||||||
for (DWORD i = 0; i < valueCount; i++) {
|
|
||||||
DWORD valueNameSize = maxValueNameSize + 1; // +1 for null terminator
|
|
||||||
DWORD valueDataSize = maxValueDataSize + 1; // +1 for null terminator
|
|
||||||
DWORD valueType;
|
|
||||||
|
|
||||||
// Clear buffers
|
|
||||||
memset(valueName, 0, valueNameSize);
|
|
||||||
memset(valueData, 0, valueDataSize);
|
|
||||||
|
|
||||||
// Get the font name and file path
|
|
||||||
if (RegEnumValue(hKey, i, valueName, &valueNameSize, NULL, &valueType,
|
|
||||||
valueData, &valueDataSize) == ERROR_SUCCESS) {
|
|
||||||
if (valueType == REG_SZ) {
|
|
||||||
// Add the font file path to the vector
|
|
||||||
std::string fontPath(reinterpret_cast<char*>(valueData),
|
|
||||||
valueDataSize);
|
|
||||||
|
|
||||||
fontPaths.push_back(fontPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] valueName;
|
|
||||||
delete[] valueData;
|
|
||||||
|
|
||||||
RegCloseKey(hKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
// List of common font face names
|
|
||||||
static const std::unordered_set<std::string> commonFontFaceNames = {
|
|
||||||
"arial",
|
|
||||||
"times",
|
|
||||||
"cour",
|
|
||||||
"verdana",
|
|
||||||
"tahoma",
|
|
||||||
"comic",
|
|
||||||
"Impact",
|
|
||||||
"ariblk",
|
|
||||||
"Trebuchet MS",
|
|
||||||
"Georgia",
|
|
||||||
"Palatino Linotype",
|
|
||||||
"Lucida Sans Unicode",
|
|
||||||
"Tahoma",
|
|
||||||
"Lucida Console"};
|
|
||||||
|
|
||||||
for (auto& fontPath : fontPaths) {
|
|
||||||
// Check if the font path has a "C:\" prefix
|
|
||||||
if (fontPath.substr(0, 2) != "C:") {
|
|
||||||
// Add "C:\Windows\Fonts\" prefix to the font path
|
|
||||||
fontPath = absl::StrFormat("C:\\Windows\\Fonts\\%s", fontPath.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the font file has a .ttf or .TTF extension
|
|
||||||
std::string extension = fontPath.substr(fontPath.find_last_of(".") + 1);
|
|
||||||
if (extension == "ttf" || extension == "TTF") {
|
|
||||||
// Get the font face name from the font path
|
|
||||||
std::string fontFaceName =
|
|
||||||
fontPath.substr(fontPath.find_last_of("\\/") + 1);
|
|
||||||
fontFaceName = fontFaceName.substr(0, fontFaceName.find_last_of("."));
|
|
||||||
|
|
||||||
// Check if the font face name is in the common font face names list
|
|
||||||
if (commonFontFaceNames.find(fontFaceName) != commonFontFaceNames.end()) {
|
|
||||||
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 16.0f);
|
|
||||||
|
|
||||||
// Merge icon set
|
|
||||||
// Icon configuration
|
|
||||||
static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
|
|
||||||
ImFontConfig icons_config;
|
|
||||||
static const float ICON_FONT_SIZE = 18.0f;
|
|
||||||
icons_config.MergeMode = true;
|
|
||||||
icons_config.GlyphOffset.y = 5.0f;
|
|
||||||
icons_config.GlyphMinAdvanceX = 13.0f;
|
|
||||||
icons_config.PixelSnapH = true;
|
|
||||||
io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_MD, ICON_FONT_SIZE,
|
|
||||||
&icons_config, icons_ranges);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__linux__)
|
|
||||||
|
|
||||||
void LoadSystemFonts() {
|
void LoadSystemFonts() {
|
||||||
// Load Linux System Fonts into ImGui
|
// Load Linux System Fonts into ImGui
|
||||||
|
// System font loading is now handled by NFD (Native File Dialog)
|
||||||
|
// This function is kept for compatibility but does nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ absl::Status YazeProject::Create(const std::string& project_name, const std::str
|
|||||||
|
|
||||||
metadata.created_date = ss.str();
|
metadata.created_date = ss.str();
|
||||||
metadata.last_modified = ss.str();
|
metadata.last_modified = ss.str();
|
||||||
metadata.yaze_version = "0.3.0"; // TODO: Get from version header
|
metadata.yaze_version = "0.3.1"; // TODO: Get from version header
|
||||||
metadata.version = "2.0";
|
metadata.version = "2.0";
|
||||||
metadata.created_by = "YAZE";
|
metadata.created_by = "YAZE";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "dungeon_object_selector.h"
|
#include "dungeon_object_selector.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "app/core/window.h"
|
#include "app/core/window.h"
|
||||||
#include "app/gfx/arena.h"
|
#include "app/gfx/arena.h"
|
||||||
@@ -1045,7 +1047,10 @@ void DungeonObjectSelector::DrawCompactPropertiesEditor() {
|
|||||||
static int music_id = 0;
|
static int music_id = 0;
|
||||||
|
|
||||||
// Copy current values
|
// Copy current values
|
||||||
strncpy(room_name, properties.name.c_str(), sizeof(room_name) - 1);
|
// Safe string copy with bounds checking
|
||||||
|
size_t name_len = std::min(properties.name.length(), sizeof(room_name) - 1);
|
||||||
|
std::memcpy(room_name, properties.name.c_str(), name_len);
|
||||||
|
room_name[name_len] = '\0';
|
||||||
dungeon_id = properties.dungeon_id;
|
dungeon_id = properties.dungeon_id;
|
||||||
floor_level = properties.floor_level;
|
floor_level = properties.floor_level;
|
||||||
is_boss_room = properties.is_boss_room;
|
is_boss_room = properties.is_boss_room;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ set(
|
|||||||
app/editor/dungeon/dungeon_room_loader.cc
|
app/editor/dungeon/dungeon_room_loader.cc
|
||||||
app/editor/dungeon/dungeon_usage_tracker.cc
|
app/editor/dungeon/dungeon_usage_tracker.cc
|
||||||
app/editor/overworld/overworld_editor.cc
|
app/editor/overworld/overworld_editor.cc
|
||||||
|
app/editor/overworld/overworld_editor_manager.cc
|
||||||
app/editor/sprite/sprite_editor.cc
|
app/editor/sprite/sprite_editor.cc
|
||||||
app/editor/music/music_editor.cc
|
app/editor/music/music_editor.cc
|
||||||
app/editor/message/message_editor.cc
|
app/editor/message/message_editor.cc
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
|
#include "app/core/features.h"
|
||||||
#include "app/core/project.h"
|
#include "app/core/project.h"
|
||||||
#include "app/editor/code/assembly_editor.h"
|
#include "app/editor/code/assembly_editor.h"
|
||||||
#include "app/editor/code/memory_editor.h"
|
#include "app/editor/code/memory_editor.h"
|
||||||
@@ -19,10 +20,9 @@
|
|||||||
#include "app/editor/overworld/overworld_editor.h"
|
#include "app/editor/overworld/overworld_editor.h"
|
||||||
#include "app/editor/sprite/sprite_editor.h"
|
#include "app/editor/sprite/sprite_editor.h"
|
||||||
#include "app/editor/system/popup_manager.h"
|
#include "app/editor/system/popup_manager.h"
|
||||||
#include "app/editor/system/toast_manager.h"
|
|
||||||
#include "app/editor/system/settings_editor.h"
|
#include "app/editor/system/settings_editor.h"
|
||||||
|
#include "app/editor/system/toast_manager.h"
|
||||||
#include "app/emu/emulator.h"
|
#include "app/emu/emulator.h"
|
||||||
#include "app/core/features.h"
|
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "yaze_config.h"
|
#include "yaze_config.h"
|
||||||
|
|
||||||
@@ -100,34 +100,41 @@ class EditorManager {
|
|||||||
absl::Status SetCurrentRom(Rom* rom);
|
absl::Status SetCurrentRom(Rom* rom);
|
||||||
auto GetCurrentRom() -> Rom* { return current_rom_; }
|
auto GetCurrentRom() -> Rom* { return current_rom_; }
|
||||||
auto GetCurrentEditorSet() -> EditorSet* { return current_editor_set_; }
|
auto GetCurrentEditorSet() -> EditorSet* { return current_editor_set_; }
|
||||||
|
|
||||||
// Get current session's feature flags (falls back to global if no session)
|
// Get current session's feature flags (falls back to global if no session)
|
||||||
core::FeatureFlags::Flags* GetCurrentFeatureFlags() {
|
core::FeatureFlags::Flags* GetCurrentFeatureFlags() {
|
||||||
size_t current_index = GetCurrentSessionIndex();
|
size_t current_index = GetCurrentSessionIndex();
|
||||||
if (current_index < sessions_.size()) {
|
if (current_index < sessions_.size()) {
|
||||||
return &sessions_[current_index].feature_flags;
|
return &sessions_[current_index].feature_flags;
|
||||||
}
|
}
|
||||||
return &core::FeatureFlags::get(); // Fallback to global
|
return &core::FeatureFlags::get(); // Fallback to global
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFontGlobalScale(float scale) {
|
||||||
|
font_global_scale_ = scale;
|
||||||
|
ImGui::GetIO().FontGlobalScale = scale;
|
||||||
|
SaveUserSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawHomepage();
|
|
||||||
void DrawWelcomeScreen();
|
void DrawWelcomeScreen();
|
||||||
absl::Status DrawRomSelector();
|
absl::Status DrawRomSelector();
|
||||||
absl::Status LoadRom();
|
absl::Status LoadRom();
|
||||||
absl::Status LoadAssets();
|
absl::Status LoadAssets();
|
||||||
absl::Status SaveRom();
|
absl::Status SaveRom();
|
||||||
|
absl::Status SaveRomAs(const std::string& filename);
|
||||||
absl::Status OpenRomOrProject(const std::string& filename);
|
absl::Status OpenRomOrProject(const std::string& filename);
|
||||||
|
|
||||||
// Enhanced project management
|
// Enhanced project management
|
||||||
absl::Status CreateNewProject(const std::string& template_name = "Basic ROM Hack");
|
absl::Status CreateNewProject(
|
||||||
|
const std::string& template_name = "Basic ROM Hack");
|
||||||
absl::Status OpenProject();
|
absl::Status OpenProject();
|
||||||
absl::Status SaveProject();
|
absl::Status SaveProject();
|
||||||
absl::Status SaveProjectAs();
|
absl::Status SaveProjectAs();
|
||||||
absl::Status ImportProject(const std::string& project_path);
|
absl::Status ImportProject(const std::string& project_path);
|
||||||
absl::Status RepairCurrentProject();
|
absl::Status RepairCurrentProject();
|
||||||
void ShowProjectHelp();
|
void ShowProjectHelp();
|
||||||
|
|
||||||
// Testing system
|
// Testing system
|
||||||
void InitializeTestSuites();
|
void InitializeTestSuites();
|
||||||
|
|
||||||
@@ -157,9 +164,10 @@ class EditorManager {
|
|||||||
bool show_global_search_ = false;
|
bool show_global_search_ = false;
|
||||||
bool show_session_rename_dialog_ = false;
|
bool show_session_rename_dialog_ = false;
|
||||||
bool show_welcome_screen_ = false;
|
bool show_welcome_screen_ = false;
|
||||||
|
bool welcome_screen_manually_closed_ = false;
|
||||||
size_t session_to_rename_ = 0;
|
size_t session_to_rename_ = 0;
|
||||||
char session_rename_buffer_[256] = {};
|
char session_rename_buffer_[256] = {};
|
||||||
|
|
||||||
// Testing interface
|
// Testing interface
|
||||||
bool show_test_dashboard_ = false;
|
bool show_test_dashboard_ = false;
|
||||||
|
|
||||||
@@ -177,18 +185,17 @@ class EditorManager {
|
|||||||
struct RomSession {
|
struct RomSession {
|
||||||
Rom rom;
|
Rom rom;
|
||||||
EditorSet editors;
|
EditorSet editors;
|
||||||
std::string custom_name; // User-defined session name
|
std::string custom_name; // User-defined session name
|
||||||
std::string filepath; // ROM filepath for duplicate detection
|
std::string filepath; // ROM filepath for duplicate detection
|
||||||
core::FeatureFlags::Flags feature_flags; // Per-session feature flags
|
core::FeatureFlags::Flags feature_flags; // Per-session feature flags
|
||||||
|
|
||||||
RomSession() = default;
|
RomSession() = default;
|
||||||
explicit RomSession(Rom&& r)
|
explicit RomSession(Rom&& r) : rom(std::move(r)), editors(&rom) {
|
||||||
: rom(std::move(r)), editors(&rom) {
|
|
||||||
filepath = rom.filename();
|
filepath = rom.filename();
|
||||||
// Initialize with default feature flags
|
// Initialize with default feature flags
|
||||||
feature_flags = core::FeatureFlags::Flags{};
|
feature_flags = core::FeatureFlags::Flags{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get display name (custom name or ROM title)
|
// Get display name (custom name or ROM title)
|
||||||
std::string GetDisplayName() const {
|
std::string GetDisplayName() const {
|
||||||
if (!custom_name.empty()) {
|
if (!custom_name.empty()) {
|
||||||
@@ -212,20 +219,24 @@ class EditorManager {
|
|||||||
// Settings helpers
|
// Settings helpers
|
||||||
void LoadUserSettings();
|
void LoadUserSettings();
|
||||||
void SaveUserSettings();
|
void SaveUserSettings();
|
||||||
|
|
||||||
void RefreshWorkspacePresets();
|
void RefreshWorkspacePresets();
|
||||||
void SaveWorkspacePreset(const std::string& name);
|
void SaveWorkspacePreset(const std::string& name);
|
||||||
void LoadWorkspacePreset(const std::string& name);
|
void LoadWorkspacePreset(const std::string& name);
|
||||||
|
|
||||||
// Workspace management
|
// Workspace management
|
||||||
void CreateNewSession();
|
void CreateNewSession();
|
||||||
void DuplicateCurrentSession();
|
void DuplicateCurrentSession();
|
||||||
void CloseCurrentSession();
|
void CloseCurrentSession();
|
||||||
|
void RemoveSession(size_t index);
|
||||||
void SwitchToSession(size_t index);
|
void SwitchToSession(size_t index);
|
||||||
size_t GetCurrentSessionIndex() const;
|
size_t GetCurrentSessionIndex() const;
|
||||||
|
size_t GetActiveSessionCount() const;
|
||||||
void ResetWorkspaceLayout();
|
void ResetWorkspaceLayout();
|
||||||
|
|
||||||
// Multi-session editor management
|
// Multi-session editor management
|
||||||
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index) const;
|
std::string GenerateUniqueEditorTitle(EditorType type,
|
||||||
|
size_t session_index) const;
|
||||||
void SaveWorkspaceLayout();
|
void SaveWorkspaceLayout();
|
||||||
void LoadWorkspaceLayout();
|
void LoadWorkspaceLayout();
|
||||||
void ShowAllWindows();
|
void ShowAllWindows();
|
||||||
@@ -236,12 +247,12 @@ class EditorManager {
|
|||||||
void LoadDeveloperLayout();
|
void LoadDeveloperLayout();
|
||||||
void LoadDesignerLayout();
|
void LoadDesignerLayout();
|
||||||
void LoadModderLayout();
|
void LoadModderLayout();
|
||||||
|
|
||||||
// Session management helpers
|
// Session management helpers
|
||||||
bool HasDuplicateSession(const std::string& filepath);
|
bool HasDuplicateSession(const std::string& filepath);
|
||||||
void RenameSession(size_t index, const std::string& new_name);
|
void RenameSession(size_t index, const std::string& new_name);
|
||||||
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index);
|
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index);
|
||||||
|
|
||||||
// UI drawing helpers
|
// UI drawing helpers
|
||||||
void DrawSessionSwitcher();
|
void DrawSessionSwitcher();
|
||||||
void DrawSessionManager();
|
void DrawSessionManager();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,7 @@ class MapPropertiesSystem {
|
|||||||
void DrawSpritePropertiesTab(int current_map);
|
void DrawSpritePropertiesTab(int current_map);
|
||||||
void DrawCustomFeaturesTab(int current_map);
|
void DrawCustomFeaturesTab(int current_map);
|
||||||
void DrawTileGraphicsTab(int current_map);
|
void DrawTileGraphicsTab(int current_map);
|
||||||
|
void DrawMusicTab(int current_map);
|
||||||
|
|
||||||
// Utility methods
|
// Utility methods
|
||||||
void RefreshMapProperties();
|
void RefreshMapProperties();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,9 +12,9 @@
|
|||||||
#include "app/gfx/tilemap.h"
|
#include "app/gfx/tilemap.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
#include "app/gui/zeml.h"
|
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
#include "app/editor/overworld/overworld_editor_manager.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
@@ -188,6 +188,16 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
|||||||
void DrawMapPropertiesPanel();
|
void DrawMapPropertiesPanel();
|
||||||
void HandleMapInteraction();
|
void HandleMapInteraction();
|
||||||
void SetupOverworldCanvasContextMenu();
|
void SetupOverworldCanvasContextMenu();
|
||||||
|
|
||||||
|
// Scratch space canvas methods
|
||||||
|
absl::Status DrawScratchSpace();
|
||||||
|
absl::Status SaveCurrentSelectionToScratch(int slot);
|
||||||
|
absl::Status LoadScratchToSelection(int slot);
|
||||||
|
absl::Status ClearScratchSpace(int slot);
|
||||||
|
void DrawScratchSpaceEdits();
|
||||||
|
void DrawScratchSpacePattern();
|
||||||
|
void DrawScratchSpaceSelection();
|
||||||
|
void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id, int slot = -1);
|
||||||
|
|
||||||
absl::Status UpdateUsageStats();
|
absl::Status UpdateUsageStats();
|
||||||
void DrawUsageGrid();
|
void DrawUsageGrid();
|
||||||
@@ -253,6 +263,24 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
|||||||
|
|
||||||
// Map properties system for UI organization
|
// Map properties system for UI organization
|
||||||
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
|
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
|
||||||
|
std::unique_ptr<OverworldEditorManager> overworld_manager_;
|
||||||
|
|
||||||
|
// Scratch space for large layouts
|
||||||
|
// Scratch space canvas for tile16 drawing (like a mini overworld)
|
||||||
|
struct ScratchSpaceSlot {
|
||||||
|
gfx::Bitmap scratch_bitmap;
|
||||||
|
std::array<std::array<int, 32>, 32> tile_data; // 32x32 grid of tile16 IDs
|
||||||
|
bool in_use = false;
|
||||||
|
std::string name = "Empty";
|
||||||
|
int width = 16; // Default 16x16 tiles
|
||||||
|
int height = 16;
|
||||||
|
// Independent selection system for scratch space
|
||||||
|
std::vector<ImVec2> selected_tiles;
|
||||||
|
std::vector<ImVec2> selected_points;
|
||||||
|
bool select_rect_active = false;
|
||||||
|
};
|
||||||
|
std::array<ScratchSpaceSlot, 4> scratch_spaces_;
|
||||||
|
int current_scratch_slot_ = 0;
|
||||||
|
|
||||||
gfx::Tilemap tile16_blockset_;
|
gfx::Tilemap tile16_blockset_;
|
||||||
|
|
||||||
@@ -295,12 +323,12 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
|||||||
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
|
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
|
||||||
gui::CanvasGridSize::k16x16};
|
gui::CanvasGridSize::k16x16};
|
||||||
gui::Canvas properties_canvas_;
|
gui::Canvas properties_canvas_;
|
||||||
|
gui::Canvas scratch_canvas_{"ScratchSpace", ImVec2(320, 480), gui::CanvasGridSize::k32x32};
|
||||||
|
|
||||||
gui::Table toolset_table_{"##ToolsetTable0", 12, kToolsetTableFlags};
|
gui::Table toolset_table_{"##ToolsetTable0", 12, kToolsetTableFlags};
|
||||||
gui::Table map_settings_table_{kOWMapTable.data(), 8, kOWMapFlags,
|
gui::Table map_settings_table_{kOWMapTable.data(), 8, kOWMapFlags,
|
||||||
ImVec2(0, 0)};
|
ImVec2(0, 0)};
|
||||||
|
|
||||||
gui::zeml::Node layout_node_;
|
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
};
|
};
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
423
src/app/editor/overworld/overworld_editor_manager.cc
Normal file
423
src/app/editor/overworld/overworld_editor_manager.cc
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
#include "overworld_editor_manager.h"
|
||||||
|
|
||||||
|
#include "app/gfx/snes_color.h"
|
||||||
|
#include "app/gui/icons.h"
|
||||||
|
#include "app/gui/input.h"
|
||||||
|
#include "app/gui/style.h"
|
||||||
|
#include "app/zelda3/overworld/overworld_map.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace editor {
|
||||||
|
|
||||||
|
using namespace ImGui;
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawV3SettingsPanel() {
|
||||||
|
if (BeginTabItem("v3 Settings")) {
|
||||||
|
Text("ZSCustomOverworld v3 Settings");
|
||||||
|
Separator();
|
||||||
|
|
||||||
|
// Check if custom ASM is applied
|
||||||
|
uint8_t asm_version = GetCustomASMVersion();
|
||||||
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
|
TextColored(ImVec4(0, 1, 0, 1), "Custom Overworld ASM v%d Applied", asm_version);
|
||||||
|
} else if (asm_version == 0x00) {
|
||||||
|
TextColored(ImVec4(1, 1, 0, 1), "Vanilla ROM - Custom features available via flag");
|
||||||
|
} else {
|
||||||
|
TextColored(ImVec4(1, 0, 0, 1), "Custom ASM v%d - Consider upgrading to v3", asm_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator();
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawCustomOverworldSettings());
|
||||||
|
RETURN_IF_ERROR(DrawAreaSpecificSettings());
|
||||||
|
RETURN_IF_ERROR(DrawTransitionSettings());
|
||||||
|
RETURN_IF_ERROR(DrawOverlaySettings());
|
||||||
|
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawCustomOverworldSettings() {
|
||||||
|
if (TreeNode("Custom Overworld Features")) {
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Area-Specific Background Colors",
|
||||||
|
&enable_area_specific_bg_,
|
||||||
|
"Allows each overworld area to have its own background color"));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Main Palette Override",
|
||||||
|
&enable_main_palette_,
|
||||||
|
"Allows each area to override the main palette"));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Mosaic Transitions",
|
||||||
|
&enable_mosaic_,
|
||||||
|
"Enables mosaic screen transitions between areas"));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Custom GFX Groups",
|
||||||
|
&enable_gfx_groups_,
|
||||||
|
"Allows each area to have custom tile GFX groups"));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Subscreen Overlays",
|
||||||
|
&enable_subscreen_overlay_,
|
||||||
|
"Enables custom subscreen overlays (fog, sky, etc.)"));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawBooleanSetting("Enable Animated GFX Override",
|
||||||
|
&enable_animated_gfx_,
|
||||||
|
"Allows each area to have custom animated tiles"));
|
||||||
|
|
||||||
|
Separator();
|
||||||
|
|
||||||
|
if (Button("Apply Custom Overworld ASM")) {
|
||||||
|
RETURN_IF_ERROR(ApplyCustomOverworldASM());
|
||||||
|
}
|
||||||
|
SameLine();
|
||||||
|
HOVER_HINT("Writes the custom overworld settings to ROM");
|
||||||
|
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawAreaSpecificSettings() {
|
||||||
|
if (TreeNode("Area-Specific Settings")) {
|
||||||
|
// Map selection
|
||||||
|
int map_count = zelda3::kNumOverworldMaps;
|
||||||
|
SliderInt("Map Index", ¤t_map_index_, 0, map_count - 1);
|
||||||
|
|
||||||
|
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||||
|
|
||||||
|
// Area size controls
|
||||||
|
RETURN_IF_ERROR(DrawAreaSizeControls());
|
||||||
|
|
||||||
|
// Background color
|
||||||
|
if (enable_area_specific_bg_) {
|
||||||
|
uint16_t bg_color = current_map->area_specific_bg_color();
|
||||||
|
RETURN_IF_ERROR(DrawColorPicker("Background Color", &bg_color));
|
||||||
|
current_map->set_area_specific_bg_color(bg_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main palette
|
||||||
|
if (enable_main_palette_) {
|
||||||
|
uint8_t main_palette = current_map->main_palette();
|
||||||
|
SliderInt("Main Palette", (int*)&main_palette, 0, 5);
|
||||||
|
current_map->set_main_palette(main_palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mosaic settings
|
||||||
|
if (enable_mosaic_) {
|
||||||
|
RETURN_IF_ERROR(DrawMosaicControls());
|
||||||
|
}
|
||||||
|
|
||||||
|
// GFX groups
|
||||||
|
if (enable_gfx_groups_) {
|
||||||
|
RETURN_IF_ERROR(DrawGfxGroupControls());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscreen overlay
|
||||||
|
if (enable_subscreen_overlay_) {
|
||||||
|
uint16_t overlay = current_map->subscreen_overlay();
|
||||||
|
RETURN_IF_ERROR(DrawOverlaySetting("Subscreen Overlay", &overlay));
|
||||||
|
current_map->set_subscreen_overlay(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animated GFX
|
||||||
|
if (enable_animated_gfx_) {
|
||||||
|
uint8_t animated_gfx = current_map->animated_gfx();
|
||||||
|
RETURN_IF_ERROR(DrawGfxGroupSetting("Animated GFX", &animated_gfx));
|
||||||
|
current_map->set_animated_gfx(animated_gfx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawAreaSizeControls() {
|
||||||
|
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||||
|
|
||||||
|
const char* area_size_names[] = {"Small", "Large", "Wide", "Tall"};
|
||||||
|
int current_size = static_cast<int>(current_map->area_size());
|
||||||
|
|
||||||
|
if (Combo("Area Size", ¤t_size, area_size_names, 4)) {
|
||||||
|
current_map->SetAreaSize(static_cast<zelda3::AreaSizeEnum>(current_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawMosaicControls() {
|
||||||
|
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||||
|
const auto& mosaic = current_map->mosaic_expanded();
|
||||||
|
|
||||||
|
bool mosaic_up = mosaic[0];
|
||||||
|
bool mosaic_down = mosaic[1];
|
||||||
|
bool mosaic_left = mosaic[2];
|
||||||
|
bool mosaic_right = mosaic[3];
|
||||||
|
|
||||||
|
if (Checkbox("Mosaic Up", &mosaic_up)) {
|
||||||
|
current_map->set_mosaic_expanded(0, mosaic_up);
|
||||||
|
}
|
||||||
|
SameLine();
|
||||||
|
if (Checkbox("Mosaic Down", &mosaic_down)) {
|
||||||
|
current_map->set_mosaic_expanded(1, mosaic_down);
|
||||||
|
}
|
||||||
|
if (Checkbox("Mosaic Left", &mosaic_left)) {
|
||||||
|
current_map->set_mosaic_expanded(2, mosaic_left);
|
||||||
|
}
|
||||||
|
SameLine();
|
||||||
|
if (Checkbox("Mosaic Right", &mosaic_right)) {
|
||||||
|
current_map->set_mosaic_expanded(3, mosaic_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawGfxGroupControls() {
|
||||||
|
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||||
|
|
||||||
|
Text("Custom Tile GFX Groups:");
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
uint8_t gfx_id = current_map->custom_tileset(i);
|
||||||
|
std::string label = "GFX " + std::to_string(i);
|
||||||
|
RETURN_IF_ERROR(DrawGfxGroupSetting(label.c_str(), &gfx_id));
|
||||||
|
current_map->set_custom_tileset(i, gfx_id);
|
||||||
|
if (i < 7) SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawTransitionSettings() {
|
||||||
|
if (TreeNode("Transition Settings")) {
|
||||||
|
Text("Complex area transition calculations are automatically handled");
|
||||||
|
Text("based on neighboring area sizes (Large, Wide, Tall, Small).");
|
||||||
|
|
||||||
|
if (GetCustomASMVersion() >= 3) {
|
||||||
|
TextColored(ImVec4(0, 1, 0, 1), "Using v3+ enhanced transitions");
|
||||||
|
} else {
|
||||||
|
TextColored(ImVec4(1, 1, 0, 1), "Using vanilla/v2 transitions");
|
||||||
|
}
|
||||||
|
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawOverlaySettings() {
|
||||||
|
if (TreeNode("Interactive Overlay Settings")) {
|
||||||
|
Text("Interactive overlays reveal holes and change map elements.");
|
||||||
|
|
||||||
|
auto* current_map = overworld_->mutable_overworld_map(current_map_index_);
|
||||||
|
|
||||||
|
Text("Map %d has %s", current_map_index_,
|
||||||
|
current_map->has_overlay() ? "interactive overlay" : "no overlay");
|
||||||
|
|
||||||
|
if (current_map->has_overlay()) {
|
||||||
|
Text("Overlay ID: 0x%04X", current_map->overlay_id());
|
||||||
|
Text("Overlay data size: %zu bytes", current_map->overlay_data().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::ApplyCustomOverworldASM() {
|
||||||
|
return overworld_->SaveCustomOverworldASM(
|
||||||
|
enable_area_specific_bg_, enable_main_palette_, enable_mosaic_,
|
||||||
|
enable_gfx_groups_, enable_subscreen_overlay_, enable_animated_gfx_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverworldEditorManager::ValidateV3Compatibility() {
|
||||||
|
uint8_t asm_version = GetCustomASMVersion();
|
||||||
|
return (asm_version >= 3 && asm_version != 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverworldEditorManager::CheckCustomASMApplied() {
|
||||||
|
uint8_t asm_version = GetCustomASMVersion();
|
||||||
|
return (asm_version != 0xFF && asm_version != 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t OverworldEditorManager::GetCustomASMVersion() {
|
||||||
|
return (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawBooleanSetting(const char* label, bool* setting,
|
||||||
|
const char* help_text) {
|
||||||
|
Checkbox(label, setting);
|
||||||
|
if (help_text && IsItemHovered()) {
|
||||||
|
SetTooltip("%s", help_text);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawColorPicker(const char* label, uint16_t* color) {
|
||||||
|
gfx::SnesColor snes_color(*color);
|
||||||
|
ImVec4 imgui_color = snes_color.rgb();
|
||||||
|
|
||||||
|
if (ColorEdit3(label, &imgui_color.x)) {
|
||||||
|
gfx::SnesColor new_color;
|
||||||
|
new_color.set_rgb(imgui_color);
|
||||||
|
*color = new_color.snes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawOverlaySetting(const char* label, uint16_t* overlay) {
|
||||||
|
int overlay_int = *overlay;
|
||||||
|
if (InputInt(label, &overlay_int, 1, 16, ImGuiInputTextFlags_CharsHexadecimal)) {
|
||||||
|
*overlay = static_cast<uint16_t>(overlay_int & 0xFFFF);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawGfxGroupSetting(const char* label, uint8_t* gfx_id,
|
||||||
|
int max_value) {
|
||||||
|
int gfx_int = *gfx_id;
|
||||||
|
if (SliderInt(label, &gfx_int, 0, max_value)) {
|
||||||
|
*gfx_id = static_cast<uint8_t>(gfx_int);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawUnifiedSettingsTable() {
|
||||||
|
// Create a comprehensive settings table that combines toolset and properties
|
||||||
|
if (BeginTable("##UnifiedOverworldSettings", 6,
|
||||||
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
||||||
|
ImGuiTableFlags_BordersV | ImGuiTableFlags_SizingFixedFit)) {
|
||||||
|
|
||||||
|
// Setup columns with proper widths
|
||||||
|
TableSetupColumn(ICON_MD_BUILD " Tools", ImGuiTableColumnFlags_WidthFixed, 120);
|
||||||
|
TableSetupColumn(ICON_MD_MAP " World", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||||
|
TableSetupColumn(ICON_MD_IMAGE " Graphics", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||||
|
TableSetupColumn(ICON_MD_PALETTE " Palette", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||||
|
TableSetupColumn(ICON_MD_SETTINGS " Properties", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
TableSetupColumn(ICON_MD_EXTENSION " v3 Features", ImGuiTableColumnFlags_WidthFixed, 120);
|
||||||
|
TableHeadersRow();
|
||||||
|
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
// Tools column
|
||||||
|
TableNextColumn();
|
||||||
|
RETURN_IF_ERROR(DrawToolsetInSettings());
|
||||||
|
|
||||||
|
// World column
|
||||||
|
TableNextColumn();
|
||||||
|
Text(ICON_MD_PUBLIC " Current World");
|
||||||
|
SetNextItemWidth(80.f);
|
||||||
|
// if (Combo("##world", ¤t_world_, kWorldList.data(), 3)) {
|
||||||
|
// // World change logic would go here
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Graphics column
|
||||||
|
TableNextColumn();
|
||||||
|
Text(ICON_MD_IMAGE " Area Graphics");
|
||||||
|
// Graphics controls would go here
|
||||||
|
|
||||||
|
// Palette column
|
||||||
|
TableNextColumn();
|
||||||
|
Text(ICON_MD_PALETTE " Area Palette");
|
||||||
|
// Palette controls would go here
|
||||||
|
|
||||||
|
// Properties column
|
||||||
|
TableNextColumn();
|
||||||
|
Text(ICON_MD_SETTINGS " Map Properties");
|
||||||
|
// Map properties would go here
|
||||||
|
|
||||||
|
// v3 Features column
|
||||||
|
TableNextColumn();
|
||||||
|
uint8_t asm_version = GetCustomASMVersion();
|
||||||
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
|
TextColored(ImVec4(0, 1, 0, 1), ICON_MD_NEW_RELEASES " v3 Active");
|
||||||
|
if (Button(ICON_MD_TUNE " Settings")) {
|
||||||
|
// Open v3 settings
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1), ICON_MD_UPGRADE " v3 Available");
|
||||||
|
if (Button(ICON_MD_UPGRADE " Upgrade")) {
|
||||||
|
// Trigger upgrade
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::DrawToolsetInSettings() {
|
||||||
|
// Compact toolset layout within the settings table
|
||||||
|
BeginGroup();
|
||||||
|
|
||||||
|
// Core editing tools in a compact grid
|
||||||
|
if (Button(ICON_MD_PAN_TOOL_ALT, ImVec2(25, 25))) {
|
||||||
|
// Set PAN mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Pan (1)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_DRAW, ImVec2(25, 25))) {
|
||||||
|
// Set DRAW_TILE mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Draw Tile (2)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_DOOR_FRONT, ImVec2(25, 25))) {
|
||||||
|
// Set ENTRANCES mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Entrances (3)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_DOOR_BACK, ImVec2(25, 25))) {
|
||||||
|
// Set EXITS mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Exits (4)");
|
||||||
|
|
||||||
|
// Second row
|
||||||
|
if (Button(ICON_MD_GRASS, ImVec2(25, 25))) {
|
||||||
|
// Set ITEMS mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Items (5)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_PEST_CONTROL_RODENT, ImVec2(25, 25))) {
|
||||||
|
// Set SPRITES mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Sprites (6)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_ADD_LOCATION, ImVec2(25, 25))) {
|
||||||
|
// Set TRANSPORTS mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Transports (7)");
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
if (Button(ICON_MD_MUSIC_NOTE, ImVec2(25, 25))) {
|
||||||
|
// Set MUSIC mode
|
||||||
|
}
|
||||||
|
HOVER_HINT("Music (8)");
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::HandleCanvasSelectionTransfer() {
|
||||||
|
// This could be called to manage bidirectional selection transfer
|
||||||
|
// For now, it's a placeholder for future canvas interaction management
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::TransferOverworldSelectionToScratch() {
|
||||||
|
// Transfer logic would go here to copy selections from overworld to scratch
|
||||||
|
// This could be integrated with the editor's context system
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditorManager::TransferScratchSelectionToOverworld() {
|
||||||
|
// Transfer logic would go here to copy selections from scratch to overworld
|
||||||
|
// This could be integrated with the editor's context system
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace editor
|
||||||
|
} // namespace yaze
|
||||||
108
src/app/editor/overworld/overworld_editor_manager.h
Normal file
108
src/app/editor/overworld/overworld_editor_manager.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#ifndef YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||||
|
#define YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
#include "app/gui/canvas.h"
|
||||||
|
#include "app/gui/input.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace editor {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
enum class EditingMode;
|
||||||
|
class OverworldEditor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class OverworldEditorManager
|
||||||
|
* @brief Manages the complex overworld editor functionality and v3 features
|
||||||
|
*
|
||||||
|
* This class separates the complex overworld editing functionality from the main
|
||||||
|
* OverworldEditor class to improve maintainability and organization, especially
|
||||||
|
* for ZSCustomOverworld v3 features.
|
||||||
|
*/
|
||||||
|
class OverworldEditorManager {
|
||||||
|
public:
|
||||||
|
OverworldEditorManager(zelda3::Overworld* overworld, Rom* rom,
|
||||||
|
OverworldEditor* editor = nullptr)
|
||||||
|
: overworld_(overworld), rom_(rom), editor_(editor) {}
|
||||||
|
|
||||||
|
// Set editor context for mode changes
|
||||||
|
void SetEditorContext(OverworldEditor* editor) { editor_ = editor; }
|
||||||
|
|
||||||
|
// v3 Feature Management
|
||||||
|
absl::Status DrawV3SettingsPanel();
|
||||||
|
absl::Status DrawCustomOverworldSettings();
|
||||||
|
absl::Status DrawAreaSpecificSettings();
|
||||||
|
absl::Status DrawTransitionSettings();
|
||||||
|
absl::Status DrawOverlaySettings();
|
||||||
|
|
||||||
|
// Map Properties Management
|
||||||
|
absl::Status DrawMapPropertiesTable();
|
||||||
|
absl::Status DrawUnifiedSettingsTable();
|
||||||
|
absl::Status DrawToolsetInSettings();
|
||||||
|
absl::Status DrawAreaSizeControls();
|
||||||
|
absl::Status DrawMosaicControls();
|
||||||
|
absl::Status DrawPaletteControls();
|
||||||
|
absl::Status DrawGfxGroupControls();
|
||||||
|
|
||||||
|
// Save/Load Operations for v3
|
||||||
|
absl::Status SaveCustomOverworldData();
|
||||||
|
absl::Status LoadCustomOverworldData();
|
||||||
|
absl::Status ApplyCustomOverworldASM();
|
||||||
|
|
||||||
|
// Canvas Interaction Management
|
||||||
|
absl::Status HandleCanvasSelectionTransfer();
|
||||||
|
absl::Status TransferOverworldSelectionToScratch();
|
||||||
|
absl::Status TransferScratchSelectionToOverworld();
|
||||||
|
|
||||||
|
// Validation and Checks
|
||||||
|
bool ValidateV3Compatibility();
|
||||||
|
bool CheckCustomASMApplied();
|
||||||
|
uint8_t GetCustomASMVersion();
|
||||||
|
|
||||||
|
// Getters/Setters for v3 settings
|
||||||
|
auto enable_area_specific_bg() const { return enable_area_specific_bg_; }
|
||||||
|
auto enable_main_palette() const { return enable_main_palette_; }
|
||||||
|
auto enable_mosaic() const { return enable_mosaic_; }
|
||||||
|
auto enable_gfx_groups() const { return enable_gfx_groups_; }
|
||||||
|
auto enable_subscreen_overlay() const { return enable_subscreen_overlay_; }
|
||||||
|
auto enable_animated_gfx() const { return enable_animated_gfx_; }
|
||||||
|
|
||||||
|
void set_enable_area_specific_bg(bool value) { enable_area_specific_bg_ = value; }
|
||||||
|
void set_enable_main_palette(bool value) { enable_main_palette_ = value; }
|
||||||
|
void set_enable_mosaic(bool value) { enable_mosaic_ = value; }
|
||||||
|
void set_enable_gfx_groups(bool value) { enable_gfx_groups_ = value; }
|
||||||
|
void set_enable_subscreen_overlay(bool value) { enable_subscreen_overlay_ = value; }
|
||||||
|
void set_enable_animated_gfx(bool value) { enable_animated_gfx_ = value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
zelda3::Overworld* overworld_;
|
||||||
|
Rom* rom_;
|
||||||
|
OverworldEditor* editor_;
|
||||||
|
|
||||||
|
// v3 Feature flags
|
||||||
|
bool enable_area_specific_bg_ = false;
|
||||||
|
bool enable_main_palette_ = false;
|
||||||
|
bool enable_mosaic_ = false;
|
||||||
|
bool enable_gfx_groups_ = false;
|
||||||
|
bool enable_subscreen_overlay_ = false;
|
||||||
|
bool enable_animated_gfx_ = false;
|
||||||
|
|
||||||
|
// Current editing state
|
||||||
|
int current_map_index_ = 0;
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
absl::Status DrawBooleanSetting(const char* label, bool* setting, const char* help_text = nullptr);
|
||||||
|
absl::Status DrawColorPicker(const char* label, uint16_t* color);
|
||||||
|
absl::Status DrawOverlaySetting(const char* label, uint16_t* overlay);
|
||||||
|
absl::Status DrawGfxGroupSetting(const char* label, uint8_t* gfx_id, int max_value = 255);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace editor
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_EDITOR_OVERWORLD_OVERWORLD_EDITOR_MANAGER_H
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@
|
|||||||
#define YAZE_APP_EDITOR_TILE16EDITOR_H
|
#define YAZE_APP_EDITOR_TILE16EDITOR_H
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
@@ -19,29 +21,43 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
|
// Constants for tile editing
|
||||||
|
constexpr int kTile16Size = 16;
|
||||||
|
constexpr int kTile8Size = 8;
|
||||||
|
constexpr int kTilesheetEditorWidth = 0x100;
|
||||||
|
constexpr int kTilesheetEditorHeight = 0x4000;
|
||||||
|
constexpr int kTile16CanvasSize = 0x20;
|
||||||
|
constexpr int kTile8CanvasHeight = 0x175;
|
||||||
|
constexpr int kNumScratchSlots = 4;
|
||||||
|
constexpr int kNumPalettes = 8;
|
||||||
|
constexpr int kTile8PixelCount = 64;
|
||||||
|
constexpr int kTile16PixelCount = 256;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Popup window to edit Tile16 data
|
* @brief Popup window to edit Tile16 data
|
||||||
*/
|
*/
|
||||||
class Tile16Editor : public gfx::GfxContext {
|
class Tile16Editor : public gfx::GfxContext {
|
||||||
public:
|
public:
|
||||||
Tile16Editor(Rom *rom, gfx::Tilemap *tile16_blockset)
|
Tile16Editor(Rom* rom, gfx::Tilemap* tile16_blockset)
|
||||||
: rom_(rom), tile16_blockset_(tile16_blockset) {}
|
: rom_(rom), tile16_blockset_(tile16_blockset) {}
|
||||||
absl::Status Initialize(const gfx::Bitmap &tile16_blockset_bmp,
|
absl::Status Initialize(const gfx::Bitmap& tile16_blockset_bmp,
|
||||||
const gfx::Bitmap ¤t_gfx_bmp,
|
const gfx::Bitmap& current_gfx_bmp,
|
||||||
std::array<uint8_t, 0x200> &all_tiles_types);
|
std::array<uint8_t, 0x200>& all_tiles_types);
|
||||||
|
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
|
|
||||||
void DrawTile16Editor();
|
void DrawTile16Editor();
|
||||||
absl::Status UpdateTile16Transfer();
|
|
||||||
absl::Status UpdateBlockset();
|
absl::Status UpdateBlockset();
|
||||||
|
|
||||||
absl::Status DrawToCurrentTile16(ImVec2 pos);
|
// Scratch space for tile16 layouts
|
||||||
|
void DrawScratchSpace();
|
||||||
|
absl::Status SaveLayoutToScratch(int slot);
|
||||||
|
absl::Status LoadLayoutFromScratch(int slot);
|
||||||
|
|
||||||
|
absl::Status DrawToCurrentTile16(ImVec2 pos, const gfx::Bitmap* source_tile = nullptr);
|
||||||
|
|
||||||
absl::Status UpdateTile16Edit();
|
absl::Status UpdateTile16Edit();
|
||||||
|
|
||||||
absl::Status UpdateTransferTileCanvas();
|
|
||||||
|
|
||||||
absl::Status LoadTile8();
|
absl::Status LoadTile8();
|
||||||
|
|
||||||
absl::Status SetCurrentTile(int id);
|
absl::Status SetCurrentTile(int id);
|
||||||
@@ -53,14 +69,69 @@ class Tile16Editor : public gfx::GfxContext {
|
|||||||
absl::Status LoadTile16FromScratchSpace(int slot);
|
absl::Status LoadTile16FromScratchSpace(int slot);
|
||||||
absl::Status ClearScratchSpace(int slot);
|
absl::Status ClearScratchSpace(int slot);
|
||||||
|
|
||||||
void set_rom(Rom *rom) { rom_ = rom; }
|
// Advanced editing features
|
||||||
Rom *rom() const { return rom_; }
|
absl::Status FlipTile16Horizontal();
|
||||||
|
absl::Status FlipTile16Vertical();
|
||||||
|
absl::Status RotateTile16();
|
||||||
|
absl::Status FillTile16WithTile8(int tile8_id);
|
||||||
|
absl::Status AutoTileTile16();
|
||||||
|
absl::Status ClearTile16();
|
||||||
|
|
||||||
|
// Palette management
|
||||||
|
absl::Status CyclePalette(bool forward = true);
|
||||||
|
absl::Status ApplyPaletteToAll(uint8_t palette_id);
|
||||||
|
absl::Status PreviewPaletteChange(uint8_t palette_id);
|
||||||
|
|
||||||
|
// Batch operations
|
||||||
|
absl::Status ApplyToSelection(const std::function<void(int)>& operation);
|
||||||
|
absl::Status BatchEdit(const std::vector<int>& tile_ids,
|
||||||
|
const std::function<void(int)>& operation);
|
||||||
|
|
||||||
|
// History and undo system
|
||||||
|
absl::Status Undo();
|
||||||
|
absl::Status Redo();
|
||||||
|
void SaveUndoState();
|
||||||
|
|
||||||
|
// Live preview system
|
||||||
|
void EnableLivePreview(bool enable) { live_preview_enabled_ = enable; }
|
||||||
|
absl::Status UpdateLivePreview();
|
||||||
|
|
||||||
|
// Validation and integrity checks
|
||||||
|
absl::Status ValidateTile16Data();
|
||||||
|
bool IsTile16Valid(int tile_id) const;
|
||||||
|
|
||||||
|
// Integration with overworld system
|
||||||
|
absl::Status SaveTile16ToROM();
|
||||||
|
absl::Status UpdateOverworldTilemap();
|
||||||
|
absl::Status CommitChangesToBlockset();
|
||||||
|
absl::Status CommitChangesToOverworld();
|
||||||
|
absl::Status DiscardChanges();
|
||||||
|
|
||||||
|
// Helper methods for palette management
|
||||||
|
absl::Status UpdateTile8Palette(int tile8_id);
|
||||||
|
absl::Status RefreshAllPalettes();
|
||||||
|
void DrawPaletteSettings();
|
||||||
|
|
||||||
|
// ROM data access and modification
|
||||||
|
absl::Status UpdateROMTile16Data();
|
||||||
|
absl::Status RefreshTile16Blockset();
|
||||||
|
gfx::Tile16* GetCurrentTile16Data();
|
||||||
|
absl::Status RegenerateTile16BitmapFromROM();
|
||||||
|
|
||||||
|
// Manual tile8 input controls
|
||||||
|
void DrawManualTile8Inputs();
|
||||||
|
|
||||||
|
void set_rom(Rom* rom) { rom_ = rom; }
|
||||||
|
Rom* rom() const { return rom_; }
|
||||||
|
|
||||||
|
// Callback for when changes are committed to notify parent editor
|
||||||
|
void set_on_changes_committed(std::function<absl::Status()> callback) {
|
||||||
|
on_changes_committed_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rom *rom_ = nullptr;
|
Rom* rom_ = nullptr;
|
||||||
bool map_blockset_loaded_ = false;
|
bool map_blockset_loaded_ = false;
|
||||||
bool transfer_started_ = false;
|
|
||||||
bool transfer_blockset_loaded_ = false;
|
|
||||||
bool x_flip = false;
|
bool x_flip = false;
|
||||||
bool y_flip = false;
|
bool y_flip = false;
|
||||||
bool priority_tile = false;
|
bool priority_tile = false;
|
||||||
@@ -78,19 +149,67 @@ class Tile16Editor : public gfx::GfxContext {
|
|||||||
std::array<gfx::Bitmap, 4> scratch_space_;
|
std::array<gfx::Bitmap, 4> scratch_space_;
|
||||||
std::array<bool, 4> scratch_space_used_ = {false, false, false, false};
|
std::array<bool, 4> scratch_space_used_ = {false, false, false, false};
|
||||||
|
|
||||||
|
// Layout scratch space for tile16 arrangements (4 slots of 8x8 grids)
|
||||||
|
struct LayoutScratch {
|
||||||
|
std::array<std::array<int, 8>, 8> tile_layout; // 8x8 grid of tile16 IDs
|
||||||
|
bool in_use = false;
|
||||||
|
std::string name = "Empty";
|
||||||
|
};
|
||||||
|
std::array<LayoutScratch, 4> layout_scratch_;
|
||||||
|
|
||||||
|
// Undo/Redo system
|
||||||
|
struct UndoState {
|
||||||
|
int tile_id;
|
||||||
|
gfx::Bitmap tile_bitmap;
|
||||||
|
gfx::Tile16 tile_data;
|
||||||
|
uint8_t palette;
|
||||||
|
bool x_flip, y_flip, priority;
|
||||||
|
};
|
||||||
|
std::vector<UndoState> undo_stack_;
|
||||||
|
std::vector<UndoState> redo_stack_;
|
||||||
|
static constexpr size_t kMaxUndoStates_ = 50;
|
||||||
|
|
||||||
|
// Live preview system
|
||||||
|
bool live_preview_enabled_ = true;
|
||||||
|
gfx::Bitmap preview_tile16_;
|
||||||
|
bool preview_dirty_ = false;
|
||||||
|
|
||||||
|
// Selection system
|
||||||
|
std::vector<int> selected_tiles_;
|
||||||
|
int selection_start_tile_ = -1;
|
||||||
|
bool multi_select_mode_ = false;
|
||||||
|
|
||||||
|
// Advanced editing state
|
||||||
|
bool auto_tile_mode_ = false;
|
||||||
|
bool grid_snap_enabled_ = true;
|
||||||
|
bool show_tile_info_ = true;
|
||||||
|
bool show_palette_preview_ = true;
|
||||||
|
|
||||||
|
// Palette management settings
|
||||||
|
bool show_palette_settings_ = false;
|
||||||
|
int current_palette_group_ = 0; // 0=overworld_main, 1=aux1, 2=aux2, etc.
|
||||||
|
uint8_t palette_normalization_mask_ = 0x0F; // Default 4-bit mask
|
||||||
|
bool auto_normalize_pixels_ = true;
|
||||||
|
|
||||||
|
// Performance tracking
|
||||||
|
std::chrono::steady_clock::time_point last_edit_time_;
|
||||||
|
bool batch_mode_ = false;
|
||||||
|
|
||||||
util::NotifyValue<uint32_t> notify_tile16;
|
util::NotifyValue<uint32_t> notify_tile16;
|
||||||
util::NotifyValue<uint8_t> notify_palette;
|
util::NotifyValue<uint8_t> notify_palette;
|
||||||
|
|
||||||
std::array<uint8_t, 0x200> all_tiles_types_;
|
std::array<uint8_t, 0x200> all_tiles_types_;
|
||||||
|
|
||||||
// Tile16 blockset for selecting the tile to edit
|
// Tile16 blockset for selecting the tile to edit
|
||||||
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100, 0x4000),
|
gui::Canvas blockset_canvas_{
|
||||||
gui::CanvasGridSize::k32x32,};
|
"blocksetCanvas", ImVec2(kTilesheetEditorWidth, kTilesheetEditorHeight),
|
||||||
|
gui::CanvasGridSize::k32x32};
|
||||||
gfx::Bitmap tile16_blockset_bmp_;
|
gfx::Bitmap tile16_blockset_bmp_;
|
||||||
|
|
||||||
// Canvas for editing the selected tile
|
// Canvas for editing the selected tile - optimized for 2x2 grid of 8x8 tiles (16x16 total)
|
||||||
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(0x40, 0x40),
|
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas",
|
||||||
gui::CanvasGridSize::k64x64};
|
ImVec2(64, 64), // Fixed 64x64 display size (16x16 pixels at 4x scale)
|
||||||
|
gui::CanvasGridSize::k8x8, 4.0F}; // 8x8 grid with 4x scale for clarity
|
||||||
gfx::Bitmap current_tile16_bmp_;
|
gfx::Bitmap current_tile16_bmp_;
|
||||||
|
|
||||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||||
@@ -100,23 +219,18 @@ class Tile16Editor : public gfx::GfxContext {
|
|||||||
gui::CanvasGridSize::k32x32};
|
gui::CanvasGridSize::k32x32};
|
||||||
gfx::Bitmap current_gfx_bmp_;
|
gfx::Bitmap current_gfx_bmp_;
|
||||||
|
|
||||||
gui::Canvas transfer_canvas_;
|
|
||||||
gfx::Bitmap transfer_blockset_bmp_;
|
|
||||||
|
|
||||||
gui::Table tile_edit_table_{"##TileEditTable", 3, ImGuiTableFlags_Borders};
|
gui::Table tile_edit_table_{"##TileEditTable", 3, ImGuiTableFlags_Borders};
|
||||||
|
|
||||||
gfx::Tilemap *tile16_blockset_ = nullptr;
|
gfx::Tilemap* tile16_blockset_ = nullptr;
|
||||||
std::vector<gfx::Bitmap> current_gfx_individual_;
|
std::vector<gfx::Bitmap> current_gfx_individual_;
|
||||||
|
|
||||||
PaletteEditor palette_editor_;
|
PaletteEditor palette_editor_;
|
||||||
gfx::SnesPalette palette_;
|
gfx::SnesPalette palette_;
|
||||||
|
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
|
|
||||||
Rom *transfer_rom_ = nullptr;
|
// Callback to notify parent editor when changes are committed
|
||||||
zelda3::Overworld transfer_overworld_{transfer_rom_};
|
std::function<absl::Status()> on_changes_committed_;
|
||||||
std::array<gfx::Bitmap, kNumGfxSheets> transfer_gfx_;
|
|
||||||
absl::Status transfer_status_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ void PopupManager::Initialize() {
|
|||||||
popups_["Workspace Help"] = {"Workspace Help", false, [this]() { DrawWorkspaceHelpPopup(); }};
|
popups_["Workspace Help"] = {"Workspace Help", false, [this]() { DrawWorkspaceHelpPopup(); }};
|
||||||
popups_["Session Limit Warning"] = {"Session Limit Warning", false, [this]() { DrawSessionLimitWarningPopup(); }};
|
popups_["Session Limit Warning"] = {"Session Limit Warning", false, [this]() { DrawSessionLimitWarningPopup(); }};
|
||||||
popups_["Layout Reset Confirm"] = {"Reset Layout Confirmation", false, [this]() { DrawLayoutResetConfirmPopup(); }};
|
popups_["Layout Reset Confirm"] = {"Reset Layout Confirmation", false, [this]() { DrawLayoutResetConfirmPopup(); }};
|
||||||
|
|
||||||
|
// Settings popups (accessible without ROM)
|
||||||
|
popups_["Display Settings"] = {"Display Settings", false, [this]() { DrawDisplaySettingsPopup(); }};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopupManager::DrawPopups() {
|
void PopupManager::DrawPopups() {
|
||||||
@@ -48,7 +51,14 @@ void PopupManager::DrawPopups() {
|
|||||||
for (auto& [name, params] : popups_) {
|
for (auto& [name, params] : popups_) {
|
||||||
if (params.is_visible) {
|
if (params.is_visible) {
|
||||||
OpenPopup(name.c_str());
|
OpenPopup(name.c_str());
|
||||||
if (BeginPopupModal(name.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
|
// Special handling for Display Settings popup to make it resizable
|
||||||
|
ImGuiWindowFlags popup_flags = ImGuiWindowFlags_AlwaysAutoResize;
|
||||||
|
if (name == "Display Settings") {
|
||||||
|
popup_flags = ImGuiWindowFlags_None; // Allow resizing for display settings
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BeginPopupModal(name.c_str(), nullptr, popup_flags)) {
|
||||||
params.draw_function();
|
params.draw_function();
|
||||||
EndPopup();
|
EndPopup();
|
||||||
}
|
}
|
||||||
@@ -491,5 +501,46 @@ void PopupManager::DrawLayoutResetConfirmPopup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PopupManager::DrawDisplaySettingsPopup() {
|
||||||
|
// Set a comfortable default size with natural constraints
|
||||||
|
SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
|
||||||
|
SetNextWindowSizeConstraints(ImVec2(600, 400), ImVec2(FLT_MAX, FLT_MAX));
|
||||||
|
|
||||||
|
Text("%s Display & Theme Settings", ICON_MD_DISPLAY_SETTINGS);
|
||||||
|
TextWrapped("Customize your YAZE experience - accessible anytime!");
|
||||||
|
Separator();
|
||||||
|
|
||||||
|
// Create a child window for scrollable content to avoid table conflicts
|
||||||
|
// Use remaining space minus the close button area
|
||||||
|
float available_height = GetContentRegionAvail().y - 60; // Reserve space for close button
|
||||||
|
if (BeginChild("DisplaySettingsContent", ImVec2(0, available_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||||
|
// Use the popup-safe version to avoid table conflicts
|
||||||
|
gui::DrawDisplaySettingsForPopup();
|
||||||
|
|
||||||
|
Separator();
|
||||||
|
gui::TextWithSeparators("Font Manager");
|
||||||
|
gui::DrawFontManager();
|
||||||
|
|
||||||
|
// Global font scale (moved from the old display settings window)
|
||||||
|
ImGuiIO &io = GetIO();
|
||||||
|
Separator();
|
||||||
|
Text("Global Font Scale");
|
||||||
|
static float font_global_scale = io.FontGlobalScale;
|
||||||
|
if (SliderFloat("##global_scale", &font_global_scale, 0.5f, 1.8f, "%.2f")) {
|
||||||
|
if (editor_manager_) {
|
||||||
|
editor_manager_->SetFontGlobalScale(font_global_scale);
|
||||||
|
} else {
|
||||||
|
io.FontGlobalScale = font_global_scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndChild();
|
||||||
|
|
||||||
|
Separator();
|
||||||
|
if (Button("Close", gui::kDefaultModalSize)) {
|
||||||
|
Hide("Display Settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ class PopupManager {
|
|||||||
void DrawWorkspaceHelpPopup();
|
void DrawWorkspaceHelpPopup();
|
||||||
void DrawSessionLimitWarningPopup();
|
void DrawSessionLimitWarningPopup();
|
||||||
void DrawLayoutResetConfirmPopup();
|
void DrawLayoutResetConfirmPopup();
|
||||||
|
|
||||||
|
// Settings popups (accessible without ROM)
|
||||||
|
void DrawDisplaySettingsPopup();
|
||||||
|
|
||||||
EditorManager* editor_manager_;
|
EditorManager* editor_manager_;
|
||||||
std::unordered_map<std::string, PopupParams> popups_;
|
std::unordered_map<std::string, PopupParams> popups_;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gui/color.h"
|
#include "app/gui/color.h"
|
||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
|
#include "app/gui/canvas_utils.h"
|
||||||
|
#include "util/log.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
#include "imgui_memory_editor.h"
|
#include "imgui_memory_editor.h"
|
||||||
|
|
||||||
@@ -45,10 +47,135 @@ ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// Canvas class implementation begins here
|
||||||
|
|
||||||
|
void Canvas::InitializeDefaults() {
|
||||||
|
// Initialize configuration with sensible defaults
|
||||||
|
config_.enable_grid = true;
|
||||||
|
config_.enable_hex_labels = false;
|
||||||
|
config_.enable_custom_labels = false;
|
||||||
|
config_.enable_context_menu = true;
|
||||||
|
config_.is_draggable = false;
|
||||||
|
config_.grid_step = 32.0f;
|
||||||
|
config_.global_scale = 1.0f;
|
||||||
|
config_.canvas_size = ImVec2(0, 0);
|
||||||
|
config_.custom_canvas_size = false;
|
||||||
|
|
||||||
|
// Initialize selection state
|
||||||
|
selection_.Clear();
|
||||||
|
|
||||||
|
// Initialize palette editor
|
||||||
|
palette_editor_ = std::make_unique<EnhancedPaletteEditor>();
|
||||||
|
|
||||||
|
// Initialize legacy compatibility variables to match config
|
||||||
|
enable_grid_ = config_.enable_grid;
|
||||||
|
enable_hex_tile_labels_ = config_.enable_hex_labels;
|
||||||
|
enable_custom_labels_ = config_.enable_custom_labels;
|
||||||
|
enable_context_menu_ = config_.enable_context_menu;
|
||||||
|
draggable_ = config_.is_draggable;
|
||||||
|
custom_step_ = config_.grid_step;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
custom_canvas_size_ = config_.custom_canvas_size;
|
||||||
|
select_rect_active_ = selection_.select_rect_active;
|
||||||
|
selected_tile_pos_ = selection_.selected_tile_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::Cleanup() {
|
||||||
|
palette_editor_.reset();
|
||||||
|
selection_.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::InitializePaletteEditor(Rom* rom) {
|
||||||
|
rom_ = rom;
|
||||||
|
if (palette_editor_) {
|
||||||
|
palette_editor_->Initialize(rom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::ShowPaletteEditor() {
|
||||||
|
if (palette_editor_ && bitmap_) {
|
||||||
|
auto mutable_palette = bitmap_->mutable_palette();
|
||||||
|
palette_editor_->ShowPaletteEditor(*mutable_palette, "Canvas Palette Editor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::ShowColorAnalysis() {
|
||||||
|
if (palette_editor_ && bitmap_) {
|
||||||
|
palette_editor_->ShowColorAnalysis(*bitmap_, "Canvas Color Analysis");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
|
||||||
|
if (palette_editor_ && bitmap_) {
|
||||||
|
return palette_editor_->ApplyROMPalette(bitmap_, group_index, palette_index);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size reporting methods for table integration
|
||||||
|
ImVec2 Canvas::GetMinimumSize() const {
|
||||||
|
return CanvasUtils::CalculateMinimumCanvasSize(config_.content_size, config_.global_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 Canvas::GetPreferredSize() const {
|
||||||
|
return CanvasUtils::CalculatePreferredCanvasSize(config_.content_size, config_.global_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::ReserveTableSpace(const std::string& label) {
|
||||||
|
ImVec2 size = config_.auto_resize ? GetPreferredSize() : config_.canvas_size;
|
||||||
|
CanvasUtils::ReserveCanvasSpace(size, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::BeginTableCanvas(const std::string& label) {
|
||||||
|
if (config_.auto_resize) {
|
||||||
|
ImVec2 preferred_size = GetPreferredSize();
|
||||||
|
CanvasUtils::SetNextCanvasSize(preferred_size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin child window that properly reports size to tables
|
||||||
|
std::string child_id = canvas_id_ + "_TableChild";
|
||||||
|
ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
|
||||||
|
|
||||||
|
bool result = ImGui::BeginChild(child_id.c_str(), child_size,
|
||||||
|
true, // Always show border for table integration
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
|
|
||||||
|
if (!label.empty()) {
|
||||||
|
ImGui::Text("%s", label.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::EndTableCanvas() {
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improved interaction detection methods
|
||||||
|
bool Canvas::HasValidSelection() const {
|
||||||
|
return !points_.empty() && points_.size() >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::WasClicked(ImGuiMouseButton button) const {
|
||||||
|
return ImGui::IsItemClicked(button) && HasValidSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
|
||||||
|
return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) && HasValidSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 Canvas::GetLastClickPosition() const {
|
||||||
|
if (HasValidSelection()) {
|
||||||
|
return points_[0]; // Return the first point of the selection
|
||||||
|
}
|
||||||
|
return ImVec2(-1, -1); // Invalid position
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
||||||
const std::function<void()> &event,
|
const std::function<void()> &event,
|
||||||
int tile_size, float scale) {
|
int tile_size, float scale) {
|
||||||
global_scale_ = scale;
|
config_.global_scale = scale;
|
||||||
|
global_scale_ = scale; // Legacy compatibility
|
||||||
DrawBackground();
|
DrawBackground();
|
||||||
DrawContextMenu();
|
DrawContextMenu();
|
||||||
DrawBitmap(bitmap, 2, scale);
|
DrawBitmap(bitmap, 2, scale);
|
||||||
@@ -60,7 +187,8 @@ void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
|
void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
|
||||||
enable_custom_labels_ = true;
|
config_.enable_custom_labels = true;
|
||||||
|
enable_custom_labels_ = true; // Legacy compatibility
|
||||||
DrawBackground(bg_size);
|
DrawBackground(bg_size);
|
||||||
DrawInfoGrid(grid_size, 8, label_id);
|
DrawInfoGrid(grid_size, 8, label_id);
|
||||||
DrawOverlay();
|
DrawOverlay();
|
||||||
@@ -69,21 +197,27 @@ void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
|
|||||||
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
||||||
draw_list_ = GetWindowDrawList();
|
draw_list_ = GetWindowDrawList();
|
||||||
canvas_p0_ = GetCursorScreenPos();
|
canvas_p0_ = GetCursorScreenPos();
|
||||||
if (!custom_canvas_size_) canvas_sz_ = GetContentRegionAvail();
|
|
||||||
if (canvas_size.x != 0) canvas_sz_ = canvas_size;
|
// Calculate canvas size using utility function
|
||||||
canvas_p1_ = ImVec2(canvas_p0_.x + (canvas_sz_.x * global_scale_),
|
ImVec2 content_region = GetContentRegionAvail();
|
||||||
canvas_p0_.y + (canvas_sz_.y * global_scale_));
|
canvas_sz_ = CanvasUtils::CalculateCanvasSize(content_region, config_.canvas_size, config_.custom_canvas_size);
|
||||||
|
|
||||||
|
if (canvas_size.x != 0) {
|
||||||
|
canvas_sz_ = canvas_size;
|
||||||
|
config_.canvas_size = canvas_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate scaled canvas bounds
|
||||||
|
ImVec2 scaled_size = CanvasUtils::CalculateScaledCanvasSize(canvas_sz_, config_.global_scale);
|
||||||
|
canvas_p1_ = ImVec2(canvas_p0_.x + scaled_size.x, canvas_p0_.y + scaled_size.y);
|
||||||
|
|
||||||
// Draw border and background color
|
// Draw border and background color
|
||||||
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
|
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
|
||||||
draw_list_->AddRect(canvas_p0_, canvas_p1_, kWhiteColor);
|
draw_list_->AddRect(canvas_p0_, canvas_p1_, kWhiteColor);
|
||||||
|
|
||||||
ImGui::InvisibleButton(
|
ImGui::InvisibleButton(canvas_id_.c_str(), scaled_size, kMouseFlags);
|
||||||
canvas_id_.c_str(),
|
|
||||||
ImVec2(canvas_sz_.x * global_scale_, canvas_sz_.y * global_scale_),
|
|
||||||
kMouseFlags);
|
|
||||||
|
|
||||||
if (draggable_ && IsItemHovered()) {
|
if (config_.is_draggable && IsItemHovered()) {
|
||||||
const ImGuiIO &io = GetIO();
|
const ImGuiIO &io = GetIO();
|
||||||
const bool is_active = IsItemActive(); // Held
|
const bool is_active = IsItemActive(); // Held
|
||||||
const ImVec2 origin(canvas_p0_.x + scrolling_.x,
|
const ImVec2 origin(canvas_p0_.x + scrolling_.x,
|
||||||
@@ -140,14 +274,20 @@ void Canvas::DrawContextMenu() {
|
|||||||
if (MenuItem("Zoom to Fit", nullptr, false) && bitmap_) {
|
if (MenuItem("Zoom to Fit", nullptr, false) && bitmap_) {
|
||||||
SetZoomToFit(*bitmap_);
|
SetZoomToFit(*bitmap_);
|
||||||
}
|
}
|
||||||
|
if (MenuItem("Advanced Properties", nullptr, false)) {
|
||||||
|
ImGui::OpenPopup("Advanced Canvas Properties");
|
||||||
|
}
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
MenuItem("Show Grid", nullptr, &enable_grid_);
|
MenuItem("Show Grid", nullptr, &enable_grid_);
|
||||||
Selectable("Show Position Labels", &enable_hex_tile_labels_);
|
Selectable("Show Position Labels", &enable_hex_tile_labels_);
|
||||||
if (MenuItem("Bitmap Properties", nullptr, false) && bitmap_) {
|
|
||||||
ImGui::OpenPopup("Bitmap Properties");
|
|
||||||
}
|
|
||||||
if (MenuItem("Edit Palette", nullptr, false) && bitmap_) {
|
if (MenuItem("Edit Palette", nullptr, false) && bitmap_) {
|
||||||
ImGui::OpenPopup("Palette Editor");
|
ShowPaletteEditor();
|
||||||
|
}
|
||||||
|
if (MenuItem("Color Analysis", nullptr, false) && bitmap_) {
|
||||||
|
ShowColorAnalysis();
|
||||||
|
}
|
||||||
|
if (MenuItem("Scaling Controls", nullptr, false)) {
|
||||||
|
ImGui::OpenPopup("Scaling Controls");
|
||||||
}
|
}
|
||||||
if (BeginMenu("Canvas Properties")) {
|
if (BeginMenu("Canvas Properties")) {
|
||||||
Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
|
Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
|
||||||
@@ -178,38 +318,78 @@ void Canvas::DrawContextMenu() {
|
|||||||
|
|
||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
if (BeginMenu("Change Palette")) {
|
if (BeginMenu("ROM Palette Selection") && rom_) {
|
||||||
Text("Work in progress");
|
Text("Select ROM Palette Group:");
|
||||||
// TODO: Get ROM data for change palette
|
|
||||||
// gui::TextWithSeparators("ROM Palette");
|
// Enhanced ROM palette group selection
|
||||||
// ImGui::SetNextItemWidth(100.f);
|
if (palette_editor_) {
|
||||||
// ImGui::Combo("Palette Group", (int *)&edit_palette_group_name_index_,
|
// Use our enhanced palette editor's ROM selection
|
||||||
// gfx::kPaletteGroupAddressesKeys,
|
if (MenuItem("Open Enhanced Palette Manager")) {
|
||||||
// IM_ARRAYSIZE(gfx::kPaletteGroupAddressesKeys));
|
palette_editor_->ShowROMPaletteManager();
|
||||||
// ImGui::SetNextItemWidth(100.f);
|
}
|
||||||
// gui::InputHexWord("Palette Group Index", &edit_palette_index_);
|
|
||||||
|
ImGui::Separator();
|
||||||
// auto palette_group = rom()->mutable_palette_group()->get_group(
|
|
||||||
// gfx::kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
// Quick palette group selection
|
||||||
// auto palette = palette_group->mutable_palette(edit_palette_index_);
|
const char* palette_groups[] = {
|
||||||
|
"Overworld Main", "Overworld Aux", "Overworld Animated",
|
||||||
// if (ImGui::BeginChild("Palette", ImVec2(0, 300), true)) {
|
"Dungeon Main", "Global Sprites", "Armor", "Swords"
|
||||||
// gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
};
|
||||||
// refresh_graphics_, *palette);
|
|
||||||
|
if (ImGui::Combo("Quick Palette Group", (int*)&edit_palette_group_name_index_,
|
||||||
// if (refresh_graphics_) {
|
palette_groups, IM_ARRAYSIZE(palette_groups))) {
|
||||||
// bitmap_->SetPaletteWithTransparent(*palette,
|
// Group selection changed
|
||||||
// edit_palette_sub_index_);
|
}
|
||||||
// Renderer::Get().UpdateBitmap(bitmap_);
|
|
||||||
// refresh_graphics_ = false;
|
ImGui::SetNextItemWidth(100.f);
|
||||||
// }
|
if (ImGui::SliderInt("Palette Index", (int*)&edit_palette_index_, 0, 7)) {
|
||||||
// ImGui::EndChild();
|
// Palette index changed
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
// Apply button with enhanced functionality
|
||||||
|
if (ImGui::Button("Apply to Canvas") && bitmap_) {
|
||||||
|
if (palette_editor_->ApplyROMPalette(bitmap_,
|
||||||
|
edit_palette_group_name_index_,
|
||||||
|
edit_palette_index_)) {
|
||||||
|
util::logf("Applied ROM palette group %d, index %d via context menu",
|
||||||
|
edit_palette_group_name_index_, edit_palette_index_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct palette editing with SelectablePalettePipeline
|
||||||
|
if (ImGui::TreeNode("Interactive Palette Editor")) {
|
||||||
|
if (rom_ && bitmap_) {
|
||||||
|
ImGui::Text("Interactive ROM Palette Editing");
|
||||||
|
ImGui::Text("Selected Group: %s", palette_groups[edit_palette_group_name_index_]);
|
||||||
|
|
||||||
|
// Get the enhanced palette editor's ROM palette if available
|
||||||
|
if (const auto* rom_palette = palette_editor_->GetSelectedROMPalette()) {
|
||||||
|
auto editable_palette = const_cast<gfx::SnesPalette&>(*rom_palette);
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("SelectablePalette", ImVec2(0, 200), true)) {
|
||||||
|
// Use the existing SelectablePalettePipeline for interactive editing
|
||||||
|
gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
||||||
|
refresh_graphics_, editable_palette);
|
||||||
|
|
||||||
|
if (refresh_graphics_) {
|
||||||
|
bitmap_->SetPaletteWithTransparent(editable_palette, edit_palette_sub_index_);
|
||||||
|
Renderer::Get().UpdateBitmap(bitmap_);
|
||||||
|
refresh_graphics_ = false;
|
||||||
|
util::logf("Applied interactive palette changes to canvas");
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::Text("Load ROM palettes first using Enhanced Palette Manager");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
if (BeginMenu("View Palette")) {
|
if (BeginMenu("View Palette")) {
|
||||||
DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette", true,
|
(void)DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette", true, 8);
|
||||||
8);
|
|
||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
EndMenu();
|
EndMenu();
|
||||||
@@ -235,11 +415,9 @@ void Canvas::DrawContextMenu() {
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw enhanced property dialogs
|
// Draw enhanced property dialogs
|
||||||
if (bitmap_) {
|
ShowAdvancedCanvasProperties();
|
||||||
ShowBitmapProperties(*bitmap_);
|
ShowScalingControls();
|
||||||
ShowPaletteEditor(*bitmap_->mutable_palette());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawContextMenuItem(const ContextMenuItem& item) {
|
void Canvas::DrawContextMenuItem(const ContextMenuItem& item) {
|
||||||
@@ -275,66 +453,7 @@ void Canvas::ClearContextMenuItems() {
|
|||||||
context_menu_items_.clear();
|
context_menu_items_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ShowBitmapProperties(const gfx::Bitmap& bitmap) {
|
// Old ShowPaletteEditor method removed - now handled by EnhancedPaletteEditor
|
||||||
if (ImGui::BeginPopupModal("Bitmap Properties", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::Text("Bitmap Information");
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::Text("Size: %d x %d", bitmap.width(), bitmap.height());
|
|
||||||
ImGui::Text("Depth: %d bits", bitmap.depth());
|
|
||||||
ImGui::Text("Data Size: %zu bytes", bitmap.size());
|
|
||||||
ImGui::Text("Active: %s", bitmap.is_active() ? "Yes" : "No");
|
|
||||||
ImGui::Text("Modified: %s", bitmap.modified() ? "Yes" : "No");
|
|
||||||
|
|
||||||
if (bitmap.surface()) {
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("SDL Surface");
|
|
||||||
ImGui::Text("Pitch: %d", bitmap.surface()->pitch);
|
|
||||||
ImGui::Text("Bits Per Pixel: %d", bitmap.surface()->format->BitsPerPixel);
|
|
||||||
ImGui::Text("Bytes Per Pixel: %d", bitmap.surface()->format->BytesPerPixel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Close")) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Canvas::ShowPaletteEditor(gfx::SnesPalette& palette) {
|
|
||||||
if (ImGui::BeginPopupModal("Palette Editor", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::Text("Palette Editor");
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
// Display palette colors in a grid
|
|
||||||
int cols = 8;
|
|
||||||
for (int i = 0; i < palette.size(); i++) {
|
|
||||||
if (i % cols != 0) ImGui::SameLine();
|
|
||||||
|
|
||||||
auto color = palette[i];
|
|
||||||
ImVec4 display_color = color.rgb();
|
|
||||||
|
|
||||||
ImGui::PushID(i);
|
|
||||||
if (ImGui::ColorButton("##color", display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(30, 30))) {
|
|
||||||
// Color selected - could open detailed editor
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("Color %d: 0x%04X\nR:%d G:%d B:%d",
|
|
||||||
i, color.snes(),
|
|
||||||
(int)(display_color.x * 255),
|
|
||||||
(int)(display_color.y * 255),
|
|
||||||
(int)(display_color.z * 255));
|
|
||||||
}
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
if (ImGui::Button("Close")) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
||||||
if (!bitmap.is_active()) return;
|
if (!bitmap.is_active()) return;
|
||||||
@@ -342,17 +461,20 @@ void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
|||||||
ImVec2 available = ImGui::GetContentRegionAvail();
|
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||||
float scale_x = available.x / bitmap.width();
|
float scale_x = available.x / bitmap.width();
|
||||||
float scale_y = available.y / bitmap.height();
|
float scale_y = available.y / bitmap.height();
|
||||||
global_scale_ = std::min(scale_x, scale_y);
|
config_.global_scale = std::min(scale_x, scale_y);
|
||||||
|
|
||||||
// Ensure minimum readable scale
|
// Ensure minimum readable scale
|
||||||
if (global_scale_ < 0.25f) global_scale_ = 0.25f;
|
if (config_.global_scale < 0.25f) config_.global_scale = 0.25f;
|
||||||
|
|
||||||
|
global_scale_ = config_.global_scale; // Legacy compatibility
|
||||||
|
|
||||||
// Center the view
|
// Center the view
|
||||||
scrolling_ = ImVec2(0, 0);
|
scrolling_ = ImVec2(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ResetView() {
|
void Canvas::ResetView() {
|
||||||
global_scale_ = 1.0f;
|
config_.global_scale = 1.0f;
|
||||||
|
global_scale_ = 1.0f; // Legacy compatibility
|
||||||
scrolling_ = ImVec2(0, 0);
|
scrolling_ = ImVec2(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,8 +681,22 @@ void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
|
|||||||
const float scaled_size = tile_size * scale;
|
const float scaled_size = tile_size * scale;
|
||||||
static bool dragging = false;
|
static bool dragging = false;
|
||||||
constexpr int small_map_size = 0x200;
|
constexpr int small_map_size = 0x200;
|
||||||
int superY = current_map / 8;
|
|
||||||
int superX = current_map % 8;
|
// Calculate superX and superY accounting for world offset
|
||||||
|
int superY, superX;
|
||||||
|
if (current_map < 0x40) {
|
||||||
|
// Light World
|
||||||
|
superY = current_map / 8;
|
||||||
|
superX = current_map % 8;
|
||||||
|
} else if (current_map < 0x80) {
|
||||||
|
// Dark World
|
||||||
|
superY = (current_map - 0x40) / 8;
|
||||||
|
superX = (current_map - 0x40) % 8;
|
||||||
|
} else {
|
||||||
|
// Special World
|
||||||
|
superY = (current_map - 0x80) / 8;
|
||||||
|
superX = (current_map - 0x80) % 8;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle right click for single tile selection
|
// Handle right click for single tile selection
|
||||||
if (IsMouseClicked(ImGuiMouseButton_Right)) {
|
if (IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
@@ -642,6 +778,10 @@ void Canvas::DrawBitmap(Bitmap &bitmap, int border_offset, float scale) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bitmap_ = &bitmap;
|
bitmap_ = &bitmap;
|
||||||
|
|
||||||
|
// Update content size for table integration
|
||||||
|
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||||
|
|
||||||
draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
|
draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
|
||||||
ImVec2(canvas_p0_.x, canvas_p0_.y),
|
ImVec2(canvas_p0_.x, canvas_p0_.y),
|
||||||
ImVec2(canvas_p0_.x + (bitmap.width() * scale),
|
ImVec2(canvas_p0_.x + (bitmap.width() * scale),
|
||||||
@@ -655,14 +795,24 @@ void Canvas::DrawBitmap(Bitmap &bitmap, int x_offset, int y_offset, float scale,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bitmap_ = &bitmap;
|
bitmap_ = &bitmap;
|
||||||
|
|
||||||
|
// Update content size for table integration
|
||||||
|
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||||
|
|
||||||
|
// Calculate the actual rendered size including scale and offsets
|
||||||
|
ImVec2 rendered_size(bitmap.width() * scale, bitmap.height() * scale);
|
||||||
|
ImVec2 total_size(x_offset + rendered_size.x, y_offset + rendered_size.y);
|
||||||
|
|
||||||
draw_list_->AddImage(
|
draw_list_->AddImage(
|
||||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||||
ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
|
ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
|
||||||
canvas_p0_.y + y_offset + scrolling_.y),
|
canvas_p0_.y + y_offset + scrolling_.y),
|
||||||
ImVec2(
|
ImVec2(
|
||||||
canvas_p0_.x + x_offset + scrolling_.x + (bitmap.width() * scale),
|
canvas_p0_.x + x_offset + scrolling_.x + rendered_size.x,
|
||||||
canvas_p0_.y + y_offset + scrolling_.y + (bitmap.height() * scale)),
|
canvas_p0_.y + y_offset + scrolling_.y + rendered_size.y),
|
||||||
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
||||||
|
|
||||||
|
// Note: Content size for child windows should be set before BeginChild, not here
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawBitmap(Bitmap &bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
void Canvas::DrawBitmap(Bitmap &bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
||||||
@@ -671,6 +821,10 @@ void Canvas::DrawBitmap(Bitmap &bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bitmap_ = &bitmap;
|
bitmap_ = &bitmap;
|
||||||
|
|
||||||
|
// Update content size for table integration
|
||||||
|
config_.content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||||
|
|
||||||
draw_list_->AddImage(
|
draw_list_->AddImage(
|
||||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||||
ImVec2(canvas_p0_.x + dest_pos.x, canvas_p0_.y + dest_pos.y),
|
ImVec2(canvas_p0_.x + dest_pos.x, canvas_p0_.y + dest_pos.y),
|
||||||
@@ -696,28 +850,15 @@ void Canvas::DrawBitmapTable(const BitmapTable &gfx_bin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutline(int x, int y, int w, int h) {
|
void Canvas::DrawOutline(int x, int y, int w, int h) {
|
||||||
ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
|
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h, IM_COL32(255, 255, 255, 200));
|
||||||
canvas_p0_.y + scrolling_.y + y);
|
|
||||||
ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
|
|
||||||
canvas_p0_.y + scrolling_.y + y + h);
|
|
||||||
draw_list_->AddRect(origin, size, kOutlineRect, 0, 0, 1.5f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
|
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
|
||||||
ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
|
CanvasUtils::DrawCanvasOutlineWithColor(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color);
|
||||||
canvas_p0_.y + scrolling_.y + y);
|
|
||||||
ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
|
|
||||||
canvas_p0_.y + scrolling_.y + y + h);
|
|
||||||
draw_list_->AddRect(origin, size,
|
|
||||||
IM_COL32(color.x, color.y, color.z, color.w));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
|
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
|
||||||
ImVec2 origin(canvas_p0_.x + scrolling_.x + x,
|
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color);
|
||||||
canvas_p0_.y + scrolling_.y + y);
|
|
||||||
ImVec2 size(canvas_p0_.x + scrolling_.x + x + w,
|
|
||||||
canvas_p0_.y + scrolling_.y + y + h);
|
|
||||||
draw_list_->AddRect(origin, size, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
||||||
@@ -731,6 +872,20 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-render all tiles to avoid timing issues
|
||||||
|
auto tilemap_size = tilemap.atlas.width() * tilemap.atlas.height() / tilemap.map_size.x;
|
||||||
|
for (int tile_id : group) {
|
||||||
|
if (tile_id >= 0 && tile_id < tilemap_size) {
|
||||||
|
gfx::RenderTile(tilemap, tile_id);
|
||||||
|
|
||||||
|
// Ensure the tile is actually rendered and active
|
||||||
|
auto tile_it = tilemap.tile_bitmaps.find(tile_id);
|
||||||
|
if (tile_it != tilemap.tile_bitmaps.end() && !tile_it->second.is_active()) {
|
||||||
|
core::Renderer::Get().RenderBitmap(&tile_it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Top-left and bottom-right corners of the rectangle
|
// Top-left and bottom-right corners of the rectangle
|
||||||
ImVec2 rect_top_left = selected_points_[0];
|
ImVec2 rect_top_left = selected_points_[0];
|
||||||
ImVec2 rect_bottom_right = selected_points_[1];
|
ImVec2 rect_bottom_right = selected_points_[1];
|
||||||
@@ -758,6 +913,11 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (int y = 0; y < tiles_per_col + 1; ++y) {
|
for (int y = 0; y < tiles_per_col + 1; ++y) {
|
||||||
for (int x = 0; x < tiles_per_row + 1; ++x) {
|
for (int x = 0; x < tiles_per_row + 1; ++x) {
|
||||||
|
// Check bounds to prevent access violations
|
||||||
|
if (i >= static_cast<int>(group.size())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
int tile_id = group[i];
|
int tile_id = group[i];
|
||||||
|
|
||||||
// Check if tile_id is within the range of tile16_individual_
|
// Check if tile_id is within the range of tile16_individual_
|
||||||
@@ -770,10 +930,28 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
|
|
||||||
// Draw the tile bitmap at the calculated position
|
// Draw the tile bitmap at the calculated position
|
||||||
gfx::RenderTile(tilemap, tile_id);
|
gfx::RenderTile(tilemap, tile_id);
|
||||||
DrawBitmap(tilemap.tile_bitmaps[tile_id], tile_pos_x, tile_pos_y, scale,
|
|
||||||
150.0f);
|
// Ensure the tile bitmap exists and is properly rendered
|
||||||
i++;
|
auto tile_it = tilemap.tile_bitmaps.find(tile_id);
|
||||||
|
if (tile_it != tilemap.tile_bitmaps.end()) {
|
||||||
|
auto& tile_bitmap = tile_it->second;
|
||||||
|
// Ensure the bitmap is active before drawing
|
||||||
|
if (tile_bitmap.is_active()) {
|
||||||
|
DrawBitmap(tile_bitmap, tile_pos_x, tile_pos_y, scale, 150);
|
||||||
|
} else {
|
||||||
|
// Force render if not active
|
||||||
|
core::Renderer::Get().RenderBitmap(&tile_bitmap);
|
||||||
|
if (tile_bitmap.is_active()) {
|
||||||
|
DrawBitmap(tile_bitmap, tile_pos_x, tile_pos_y, scale, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// Break outer loop if we've run out of tiles
|
||||||
|
if (i >= static_cast<int>(group.size())) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -789,50 +967,15 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
|
void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
|
||||||
// Apply global scale to position and size
|
CanvasUtils::DrawCanvasRect(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color, config_.global_scale);
|
||||||
float scaled_x = x * global_scale_;
|
|
||||||
float scaled_y = y * global_scale_;
|
|
||||||
float scaled_w = w * global_scale_;
|
|
||||||
float scaled_h = h * global_scale_;
|
|
||||||
|
|
||||||
ImVec2 origin(canvas_p0_.x + scrolling_.x + scaled_x,
|
|
||||||
canvas_p0_.y + scrolling_.y + scaled_y);
|
|
||||||
ImVec2 size(canvas_p0_.x + scrolling_.x + scaled_x + scaled_w,
|
|
||||||
canvas_p0_.y + scrolling_.y + scaled_y + scaled_h);
|
|
||||||
draw_list_->AddRectFilled(origin, size,
|
|
||||||
IM_COL32(color.x, color.y, color.z, color.w));
|
|
||||||
// Add a black outline
|
|
||||||
ImVec2 outline_origin(origin.x - 1, origin.y - 1);
|
|
||||||
ImVec2 outline_size(size.x + 1, size.y + 1);
|
|
||||||
draw_list_->AddRect(outline_origin, outline_size, kBlackColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawText(std::string text, int x, int y) {
|
void Canvas::DrawText(std::string text, int x, int y) {
|
||||||
// Apply global scale to text position
|
CanvasUtils::DrawCanvasText(draw_list_, canvas_p0_, scrolling_, text, x, y, config_.global_scale);
|
||||||
float scaled_x = x * global_scale_;
|
|
||||||
float scaled_y = y * global_scale_;
|
|
||||||
|
|
||||||
draw_list_->AddText(ImVec2(canvas_p0_.x + scrolling_.x + scaled_x + 1,
|
|
||||||
canvas_p0_.y + scrolling_.y + scaled_y + 1),
|
|
||||||
kBlackColor, text.data());
|
|
||||||
draw_list_->AddText(
|
|
||||||
ImVec2(canvas_p0_.x + scrolling_.x + scaled_x, canvas_p0_.y + scrolling_.y + scaled_y),
|
|
||||||
kWhiteColor, text.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawGridLines(float grid_step) {
|
void Canvas::DrawGridLines(float grid_step) {
|
||||||
const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
|
CanvasUtils::DrawCanvasGridLines(draw_list_, canvas_p0_, canvas_p1_, scrolling_, grid_step, config_.global_scale);
|
||||||
const float grid_thickness = 0.5f;
|
|
||||||
for (float x = fmodf(scrolling_.x, grid_step);
|
|
||||||
x < canvas_sz_.x * global_scale_; x += grid_step)
|
|
||||||
draw_list_->AddLine(ImVec2(canvas_p0_.x + x, canvas_p0_.y),
|
|
||||||
ImVec2(canvas_p0_.x + x, canvas_p1_.y), grid_color,
|
|
||||||
grid_thickness);
|
|
||||||
for (float y = fmodf(scrolling_.y, grid_step);
|
|
||||||
y < canvas_sz_.y * global_scale_; y += grid_step)
|
|
||||||
draw_list_->AddLine(ImVec2(canvas_p0_.x, canvas_p0_.y + y),
|
|
||||||
ImVec2(canvas_p1_.x, canvas_p0_.y + y), grid_color,
|
|
||||||
grid_thickness);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
||||||
@@ -872,91 +1015,50 @@ void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawCustomHighlight(float grid_step) {
|
void Canvas::DrawCustomHighlight(float grid_step) {
|
||||||
if (highlight_tile_id != -1) {
|
CanvasUtils::DrawCustomHighlight(draw_list_, canvas_p0_, scrolling_, highlight_tile_id, grid_step);
|
||||||
int tile_x = highlight_tile_id % 8;
|
|
||||||
int tile_y = highlight_tile_id / 8;
|
|
||||||
ImVec2 tile_pos(canvas_p0_.x + scrolling_.x + tile_x * grid_step,
|
|
||||||
canvas_p0_.y + scrolling_.y + tile_y * grid_step);
|
|
||||||
ImVec2 tile_pos_end(tile_pos.x + grid_step, tile_pos.y + grid_step);
|
|
||||||
|
|
||||||
draw_list_->AddRectFilled(tile_pos, tile_pos_end,
|
|
||||||
IM_COL32(255, 0, 255, 255));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
||||||
// Draw grid + all lines in the canvas
|
if (config_.grid_step != 0.f) grid_step = config_.grid_step;
|
||||||
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
|
||||||
if (enable_grid_) {
|
// Create render context for utilities
|
||||||
if (custom_step_ != 0.f) grid_step = custom_step_;
|
CanvasUtils::CanvasRenderContext ctx = {
|
||||||
grid_step *= global_scale_; // Apply global scale to grid step
|
.draw_list = draw_list_,
|
||||||
|
.canvas_p0 = canvas_p0_,
|
||||||
DrawGridLines(grid_step);
|
.canvas_p1 = canvas_p1_,
|
||||||
DrawCustomHighlight(grid_step);
|
.scrolling = scrolling_,
|
||||||
|
.global_scale = config_.global_scale,
|
||||||
if (enable_hex_tile_labels_) {
|
.enable_grid = config_.enable_grid,
|
||||||
// Draw the hex ID of the tile in the center of the tile square
|
.enable_hex_labels = config_.enable_hex_labels,
|
||||||
for (float x = fmodf(scrolling_.x, grid_step);
|
.grid_step = grid_step
|
||||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
};
|
||||||
for (float y = fmodf(scrolling_.y, grid_step);
|
|
||||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
// Use high-level utility function
|
||||||
int tile_x = (x - scrolling_.x) / grid_step;
|
CanvasUtils::DrawCanvasGrid(ctx, highlight_tile_id);
|
||||||
int tile_y = (y - scrolling_.y) / grid_step;
|
|
||||||
int tile_id = tile_x + (tile_y * 16);
|
// Draw custom labels if enabled
|
||||||
std::string hex_id = absl::StrFormat("%02X", tile_id);
|
if (config_.enable_custom_labels) {
|
||||||
draw_list_->AddText(ImVec2(canvas_p0_.x + x + (grid_step / 2) - 4,
|
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
||||||
canvas_p0_.y + y + (grid_step / 2) - 4),
|
CanvasUtils::DrawCanvasLabels(ctx, labels_, current_labels_, tile_id_offset);
|
||||||
kWhiteColor, hex_id.data());
|
draw_list_->PopClipRect();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!enable_custom_labels_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Draw the contents of labels on the grid
|
|
||||||
for (float x = fmodf(scrolling_.x, grid_step);
|
|
||||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
|
||||||
for (float y = fmodf(scrolling_.y, grid_step);
|
|
||||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
|
||||||
int tile_x = (x - scrolling_.x) / grid_step;
|
|
||||||
int tile_y = (y - scrolling_.y) / grid_step;
|
|
||||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
|
||||||
|
|
||||||
if (tile_id >= labels_[current_labels_].size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::string label = labels_[current_labels_][tile_id];
|
|
||||||
draw_list_->AddText(
|
|
||||||
ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
|
|
||||||
canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
|
|
||||||
kWhiteColor, label.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOverlay() {
|
void Canvas::DrawOverlay() {
|
||||||
const ImVec2 origin(canvas_p0_.x + scrolling_.x,
|
// Create render context for utilities
|
||||||
canvas_p0_.y + scrolling_.y); // Lock scrolled origin
|
CanvasUtils::CanvasRenderContext ctx = {
|
||||||
for (int n = 0; n < points_.Size; n += 2) {
|
.draw_list = draw_list_,
|
||||||
draw_list_->AddRect(
|
.canvas_p0 = canvas_p0_,
|
||||||
ImVec2(origin.x + points_[n].x, origin.y + points_[n].y),
|
.canvas_p1 = canvas_p1_,
|
||||||
ImVec2(origin.x + points_[n + 1].x, origin.y + points_[n + 1].y),
|
.scrolling = scrolling_,
|
||||||
kWhiteColor, 1.0f);
|
.global_scale = config_.global_scale,
|
||||||
}
|
.enable_grid = config_.enable_grid,
|
||||||
|
.enable_hex_labels = config_.enable_hex_labels,
|
||||||
if (!selected_points_.empty()) {
|
.grid_step = config_.grid_step
|
||||||
for (int n = 0; n < selected_points_.size(); n += 2) {
|
};
|
||||||
draw_list_->AddRect(ImVec2(origin.x + selected_points_[n].x,
|
|
||||||
origin.y + selected_points_[n].y),
|
// Use high-level utility function
|
||||||
ImVec2(origin.x + selected_points_[n + 1].x + 0x10,
|
CanvasUtils::DrawCanvasOverlay(ctx, points_, selected_points_);
|
||||||
origin.y + selected_points_[n + 1].y + 0x10),
|
|
||||||
kWhiteColor, 1.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_list_->PopClipRect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawLayeredElements() {
|
void Canvas::DrawLayeredElements() {
|
||||||
@@ -1002,7 +1104,20 @@ void Canvas::DrawLayeredElements() {
|
|||||||
|
|
||||||
void BeginCanvas(Canvas &canvas, ImVec2 child_size) {
|
void BeginCanvas(Canvas &canvas, ImVec2 child_size) {
|
||||||
gui::BeginPadding(1);
|
gui::BeginPadding(1);
|
||||||
ImGui::BeginChild(canvas.canvas_id().c_str(), child_size, true);
|
|
||||||
|
// Use improved canvas sizing for table integration
|
||||||
|
ImVec2 effective_size = child_size;
|
||||||
|
if (child_size.x == 0 && child_size.y == 0) {
|
||||||
|
// Auto-size based on canvas configuration
|
||||||
|
if (canvas.IsAutoResize()) {
|
||||||
|
effective_size = canvas.GetPreferredSize();
|
||||||
|
} else {
|
||||||
|
effective_size = canvas.GetCurrentSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginChild(canvas.canvas_id().c_str(), effective_size, true,
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
canvas.DrawBackground();
|
canvas.DrawBackground();
|
||||||
gui::EndPadding();
|
gui::EndPadding();
|
||||||
canvas.DrawContextMenu();
|
canvas.DrawContextMenu();
|
||||||
@@ -1071,4 +1186,208 @@ void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap,
|
||||||
|
const std::string& label, bool auto_resize) {
|
||||||
|
// Configure canvas for table integration
|
||||||
|
canvas.SetAutoResize(auto_resize);
|
||||||
|
|
||||||
|
if (auto_resize && bitmap.is_active()) {
|
||||||
|
// Auto-calculate size based on bitmap content
|
||||||
|
ImVec2 content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||||
|
ImVec2 preferred_size = CanvasUtils::CalculatePreferredCanvasSize(content_size, canvas.GetGlobalScale());
|
||||||
|
canvas.SetCanvasSize(preferred_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin table-aware canvas
|
||||||
|
if (canvas.BeginTableCanvas(label)) {
|
||||||
|
// Draw the canvas content
|
||||||
|
canvas.DrawBackground();
|
||||||
|
canvas.DrawContextMenu();
|
||||||
|
|
||||||
|
if (bitmap.is_active()) {
|
||||||
|
canvas.DrawBitmap(bitmap, 2, 2, canvas.GetGlobalScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.DrawGrid();
|
||||||
|
canvas.DrawOverlay();
|
||||||
|
}
|
||||||
|
canvas.EndTableCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::ShowAdvancedCanvasProperties() {
|
||||||
|
if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::Text("Advanced Canvas Configuration");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Canvas properties (read-only info)
|
||||||
|
ImGui::Text("Canvas Properties");
|
||||||
|
ImGui::Text("ID: %s", canvas_id_.c_str());
|
||||||
|
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x, config_.canvas_size.y);
|
||||||
|
ImGui::Text("Content Size: %.0f x %.0f", config_.content_size.x, config_.content_size.y);
|
||||||
|
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
||||||
|
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
||||||
|
|
||||||
|
if (config_.content_size.x > 0 && config_.content_size.y > 0) {
|
||||||
|
ImVec2 min_size = GetMinimumSize();
|
||||||
|
ImVec2 preferred_size = GetPreferredSize();
|
||||||
|
ImGui::Text("Minimum Size: %.0f x %.0f", min_size.x, min_size.y);
|
||||||
|
ImGui::Text("Preferred Size: %.0f x %.0f", preferred_size.x, preferred_size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editable properties using new config system
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("View Settings");
|
||||||
|
if (ImGui::Checkbox("Enable Grid", &config_.enable_grid)) {
|
||||||
|
enable_grid_ = config_.enable_grid; // Legacy sync
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox("Enable Hex Labels", &config_.enable_hex_labels)) {
|
||||||
|
enable_hex_tile_labels_ = config_.enable_hex_labels; // Legacy sync
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox("Enable Custom Labels", &config_.enable_custom_labels)) {
|
||||||
|
enable_custom_labels_ = config_.enable_custom_labels; // Legacy sync
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox("Enable Context Menu", &config_.enable_context_menu)) {
|
||||||
|
enable_context_menu_ = config_.enable_context_menu; // Legacy sync
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox("Draggable", &config_.is_draggable)) {
|
||||||
|
draggable_ = config_.is_draggable; // Legacy sync
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox("Auto Resize for Tables", &config_.auto_resize)) {
|
||||||
|
// Auto resize setting changed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid controls
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Grid Configuration");
|
||||||
|
if (ImGui::SliderFloat("Grid Step", &config_.grid_step, 1.0f, 128.0f, "%.1f")) {
|
||||||
|
custom_step_ = config_.grid_step; // Legacy sync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale controls
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Scale Configuration");
|
||||||
|
if (ImGui::SliderFloat("Global Scale", &config_.global_scale, 0.1f, 10.0f, "%.2f")) {
|
||||||
|
global_scale_ = config_.global_scale; // Legacy sync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrolling controls
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Scrolling Configuration");
|
||||||
|
ImGui::Text("Current Scroll: %.1f, %.1f", scrolling_.x, scrolling_.y);
|
||||||
|
if (ImGui::Button("Reset Scroll")) {
|
||||||
|
scrolling_ = ImVec2(0, 0);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Center View")) {
|
||||||
|
if (bitmap_) {
|
||||||
|
scrolling_ = ImVec2(-(bitmap_->width() * config_.global_scale - config_.canvas_size.x) / 2.0f,
|
||||||
|
-(bitmap_->height() * config_.global_scale - config_.canvas_size.y) / 2.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old ShowPaletteManager method removed - now handled by EnhancedPaletteEditor
|
||||||
|
|
||||||
|
void Canvas::ShowScalingControls() {
|
||||||
|
if (ImGui::BeginPopupModal("Scaling Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::Text("Canvas Scaling and Display Controls");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Global scale with new config system
|
||||||
|
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
||||||
|
if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f, "%.2f")) {
|
||||||
|
global_scale_ = config_.global_scale; // Legacy sync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preset scale buttons
|
||||||
|
ImGui::Text("Preset Scales:");
|
||||||
|
if (ImGui::Button("0.25x")) {
|
||||||
|
config_.global_scale = 0.25f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("0.5x")) {
|
||||||
|
config_.global_scale = 0.5f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("1x")) {
|
||||||
|
config_.global_scale = 1.0f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("2x")) {
|
||||||
|
config_.global_scale = 2.0f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("4x")) {
|
||||||
|
config_.global_scale = 4.0f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("8x")) {
|
||||||
|
config_.global_scale = 8.0f;
|
||||||
|
global_scale_ = config_.global_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid configuration
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Grid Configuration");
|
||||||
|
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
||||||
|
if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f, "%.1f")) {
|
||||||
|
custom_step_ = config_.grid_step; // Legacy sync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid size presets
|
||||||
|
ImGui::Text("Grid Presets:");
|
||||||
|
if (ImGui::Button("8x8")) {
|
||||||
|
config_.grid_step = 8.0f;
|
||||||
|
custom_step_ = config_.grid_step;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("16x16")) {
|
||||||
|
config_.grid_step = 16.0f;
|
||||||
|
custom_step_ = config_.grid_step;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("32x32")) {
|
||||||
|
config_.grid_step = 32.0f;
|
||||||
|
custom_step_ = config_.grid_step;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("64x64")) {
|
||||||
|
config_.grid_step = 64.0f;
|
||||||
|
custom_step_ = config_.grid_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canvas size info
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Canvas Information");
|
||||||
|
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x, config_.canvas_size.y);
|
||||||
|
ImGui::Text("Scaled Size: %.0f x %.0f",
|
||||||
|
config_.canvas_size.x * config_.global_scale,
|
||||||
|
config_.canvas_size.y * config_.global_scale);
|
||||||
|
if (bitmap_) {
|
||||||
|
ImGui::Text("Bitmap Size: %d x %d", bitmap_->width(), bitmap_->height());
|
||||||
|
ImGui::Text("Effective Scale: %.3f x %.3f",
|
||||||
|
(config_.canvas_size.x * config_.global_scale) / bitmap_->width(),
|
||||||
|
(config_.canvas_size.y * config_.global_scale) / bitmap_->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old ROM palette management methods removed - now handled by EnhancedPaletteEditor
|
||||||
|
|
||||||
} // namespace yaze::gui
|
} // namespace yaze::gui
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
#include "app/gui/canvas_utils.h"
|
||||||
|
#include "app/gui/enhanced_palette_editor.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
@@ -28,55 +32,68 @@ enum class CanvasGridSize { k8x8, k16x16, k32x32, k64x64 };
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Canvas
|
* @class Canvas
|
||||||
* @brief Represents a canvas for drawing and manipulating graphics.
|
* @brief Modern, robust canvas for drawing and manipulating graphics.
|
||||||
*
|
*
|
||||||
* The Canvas class provides various functions for updating and drawing graphics
|
* Following ImGui design patterns, this Canvas class provides:
|
||||||
* on a canvas. It supports features such as bitmap drawing, context menu
|
* - Modular configuration through CanvasConfig
|
||||||
* handling, tile painting, custom grid, and more.
|
* - Separate selection state management
|
||||||
|
* - Enhanced palette management integration
|
||||||
|
* - Performance-optimized rendering
|
||||||
|
* - Comprehensive context menu system
|
||||||
*/
|
*/
|
||||||
class Canvas {
|
class Canvas {
|
||||||
public:
|
public:
|
||||||
Canvas() = default;
|
Canvas() = default;
|
||||||
explicit Canvas(const std::string &id) : canvas_id_(id) {
|
|
||||||
context_id_ = id + "Context";
|
explicit Canvas(const std::string& id)
|
||||||
|
: canvas_id_(id), context_id_(id + "Context") {
|
||||||
|
InitializeDefaults();
|
||||||
}
|
}
|
||||||
explicit Canvas(const std::string &id, ImVec2 canvas_size)
|
|
||||||
: custom_canvas_size_(true), canvas_sz_(canvas_size), canvas_id_(id) {
|
explicit Canvas(const std::string& id, ImVec2 canvas_size)
|
||||||
context_id_ = id + "Context";
|
: canvas_id_(id), context_id_(id + "Context") {
|
||||||
|
InitializeDefaults();
|
||||||
|
config_.canvas_size = canvas_size;
|
||||||
|
config_.custom_canvas_size = true;
|
||||||
}
|
}
|
||||||
explicit Canvas(const std::string &id, ImVec2 canvas_size,
|
|
||||||
CanvasGridSize grid_size)
|
explicit Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
|
||||||
: custom_canvas_size_(true), canvas_sz_(canvas_size), canvas_id_(id) {
|
: canvas_id_(id), context_id_(id + "Context") {
|
||||||
context_id_ = id + "Context";
|
InitializeDefaults();
|
||||||
SetCanvasGridSize(grid_size);
|
config_.canvas_size = canvas_size;
|
||||||
|
config_.custom_canvas_size = true;
|
||||||
|
SetGridSize(grid_size);
|
||||||
}
|
}
|
||||||
explicit Canvas(const std::string &id, ImVec2 canvas_size,
|
|
||||||
CanvasGridSize grid_size, float global_scale)
|
explicit Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
|
||||||
: custom_canvas_size_(true),
|
: canvas_id_(id), context_id_(id + "Context") {
|
||||||
global_scale_(global_scale),
|
InitializeDefaults();
|
||||||
canvas_sz_(canvas_size),
|
config_.canvas_size = canvas_size;
|
||||||
canvas_id_(id) {
|
config_.custom_canvas_size = true;
|
||||||
context_id_ = id + "Context";
|
config_.global_scale = global_scale;
|
||||||
SetCanvasGridSize(grid_size);
|
SetGridSize(grid_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCanvasGridSize(CanvasGridSize grid_size) {
|
void SetGridSize(CanvasGridSize grid_size) {
|
||||||
switch (grid_size) {
|
switch (grid_size) {
|
||||||
case CanvasGridSize::k8x8:
|
case CanvasGridSize::k8x8:
|
||||||
custom_step_ = 8.0f;
|
config_.grid_step = 8.0f;
|
||||||
break;
|
break;
|
||||||
case CanvasGridSize::k16x16:
|
case CanvasGridSize::k16x16:
|
||||||
custom_step_ = 16.0f;
|
config_.grid_step = 16.0f;
|
||||||
break;
|
break;
|
||||||
case CanvasGridSize::k32x32:
|
case CanvasGridSize::k32x32:
|
||||||
custom_step_ = 32.0f;
|
config_.grid_step = 32.0f;
|
||||||
break;
|
break;
|
||||||
case CanvasGridSize::k64x64:
|
case CanvasGridSize::k64x64:
|
||||||
custom_step_ = 64.0f;
|
config_.grid_step = 64.0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legacy compatibility
|
||||||
|
void SetCanvasGridSize(CanvasGridSize grid_size) { SetGridSize(grid_size); }
|
||||||
|
|
||||||
void UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
void UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
||||||
const std::function<void()> &event, int tile_size,
|
const std::function<void()> &event, int tile_size,
|
||||||
float scale = 1.0f);
|
float scale = 1.0f);
|
||||||
@@ -106,11 +123,45 @@ class Canvas {
|
|||||||
void SetContextMenuEnabled(bool enabled) { context_menu_enabled_ = enabled; }
|
void SetContextMenuEnabled(bool enabled) { context_menu_enabled_ = enabled; }
|
||||||
|
|
||||||
// Enhanced view and edit operations
|
// Enhanced view and edit operations
|
||||||
void ShowBitmapProperties(const gfx::Bitmap& bitmap);
|
void ShowAdvancedCanvasProperties();
|
||||||
void ShowPaletteEditor(gfx::SnesPalette& palette);
|
void ShowScalingControls();
|
||||||
void SetZoomToFit(const gfx::Bitmap& bitmap);
|
void SetZoomToFit(const gfx::Bitmap& bitmap);
|
||||||
void ResetView();
|
void ResetView();
|
||||||
|
|
||||||
|
// Modular component access
|
||||||
|
CanvasConfig& GetConfig() { return config_; }
|
||||||
|
const CanvasConfig& GetConfig() const { return config_; }
|
||||||
|
CanvasSelection& GetSelection() { return selection_; }
|
||||||
|
const CanvasSelection& GetSelection() const { return selection_; }
|
||||||
|
|
||||||
|
// Enhanced palette management
|
||||||
|
void InitializePaletteEditor(Rom* rom);
|
||||||
|
void ShowPaletteEditor();
|
||||||
|
void ShowColorAnalysis();
|
||||||
|
bool ApplyROMPalette(int group_index, int palette_index);
|
||||||
|
|
||||||
|
// Initialization and cleanup
|
||||||
|
void InitializeDefaults();
|
||||||
|
void Cleanup();
|
||||||
|
|
||||||
|
// Size reporting for ImGui table integration
|
||||||
|
ImVec2 GetMinimumSize() const;
|
||||||
|
ImVec2 GetPreferredSize() const;
|
||||||
|
ImVec2 GetCurrentSize() const { return config_.canvas_size; }
|
||||||
|
void SetAutoResize(bool auto_resize) { config_.auto_resize = auto_resize; }
|
||||||
|
bool IsAutoResize() const { return config_.auto_resize; }
|
||||||
|
|
||||||
|
// Table integration helpers
|
||||||
|
void ReserveTableSpace(const std::string& label = "");
|
||||||
|
bool BeginTableCanvas(const std::string& label = "");
|
||||||
|
void EndTableCanvas();
|
||||||
|
|
||||||
|
// Improved interaction detection
|
||||||
|
bool HasValidSelection() const;
|
||||||
|
bool WasClicked(ImGuiMouseButton button = ImGuiMouseButton_Left) const;
|
||||||
|
bool WasDoubleClicked(ImGuiMouseButton button = ImGuiMouseButton_Left) const;
|
||||||
|
ImVec2 GetLastClickPosition() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawContextMenuItem(const ContextMenuItem& item);
|
void DrawContextMenuItem(const ContextMenuItem& item);
|
||||||
|
|
||||||
@@ -173,19 +224,34 @@ class Canvas {
|
|||||||
void set_global_scale(float scale) { global_scale_ = scale; }
|
void set_global_scale(float scale) { global_scale_ = scale; }
|
||||||
void set_draggable(bool draggable) { draggable_ = draggable; }
|
void set_draggable(bool draggable) { draggable_ = draggable; }
|
||||||
|
|
||||||
// Public accessors for commonly used private members
|
// Modern accessors using modular structure
|
||||||
|
bool IsSelectRectActive() const { return select_rect_active_; }
|
||||||
|
const std::vector<ImVec2>& GetSelectedTiles() const { return selected_tiles_; }
|
||||||
|
ImVec2 GetSelectedTilePos() const { return selected_tile_pos_; }
|
||||||
|
void SetSelectedTilePos(ImVec2 pos) { selected_tile_pos_ = pos; }
|
||||||
|
|
||||||
|
// Configuration accessors
|
||||||
|
void SetCanvasSize(ImVec2 canvas_size) {
|
||||||
|
config_.canvas_size = canvas_size;
|
||||||
|
config_.custom_canvas_size = true;
|
||||||
|
}
|
||||||
|
float GetGlobalScale() const { return config_.global_scale; }
|
||||||
|
void SetGlobalScale(float scale) { config_.global_scale = scale; }
|
||||||
|
bool* GetCustomLabelsEnabled() { return &config_.enable_custom_labels; }
|
||||||
|
float GetGridStep() const { return config_.grid_step; }
|
||||||
|
float GetCanvasWidth() const { return config_.canvas_size.x; }
|
||||||
|
float GetCanvasHeight() const { return config_.canvas_size.y; }
|
||||||
|
|
||||||
|
// Legacy compatibility accessors
|
||||||
auto select_rect_active() const { return select_rect_active_; }
|
auto select_rect_active() const { return select_rect_active_; }
|
||||||
auto selected_tiles() const { return selected_tiles_; }
|
auto selected_tiles() const { return selected_tiles_; }
|
||||||
auto selected_tile_pos() const { return selected_tile_pos_; }
|
auto selected_tile_pos() const { return selected_tile_pos_; }
|
||||||
void set_selected_tile_pos(ImVec2 pos) { selected_tile_pos_ = pos; }
|
void set_selected_tile_pos(ImVec2 pos) { selected_tile_pos_ = pos; }
|
||||||
|
auto global_scale() const { return config_.global_scale; }
|
||||||
// Public methods for commonly used private methods
|
auto custom_labels_enabled() { return &config_.enable_custom_labels; }
|
||||||
void SetCanvasSize(ImVec2 canvas_size) { canvas_sz_ = canvas_size; custom_canvas_size_ = true; }
|
auto custom_step() const { return config_.grid_step; }
|
||||||
auto global_scale() const { return global_scale_; }
|
auto width() const { return config_.canvas_size.x; }
|
||||||
auto custom_labels_enabled() { return &enable_custom_labels_; }
|
auto height() const { return config_.canvas_size.y; }
|
||||||
auto custom_step() const { return custom_step_; }
|
|
||||||
auto width() const { return canvas_sz_.x; }
|
|
||||||
auto height() const { return canvas_sz_.y; }
|
|
||||||
|
|
||||||
// Public accessors for methods that need to be accessed externally
|
// Public accessors for methods that need to be accessed externally
|
||||||
auto canvas_id() const { return canvas_id_; }
|
auto canvas_id() const { return canvas_id_; }
|
||||||
@@ -231,50 +297,60 @@ class Canvas {
|
|||||||
Rom *rom() const { return rom_; }
|
Rom *rom() const { return rom_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool draggable_ = false;
|
// Modular configuration and state
|
||||||
|
CanvasConfig config_;
|
||||||
|
CanvasSelection selection_;
|
||||||
|
std::unique_ptr<EnhancedPaletteEditor> palette_editor_;
|
||||||
|
|
||||||
|
// Core canvas state
|
||||||
bool is_hovered_ = false;
|
bool is_hovered_ = false;
|
||||||
bool enable_grid_ = true;
|
|
||||||
bool enable_hex_tile_labels_ = false;
|
|
||||||
bool enable_custom_labels_ = false;
|
|
||||||
bool enable_context_menu_ = true;
|
|
||||||
bool custom_canvas_size_ = false;
|
|
||||||
bool select_rect_active_ = false;
|
|
||||||
bool refresh_graphics_ = false;
|
bool refresh_graphics_ = false;
|
||||||
|
|
||||||
// Context menu system
|
// Context menu system
|
||||||
std::vector<ContextMenuItem> context_menu_items_;
|
std::vector<ContextMenuItem> context_menu_items_;
|
||||||
bool context_menu_enabled_ = true;
|
bool context_menu_enabled_ = true;
|
||||||
|
|
||||||
float custom_step_ = 0.0f;
|
// Legacy members (to be gradually replaced)
|
||||||
float global_scale_ = 1.0f;
|
|
||||||
|
|
||||||
int current_labels_ = 0;
|
int current_labels_ = 0;
|
||||||
int highlight_tile_id = -1;
|
int highlight_tile_id = -1;
|
||||||
|
|
||||||
uint16_t edit_palette_index_ = 0;
|
uint16_t edit_palette_index_ = 0;
|
||||||
uint64_t edit_palette_group_name_index_ = 0;
|
uint64_t edit_palette_group_name_index_ = 0;
|
||||||
uint64_t edit_palette_sub_index_ = 0;
|
uint64_t edit_palette_sub_index_ = 0;
|
||||||
|
|
||||||
|
// Core canvas state
|
||||||
Bitmap *bitmap_ = nullptr;
|
Bitmap *bitmap_ = nullptr;
|
||||||
Rom *rom_ = nullptr;
|
Rom *rom_ = nullptr;
|
||||||
|
|
||||||
ImDrawList *draw_list_ = nullptr;
|
ImDrawList *draw_list_ = nullptr;
|
||||||
|
|
||||||
|
// Canvas geometry and interaction state
|
||||||
ImVec2 scrolling_;
|
ImVec2 scrolling_;
|
||||||
ImVec2 canvas_sz_;
|
ImVec2 canvas_sz_;
|
||||||
ImVec2 canvas_p0_;
|
ImVec2 canvas_p0_;
|
||||||
ImVec2 canvas_p1_;
|
ImVec2 canvas_p1_;
|
||||||
ImVec2 drawn_tile_pos_;
|
ImVec2 drawn_tile_pos_;
|
||||||
ImVec2 mouse_pos_in_canvas_;
|
ImVec2 mouse_pos_in_canvas_;
|
||||||
ImVec2 selected_tile_pos_ = ImVec2(-1, -1);
|
|
||||||
|
|
||||||
|
// Drawing and labeling
|
||||||
ImVector<ImVec2> points_;
|
ImVector<ImVec2> points_;
|
||||||
ImVector<ImVec2> selected_points_;
|
|
||||||
ImVector<ImVector<std::string>> labels_;
|
ImVector<ImVector<std::string>> labels_;
|
||||||
|
|
||||||
|
// Identification
|
||||||
std::string canvas_id_ = "Canvas";
|
std::string canvas_id_ = "Canvas";
|
||||||
std::string context_id_ = "CanvasContext";
|
std::string context_id_ = "CanvasContext";
|
||||||
|
|
||||||
|
// Legacy compatibility (gradually being replaced by selection_)
|
||||||
std::vector<ImVec2> selected_tiles_;
|
std::vector<ImVec2> selected_tiles_;
|
||||||
|
ImVector<ImVec2> selected_points_;
|
||||||
|
ImVec2 selected_tile_pos_ = ImVec2(-1, -1);
|
||||||
|
bool select_rect_active_ = false;
|
||||||
|
float custom_step_ = 32.0f;
|
||||||
|
float global_scale_ = 1.0f;
|
||||||
|
bool enable_grid_ = true;
|
||||||
|
bool enable_hex_tile_labels_ = false;
|
||||||
|
bool enable_custom_labels_ = false;
|
||||||
|
bool enable_context_menu_ = true;
|
||||||
|
bool custom_canvas_size_ = false;
|
||||||
|
bool draggable_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void BeginCanvas(Canvas &canvas, ImVec2 child_size = ImVec2(0, 0));
|
void BeginCanvas(Canvas &canvas, ImVec2 child_size = ImVec2(0, 0));
|
||||||
@@ -288,6 +364,10 @@ void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width,
|
|||||||
int height, int tile_size, bool is_loaded,
|
int height, int tile_size, bool is_loaded,
|
||||||
bool scrollbar, int canvas_id);
|
bool scrollbar, int canvas_id);
|
||||||
|
|
||||||
|
// Table-optimized canvas pipeline with automatic sizing
|
||||||
|
void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap,
|
||||||
|
const std::string& label = "", bool auto_resize = true);
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
|
|||||||
396
src/app/gui/canvas_utils.cc
Normal file
396
src/app/gui/canvas_utils.cc
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
#include "canvas_utils.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include "app/core/window.h"
|
||||||
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
namespace CanvasUtils {
|
||||||
|
|
||||||
|
using core::Renderer;
|
||||||
|
|
||||||
|
ImVec2 AlignToGrid(ImVec2 pos, float grid_step) {
|
||||||
|
return ImVec2(std::floor(pos.x / grid_step) * grid_step,
|
||||||
|
std::floor(pos.y / grid_step) * grid_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size,
|
||||||
|
float global_scale) {
|
||||||
|
if (content_size.x <= 0 || content_size.y <= 0)
|
||||||
|
return global_scale;
|
||||||
|
|
||||||
|
float scale_x = (canvas_size.x * global_scale) / content_size.x;
|
||||||
|
float scale_y = (canvas_size.y * global_scale) / content_size.y;
|
||||||
|
return std::min(scale_x, scale_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale,
|
||||||
|
int tiles_per_row) {
|
||||||
|
float scaled_tile_size = tile_size * scale;
|
||||||
|
int tile_x = static_cast<int>(mouse_pos.x / scaled_tile_size);
|
||||||
|
int tile_y = static_cast<int>(mouse_pos.y / scaled_tile_size);
|
||||||
|
|
||||||
|
return tile_x + (tile_y * tiles_per_row);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
||||||
|
if (!rom || palette_manager.palettes_loaded) {
|
||||||
|
return palette_manager.palettes_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto& palette_groups = rom->palette_group();
|
||||||
|
palette_manager.rom_palette_groups.clear();
|
||||||
|
palette_manager.palette_group_names.clear();
|
||||||
|
|
||||||
|
// Overworld palettes
|
||||||
|
if (palette_groups.overworld_main.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(
|
||||||
|
palette_groups.overworld_main[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Overworld Main");
|
||||||
|
}
|
||||||
|
if (palette_groups.overworld_aux.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(
|
||||||
|
palette_groups.overworld_aux[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Overworld Aux");
|
||||||
|
}
|
||||||
|
if (palette_groups.overworld_animated.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(
|
||||||
|
palette_groups.overworld_animated[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Overworld Animated");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dungeon palettes
|
||||||
|
if (palette_groups.dungeon_main.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(
|
||||||
|
palette_groups.dungeon_main[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Dungeon Main");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprite palettes
|
||||||
|
if (palette_groups.global_sprites.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(
|
||||||
|
palette_groups.global_sprites[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Global Sprites");
|
||||||
|
}
|
||||||
|
if (palette_groups.armors.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(palette_groups.armors[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Armor");
|
||||||
|
}
|
||||||
|
if (palette_groups.swords.size() > 0) {
|
||||||
|
palette_manager.rom_palette_groups.push_back(palette_groups.swords[0]);
|
||||||
|
palette_manager.palette_group_names.push_back("Swords");
|
||||||
|
}
|
||||||
|
|
||||||
|
palette_manager.palettes_loaded = true;
|
||||||
|
util::logf("Canvas: Loaded %zu ROM palette groups",
|
||||||
|
palette_manager.rom_palette_groups.size());
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
util::logf("Canvas: Failed to load ROM palette groups: %s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
||||||
|
const CanvasPaletteManager& palette_manager,
|
||||||
|
int group_index, int palette_index) {
|
||||||
|
if (!bitmap || group_index < 0 ||
|
||||||
|
group_index >=
|
||||||
|
static_cast<int>(palette_manager.rom_palette_groups.size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto& selected_palette =
|
||||||
|
palette_manager.rom_palette_groups[group_index];
|
||||||
|
|
||||||
|
// Apply the palette based on the index
|
||||||
|
if (palette_index >= 0 && palette_index < 8) {
|
||||||
|
bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
|
||||||
|
} else {
|
||||||
|
bitmap->SetPalette(selected_palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::Get().UpdateBitmap(bitmap);
|
||||||
|
util::logf("Canvas: Applied palette group %d, index %d to bitmap",
|
||||||
|
group_index, palette_index);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
util::logf("Canvas: Failed to apply palette: %s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawing utility functions
|
||||||
|
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
int x, int y, int w, int h, ImVec4 color,
|
||||||
|
float global_scale) {
|
||||||
|
// Apply global scale to position and size
|
||||||
|
float scaled_x = x * global_scale;
|
||||||
|
float scaled_y = y * global_scale;
|
||||||
|
float scaled_w = w * global_scale;
|
||||||
|
float scaled_h = h * global_scale;
|
||||||
|
|
||||||
|
ImVec2 origin(canvas_p0.x + scrolling.x + scaled_x,
|
||||||
|
canvas_p0.y + scrolling.y + scaled_y);
|
||||||
|
ImVec2 size(canvas_p0.x + scrolling.x + scaled_x + scaled_w,
|
||||||
|
canvas_p0.y + scrolling.y + scaled_y + scaled_h);
|
||||||
|
|
||||||
|
uint32_t color_u32 = IM_COL32(color.x, color.y, color.z, color.w);
|
||||||
|
draw_list->AddRectFilled(origin, size, color_u32);
|
||||||
|
|
||||||
|
// Add a black outline
|
||||||
|
ImVec2 outline_origin(origin.x - 1, origin.y - 1);
|
||||||
|
ImVec2 outline_size(size.x + 1, size.y + 1);
|
||||||
|
draw_list->AddRect(outline_origin, outline_size, IM_COL32(0, 0, 0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
const std::string& text, int x, int y, float global_scale) {
|
||||||
|
// Apply global scale to text position
|
||||||
|
float scaled_x = x * global_scale;
|
||||||
|
float scaled_y = y * global_scale;
|
||||||
|
|
||||||
|
ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x,
|
||||||
|
canvas_p0.y + scrolling.y + scaled_y);
|
||||||
|
|
||||||
|
// Draw text with black shadow for better visibility
|
||||||
|
draw_list->AddText(ImVec2(text_pos.x + 1, text_pos.y + 1),
|
||||||
|
IM_COL32(0, 0, 0, 255), text.c_str());
|
||||||
|
draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, int x, int y, int w, int h,
|
||||||
|
uint32_t color) {
|
||||||
|
ImVec2 origin(canvas_p0.x + scrolling.x + x, canvas_p0.y + scrolling.y + y);
|
||||||
|
ImVec2 size(canvas_p0.x + scrolling.x + x + w,
|
||||||
|
canvas_p0.y + scrolling.y + y + h);
|
||||||
|
draw_list->AddRect(origin, size, color, 0, 0, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, int x, int y, int w, int h,
|
||||||
|
ImVec4 color) {
|
||||||
|
uint32_t color_u32 =
|
||||||
|
IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
|
||||||
|
DrawCanvasOutline(draw_list, canvas_p0, scrolling, x, y, w, h, color_u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid utility functions
|
||||||
|
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||||
|
ImVec2 canvas_p1, ImVec2 scrolling, float grid_step,
|
||||||
|
float global_scale) {
|
||||||
|
const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
|
||||||
|
const float grid_thickness = 0.5f;
|
||||||
|
|
||||||
|
float scaled_grid_step = grid_step * global_scale;
|
||||||
|
|
||||||
|
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||||
|
x < (canvas_p1.x - canvas_p0.x); x += scaled_grid_step) {
|
||||||
|
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y),
|
||||||
|
ImVec2(canvas_p0.x + x, canvas_p1.y), grid_color,
|
||||||
|
grid_thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||||
|
y < (canvas_p1.y - canvas_p0.y); y += scaled_grid_step) {
|
||||||
|
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y),
|
||||||
|
ImVec2(canvas_p1.x, canvas_p0.y + y), grid_color,
|
||||||
|
grid_thickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, int highlight_tile_id,
|
||||||
|
float grid_step) {
|
||||||
|
if (highlight_tile_id == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int tile_x = highlight_tile_id % 8;
|
||||||
|
int tile_y = highlight_tile_id / 8;
|
||||||
|
ImVec2 tile_pos(canvas_p0.x + scrolling.x + tile_x * grid_step,
|
||||||
|
canvas_p0.y + scrolling.y + tile_y * grid_step);
|
||||||
|
ImVec2 tile_pos_end(tile_pos.x + grid_step, tile_pos.y + grid_step);
|
||||||
|
|
||||||
|
draw_list->AddRectFilled(tile_pos, tile_pos_end, IM_COL32(255, 0, 255, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||||
|
ImVec2 scrolling, ImVec2 canvas_sz, float grid_step,
|
||||||
|
float global_scale) {
|
||||||
|
float scaled_grid_step = grid_step * global_scale;
|
||||||
|
|
||||||
|
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||||
|
x < canvas_sz.x * global_scale; x += scaled_grid_step) {
|
||||||
|
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||||
|
y < canvas_sz.y * global_scale; y += scaled_grid_step) {
|
||||||
|
int tile_x = (x - scrolling.x) / scaled_grid_step;
|
||||||
|
int tile_y = (y - scrolling.y) / scaled_grid_step;
|
||||||
|
int tile_id = tile_x + (tile_y * 16);
|
||||||
|
|
||||||
|
char hex_id[8];
|
||||||
|
snprintf(hex_id, sizeof(hex_id), "%02X", tile_id);
|
||||||
|
|
||||||
|
draw_list->AddText(ImVec2(canvas_p0.x + x + (scaled_grid_step / 2) - 4,
|
||||||
|
canvas_p0.y + y + (scaled_grid_step / 2) - 4),
|
||||||
|
IM_COL32(255, 255, 255, 255), hex_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout and interaction utilities
|
||||||
|
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size,
|
||||||
|
bool use_custom) {
|
||||||
|
return use_custom ? custom_size : content_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale) {
|
||||||
|
return ImVec2(canvas_size.x * global_scale, canvas_size.y * global_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1) {
|
||||||
|
return point.x >= canvas_p0.x && point.x <= canvas_p1.x &&
|
||||||
|
point.y >= canvas_p0.y && point.y <= canvas_p1.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size reporting for ImGui table integration
|
||||||
|
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale,
|
||||||
|
float padding) {
|
||||||
|
// Calculate minimum size needed to display content with padding
|
||||||
|
ImVec2 min_size = ImVec2(content_size.x * global_scale + padding * 2,
|
||||||
|
content_size.y * global_scale + padding * 2);
|
||||||
|
|
||||||
|
// Ensure minimum practical size
|
||||||
|
min_size.x = std::max(min_size.x, 64.0f);
|
||||||
|
min_size.y = std::max(min_size.y, 64.0f);
|
||||||
|
|
||||||
|
return min_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale,
|
||||||
|
float min_scale) {
|
||||||
|
// Calculate preferred size with minimum scale constraint
|
||||||
|
float effective_scale = std::max(global_scale, min_scale);
|
||||||
|
ImVec2 preferred_size = ImVec2(content_size.x * effective_scale + 8.0f,
|
||||||
|
content_size.y * effective_scale + 8.0f);
|
||||||
|
|
||||||
|
// Cap to reasonable maximum sizes for table integration
|
||||||
|
preferred_size.x = std::min(preferred_size.x, 800.0f);
|
||||||
|
preferred_size.y = std::min(preferred_size.y, 600.0f);
|
||||||
|
|
||||||
|
return preferred_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label) {
|
||||||
|
// Reserve space in ImGui layout so tables know the size
|
||||||
|
if (!label.empty()) {
|
||||||
|
ImGui::Text("%s", label.c_str());
|
||||||
|
}
|
||||||
|
ImGui::Dummy(canvas_size);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() -
|
||||||
|
canvas_size.x); // Move back to start
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNextCanvasSize(ImVec2 size, bool auto_resize) {
|
||||||
|
if (auto_resize) {
|
||||||
|
// Use auto-sizing child window for table integration
|
||||||
|
ImGui::SetNextWindowContentSize(size);
|
||||||
|
} else {
|
||||||
|
// Fixed size
|
||||||
|
ImGui::SetNextWindowSize(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// High-level composite operations
|
||||||
|
void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id) {
|
||||||
|
if (!ctx.enable_grid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ctx.draw_list->PushClipRect(ctx.canvas_p0, ctx.canvas_p1, true);
|
||||||
|
|
||||||
|
// Draw grid lines
|
||||||
|
DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1,
|
||||||
|
ctx.scrolling, ctx.grid_step, ctx.global_scale);
|
||||||
|
|
||||||
|
// Draw highlight if specified
|
||||||
|
if (highlight_tile_id != -1) {
|
||||||
|
DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||||
|
highlight_tile_id, ctx.grid_step * ctx.global_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw hex labels if enabled
|
||||||
|
if (ctx.enable_hex_labels) {
|
||||||
|
DrawHexTileLabels(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||||
|
ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x,
|
||||||
|
ctx.canvas_p1.y - ctx.canvas_p0.y),
|
||||||
|
ctx.grid_step, ctx.global_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.draw_list->PopClipRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCanvasOverlay(const CanvasRenderContext& ctx,
|
||||||
|
const ImVector<ImVec2>& points,
|
||||||
|
const ImVector<ImVec2>& selected_points) {
|
||||||
|
const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x,
|
||||||
|
ctx.canvas_p0.y + ctx.scrolling.y);
|
||||||
|
|
||||||
|
// Draw hover points
|
||||||
|
for (int n = 0; n < points.Size; n += 2) {
|
||||||
|
ctx.draw_list->AddRect(
|
||||||
|
ImVec2(origin.x + points[n].x, origin.y + points[n].y),
|
||||||
|
ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y),
|
||||||
|
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw selection rectangles
|
||||||
|
if (!selected_points.empty()) {
|
||||||
|
for (int n = 0; n < selected_points.size(); n += 2) {
|
||||||
|
ctx.draw_list->AddRect(ImVec2(origin.x + selected_points[n].x,
|
||||||
|
origin.y + selected_points[n].y),
|
||||||
|
ImVec2(origin.x + selected_points[n + 1].x + 0x10,
|
||||||
|
origin.y + selected_points[n + 1].y + 0x10),
|
||||||
|
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawCanvasLabels(const CanvasRenderContext& ctx,
|
||||||
|
const ImVector<ImVector<std::string>>& labels,
|
||||||
|
int current_labels, int tile_id_offset) {
|
||||||
|
if (current_labels >= labels.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
float scaled_grid_step = ctx.grid_step * ctx.global_scale;
|
||||||
|
|
||||||
|
for (float x = fmodf(ctx.scrolling.x, scaled_grid_step);
|
||||||
|
x < (ctx.canvas_p1.x - ctx.canvas_p0.x); x += scaled_grid_step) {
|
||||||
|
for (float y = fmodf(ctx.scrolling.y, scaled_grid_step);
|
||||||
|
y < (ctx.canvas_p1.y - ctx.canvas_p0.y); y += scaled_grid_step) {
|
||||||
|
int tile_x = (x - ctx.scrolling.x) / scaled_grid_step;
|
||||||
|
int tile_y = (y - ctx.scrolling.y) / scaled_grid_step;
|
||||||
|
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||||
|
|
||||||
|
if (tile_id >= labels[current_labels].size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& label = labels[current_labels][tile_id];
|
||||||
|
ctx.draw_list->AddText(
|
||||||
|
ImVec2(ctx.canvas_p0.x + x + (scaled_grid_step / 2) - tile_id_offset,
|
||||||
|
ctx.canvas_p0.y + y + (scaled_grid_step / 2) - tile_id_offset),
|
||||||
|
IM_COL32(255, 255, 255, 255), label.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CanvasUtils
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
147
src/app/gui/canvas_utils.h
Normal file
147
src/app/gui/canvas_utils.h
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_CANVAS_UTILS_H
|
||||||
|
#define YAZE_APP_GUI_CANVAS_UTILS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration for canvas display and interaction
|
||||||
|
*/
|
||||||
|
struct CanvasConfig {
|
||||||
|
bool enable_grid = true;
|
||||||
|
bool enable_hex_labels = false;
|
||||||
|
bool enable_custom_labels = false;
|
||||||
|
bool enable_context_menu = true;
|
||||||
|
bool is_draggable = false;
|
||||||
|
bool auto_resize = false;
|
||||||
|
float grid_step = 32.0f;
|
||||||
|
float global_scale = 1.0f;
|
||||||
|
ImVec2 canvas_size = ImVec2(0, 0);
|
||||||
|
ImVec2 content_size = ImVec2(0, 0); // Size of actual content (bitmap, etc.)
|
||||||
|
bool custom_canvas_size = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Selection state for canvas interactions
|
||||||
|
*/
|
||||||
|
struct CanvasSelection {
|
||||||
|
std::vector<ImVec2> selected_tiles;
|
||||||
|
std::vector<ImVec2> selected_points;
|
||||||
|
ImVec2 selected_tile_pos = ImVec2(-1, -1);
|
||||||
|
bool select_rect_active = false;
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
selected_tiles.clear();
|
||||||
|
selected_points.clear();
|
||||||
|
selected_tile_pos = ImVec2(-1, -1);
|
||||||
|
select_rect_active = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Palette management state for canvas
|
||||||
|
*/
|
||||||
|
struct CanvasPaletteManager {
|
||||||
|
std::vector<gfx::SnesPalette> rom_palette_groups;
|
||||||
|
std::vector<std::string> palette_group_names;
|
||||||
|
gfx::SnesPalette original_palette;
|
||||||
|
bool palettes_loaded = false;
|
||||||
|
int current_group_index = 0;
|
||||||
|
int current_palette_index = 0;
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
rom_palette_groups.clear();
|
||||||
|
palette_group_names.clear();
|
||||||
|
original_palette.clear();
|
||||||
|
palettes_loaded = false;
|
||||||
|
current_group_index = 0;
|
||||||
|
current_palette_index = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Context menu item configuration
|
||||||
|
*/
|
||||||
|
struct CanvasContextMenuItem {
|
||||||
|
std::string label;
|
||||||
|
std::string shortcut;
|
||||||
|
std::function<void()> callback;
|
||||||
|
std::function<bool()> enabled_condition = []() { return true; };
|
||||||
|
std::vector<CanvasContextMenuItem> subitems;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility functions for canvas operations
|
||||||
|
*/
|
||||||
|
namespace CanvasUtils {
|
||||||
|
|
||||||
|
// Core utility functions
|
||||||
|
ImVec2 AlignToGrid(ImVec2 pos, float grid_step);
|
||||||
|
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, float global_scale);
|
||||||
|
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int tiles_per_row);
|
||||||
|
|
||||||
|
// Palette management utilities
|
||||||
|
bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager);
|
||||||
|
bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||||
|
int group_index, int palette_index);
|
||||||
|
|
||||||
|
// Drawing utility functions (moved from Canvas class)
|
||||||
|
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
int x, int y, int w, int h, ImVec4 color, float global_scale);
|
||||||
|
void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
const std::string& text, int x, int y, float global_scale);
|
||||||
|
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
int x, int y, int w, int h, uint32_t color = IM_COL32(255, 255, 255, 200));
|
||||||
|
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
int x, int y, int w, int h, ImVec4 color);
|
||||||
|
|
||||||
|
// Grid utility functions
|
||||||
|
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1,
|
||||||
|
ImVec2 scrolling, float grid_step, float global_scale);
|
||||||
|
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
int highlight_tile_id, float grid_step);
|
||||||
|
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||||
|
ImVec2 canvas_sz, float grid_step, float global_scale);
|
||||||
|
|
||||||
|
// Layout and interaction utilities
|
||||||
|
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom);
|
||||||
|
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale);
|
||||||
|
bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1);
|
||||||
|
|
||||||
|
// Size reporting for ImGui table integration
|
||||||
|
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding = 4.0f);
|
||||||
|
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale = 1.0f);
|
||||||
|
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label = "");
|
||||||
|
void SetNextCanvasSize(ImVec2 size, bool auto_resize = false);
|
||||||
|
|
||||||
|
// High-level canvas operations
|
||||||
|
struct CanvasRenderContext {
|
||||||
|
ImDrawList* draw_list;
|
||||||
|
ImVec2 canvas_p0;
|
||||||
|
ImVec2 canvas_p1;
|
||||||
|
ImVec2 scrolling;
|
||||||
|
float global_scale;
|
||||||
|
bool enable_grid;
|
||||||
|
bool enable_hex_labels;
|
||||||
|
float grid_step;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Composite drawing operations
|
||||||
|
void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id = -1);
|
||||||
|
void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector<ImVec2>& points,
|
||||||
|
const ImVector<ImVec2>& selected_points);
|
||||||
|
void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector<ImVector<std::string>>& labels,
|
||||||
|
int current_labels, int tile_id_offset);
|
||||||
|
|
||||||
|
} // namespace CanvasUtils
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_CANVAS_UTILS_H
|
||||||
@@ -13,8 +13,8 @@ namespace gui {
|
|||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
float red;
|
float red;
|
||||||
float blue;
|
|
||||||
float green;
|
float green;
|
||||||
|
float blue;
|
||||||
float alpha;
|
float alpha;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
466
src/app/gui/enhanced_palette_editor.cc
Normal file
466
src/app/gui/enhanced_palette_editor.cc
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
#include "enhanced_palette_editor.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include "app/core/window.h"
|
||||||
|
#include "app/gui/color.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
using core::Renderer;
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::Initialize(Rom* rom) {
|
||||||
|
rom_ = rom;
|
||||||
|
rom_palettes_loaded_ = false;
|
||||||
|
if (rom_) {
|
||||||
|
LoadROMPalettes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::ShowPaletteEditor(gfx::SnesPalette& palette, const std::string& title) {
|
||||||
|
if (ImGui::BeginPopupModal(title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::Text("Enhanced Palette Editor");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Palette grid editor
|
||||||
|
DrawPaletteGrid(palette);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Analysis and tools
|
||||||
|
if (ImGui::CollapsingHeader("Palette Analysis")) {
|
||||||
|
DrawPaletteAnalysis(palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("ROM Palette Manager") && rom_) {
|
||||||
|
DrawROMPaletteSelector();
|
||||||
|
|
||||||
|
if (ImGui::Button("Apply ROM Palette") && !rom_palette_groups_.empty()) {
|
||||||
|
if (current_group_index_ < static_cast<int>(rom_palette_groups_.size())) {
|
||||||
|
palette = rom_palette_groups_[current_group_index_];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Action buttons
|
||||||
|
if (ImGui::Button("Save Backup")) {
|
||||||
|
SavePaletteBackup(palette);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Restore Backup")) {
|
||||||
|
RestorePaletteBackup(palette);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Close")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::ShowROMPaletteManager() {
|
||||||
|
if (!show_rom_manager_) return;
|
||||||
|
|
||||||
|
if (ImGui::Begin("ROM Palette Manager", &show_rom_manager_)) {
|
||||||
|
if (!rom_) {
|
||||||
|
ImGui::Text("No ROM loaded");
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rom_palettes_loaded_) {
|
||||||
|
LoadROMPalettes();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawROMPaletteSelector();
|
||||||
|
|
||||||
|
if (current_group_index_ < static_cast<int>(rom_palette_groups_.size())) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Preview of %s:", palette_group_names_[current_group_index_].c_str());
|
||||||
|
|
||||||
|
const auto& preview_palette = rom_palette_groups_[current_group_index_];
|
||||||
|
DrawPaletteGrid(const_cast<gfx::SnesPalette&>(preview_palette));
|
||||||
|
|
||||||
|
DrawPaletteAnalysis(preview_palette);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::ShowColorAnalysis(const gfx::Bitmap& bitmap, const std::string& title) {
|
||||||
|
if (!show_color_analysis_) return;
|
||||||
|
|
||||||
|
if (ImGui::Begin(title.c_str(), &show_color_analysis_)) {
|
||||||
|
ImGui::Text("Bitmap Color Analysis");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (!bitmap.is_active()) {
|
||||||
|
ImGui::Text("Bitmap is not active");
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze pixel distribution
|
||||||
|
std::map<uint8_t, int> pixel_counts;
|
||||||
|
const auto& data = bitmap.vector();
|
||||||
|
|
||||||
|
for (uint8_t pixel : data) {
|
||||||
|
uint8_t palette_index = pixel & 0x0F; // 4-bit palette index
|
||||||
|
pixel_counts[palette_index]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Bitmap Size: %d x %d (%zu pixels)",
|
||||||
|
bitmap.width(), bitmap.height(), data.size());
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Pixel Distribution:");
|
||||||
|
|
||||||
|
// Show distribution as bars
|
||||||
|
int total_pixels = static_cast<int>(data.size());
|
||||||
|
for (const auto& [index, count] : pixel_counts) {
|
||||||
|
float percentage = (static_cast<float>(count) / total_pixels) * 100.0f;
|
||||||
|
ImGui::Text("Index %d: %d pixels (%.1f%%)", index, count, percentage);
|
||||||
|
|
||||||
|
// Progress bar visualization
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ProgressBar(percentage / 100.0f, ImVec2(100, 0));
|
||||||
|
|
||||||
|
// Color swatch if palette is available
|
||||||
|
if (index < static_cast<int>(bitmap.palette().size())) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
auto color = bitmap.palette()[index];
|
||||||
|
ImVec4 display_color = color.rgb();
|
||||||
|
ImGui::ColorButton(("##color" + std::to_string(index)).c_str(),
|
||||||
|
display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("SNES Color: 0x%04X\nRGB: (%d, %d, %d)",
|
||||||
|
color.snes(),
|
||||||
|
static_cast<int>(display_color.x * 255),
|
||||||
|
static_cast<int>(display_color.y * 255),
|
||||||
|
static_cast<int>(display_color.z * 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnhancedPaletteEditor::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index, int palette_index) {
|
||||||
|
if (!bitmap || !rom_palettes_loaded_ ||
|
||||||
|
group_index < 0 || group_index >= static_cast<int>(rom_palette_groups_.size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto& selected_palette = rom_palette_groups_[group_index];
|
||||||
|
|
||||||
|
// Save current palette as backup
|
||||||
|
SavePaletteBackup(bitmap->palette());
|
||||||
|
|
||||||
|
// Apply new palette
|
||||||
|
if (palette_index >= 0 && palette_index < 8) {
|
||||||
|
bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
|
||||||
|
} else {
|
||||||
|
bitmap->SetPalette(selected_palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::Get().UpdateBitmap(bitmap);
|
||||||
|
|
||||||
|
current_group_index_ = group_index;
|
||||||
|
current_palette_index_ = palette_index;
|
||||||
|
|
||||||
|
util::logf("Applied ROM palette: %s (index %d)",
|
||||||
|
palette_group_names_[group_index].c_str(), palette_index);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
util::logf("Failed to apply ROM palette: %s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gfx::SnesPalette* EnhancedPaletteEditor::GetSelectedROMPalette() const {
|
||||||
|
if (!rom_palettes_loaded_ || current_group_index_ < 0 ||
|
||||||
|
current_group_index_ >= static_cast<int>(rom_palette_groups_.size())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rom_palette_groups_[current_group_index_];
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::SavePaletteBackup(const gfx::SnesPalette& palette) {
|
||||||
|
backup_palette_ = palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnhancedPaletteEditor::RestorePaletteBackup(gfx::SnesPalette& palette) {
|
||||||
|
if (backup_palette_.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
palette = backup_palette_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::DrawPaletteGrid(gfx::SnesPalette& palette, int cols) {
|
||||||
|
for (int i = 0; i < static_cast<int>(palette.size()); i++) {
|
||||||
|
if (i % cols != 0) ImGui::SameLine();
|
||||||
|
|
||||||
|
auto color = palette[i];
|
||||||
|
ImVec4 display_color = color.rgb();
|
||||||
|
|
||||||
|
ImGui::PushID(i);
|
||||||
|
|
||||||
|
// Color button with editing capability
|
||||||
|
if (ImGui::ColorButton("##color", display_color,
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(30, 30))) {
|
||||||
|
editing_color_index_ = i;
|
||||||
|
temp_color_ = display_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context menu for individual colors
|
||||||
|
if (ImGui::BeginPopupContextItem()) {
|
||||||
|
ImGui::Text("Color %d (0x%04X)", i, color.snes());
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Edit Color")) {
|
||||||
|
editing_color_index_ = i;
|
||||||
|
temp_color_ = display_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Copy Color")) {
|
||||||
|
// Could implement color clipboard here
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Reset to Black")) {
|
||||||
|
palette[i] = gfx::SnesColor(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tooltip with detailed info
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Color %d\nSNES: 0x%04X\nRGB: (%d, %d, %d)\nClick to edit",
|
||||||
|
i, color.snes(),
|
||||||
|
static_cast<int>(display_color.x * 255),
|
||||||
|
static_cast<int>(display_color.y * 255),
|
||||||
|
static_cast<int>(display_color.z * 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color editor popup
|
||||||
|
if (editing_color_index_ >= 0) {
|
||||||
|
ImGui::OpenPopup("Edit Color");
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal("Edit Color", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::Text("Editing Color %d", editing_color_index_);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit4("Color", &temp_color_.x,
|
||||||
|
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_DisplayRGB)) {
|
||||||
|
// Update the palette color in real-time
|
||||||
|
auto new_snes_color = gfx::SnesColor(temp_color_);
|
||||||
|
palette[editing_color_index_] = new_snes_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Apply")) {
|
||||||
|
editing_color_index_ = -1;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel")) {
|
||||||
|
editing_color_index_ = -1;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::DrawROMPaletteSelector() {
|
||||||
|
if (!rom_palettes_loaded_) {
|
||||||
|
LoadROMPalettes();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rom_palette_groups_.empty()) {
|
||||||
|
ImGui::Text("No ROM palettes available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group selector
|
||||||
|
ImGui::Text("Palette Group:");
|
||||||
|
if (ImGui::Combo("##PaletteGroup", ¤t_group_index_,
|
||||||
|
[](void* data, int idx, const char** out_text) -> bool {
|
||||||
|
auto* names = static_cast<std::vector<std::string>*>(data);
|
||||||
|
if (idx < 0 || idx >= static_cast<int>(names->size())) return false;
|
||||||
|
*out_text = (*names)[idx].c_str();
|
||||||
|
return true;
|
||||||
|
}, &palette_group_names_, static_cast<int>(palette_group_names_.size()))) {
|
||||||
|
// Group changed - could trigger preview update
|
||||||
|
}
|
||||||
|
|
||||||
|
// Palette index selector
|
||||||
|
ImGui::Text("Palette Index:");
|
||||||
|
ImGui::SliderInt("##PaletteIndex", ¤t_palette_index_, 0, 7, "%d");
|
||||||
|
|
||||||
|
// Quick palette preview
|
||||||
|
if (current_group_index_ < static_cast<int>(rom_palette_groups_.size())) {
|
||||||
|
ImGui::Text("Preview:");
|
||||||
|
const auto& preview_palette = rom_palette_groups_[current_group_index_];
|
||||||
|
|
||||||
|
// Show just first 8 colors in a row
|
||||||
|
for (int i = 0; i < 8 && i < static_cast<int>(preview_palette.size()); i++) {
|
||||||
|
if (i > 0) ImGui::SameLine();
|
||||||
|
auto color = preview_palette[i];
|
||||||
|
ImVec4 display_color = color.rgb();
|
||||||
|
ImGui::ColorButton(("##preview" + std::to_string(i)).c_str(),
|
||||||
|
display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::DrawColorEditControls(gfx::SnesColor& color, int color_index) {
|
||||||
|
ImVec4 rgba = color.rgb();
|
||||||
|
|
||||||
|
ImGui::PushID(color_index);
|
||||||
|
|
||||||
|
if (ImGui::ColorEdit4("##color_edit", &rgba.x,
|
||||||
|
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_DisplayRGB)) {
|
||||||
|
color = gfx::SnesColor(rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SNES-specific controls
|
||||||
|
ImGui::Text("SNES Color: 0x%04X", color.snes());
|
||||||
|
|
||||||
|
// Individual RGB component sliders (0-31 for SNES)
|
||||||
|
int r = (color.snes() & 0x1F);
|
||||||
|
int g = (color.snes() >> 5) & 0x1F;
|
||||||
|
int b = (color.snes() >> 10) & 0x1F;
|
||||||
|
|
||||||
|
if (ImGui::SliderInt("Red", &r, 0, 31)) {
|
||||||
|
uint16_t new_color = (color.snes() & 0xFFE0) | (r & 0x1F);
|
||||||
|
color = gfx::SnesColor(new_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::SliderInt("Green", &g, 0, 31)) {
|
||||||
|
uint16_t new_color = (color.snes() & 0xFC1F) | ((g & 0x1F) << 5);
|
||||||
|
color = gfx::SnesColor(new_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::SliderInt("Blue", &b, 0, 31)) {
|
||||||
|
uint16_t new_color = (color.snes() & 0x83FF) | ((b & 0x1F) << 10);
|
||||||
|
color = gfx::SnesColor(new_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::DrawPaletteAnalysis(const gfx::SnesPalette& palette) {
|
||||||
|
ImGui::Text("Palette Information:");
|
||||||
|
ImGui::Text("Size: %zu colors", palette.size());
|
||||||
|
|
||||||
|
// Color distribution analysis
|
||||||
|
std::map<uint16_t, int> color_frequency;
|
||||||
|
for (int i = 0; i < static_cast<int>(palette.size()); i++) {
|
||||||
|
color_frequency[palette[i].snes()]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Unique Colors: %zu", color_frequency.size());
|
||||||
|
|
||||||
|
if (color_frequency.size() < palette.size()) {
|
||||||
|
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Warning: Duplicate colors detected!");
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Duplicate Colors")) {
|
||||||
|
for (const auto& [snes_color, count] : color_frequency) {
|
||||||
|
if (count > 1) {
|
||||||
|
ImVec4 display_color = gfx::SnesColor(snes_color).rgb();
|
||||||
|
ImGui::ColorButton(("##dup" + std::to_string(snes_color)).c_str(),
|
||||||
|
display_color, ImGuiColorEditFlags_NoTooltip, ImVec2(16, 16));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("0x%04X appears %d times", snes_color, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brightness analysis
|
||||||
|
float total_brightness = 0.0f;
|
||||||
|
float min_brightness = 1.0f;
|
||||||
|
float max_brightness = 0.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(palette.size()); i++) {
|
||||||
|
ImVec4 color = palette[i].rgb();
|
||||||
|
float brightness = (color.x + color.y + color.z) / 3.0f;
|
||||||
|
total_brightness += brightness;
|
||||||
|
min_brightness = std::min(min_brightness, brightness);
|
||||||
|
max_brightness = std::max(max_brightness, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
float avg_brightness = total_brightness / palette.size();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Brightness Analysis:");
|
||||||
|
ImGui::Text("Average: %.2f", avg_brightness);
|
||||||
|
ImGui::Text("Range: %.2f - %.2f", min_brightness, max_brightness);
|
||||||
|
|
||||||
|
// Show brightness as progress bar
|
||||||
|
ImGui::Text("Brightness Distribution:");
|
||||||
|
ImGui::ProgressBar(avg_brightness, ImVec2(-1, 0), "Avg");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnhancedPaletteEditor::LoadROMPalettes() {
|
||||||
|
if (!rom_ || rom_palettes_loaded_) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto& palette_groups = rom_->palette_group();
|
||||||
|
rom_palette_groups_.clear();
|
||||||
|
palette_group_names_.clear();
|
||||||
|
|
||||||
|
// Load all available palette groups
|
||||||
|
if (palette_groups.overworld_main.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.overworld_main[0]);
|
||||||
|
palette_group_names_.push_back("Overworld Main");
|
||||||
|
}
|
||||||
|
if (palette_groups.overworld_aux.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.overworld_aux[0]);
|
||||||
|
palette_group_names_.push_back("Overworld Aux");
|
||||||
|
}
|
||||||
|
if (palette_groups.overworld_animated.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.overworld_animated[0]);
|
||||||
|
palette_group_names_.push_back("Overworld Animated");
|
||||||
|
}
|
||||||
|
if (palette_groups.dungeon_main.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.dungeon_main[0]);
|
||||||
|
palette_group_names_.push_back("Dungeon Main");
|
||||||
|
}
|
||||||
|
if (palette_groups.global_sprites.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.global_sprites[0]);
|
||||||
|
palette_group_names_.push_back("Global Sprites");
|
||||||
|
}
|
||||||
|
if (palette_groups.armors.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.armors[0]);
|
||||||
|
palette_group_names_.push_back("Armor");
|
||||||
|
}
|
||||||
|
if (palette_groups.swords.size() > 0) {
|
||||||
|
rom_palette_groups_.push_back(palette_groups.swords[0]);
|
||||||
|
palette_group_names_.push_back("Swords");
|
||||||
|
}
|
||||||
|
|
||||||
|
rom_palettes_loaded_ = true;
|
||||||
|
util::logf("Enhanced Palette Editor: Loaded %zu ROM palette groups", rom_palette_groups_.size());
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
util::logf("Enhanced Palette Editor: Failed to load ROM palettes: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
92
src/app/gui/enhanced_palette_editor.h
Normal file
92
src/app/gui/enhanced_palette_editor.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||||
|
#define YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enhanced palette editor with ROM integration and analysis tools
|
||||||
|
*/
|
||||||
|
class EnhancedPaletteEditor {
|
||||||
|
public:
|
||||||
|
EnhancedPaletteEditor() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the palette editor with ROM data
|
||||||
|
*/
|
||||||
|
void Initialize(Rom* rom);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show the main palette editor window
|
||||||
|
*/
|
||||||
|
void ShowPaletteEditor(gfx::SnesPalette& palette, const std::string& title = "Palette Editor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show the ROM palette manager window
|
||||||
|
*/
|
||||||
|
void ShowROMPaletteManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Show color analysis window for a bitmap
|
||||||
|
*/
|
||||||
|
void ShowColorAnalysis(const gfx::Bitmap& bitmap, const std::string& title = "Color Analysis");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Apply a ROM palette group to a bitmap
|
||||||
|
*/
|
||||||
|
bool ApplyROMPalette(gfx::Bitmap* bitmap, int group_index, int palette_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the currently selected ROM palette
|
||||||
|
*/
|
||||||
|
const gfx::SnesPalette* GetSelectedROMPalette() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save current palette as backup
|
||||||
|
*/
|
||||||
|
void SavePaletteBackup(const gfx::SnesPalette& palette);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Restore palette from backup
|
||||||
|
*/
|
||||||
|
bool RestorePaletteBackup(gfx::SnesPalette& palette);
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
bool IsROMLoaded() const { return rom_ != nullptr; }
|
||||||
|
int GetCurrentGroupIndex() const { return current_group_index_; }
|
||||||
|
int GetCurrentPaletteIndex() const { return current_palette_index_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DrawPaletteGrid(gfx::SnesPalette& palette, int cols = 8);
|
||||||
|
void DrawROMPaletteSelector();
|
||||||
|
void DrawColorEditControls(gfx::SnesColor& color, int color_index);
|
||||||
|
void DrawPaletteAnalysis(const gfx::SnesPalette& palette);
|
||||||
|
void LoadROMPalettes();
|
||||||
|
|
||||||
|
Rom* rom_ = nullptr;
|
||||||
|
std::vector<gfx::SnesPalette> rom_palette_groups_;
|
||||||
|
std::vector<std::string> palette_group_names_;
|
||||||
|
gfx::SnesPalette backup_palette_;
|
||||||
|
|
||||||
|
int current_group_index_ = 0;
|
||||||
|
int current_palette_index_ = 0;
|
||||||
|
bool rom_palettes_loaded_ = false;
|
||||||
|
bool show_color_analysis_ = false;
|
||||||
|
bool show_rom_manager_ = false;
|
||||||
|
|
||||||
|
// Color editing state
|
||||||
|
int editing_color_index_ = -1;
|
||||||
|
ImVec4 temp_color_ = ImVec4(0, 0, 0, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||||
@@ -3,6 +3,8 @@ set(
|
|||||||
app/gui/modules/asset_browser.cc
|
app/gui/modules/asset_browser.cc
|
||||||
app/gui/modules/text_editor.cc
|
app/gui/modules/text_editor.cc
|
||||||
app/gui/canvas.cc
|
app/gui/canvas.cc
|
||||||
|
app/gui/canvas_utils.cc
|
||||||
|
app/gui/enhanced_palette_editor.cc
|
||||||
app/gui/input.cc
|
app/gui/input.cc
|
||||||
app/gui/style.cc
|
app/gui/style.cc
|
||||||
app/gui/color.cc
|
app/gui/color.cc
|
||||||
|
|||||||
4367
src/app/gui/icons.h
4367
src/app/gui/icons.h
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
|||||||
#include "app/gui/background_renderer.h"
|
#include "app/gui/background_renderer.h"
|
||||||
#include "core/platform/font_loader.h"
|
#include "core/platform/font_loader.h"
|
||||||
#include "gui/color.h"
|
#include "gui/color.h"
|
||||||
|
#include "gui/icons.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
#include "imgui/imgui_internal.h"
|
#include "imgui/imgui_internal.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
@@ -26,114 +27,8 @@ Color ParseColor(const std::string &color) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ParseThemeContents(const std::string &key,
|
|
||||||
const std::string &value, Theme &theme) {
|
|
||||||
try {
|
|
||||||
if (key == "MenuBarBg") {
|
|
||||||
theme.menu_bar_bg = ParseColor(value);
|
|
||||||
} else if (key == "TitleBgActive") {
|
|
||||||
theme.title_bg_active = ParseColor(value);
|
|
||||||
} else if (key == "TitleBgCollapsed") {
|
|
||||||
theme.title_bg_collapsed = ParseColor(value);
|
|
||||||
} else if (key == "Tab") {
|
|
||||||
theme.tab = ParseColor(value);
|
|
||||||
} else if (key == "TabHovered") {
|
|
||||||
theme.tab_hovered = ParseColor(value);
|
|
||||||
} else if (key == "TabActive") {
|
|
||||||
theme.tab_active = ParseColor(value);
|
|
||||||
}
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
return absl::InvalidArgumentError(e.what());
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
absl::StatusOr<Theme> LoadTheme(const std::string &filename) {
|
|
||||||
std::string theme_contents;
|
|
||||||
try {
|
|
||||||
theme_contents = core::LoadFile(filename);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
return absl::InternalError(e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
Theme theme;
|
|
||||||
std::istringstream theme_stream(theme_contents);
|
|
||||||
while (theme_stream.good()) {
|
|
||||||
std::string line;
|
|
||||||
std::getline(theme_stream, line);
|
|
||||||
if (line.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::istringstream line_stream(line);
|
|
||||||
std::string key;
|
|
||||||
std::string value;
|
|
||||||
std::getline(line_stream, key, '=');
|
|
||||||
std::getline(line_stream, value);
|
|
||||||
RETURN_IF_ERROR(ParseThemeContents(key, value, theme));
|
|
||||||
}
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status SaveTheme(const Theme &theme) {
|
|
||||||
std::ostringstream theme_stream;
|
|
||||||
theme_stream << theme.name << "Theme\n";
|
|
||||||
theme_stream << "MenuBarBg=#" << gui::ColorToHexString(theme.menu_bar_bg)
|
|
||||||
<< "\n";
|
|
||||||
theme_stream << "TitleBg=#" << gui::ColorToHexString(theme.title_bar_bg)
|
|
||||||
<< "\n";
|
|
||||||
theme_stream << "Header=#" << gui::ColorToHexString(theme.header) << "\n";
|
|
||||||
theme_stream << "HeaderHovered=#"
|
|
||||||
<< gui::ColorToHexString(theme.header_hovered) << "\n";
|
|
||||||
theme_stream << "HeaderActive=#" << gui::ColorToHexString(theme.header_active)
|
|
||||||
<< "\n";
|
|
||||||
theme_stream << "TitleBgActive=#"
|
|
||||||
<< gui::ColorToHexString(theme.title_bg_active) << "\n";
|
|
||||||
theme_stream << "TitleBgCollapsed=#"
|
|
||||||
<< gui::ColorToHexString(theme.title_bg_collapsed) << "\n";
|
|
||||||
theme_stream << "Tab=#" << gui::ColorToHexString(theme.tab) << "\n";
|
|
||||||
theme_stream << "TabHovered=#" << gui::ColorToHexString(theme.tab_hovered)
|
|
||||||
<< "\n";
|
|
||||||
theme_stream << "TabActive=#" << gui::ColorToHexString(theme.tab_active)
|
|
||||||
<< "\n";
|
|
||||||
theme_stream << "Button=#" << gui::ColorToHexString(theme.button) << "\n";
|
|
||||||
theme_stream << "ButtonHovered=#"
|
|
||||||
<< gui::ColorToHexString(theme.button_hovered) << "\n";
|
|
||||||
theme_stream << "ButtonActive=#" << gui::ColorToHexString(theme.button_active)
|
|
||||||
<< "\n";
|
|
||||||
|
|
||||||
// Save the theme to a file.
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyTheme(const Theme &theme) {
|
|
||||||
ImGuiStyle *style = &ImGui::GetStyle();
|
|
||||||
ImVec4 *colors = style->Colors;
|
|
||||||
|
|
||||||
colors[ImGuiCol_MenuBarBg] = gui::ConvertColorToImVec4(theme.menu_bar_bg);
|
|
||||||
colors[ImGuiCol_TitleBg] = gui::ConvertColorToImVec4(theme.title_bar_bg);
|
|
||||||
colors[ImGuiCol_Header] = gui::ConvertColorToImVec4(theme.header);
|
|
||||||
colors[ImGuiCol_HeaderHovered] =
|
|
||||||
gui::ConvertColorToImVec4(theme.header_hovered);
|
|
||||||
colors[ImGuiCol_HeaderActive] =
|
|
||||||
gui::ConvertColorToImVec4(theme.header_active);
|
|
||||||
colors[ImGuiCol_TitleBgActive] =
|
|
||||||
gui::ConvertColorToImVec4(theme.title_bg_active);
|
|
||||||
colors[ImGuiCol_TitleBgCollapsed] =
|
|
||||||
gui::ConvertColorToImVec4(theme.title_bg_collapsed);
|
|
||||||
colors[ImGuiCol_Tab] = gui::ConvertColorToImVec4(theme.tab);
|
|
||||||
colors[ImGuiCol_TabHovered] = gui::ConvertColorToImVec4(theme.tab_hovered);
|
|
||||||
colors[ImGuiCol_TabActive] = gui::ConvertColorToImVec4(theme.tab_active);
|
|
||||||
colors[ImGuiCol_Button] = gui::ConvertColorToImVec4(theme.button);
|
|
||||||
colors[ImGuiCol_ButtonHovered] =
|
|
||||||
gui::ConvertColorToImVec4(theme.button_hovered);
|
|
||||||
colors[ImGuiCol_ButtonActive] =
|
|
||||||
gui::ConvertColorToImVec4(theme.button_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColorsYaze() {
|
void ColorsYaze() {
|
||||||
ImGuiStyle *style = &ImGui::GetStyle();
|
ImGuiStyle *style = &ImGui::GetStyle();
|
||||||
ImVec4 *colors = style->Colors;
|
ImVec4 *colors = style->Colors;
|
||||||
@@ -387,7 +282,27 @@ void BeginNoPadding() {
|
|||||||
void EndNoPadding() { ImGui::PopStyleVar(2); }
|
void EndNoPadding() { ImGui::PopStyleVar(2); }
|
||||||
|
|
||||||
void BeginChildWithScrollbar(const char *str_id) {
|
void BeginChildWithScrollbar(const char *str_id) {
|
||||||
ImGui::BeginChild(str_id, ImGui::GetContentRegionAvail(), true,
|
// Get available region but ensure minimum size for proper scrolling
|
||||||
|
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||||
|
if (available.x < 64.0f) available.x = 64.0f;
|
||||||
|
if (available.y < 64.0f) available.y = 64.0f;
|
||||||
|
|
||||||
|
ImGui::BeginChild(str_id, available, true,
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginChildWithScrollbar(const char *str_id, ImVec2 content_size) {
|
||||||
|
// Set content size before beginning child to enable proper scrolling
|
||||||
|
if (content_size.x > 0 && content_size.y > 0) {
|
||||||
|
ImGui::SetNextWindowContentSize(content_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get available region but ensure minimum size for proper scrolling
|
||||||
|
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||||
|
if (available.x < 64.0f) available.x = 64.0f;
|
||||||
|
if (available.y < 64.0f) available.y = 64.0f;
|
||||||
|
|
||||||
|
ImGui::BeginChild(str_id, available, true,
|
||||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +313,50 @@ void BeginChildBothScrollbars(int id) {
|
|||||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper functions for table canvas management
|
||||||
|
void BeginTableCanvas(const char* table_id, int columns, ImVec2 canvas_size) {
|
||||||
|
// Use proper sizing for tables containing canvas elements
|
||||||
|
ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchProp;
|
||||||
|
|
||||||
|
// If canvas size is specified, use it as minimum size
|
||||||
|
ImVec2 outer_size = ImVec2(0, 0);
|
||||||
|
if (canvas_size.x > 0 || canvas_size.y > 0) {
|
||||||
|
outer_size = canvas_size;
|
||||||
|
flags |= ImGuiTableFlags_NoHostExtendY; // Prevent auto-extending past canvas size
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::BeginTable(table_id, columns, flags, outer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndTableCanvas() {
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupCanvasTableColumn(const char* label, float width_ratio) {
|
||||||
|
if (width_ratio > 0) {
|
||||||
|
ImGui::TableSetupColumn(label, ImGuiTableColumnFlags_WidthStretch, width_ratio);
|
||||||
|
} else {
|
||||||
|
ImGui::TableSetupColumn(label, ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginCanvasTableCell(ImVec2 min_size) {
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
// Ensure minimum size for canvas cells
|
||||||
|
if (min_size.x > 0 || min_size.y > 0) {
|
||||||
|
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||||
|
ImVec2 actual_size = ImVec2(
|
||||||
|
std::max(avail.x, min_size.x),
|
||||||
|
std::max(avail.y, min_size.y)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reserve space for the canvas
|
||||||
|
ImGui::Dummy(actual_size);
|
||||||
|
// ImGui::SetCursorPos(ImGui::GetCursorPos() - actual_size); // Reset cursor for drawing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DrawDisplaySettings(ImGuiStyle *ref) {
|
void DrawDisplaySettings(ImGuiStyle *ref) {
|
||||||
// You can pass in a reference ImGuiStyle structure to compare to, revert to
|
// You can pass in a reference ImGuiStyle structure to compare to, revert to
|
||||||
// and save to (without a reference style pointer, we will use one compared
|
// and save to (without a reference style pointer, we will use one compared
|
||||||
@@ -413,66 +372,121 @@ void DrawDisplaySettings(ImGuiStyle *ref) {
|
|||||||
|
|
||||||
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
||||||
|
|
||||||
// Enhanced theme selector
|
// Enhanced theme management section
|
||||||
auto& theme_manager = ThemeManager::Get();
|
if (ImGui::CollapsingHeader("Theme Management", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
static bool show_theme_selector = false;
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
static bool show_theme_selector = false;
|
||||||
ImGui::Text("Theme Selection:");
|
static bool show_theme_editor = false;
|
||||||
|
|
||||||
// Classic YAZE button
|
ImGui::Text("%s Current Theme:", ICON_MD_PALETTE);
|
||||||
std::string current_theme_name = theme_manager.GetCurrentThemeName();
|
ImGui::SameLine();
|
||||||
bool is_classic_active = (current_theme_name == "Classic YAZE");
|
|
||||||
|
std::string current_theme_name = theme_manager.GetCurrentThemeName();
|
||||||
if (is_classic_active) {
|
bool is_classic_active = (current_theme_name == "Classic YAZE");
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.6f, 0.2f, 1.0f));
|
|
||||||
}
|
// Current theme display with color preview
|
||||||
|
if (is_classic_active) {
|
||||||
if (ImGui::Button("Classic YAZE")) {
|
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s", current_theme_name.c_str());
|
||||||
theme_manager.ApplyClassicYazeTheme();
|
} else {
|
||||||
ref_saved_style = style;
|
ImGui::Text("%s", current_theme_name.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("ColorsYaze")) {
|
|
||||||
gui::ColorsYaze();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_classic_active) {
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Text(" | ");
|
|
||||||
ImGui::SameLine();
|
|
||||||
|
|
||||||
// File themes dropdown - just the raw list, no sorting
|
|
||||||
auto available_themes = theme_manager.GetAvailableThemes();
|
|
||||||
const char* current_file_theme = "";
|
|
||||||
|
|
||||||
// Find current file theme for display
|
|
||||||
for (const auto& theme_name : available_themes) {
|
|
||||||
if (theme_name == current_theme_name) {
|
|
||||||
current_file_theme = theme_name.c_str();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Theme color preview
|
||||||
if (ImGui::BeginCombo("File Themes", current_file_theme)) {
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
for (const auto& theme_name : available_themes) {
|
ImGui::SameLine();
|
||||||
if (ImGui::Selectable(theme_name.c_str())) {
|
ImGui::ColorButton("##primary_preview", gui::ConvertColorToImVec4(current_theme.primary),
|
||||||
theme_manager.LoadTheme(theme_name);
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton("##secondary_preview", gui::ConvertColorToImVec4(current_theme.secondary),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton("##accent_preview", gui::ConvertColorToImVec4(current_theme.accent),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// Theme selection table for better organization
|
||||||
|
if (ImGui::BeginTable("ThemeSelectionTable", 3,
|
||||||
|
ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_NoHostExtendY,
|
||||||
|
ImVec2(0, 80))) {
|
||||||
|
|
||||||
|
ImGui::TableSetupColumn("Built-in", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||||
|
ImGui::TableSetupColumn("File Themes", ImGuiTableColumnFlags_WidthStretch, 0.4f);
|
||||||
|
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
// Built-in themes column
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.6f, 0.2f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Classic YAZE", ImVec2(-1, 30))) {
|
||||||
|
theme_manager.ApplyClassicYazeTheme();
|
||||||
ref_saved_style = style;
|
ref_saved_style = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Reset ColorsYaze", ImVec2(-1, 30))) {
|
||||||
|
gui::ColorsYaze();
|
||||||
|
ref_saved_style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File themes column
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
auto available_themes = theme_manager.GetAvailableThemes();
|
||||||
|
const char* current_file_theme = "";
|
||||||
|
|
||||||
|
// Find current file theme for display
|
||||||
|
for (const auto& theme_name : available_themes) {
|
||||||
|
if (theme_name == current_theme_name) {
|
||||||
|
current_file_theme = theme_name.c_str();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
if (ImGui::BeginCombo("##FileThemes", current_file_theme)) {
|
||||||
|
for (const auto& theme_name : available_themes) {
|
||||||
|
bool is_selected = (theme_name == current_theme_name);
|
||||||
|
if (ImGui::Selectable(theme_name.c_str(), is_selected)) {
|
||||||
|
theme_manager.LoadTheme(theme_name);
|
||||||
|
ref_saved_style = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Refresh Themes", ImVec2(-1, 30))) {
|
||||||
|
theme_manager.RefreshAvailableThemes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions column
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
if (ImGui::Button("Theme Selector", ImVec2(-1, 30))) {
|
||||||
|
show_theme_selector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Theme Editor", ImVec2(-1, 30))) {
|
||||||
|
show_theme_editor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show theme dialogs
|
||||||
|
if (show_theme_selector) {
|
||||||
|
theme_manager.ShowThemeSelector(&show_theme_selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_theme_editor) {
|
||||||
|
theme_manager.ShowSimpleThemeEditor(&show_theme_editor);
|
||||||
}
|
}
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Theme Settings")) {
|
|
||||||
show_theme_selector = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show_theme_selector) {
|
|
||||||
theme_manager.ShowThemeSelector(&show_theme_selector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -825,6 +839,444 @@ void DrawDisplaySettings(ImGuiStyle *ref) {
|
|||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawDisplaySettingsForPopup(ImGuiStyle *ref) {
|
||||||
|
// Popup-safe version of DrawDisplaySettings without problematic tables
|
||||||
|
ImGuiStyle &style = ImGui::GetStyle();
|
||||||
|
static ImGuiStyle ref_saved_style;
|
||||||
|
|
||||||
|
// Default to using internal storage as reference
|
||||||
|
static bool init = true;
|
||||||
|
if (init && ref == NULL) ref_saved_style = style;
|
||||||
|
init = false;
|
||||||
|
if (ref == NULL) ref = &ref_saved_style;
|
||||||
|
|
||||||
|
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);
|
||||||
|
|
||||||
|
// Enhanced theme management section (simplified for popup)
|
||||||
|
if (ImGui::CollapsingHeader("Theme Management", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
auto& theme_manager = ThemeManager::Get();
|
||||||
|
|
||||||
|
ImGui::Text("%s Current Theme:", ICON_MD_PALETTE);
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
std::string current_theme_name = theme_manager.GetCurrentThemeName();
|
||||||
|
bool is_classic_active = (current_theme_name == "Classic YAZE");
|
||||||
|
|
||||||
|
// Current theme display with color preview
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s", current_theme_name.c_str());
|
||||||
|
} else {
|
||||||
|
ImGui::Text("%s", current_theme_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme color preview
|
||||||
|
auto current_theme = theme_manager.GetCurrentTheme();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton("##primary_preview", gui::ConvertColorToImVec4(current_theme.primary),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton("##secondary_preview", gui::ConvertColorToImVec4(current_theme.secondary),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::ColorButton("##accent_preview", gui::ConvertColorToImVec4(current_theme.accent),
|
||||||
|
ImGuiColorEditFlags_NoTooltip, ImVec2(20, 20));
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// Simplified theme selection (no table to avoid popup conflicts)
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.6f, 0.2f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Classic YAZE")) {
|
||||||
|
theme_manager.ApplyClassicYazeTheme();
|
||||||
|
ref_saved_style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_classic_active) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Reset ColorsYaze")) {
|
||||||
|
gui::ColorsYaze();
|
||||||
|
ref_saved_style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File themes dropdown
|
||||||
|
auto available_themes = theme_manager.GetAvailableThemes();
|
||||||
|
const char* current_file_theme = "";
|
||||||
|
|
||||||
|
// Find current file theme for display
|
||||||
|
for (const auto& theme_name : available_themes) {
|
||||||
|
if (theme_name == current_theme_name) {
|
||||||
|
current_file_theme = theme_name.c_str();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("File Themes:");
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
if (ImGui::BeginCombo("##FileThemes", current_file_theme)) {
|
||||||
|
for (const auto& theme_name : available_themes) {
|
||||||
|
bool is_selected = (theme_name == current_theme_name);
|
||||||
|
if (ImGui::Selectable(theme_name.c_str(), is_selected)) {
|
||||||
|
theme_manager.LoadTheme(theme_name);
|
||||||
|
ref_saved_style = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Refresh Themes")) {
|
||||||
|
theme_manager.RefreshAvailableThemes();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Open Theme Editor")) {
|
||||||
|
static bool show_theme_editor = true;
|
||||||
|
theme_manager.ShowSimpleThemeEditor(&show_theme_editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Background effects settings
|
||||||
|
auto& bg_renderer = gui::BackgroundRenderer::Get();
|
||||||
|
bg_renderer.DrawSettingsUI();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::ShowStyleSelector("Colors##Selector")) ref_saved_style = style;
|
||||||
|
ImGui::ShowFontSelector("Fonts##Selector");
|
||||||
|
|
||||||
|
// Quick style controls before the tabbed section
|
||||||
|
if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
|
||||||
|
style.GrabRounding = style.FrameRounding;
|
||||||
|
|
||||||
|
// Border checkboxes (simplified layout)
|
||||||
|
bool window_border = (style.WindowBorderSize > 0.0f);
|
||||||
|
if (ImGui::Checkbox("WindowBorder", &window_border)) {
|
||||||
|
style.WindowBorderSize = window_border ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
bool frame_border = (style.FrameBorderSize > 0.0f);
|
||||||
|
if (ImGui::Checkbox("FrameBorder", &frame_border)) {
|
||||||
|
style.FrameBorderSize = frame_border ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
bool popup_border = (style.PopupBorderSize > 0.0f);
|
||||||
|
if (ImGui::Checkbox("PopupBorder", &popup_border)) {
|
||||||
|
style.PopupBorderSize = popup_border ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save/Revert buttons
|
||||||
|
if (ImGui::Button("Save Ref")) *ref = ref_saved_style = style;
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Revert Ref")) style = *ref;
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
// Add the comprehensive tabbed settings from the original DrawDisplaySettings
|
||||||
|
if (ImGui::BeginTabBar("DisplaySettingsTabs", ImGuiTabBarFlags_None)) {
|
||||||
|
|
||||||
|
if (ImGui::BeginTabItem("Sizes")) {
|
||||||
|
ImGui::SeparatorText("Main");
|
||||||
|
ImGui::SliderFloat2("WindowPadding", (float *)&style.WindowPadding, 0.0f,
|
||||||
|
20.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat2("FramePadding", (float *)&style.FramePadding, 0.0f,
|
||||||
|
20.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat2("ItemSpacing", (float *)&style.ItemSpacing, 0.0f,
|
||||||
|
20.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat2("ItemInnerSpacing", (float *)&style.ItemInnerSpacing,
|
||||||
|
0.0f, 20.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat2("TouchExtraPadding",
|
||||||
|
(float *)&style.TouchExtraPadding, 0.0f, 10.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f,
|
||||||
|
"%.0f");
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Borders");
|
||||||
|
ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f,
|
||||||
|
1.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f,
|
||||||
|
2.0f, "%.0f");
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Rounding");
|
||||||
|
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f,
|
||||||
|
12.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f,
|
||||||
|
"%.0f");
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Tables");
|
||||||
|
ImGui::SliderFloat2("CellPadding", (float *)&style.CellPadding, 0.0f,
|
||||||
|
20.0f, "%.0f");
|
||||||
|
ImGui::SliderAngle("TableAngledHeadersAngle",
|
||||||
|
&style.TableAngledHeadersAngle, -50.0f, +50.0f);
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Widgets");
|
||||||
|
ImGui::SliderFloat2("WindowTitleAlign", (float *)&style.WindowTitleAlign,
|
||||||
|
0.0f, 1.0f, "%.2f");
|
||||||
|
ImGui::Combo("ColorButtonPosition", (int *)&style.ColorButtonPosition,
|
||||||
|
"Left\0Right\0");
|
||||||
|
ImGui::SliderFloat2("ButtonTextAlign", (float *)&style.ButtonTextAlign,
|
||||||
|
0.0f, 1.0f, "%.2f");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::SliderFloat2("SelectableTextAlign",
|
||||||
|
(float *)&style.SelectableTextAlign, 0.0f, 1.0f,
|
||||||
|
"%.2f");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::SliderFloat("SeparatorTextBorderSize",
|
||||||
|
&style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
|
||||||
|
ImGui::SliderFloat2("SeparatorTextAlign",
|
||||||
|
(float *)&style.SeparatorTextAlign, 0.0f, 1.0f,
|
||||||
|
"%.2f");
|
||||||
|
ImGui::SliderFloat2("SeparatorTextPadding",
|
||||||
|
(float *)&style.SeparatorTextPadding, 0.0f, 40.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f,
|
||||||
|
12.0f, "%.0f");
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Tooltips");
|
||||||
|
for (int n = 0; n < 2; n++)
|
||||||
|
if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse"
|
||||||
|
: "HoverFlagsForTooltipNav")) {
|
||||||
|
ImGuiHoveredFlags *p = (n == 0) ? &style.HoverFlagsForTooltipMouse
|
||||||
|
: &style.HoverFlagsForTooltipNav;
|
||||||
|
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p,
|
||||||
|
ImGuiHoveredFlags_DelayNone);
|
||||||
|
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p,
|
||||||
|
ImGuiHoveredFlags_DelayShort);
|
||||||
|
ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p,
|
||||||
|
ImGuiHoveredFlags_DelayNormal);
|
||||||
|
ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p,
|
||||||
|
ImGuiHoveredFlags_Stationary);
|
||||||
|
ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p,
|
||||||
|
ImGuiHoveredFlags_NoSharedDelay);
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Misc");
|
||||||
|
ImGui::SliderFloat2("DisplaySafeAreaPadding",
|
||||||
|
(float *)&style.DisplaySafeAreaPadding, 0.0f, 30.0f,
|
||||||
|
"%.0f");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTabItem("Colors")) {
|
||||||
|
static int output_dest = 0;
|
||||||
|
static bool output_only_modified = true;
|
||||||
|
if (ImGui::Button("Export")) {
|
||||||
|
if (output_dest == 0)
|
||||||
|
ImGui::LogToClipboard();
|
||||||
|
else
|
||||||
|
ImGui::LogToTTY();
|
||||||
|
ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE);
|
||||||
|
for (int i = 0; i < ImGuiCol_COUNT; i++) {
|
||||||
|
const ImVec4 &col = style.Colors[i];
|
||||||
|
const char *name = ImGui::GetStyleColorName(i);
|
||||||
|
if (!output_only_modified ||
|
||||||
|
memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
|
||||||
|
ImGui::LogText(
|
||||||
|
"colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, "
|
||||||
|
"%.2ff);" IM_NEWLINE,
|
||||||
|
name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
|
||||||
|
}
|
||||||
|
ImGui::LogFinish();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(120);
|
||||||
|
ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Checkbox("Only Modified Colors", &output_only_modified);
|
||||||
|
|
||||||
|
static ImGuiTextFilter filter;
|
||||||
|
filter.Draw("Filter colors", ImGui::GetFontSize() * 16);
|
||||||
|
|
||||||
|
static ImGuiColorEditFlags alpha_flags = 0;
|
||||||
|
if (ImGui::RadioButton("Opaque",
|
||||||
|
alpha_flags == ImGuiColorEditFlags_None)) {
|
||||||
|
alpha_flags = ImGuiColorEditFlags_None;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton("Alpha",
|
||||||
|
alpha_flags == ImGuiColorEditFlags_AlphaPreview)) {
|
||||||
|
alpha_flags = ImGuiColorEditFlags_AlphaPreview;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::RadioButton(
|
||||||
|
"Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) {
|
||||||
|
alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSizeConstraints(
|
||||||
|
ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10),
|
||||||
|
ImVec2(FLT_MAX, FLT_MAX));
|
||||||
|
ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border,
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
||||||
|
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
|
||||||
|
ImGuiWindowFlags_NavFlattened);
|
||||||
|
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
|
||||||
|
for (int i = 0; i < ImGuiCol_COUNT; i++) {
|
||||||
|
const char *name = ImGui::GetStyleColorName(i);
|
||||||
|
if (!filter.PassFilter(name)) continue;
|
||||||
|
ImGui::PushID(i);
|
||||||
|
ImGui::ColorEdit4("##color", (float *)&style.Colors[i],
|
||||||
|
ImGuiColorEditFlags_AlphaBar | alpha_flags);
|
||||||
|
if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) {
|
||||||
|
// Tips: in a real user application, you may want to merge and use
|
||||||
|
// an icon font into the main font, so instead of "Save"/"Revert"
|
||||||
|
// you'd use icons! Read the FAQ and docs/FONTS.md about using icon
|
||||||
|
// fonts. It's really easy and super convenient!
|
||||||
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||||
|
if (ImGui::Button("Save")) {
|
||||||
|
ref->Colors[i] = style.Colors[i];
|
||||||
|
}
|
||||||
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||||
|
if (ImGui::Button("Revert")) {
|
||||||
|
style.Colors[i] = ref->Colors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);
|
||||||
|
ImGui::TextUnformatted(name);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTabItem("Fonts")) {
|
||||||
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
|
ImFontAtlas *atlas = io.Fonts;
|
||||||
|
ImGui::ShowFontAtlas(atlas);
|
||||||
|
|
||||||
|
// Post-baking font scaling. Note that this is NOT the nice way of
|
||||||
|
// scaling fonts, read below. (we enforce hard clamping manually as by
|
||||||
|
// default DragFloat/SliderFloat allows CTRL+Click text to get out of
|
||||||
|
// bounds).
|
||||||
|
const float MIN_SCALE = 0.3f;
|
||||||
|
const float MAX_SCALE = 2.0f;
|
||||||
|
|
||||||
|
static float window_scale = 1.0f;
|
||||||
|
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
||||||
|
if (ImGui::DragFloat(
|
||||||
|
"window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE,
|
||||||
|
"%.2f",
|
||||||
|
ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
|
||||||
|
ImGui::SetWindowFontScale(window_scale);
|
||||||
|
ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE,
|
||||||
|
MAX_SCALE, "%.2f",
|
||||||
|
ImGuiSliderFlags_AlwaysClamp); // Scale everything
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTabItem("Rendering")) {
|
||||||
|
ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::Checkbox("Anti-aliased lines use texture",
|
||||||
|
&style.AntiAliasedLinesUseTex);
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
|
||||||
|
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
|
||||||
|
ImGui::DragFloat("Curve Tessellation Tolerance",
|
||||||
|
&style.CurveTessellationTol, 0.02f, 0.10f, 10.0f,
|
||||||
|
"%.2f");
|
||||||
|
if (style.CurveTessellationTol < 0.10f)
|
||||||
|
style.CurveTessellationTol = 0.10f;
|
||||||
|
|
||||||
|
// When editing the "Circle Segment Max Error" value, draw a preview of
|
||||||
|
// its effect on auto-tessellated circles.
|
||||||
|
ImGui::DragFloat("Circle Tessellation Max Error",
|
||||||
|
&style.CircleTessellationMaxError, 0.005f, 0.10f, 5.0f,
|
||||||
|
"%.2f", ImGuiSliderFlags_AlwaysClamp);
|
||||||
|
const bool show_samples = ImGui::IsItemActive();
|
||||||
|
if (show_samples) ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
|
||||||
|
if (show_samples && ImGui::BeginTooltip()) {
|
||||||
|
ImGui::TextUnformatted("(R = radius, N = number of segments)");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||||
|
const float min_widget_width = ImGui::CalcTextSize("N: MMM\nR: MMM").x;
|
||||||
|
for (int n = 0; n < 8; n++) {
|
||||||
|
const float RAD_MIN = 5.0f;
|
||||||
|
const float RAD_MAX = 70.0f;
|
||||||
|
const float rad =
|
||||||
|
RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
|
||||||
|
ImGui::Text("R: %.f\nN: %d", rad,
|
||||||
|
draw_list->_CalcCircleAutoSegmentCount(rad));
|
||||||
|
|
||||||
|
const float canvas_width = std::max(min_widget_width, rad * 2.0f);
|
||||||
|
const float offset_x = floorf(canvas_width * 0.5f);
|
||||||
|
const float offset_y = floorf(RAD_MAX);
|
||||||
|
|
||||||
|
const ImVec2 p1 = ImGui::GetCursorScreenPos();
|
||||||
|
draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad,
|
||||||
|
ImGui::GetColorU32(ImGuiCol_Text));
|
||||||
|
ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
|
||||||
|
|
||||||
|
ImGui::EndGroup();
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f,
|
||||||
|
"%.2f"); // Not exposing zero here so user doesn't
|
||||||
|
// "lose" the UI (zero alpha clips all
|
||||||
|
// widgets). But application code could have a
|
||||||
|
// toggle to switch between zero and non-zero.
|
||||||
|
ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f,
|
||||||
|
1.0f, "%.2f");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
}
|
||||||
|
|
||||||
void TextWithSeparators(const absl::string_view &text) {
|
void TextWithSeparators(const absl::string_view &text) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("%s", text.data());
|
ImGui::Text("%s", text.data());
|
||||||
|
|||||||
@@ -14,35 +14,6 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
struct Theme {
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
Color menu_bar_bg;
|
|
||||||
Color title_bar_bg;
|
|
||||||
|
|
||||||
Color header;
|
|
||||||
Color header_hovered;
|
|
||||||
Color header_active;
|
|
||||||
|
|
||||||
Color title_bg_active;
|
|
||||||
Color title_bg_collapsed;
|
|
||||||
|
|
||||||
Color tab;
|
|
||||||
Color tab_hovered;
|
|
||||||
Color tab_active;
|
|
||||||
|
|
||||||
Color button;
|
|
||||||
Color button_hovered;
|
|
||||||
Color button_active;
|
|
||||||
|
|
||||||
Color clickable_text;
|
|
||||||
Color clickable_text_hovered;
|
|
||||||
};
|
|
||||||
|
|
||||||
absl::StatusOr<Theme> LoadTheme(const std::string &filename);
|
|
||||||
absl::Status SaveTheme(const Theme &theme);
|
|
||||||
void ApplyTheme(const Theme &theme);
|
|
||||||
|
|
||||||
void ColorsYaze();
|
void ColorsYaze();
|
||||||
|
|
||||||
TextEditor::LanguageDefinition GetAssemblyLanguageDef();
|
TextEditor::LanguageDefinition GetAssemblyLanguageDef();
|
||||||
@@ -63,10 +34,18 @@ void BeginNoPadding();
|
|||||||
void EndNoPadding();
|
void EndNoPadding();
|
||||||
|
|
||||||
void BeginChildWithScrollbar(const char *str_id);
|
void BeginChildWithScrollbar(const char *str_id);
|
||||||
|
void BeginChildWithScrollbar(const char *str_id, ImVec2 content_size);
|
||||||
|
|
||||||
void BeginChildBothScrollbars(int id);
|
void BeginChildBothScrollbars(int id);
|
||||||
|
|
||||||
|
// Table canvas management helpers for GUI elements that need proper sizing
|
||||||
|
void BeginTableCanvas(const char* table_id, int columns, ImVec2 canvas_size = ImVec2(0, 0));
|
||||||
|
void EndTableCanvas();
|
||||||
|
void SetupCanvasTableColumn(const char* label, float width_ratio = 0.0f);
|
||||||
|
void BeginCanvasTableCell(ImVec2 min_size = ImVec2(0, 0));
|
||||||
|
|
||||||
void DrawDisplaySettings(ImGuiStyle *ref = nullptr);
|
void DrawDisplaySettings(ImGuiStyle *ref = nullptr);
|
||||||
|
void DrawDisplaySettingsForPopup(ImGuiStyle *ref = nullptr); // Popup-safe version
|
||||||
|
|
||||||
void TextWithSeparators(const absl::string_view &text);
|
void TextWithSeparators(const absl::string_view &text);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -105,6 +105,34 @@ struct EnhancedTheme {
|
|||||||
Color plot_lines_hovered;
|
Color plot_lines_hovered;
|
||||||
Color plot_histogram;
|
Color plot_histogram;
|
||||||
Color plot_histogram_hovered;
|
Color plot_histogram_hovered;
|
||||||
|
Color tree_lines;
|
||||||
|
|
||||||
|
// Additional ImGui colors for complete coverage
|
||||||
|
Color tab_dimmed;
|
||||||
|
Color tab_dimmed_selected;
|
||||||
|
Color tab_dimmed_selected_overline;
|
||||||
|
Color tab_selected_overline;
|
||||||
|
|
||||||
|
// Enhanced theme system - semantic colors
|
||||||
|
Color text_highlight; // For selected text, highlighted items
|
||||||
|
Color link_hover; // For hover state of links
|
||||||
|
Color code_background; // For code blocks, monospace text backgrounds
|
||||||
|
Color success_light; // Lighter variant of success color
|
||||||
|
Color warning_light; // Lighter variant of warning color
|
||||||
|
Color error_light; // Lighter variant of error color
|
||||||
|
Color info_light; // Lighter variant of info color
|
||||||
|
|
||||||
|
// UI state colors
|
||||||
|
Color active_selection; // For active/selected UI elements
|
||||||
|
Color hover_highlight; // General hover state
|
||||||
|
Color focus_border; // For focused input elements
|
||||||
|
Color disabled_overlay; // Semi-transparent overlay for disabled elements
|
||||||
|
|
||||||
|
// Editor-specific colors
|
||||||
|
Color editor_background; // Main editor canvas background
|
||||||
|
Color editor_grid; // Grid lines in editors
|
||||||
|
Color editor_cursor; // Cursor/selection in editors
|
||||||
|
Color editor_selection; // Selected area in editors
|
||||||
|
|
||||||
// Style parameters
|
// Style parameters
|
||||||
float window_rounding = 0.0f;
|
float window_rounding = 0.0f;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -87,6 +87,29 @@ constexpr int kMap32ExpandedFlagPos = 0x01772E; // 0x04
|
|||||||
constexpr int kMap16ExpandedFlagPos = 0x02FD28; // 0x0F
|
constexpr int kMap16ExpandedFlagPos = 0x02FD28; // 0x0F
|
||||||
constexpr int kOverworldEntranceExpandedFlagPos = 0x0DB895; // 0xB8
|
constexpr int kOverworldEntranceExpandedFlagPos = 0x0DB895; // 0xB8
|
||||||
|
|
||||||
|
constexpr int overworldSpritesBeginingExpanded = 0x141438;
|
||||||
|
constexpr int overworldSpritesZeldaExpanded = 0x141578;
|
||||||
|
constexpr int overworldSpritesAgahnimExpanded = 0x1416B8;
|
||||||
|
constexpr int overworldSpritesDataStartExpanded = 0x04C881;
|
||||||
|
|
||||||
|
constexpr int overworldSpecialSpriteGFXGroupExpandedTemp = 0x0166E1;
|
||||||
|
constexpr int overworldSpecialSpritePaletteExpandedTemp = 0x016701;
|
||||||
|
|
||||||
|
constexpr int ExpandedOverlaySpace = 0x120000;
|
||||||
|
|
||||||
|
constexpr int overworldTilesType = 0x071459;
|
||||||
|
constexpr int overworldMessages = 0x03F51D;
|
||||||
|
constexpr int overworldMessagesExpanded = 0x1417F8;
|
||||||
|
|
||||||
|
constexpr int overworldItemsPointers = 0x0DC2F9;
|
||||||
|
constexpr int overworldItemsAddress = 0x0DC8B9; // 1BC2F9
|
||||||
|
constexpr int overworldItemsAddressBank = 0x0DC8BF;
|
||||||
|
constexpr int overworldItemsEndData = 0x0DC89C; // 0DC89E
|
||||||
|
|
||||||
|
constexpr int overworldBombDoorItemLocationsNew = 0x012644;
|
||||||
|
constexpr int overworldItemsPointersNew = 0x012784;
|
||||||
|
constexpr int overworldItemsStartDataNew = 0x0DC2F9;
|
||||||
|
|
||||||
constexpr int kOverworldCompressedMapPos = 0x058000;
|
constexpr int kOverworldCompressedMapPos = 0x058000;
|
||||||
constexpr int kOverworldCompressedOverflowPos = 0x137FFF;
|
constexpr int kOverworldCompressedOverflowPos = 0x137FFF;
|
||||||
|
|
||||||
@@ -127,9 +150,36 @@ class Overworld {
|
|||||||
absl::Status Save(Rom *rom);
|
absl::Status Save(Rom *rom);
|
||||||
absl::Status SaveOverworldMaps();
|
absl::Status SaveOverworldMaps();
|
||||||
absl::Status SaveLargeMaps();
|
absl::Status SaveLargeMaps();
|
||||||
|
absl::Status SaveLargeMapsExpanded();
|
||||||
|
absl::Status SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
|
||||||
|
int transition_target_north, int transition_target_west,
|
||||||
|
int transition_pos_x, int transition_pos_y,
|
||||||
|
int screen_change_1, int screen_change_2,
|
||||||
|
int screen_change_3, int screen_change_4);
|
||||||
|
absl::Status SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
|
||||||
|
int transition_target_north, int transition_target_west,
|
||||||
|
int transition_pos_x, int transition_pos_y,
|
||||||
|
int screen_change_1, int screen_change_2,
|
||||||
|
int screen_change_3, int screen_change_4);
|
||||||
|
absl::Status SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
|
||||||
|
int transition_target_north, int transition_target_west,
|
||||||
|
int transition_pos_x, int transition_pos_y,
|
||||||
|
int screen_change_1, int screen_change_2,
|
||||||
|
int screen_change_3, int screen_change_4);
|
||||||
|
absl::Status SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
|
||||||
|
int transition_target_north, int transition_target_west,
|
||||||
|
int transition_pos_x, int transition_pos_y,
|
||||||
|
int screen_change_1, int screen_change_2,
|
||||||
|
int screen_change_3, int screen_change_4);
|
||||||
absl::Status SaveEntrances();
|
absl::Status SaveEntrances();
|
||||||
absl::Status SaveExits();
|
absl::Status SaveExits();
|
||||||
absl::Status SaveItems();
|
absl::Status SaveItems();
|
||||||
|
absl::Status SaveMapOverlays();
|
||||||
|
absl::Status SaveOverworldTilesType();
|
||||||
|
absl::Status SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette,
|
||||||
|
bool enable_mosaic, bool enable_gfx_groups,
|
||||||
|
bool enable_subscreen_overlay, bool enable_animated);
|
||||||
|
absl::Status SaveAreaSpecificBGColors();
|
||||||
|
|
||||||
absl::Status CreateTile32Tilemap();
|
absl::Status CreateTile32Tilemap();
|
||||||
absl::Status SaveMap16Expanded();
|
absl::Status SaveMap16Expanded();
|
||||||
@@ -138,7 +188,9 @@ class Overworld {
|
|||||||
absl::Status SaveMap32Tiles();
|
absl::Status SaveMap32Tiles();
|
||||||
|
|
||||||
absl::Status SaveMapProperties();
|
absl::Status SaveMapProperties();
|
||||||
|
absl::Status SaveMusic();
|
||||||
absl::Status SaveAreaSizes();
|
absl::Status SaveAreaSizes();
|
||||||
|
void AssignMapSizes(std::vector<OverworldMap>& maps);
|
||||||
|
|
||||||
auto rom() const { return rom_; }
|
auto rom() const { return rom_; }
|
||||||
auto mutable_rom() { return rom_; }
|
auto mutable_rom() { return rom_; }
|
||||||
@@ -212,6 +264,7 @@ class Overworld {
|
|||||||
}
|
}
|
||||||
auto is_loaded() const { return is_loaded_; }
|
auto is_loaded() const { return is_loaded_; }
|
||||||
void set_current_map(int i) { current_map_ = i; }
|
void set_current_map(int i) { current_map_ = i; }
|
||||||
|
void set_current_world(int world) { current_world_ = world; }
|
||||||
auto map_tiles() const { return map_tiles_; }
|
auto map_tiles() const { return map_tiles_; }
|
||||||
auto mutable_map_tiles() { return &map_tiles_; }
|
auto mutable_map_tiles() { return &map_tiles_; }
|
||||||
auto all_items() const { return all_items_; }
|
auto all_items() const { return all_items_; }
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ constexpr int kOverworldItemsAddress = 0xDC8B9; // 1BC2F9
|
|||||||
constexpr int kOverworldItemsBank = 0xDC8BF;
|
constexpr int kOverworldItemsBank = 0xDC8BF;
|
||||||
constexpr int kOverworldItemsEndData = 0xDC89C; // 0DC89E
|
constexpr int kOverworldItemsEndData = 0xDC89C; // 0DC89E
|
||||||
|
|
||||||
|
constexpr int kOverworldBombDoorItemLocationsNew = 0x012644;
|
||||||
|
constexpr int kOverworldItemsPointersNew = 0x012784;
|
||||||
|
constexpr int kOverworldItemsStartDataNew = 0x0DC2F9;
|
||||||
|
|
||||||
class OverworldItem : public GameEntity {
|
class OverworldItem : public GameEntity {
|
||||||
public:
|
public:
|
||||||
OverworldItem() = default;
|
OverworldItem() = default;
|
||||||
|
|||||||
@@ -14,38 +14,52 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
OverworldMap::OverworldMap(int index, Rom *rom)
|
OverworldMap::OverworldMap(int index, Rom* rom)
|
||||||
: index_(index), parent_(index), rom_(rom) {
|
: index_(index), parent_(index), rom_(rom) {
|
||||||
LoadAreaInfo();
|
LoadAreaInfo();
|
||||||
|
// Load parent ID from ROM data if available (for custom ASM versions)
|
||||||
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
if (asm_version != 0xFF && asm_version >= 0x03) {
|
||||||
|
// For v3+, parent ID is stored in expanded table
|
||||||
|
parent_ = (*rom_)[kOverworldMapParentIdExpanded + index_];
|
||||||
|
}
|
||||||
|
|
||||||
if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
|
if (asm_version != 0xFF) {
|
||||||
// If the custom overworld ASM has NOT already been applied, manually set
|
if (asm_version == 0x03) {
|
||||||
// the vanilla values.
|
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
|
||||||
if (asm_version == 0x00) {
|
|
||||||
LoadCustomOverworldData();
|
LoadCustomOverworldData();
|
||||||
} else {
|
} else {
|
||||||
SetupCustomTileset(asm_version);
|
SetupCustomTileset(asm_version);
|
||||||
}
|
}
|
||||||
|
} else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
|
||||||
|
// Pure vanilla ROM but flag enabled - set up custom data manually
|
||||||
|
LoadCustomOverworldData();
|
||||||
}
|
}
|
||||||
|
// For pure vanilla ROMs, LoadAreaInfo already handles everything
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
|
absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
|
||||||
std::vector<gfx::Tile16> &tiles16,
|
std::vector<gfx::Tile16>& tiles16,
|
||||||
OverworldBlockset &world_blockset) {
|
OverworldBlockset& world_blockset) {
|
||||||
game_state_ = game_state;
|
game_state_ = game_state;
|
||||||
world_ = world;
|
world_ = world;
|
||||||
if (large_map_) {
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
|
||||||
|
// For large maps in vanilla ROMs, we need to handle special world graphics
|
||||||
|
// This ensures proper rendering of special overworld areas like Zora's Domain
|
||||||
|
if (large_map_ && asm_version == 0xFF) {
|
||||||
if (parent_ != index_ && !initialized_) {
|
if (parent_ != index_ && !initialized_) {
|
||||||
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
|
if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
|
||||||
index_ != 0x88) {
|
index_ != 0x88) {
|
||||||
|
// Most special world areas use the special graphics group
|
||||||
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
|
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
|
||||||
(parent_ - kSpecialWorldMapIdStart)];
|
(parent_ - kSpecialWorldMapIdStart)];
|
||||||
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
|
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
|
||||||
} else if (index_ == 0x88) {
|
} else if (index_ == 0x88) {
|
||||||
|
// Triforce room has special hardcoded values
|
||||||
area_graphics_ = 0x51;
|
area_graphics_ = 0x51;
|
||||||
area_palette_ = 0x00;
|
area_palette_ = 0x00;
|
||||||
} else {
|
} else {
|
||||||
|
// Fallback to standard area graphics
|
||||||
area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_];
|
area_graphics_ = (*rom_)[kAreaGfxIdPtr + parent_];
|
||||||
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
}
|
}
|
||||||
@@ -67,6 +81,11 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
|
|||||||
void OverworldMap::LoadAreaInfo() {
|
void OverworldMap::LoadAreaInfo() {
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
|
||||||
|
// ZSCustomOverworld ASM Version System:
|
||||||
|
// 0x00-0x02: Legacy versions with limited features
|
||||||
|
// 0x03: Current version with full area size expansion and custom data support
|
||||||
|
// 0xFF: Pure vanilla ROM (no ASM applied)
|
||||||
|
|
||||||
// Load message ID and area size based on ASM version
|
// Load message ID and area size based on ASM version
|
||||||
if (asm_version < 3 || asm_version == 0xFF) {
|
if (asm_version < 3 || asm_version == 0xFF) {
|
||||||
// v2 and vanilla: use original message table
|
// v2 and vanilla: use original message table
|
||||||
@@ -76,6 +95,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
// Load area size for v2/vanilla
|
// Load area size for v2/vanilla
|
||||||
if (index_ < 0x80) {
|
if (index_ < 0x80) {
|
||||||
// For LW and DW, check the screen size byte
|
// For LW and DW, check the screen size byte
|
||||||
|
// Note: v2 had a bug where large/small values were swapped
|
||||||
uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
|
uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
|
||||||
switch (size_byte) {
|
switch (size_byte) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -94,6 +114,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For SW, use hardcoded values for v2 compatibility
|
// For SW, use hardcoded values for v2 compatibility
|
||||||
|
// Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas
|
||||||
area_size_ =
|
area_size_ =
|
||||||
(index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
|
(index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
|
||||||
? AreaSizeEnum::LargeArea
|
? AreaSizeEnum::LargeArea
|
||||||
@@ -101,6 +122,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// v3: use expanded message table and area size table
|
// v3: use expanded message table and area size table
|
||||||
|
// All area sizes are now stored in the expanded table, supporting all size types
|
||||||
message_id_ =
|
message_id_ =
|
||||||
(*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] |
|
(*rom_)[kOverworldMessagesExpanded + (parent_ * 2)] |
|
||||||
((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8);
|
((*rom_)[kOverworldMessagesExpanded + (parent_ * 2) + 1] << 8);
|
||||||
@@ -165,10 +187,10 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Special World (SW) areas
|
// Special World (SW) areas (index >= 0x80)
|
||||||
// Message ID already loaded above based on ASM version
|
// Message ID already loaded above based on ASM version
|
||||||
|
|
||||||
// For v3, use expanded sprite tables
|
// For v3, use expanded sprite tables with full customization support
|
||||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
sprite_graphics_[0] =
|
sprite_graphics_[0] =
|
||||||
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
|
(*rom_)[kOverworldSpecialSpriteGfxGroupExpandedTemp + parent_ -
|
||||||
@@ -207,20 +229,24 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_];
|
area_palette_ = (*rom_)[kOverworldPalettesScreenToSetNew + parent_];
|
||||||
|
|
||||||
// For v2/vanilla, use original palette table and handle special cases
|
// For v2/vanilla, use original palette table and handle special cases
|
||||||
|
// These hardcoded cases are needed for vanilla compatibility
|
||||||
if (asm_version < 3 || asm_version == 0xFF) {
|
if (asm_version < 3 || asm_version == 0xFF) {
|
||||||
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + parent_];
|
||||||
|
|
||||||
// Handle special world area cases
|
// Handle special world area cases based on ZScream documentation
|
||||||
if (index_ == 0x88 || index_ == 0x93) {
|
if (index_ == 0x88 || index_ == 0x93) {
|
||||||
|
// Triforce room - special graphics and palette
|
||||||
area_graphics_ = 0x51;
|
area_graphics_ = 0x51;
|
||||||
area_palette_ = 0x00;
|
area_palette_ = 0x00;
|
||||||
} else if (index_ == 0x80) {
|
} else if (index_ == 0x80) {
|
||||||
|
// Master Sword area - use special graphics group
|
||||||
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
|
area_graphics_ = (*rom_)[kOverworldSpecialGfxGroup +
|
||||||
(parent_ - kSpecialWorldMapIdStart)];
|
(parent_ - kSpecialWorldMapIdStart)];
|
||||||
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
|
area_palette_ = (*rom_)[kOverworldSpecialPalGroup + 1];
|
||||||
} else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
|
} else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
|
||||||
index_ == 0x8A) {
|
index_ == 0x8A) {
|
||||||
// Zora's Domain areas - use special sprite graphics
|
// Zora's Domain areas - use special sprite graphics and area graphics
|
||||||
|
// Note: These are the large area maps that were causing crashes
|
||||||
sprite_graphics_[0] = 0x0E;
|
sprite_graphics_[0] = 0x0E;
|
||||||
sprite_graphics_[1] = 0x0E;
|
sprite_graphics_[1] = 0x0E;
|
||||||
sprite_graphics_[2] = 0x0E;
|
sprite_graphics_[2] = 0x0E;
|
||||||
@@ -246,7 +272,7 @@ void OverworldMap::LoadAreaInfo() {
|
|||||||
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
|
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
|
||||||
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
|
||||||
} else {
|
} else {
|
||||||
// Default case
|
// Default case - use basic graphics
|
||||||
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
|
area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
|
||||||
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
|
area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
|
||||||
}
|
}
|
||||||
@@ -571,7 +597,7 @@ void OverworldMap::LoadAreaGraphics() {
|
|||||||
|
|
||||||
namespace palette_internal {
|
namespace palette_internal {
|
||||||
|
|
||||||
absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette ¤t,
|
absl::Status SetColorsPalette(Rom& rom, int index, gfx::SnesPalette& current,
|
||||||
gfx::SnesPalette main, gfx::SnesPalette animated,
|
gfx::SnesPalette main, gfx::SnesPalette animated,
|
||||||
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
||||||
gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
|
gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
|
||||||
@@ -692,7 +718,7 @@ absl::Status SetColorsPalette(Rom &rom, int index, gfx::SnesPalette ¤t,
|
|||||||
} // namespace palette_internal
|
} // namespace palette_internal
|
||||||
|
|
||||||
absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
|
absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
|
||||||
const gfx::PaletteGroup &palette_group, int index, int previous_index,
|
const gfx::PaletteGroup& palette_group, int index, int previous_index,
|
||||||
int limit) {
|
int limit) {
|
||||||
if (index == 255) {
|
if (index == 255) {
|
||||||
index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
index = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
||||||
@@ -706,10 +732,10 @@ absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
|
|||||||
|
|
||||||
absl::Status OverworldMap::LoadPalette() {
|
absl::Status OverworldMap::LoadPalette() {
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
|
||||||
int previous_pal_id = 0;
|
int previous_pal_id = 0;
|
||||||
int previous_spr_pal_id = 0;
|
int previous_spr_pal_id = 0;
|
||||||
|
|
||||||
if (index_ > 0) {
|
if (index_ > 0) {
|
||||||
// Load previous palette ID based on ASM version
|
// Load previous palette ID based on ASM version
|
||||||
if (asm_version < 3 || asm_version == 0xFF) {
|
if (asm_version < 3 || asm_version == 0xFF) {
|
||||||
@@ -718,7 +744,7 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
// v3 uses expanded palette table
|
// v3 uses expanded palette table
|
||||||
previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
|
previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
|
previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,12 +770,12 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
pal1 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
||||||
(previous_pal_id * 4)];
|
(previous_pal_id * 4)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal2 == 0xFF) {
|
if (pal2 == 0xFF) {
|
||||||
pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
pal2 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
||||||
(previous_pal_id * 4) + 1];
|
(previous_pal_id * 4) + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal3 == 0xFF) {
|
if (pal3 == 0xFF) {
|
||||||
pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
pal3 = (*rom_)[rom_->version_constants().kOverworldMapPaletteGroup +
|
||||||
(previous_pal_id * 4) + 2];
|
(previous_pal_id * 4) + 2];
|
||||||
@@ -762,11 +788,14 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
|
GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
|
||||||
|
|
||||||
// Set background color based on world type and area-specific settings
|
// Set background color based on world type and area-specific settings
|
||||||
bool use_area_specific_bg = (*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00;
|
bool use_area_specific_bg =
|
||||||
|
(*rom_)[OverworldCustomAreaSpecificBGEnabled] != 0x00;
|
||||||
if (use_area_specific_bg) {
|
if (use_area_specific_bg) {
|
||||||
// Use area-specific background color from custom array
|
// Use area-specific background color from custom array
|
||||||
area_specific_bg_color_ = (*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] |
|
area_specific_bg_color_ =
|
||||||
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1] << 8);
|
(*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2)] |
|
||||||
|
((*rom_)[OverworldCustomAreaSpecificBGPalette + (parent_ * 2) + 1]
|
||||||
|
<< 8);
|
||||||
// Convert 15-bit SNES color to palette color
|
// Convert 15-bit SNES color to palette color
|
||||||
bgr = gfx::SnesColor(area_specific_bg_color_);
|
bgr = gfx::SnesColor(area_specific_bg_color_);
|
||||||
} else {
|
} else {
|
||||||
@@ -799,15 +828,16 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
if (pal4 == 0xFF) {
|
if (pal4 == 0xFF) {
|
||||||
pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
|
pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal4 == 0xFF) {
|
if (pal4 == 0xFF) {
|
||||||
pal4 = 0; // Fallback to 0 if still 0xFF
|
pal4 = 0; // Fallback to 0 if still 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal5 == 0xFF) {
|
if (pal5 == 0xFF) {
|
||||||
pal5 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
|
pal5 =
|
||||||
|
(*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pal5 == 0xFF) {
|
if (pal5 == 0xFF) {
|
||||||
pal5 = 0; // Fallback to 0 if still 0xFF
|
pal5 = 0; // Fallback to 0 if still 0xFF
|
||||||
}
|
}
|
||||||
@@ -833,7 +863,7 @@ absl::Status OverworldMap::LoadPalette() {
|
|||||||
|
|
||||||
absl::Status OverworldMap::LoadOverlay() {
|
absl::Status OverworldMap::LoadOverlay() {
|
||||||
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
|
||||||
|
|
||||||
// Load overlays based on ROM version
|
// Load overlays based on ROM version
|
||||||
if (asm_version == 0xFF) {
|
if (asm_version == 0xFF) {
|
||||||
// Vanilla ROM - load overlay from overlay pointers
|
// Vanilla ROM - load overlay from overlay pointers
|
||||||
@@ -848,15 +878,15 @@ absl::Status OverworldMap::LoadOverlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::LoadVanillaOverlayData() {
|
absl::Status OverworldMap::LoadVanillaOverlayData() {
|
||||||
|
|
||||||
// Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements)
|
// Load vanilla overlay for this map (interactive overlays for revealing holes/changing elements)
|
||||||
int address = (kOverlayPointersBank << 16) +
|
int address = (kOverlayPointersBank << 16) +
|
||||||
((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
|
((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
|
||||||
(*rom_)[kOverlayPointers + (index_ * 2)];
|
(*rom_)[kOverlayPointers + (index_ * 2)];
|
||||||
|
|
||||||
// Convert SNES address to PC address
|
// Convert SNES address to PC address
|
||||||
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
|
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
|
||||||
|
|
||||||
// Check if custom overlay code is present
|
// Check if custom overlay code is present
|
||||||
if ((*rom_)[kOverlayData1] == 0x6B) {
|
if ((*rom_)[kOverlayData1] == 0x6B) {
|
||||||
// Use custom overlay data pointer
|
// Use custom overlay data pointer
|
||||||
@@ -865,7 +895,7 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
|
|||||||
(*rom_)[kOverlayData2 + (index_ * 3)];
|
(*rom_)[kOverlayData2 + (index_ * 3)];
|
||||||
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
|
address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate address
|
// Validate address
|
||||||
if (address >= rom_->size()) {
|
if (address >= rom_->size()) {
|
||||||
has_overlay_ = false;
|
has_overlay_ = false;
|
||||||
@@ -873,15 +903,15 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
|
|||||||
overlay_data_.clear();
|
overlay_data_.clear();
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse overlay data (interactive overlays)
|
// Parse overlay data (interactive overlays)
|
||||||
overlay_data_.clear();
|
overlay_data_.clear();
|
||||||
uint8_t b = (*rom_)[address];
|
uint8_t b = (*rom_)[address];
|
||||||
|
|
||||||
// Parse overlay commands until we hit END (0x60)
|
// Parse overlay commands until we hit END (0x60)
|
||||||
while (b != 0x60 && address < rom_->size()) {
|
while (b != 0x60 && address < rom_->size()) {
|
||||||
overlay_data_.push_back(b);
|
overlay_data_.push_back(b);
|
||||||
|
|
||||||
// Handle different overlay commands
|
// Handle different overlay commands
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case 0xA9: // LDA #$
|
case 0xA9: // LDA #$
|
||||||
@@ -950,28 +980,28 @@ absl::Status OverworldMap::LoadVanillaOverlayData() {
|
|||||||
address++;
|
address++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address < rom_->size()) {
|
if (address < rom_->size()) {
|
||||||
b = (*rom_)[address];
|
b = (*rom_)[address];
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the END command if we found it
|
// Add the END command if we found it
|
||||||
if (b == 0x60) {
|
if (b == 0x60) {
|
||||||
overlay_data_.push_back(0x60);
|
overlay_data_.push_back(0x60);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set overlay ID based on map index (simplified)
|
// Set overlay ID based on map index (simplified)
|
||||||
overlay_id_ = index_;
|
overlay_id_ = index_;
|
||||||
has_overlay_ = !overlay_data_.empty();
|
has_overlay_ = !overlay_data_.empty();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
|
void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
|
||||||
int size, uint8_t *all_gfx) {
|
int size, uint8_t* all_gfx) {
|
||||||
// Ensure we don't go out of bounds
|
// Ensure we don't go out of bounds
|
||||||
int max_offset = static_graphics_offset * size + size;
|
int max_offset = static_graphics_offset * size + size;
|
||||||
if (max_offset > rom_->graphics_buffer().size()) {
|
if (max_offset > rom_->graphics_buffer().size()) {
|
||||||
@@ -981,7 +1011,7 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
auto byte = all_gfx[i + (static_graphics_offset * size)];
|
auto byte = all_gfx[i + (static_graphics_offset * size)];
|
||||||
switch (index) {
|
switch (index) {
|
||||||
@@ -997,8 +1027,9 @@ void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::BuildTileset() {
|
absl::Status OverworldMap::BuildTileset() {
|
||||||
if (current_gfx_.size() == 0) current_gfx_.resize(0x10000, 0x00);
|
if (current_gfx_.size() == 0)
|
||||||
|
current_gfx_.resize(0x10000, 0x00);
|
||||||
|
|
||||||
// Process the 8 main graphics sheets (slots 0-7)
|
// Process the 8 main graphics sheets (slots 0-7)
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if (static_graphics_[i] != 0) {
|
if (static_graphics_[i] != 0) {
|
||||||
@@ -1006,7 +1037,7 @@ absl::Status OverworldMap::BuildTileset() {
|
|||||||
rom_->graphics_buffer().data());
|
rom_->graphics_buffer().data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process sprite graphics (slots 8-15)
|
// Process sprite graphics (slots 8-15)
|
||||||
for (int i = 8; i < 16; i++) {
|
for (int i = 8; i < 16; i++) {
|
||||||
if (static_graphics_[i] != 0) {
|
if (static_graphics_[i] != 0) {
|
||||||
@@ -1014,19 +1045,20 @@ absl::Status OverworldMap::BuildTileset() {
|
|||||||
rom_->graphics_buffer().data());
|
rom_->graphics_buffer().data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process animated graphics if available (slot 16)
|
// Process animated graphics if available (slot 16)
|
||||||
if (static_graphics_[16] != 0) {
|
if (static_graphics_[16] != 0) {
|
||||||
ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000,
|
ProcessGraphicsBuffer(7, static_graphics_[16], 0x1000,
|
||||||
rom_->graphics_buffer().data());
|
rom_->graphics_buffer().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16,
|
absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16,
|
||||||
int count) {
|
int count) {
|
||||||
if (current_blockset_.size() == 0) current_blockset_.resize(0x100000, 0x00);
|
if (current_blockset_.size() == 0)
|
||||||
|
current_blockset_.resize(0x100000, 0x00);
|
||||||
|
|
||||||
const int offsets[] = {0x00, 0x08, 0x400, 0x408};
|
const int offsets[] = {0x00, 0x08, 0x400, 0x408};
|
||||||
auto yy = 0;
|
auto yy = 0;
|
||||||
@@ -1070,7 +1102,7 @@ absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16> &tiles16,
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldMap::BuildBitmap(OverworldBlockset &world_blockset) {
|
absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
|
||||||
if (bitmap_data_.size() != 0) {
|
if (bitmap_data_.size() != 0) {
|
||||||
bitmap_data_.clear();
|
bitmap_data_.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ constexpr int kOverworldPalettesScreenToSetNew = 0x4C635;
|
|||||||
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp = 0x0166E1;
|
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp = 0x0166E1;
|
||||||
constexpr int kOverworldSpecialSpritePaletteExpandedTemp = 0x016701;
|
constexpr int kOverworldSpecialSpritePaletteExpandedTemp = 0x016701;
|
||||||
|
|
||||||
|
constexpr int transition_target_northExpanded = 0x1411B8;
|
||||||
|
constexpr int transition_target_westExpanded = 0x1412F8;
|
||||||
|
|
||||||
constexpr int kDarkWorldMapIdStart = 0x40;
|
constexpr int kDarkWorldMapIdStart = 0x40;
|
||||||
constexpr int kSpecialWorldMapIdStart = 0x80;
|
constexpr int kSpecialWorldMapIdStart = 0x80;
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class ModernCLI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ShowVersion() {
|
void ShowVersion() {
|
||||||
std::cout << "z3ed v0.3.0 - Yet Another Zelda3 Editor CLI" << std::endl;
|
std::cout << "z3ed v0.3.1 - Yet Another Zelda3 Editor CLI" << std::endl;
|
||||||
std::cout << "Built with Asar integration" << std::endl;
|
std::cout << "Built with Asar integration" << std::endl;
|
||||||
std::cout << "Copyright (c) 2025 scawful" << std::endl;
|
std::cout << "Copyright (c) 2025 scawful" << std::endl;
|
||||||
}
|
}
|
||||||
@@ -434,7 +434,11 @@ class ModernCLI {
|
|||||||
} // namespace cli
|
} // namespace cli
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
extern "C" int SDL_main(int argc, char* argv[]) {
|
||||||
|
#else
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
#endif
|
||||||
absl::SetProgramUsageMessage(
|
absl::SetProgramUsageMessage(
|
||||||
"z3ed - Yet Another Zelda3 Editor CLI Tool\n"
|
"z3ed - Yet Another Zelda3 Editor CLI Tool\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ void PaletteEditorComponent(ftxui::ScreenInteractive &screen) {
|
|||||||
|
|
||||||
void HelpComponent(ftxui::ScreenInteractive &screen) {
|
void HelpComponent(ftxui::ScreenInteractive &screen) {
|
||||||
auto help_text = vbox({
|
auto help_text = vbox({
|
||||||
text("z3ed v0.3.0") | bold | color(Color::Yellow),
|
text("z3ed v0.3.1") | bold | color(Color::Yellow),
|
||||||
text("by scawful") | color(Color::Magenta),
|
text("by scawful") | color(Color::Magenta),
|
||||||
text("The Legend of Zelda: A Link to the Past Hacking Tool") |
|
text("The Legend of Zelda: A Link to the Past Hacking Tool") |
|
||||||
color(Color::Red),
|
color(Color::Red),
|
||||||
@@ -866,7 +866,7 @@ void MainMenuComponent(ftxui::ScreenInteractive &screen) {
|
|||||||
auto title = border(hbox({
|
auto title = border(hbox({
|
||||||
text("z3ed") | bold | color(Color::Blue1),
|
text("z3ed") | bold | color(Color::Blue1),
|
||||||
separator(),
|
separator(),
|
||||||
text("v0.3.0") | bold | color(Color::Green1),
|
text("v0.3.1") | bold | color(Color::Green1),
|
||||||
separator(),
|
separator(),
|
||||||
text(rom_information) | bold | color(Color::Red1),
|
text(rom_information) | bold | color(Color::Red1),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ DEFINE_FLAG(std::string, length, "", "The length of the data to read.");
|
|||||||
DEFINE_FLAG(std::string, file_size, "", "The size of the file to expand to.");
|
DEFINE_FLAG(std::string, file_size, "", "The size of the file to expand to.");
|
||||||
DEFINE_FLAG(std::string, dest_rom, "", "The destination ROM file.");
|
DEFINE_FLAG(std::string, dest_rom, "", "The destination ROM file.");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
extern "C" int SDL_main(int argc, char *argv[]) {
|
||||||
|
#else
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
#endif
|
||||||
yaze::util::FlagParser flag_parser(yaze::util::global_flag_registry());
|
yaze::util::FlagParser flag_parser(yaze::util::global_flag_registry());
|
||||||
RETURN_IF_EXCEPTION(flag_parser.Parse(argc, argv));
|
RETURN_IF_EXCEPTION(flag_parser.Parse(argc, argv));
|
||||||
yaze::cli::ShowMain();
|
yaze::cli::ShowMain();
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ target_include_directories(
|
|||||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||||
${PNG_INCLUDE_DIRS}
|
${PNG_INCLUDE_DIRS}
|
||||||
${SDL2_INCLUDE_DIR}
|
${SDL2_INCLUDE_DIR}
|
||||||
${GLEW_INCLUDE_DIRS}
|
|
||||||
${PROJECT_BINARY_DIR}
|
${PROJECT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,8 +66,6 @@ target_link_libraries(
|
|||||||
${ABSL_TARGETS}
|
${ABSL_TARGETS}
|
||||||
${SDL_TARGETS}
|
${SDL_TARGETS}
|
||||||
${PNG_LIBRARIES}
|
${PNG_LIBRARIES}
|
||||||
${GLEW_LIBRARIES}
|
|
||||||
${OPENGL_LIBRARIES}
|
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
ImGuiTestEngine
|
ImGuiTestEngine
|
||||||
ImGui
|
ImGui
|
||||||
|
|||||||
103
src/yaze.cc
103
src/yaze.cc
@@ -1,9 +1,10 @@
|
|||||||
#include "yaze.h"
|
#include "yaze.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "app/core/controller.h"
|
#include "app/core/controller.h"
|
||||||
@@ -21,7 +22,7 @@ DEFINE_FLAG(std::string, rom_file, "",
|
|||||||
// Static variables for library state
|
// Static variables for library state
|
||||||
static bool g_library_initialized = false;
|
static bool g_library_initialized = false;
|
||||||
|
|
||||||
int yaze_app_main(int argc, char **argv) {
|
int yaze_app_main(int argc, char** argv) {
|
||||||
yaze::util::FlagParser parser(yaze::util::global_flag_registry());
|
yaze::util::FlagParser parser(yaze::util::global_flag_registry());
|
||||||
RETURN_IF_EXCEPTION(parser.Parse(argc, argv));
|
RETURN_IF_EXCEPTION(parser.Parse(argc, argv));
|
||||||
std::string rom_filename = "";
|
std::string rom_filename = "";
|
||||||
@@ -52,7 +53,7 @@ yaze_status yaze_library_init() {
|
|||||||
if (g_library_initialized) {
|
if (g_library_initialized) {
|
||||||
return YAZE_OK;
|
return YAZE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize SDL and other subsystems if needed
|
// Initialize SDL and other subsystems if needed
|
||||||
g_library_initialized = true;
|
g_library_initialized = true;
|
||||||
return YAZE_OK;
|
return YAZE_OK;
|
||||||
@@ -62,7 +63,7 @@ void yaze_library_shutdown() {
|
|||||||
if (!g_library_initialized) {
|
if (!g_library_initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup subsystems
|
// Cleanup subsystems
|
||||||
g_library_initialized = false;
|
g_library_initialized = false;
|
||||||
|
|
||||||
@@ -111,17 +112,17 @@ yaze_status yaze_init(yaze_editor_context* context, const char* rom_filename) {
|
|||||||
if (context == nullptr) {
|
if (context == nullptr) {
|
||||||
return YAZE_ERROR_INVALID_ARG;
|
return YAZE_ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_library_initialized) {
|
if (!g_library_initialized) {
|
||||||
yaze_status init_status = yaze_library_init();
|
yaze_status init_status = yaze_library_init();
|
||||||
if (init_status != YAZE_OK) {
|
if (init_status != YAZE_OK) {
|
||||||
return init_status;
|
return init_status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context->rom = nullptr;
|
context->rom = nullptr;
|
||||||
context->error_message = nullptr;
|
context->error_message = nullptr;
|
||||||
|
|
||||||
if (rom_filename != nullptr && strlen(rom_filename) > 0) {
|
if (rom_filename != nullptr && strlen(rom_filename) > 0) {
|
||||||
context->rom = yaze_load_rom(rom_filename);
|
context->rom = yaze_load_rom(rom_filename);
|
||||||
if (context->rom == nullptr) {
|
if (context->rom == nullptr) {
|
||||||
@@ -137,12 +138,12 @@ yaze_status yaze_shutdown(yaze_editor_context* context) {
|
|||||||
if (context == nullptr) {
|
if (context == nullptr) {
|
||||||
return YAZE_ERROR_INVALID_ARG;
|
return YAZE_ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context->rom != nullptr) {
|
if (context->rom != nullptr) {
|
||||||
yaze_unload_rom(context->rom);
|
yaze_unload_rom(context->rom);
|
||||||
context->rom = nullptr;
|
context->rom = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->error_message = nullptr;
|
context->error_message = nullptr;
|
||||||
return YAZE_OK;
|
return YAZE_OK;
|
||||||
}
|
}
|
||||||
@@ -151,7 +152,7 @@ zelda3_rom* yaze_load_rom(const char* filename) {
|
|||||||
if (filename == nullptr || strlen(filename) == 0) {
|
if (filename == nullptr || strlen(filename) == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto internal_rom = std::make_unique<yaze::Rom>();
|
auto internal_rom = std::make_unique<yaze::Rom>();
|
||||||
if (!internal_rom->LoadFromFile(filename).ok()) {
|
if (!internal_rom->LoadFromFile(filename).ok()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -171,7 +172,7 @@ void yaze_unload_rom(zelda3_rom* rom) {
|
|||||||
if (rom == nullptr) {
|
if (rom == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rom->impl != nullptr) {
|
if (rom->impl != nullptr) {
|
||||||
delete static_cast<yaze::Rom*>(rom->impl);
|
delete static_cast<yaze::Rom*>(rom->impl);
|
||||||
rom->impl = nullptr;
|
rom->impl = nullptr;
|
||||||
@@ -184,27 +185,24 @@ int yaze_save_rom(zelda3_rom* rom, const char* filename) {
|
|||||||
if (rom == nullptr || filename == nullptr) {
|
if (rom == nullptr || filename == nullptr) {
|
||||||
return YAZE_ERROR_INVALID_ARG;
|
return YAZE_ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rom->impl == nullptr) {
|
if (rom->impl == nullptr) {
|
||||||
return YAZE_ERROR_NOT_INITIALIZED;
|
return YAZE_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* internal_rom = static_cast<yaze::Rom*>(rom->impl);
|
auto* internal_rom = static_cast<yaze::Rom*>(rom->impl);
|
||||||
auto status = internal_rom->SaveToFile(yaze::Rom::SaveSettings{
|
auto status = internal_rom->SaveToFile(yaze::Rom::SaveSettings{
|
||||||
.backup = true,
|
.backup = true, .save_new = false, .filename = filename});
|
||||||
.save_new = false,
|
|
||||||
.filename = filename
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return YAZE_ERROR_IO;
|
return YAZE_ERROR_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
rom->is_modified = false;
|
rom->is_modified = false;
|
||||||
return YAZE_OK;
|
return YAZE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
yaze_bitmap yaze_load_bitmap(const char *filename) {
|
yaze_bitmap yaze_load_bitmap(const char* filename) {
|
||||||
yaze_bitmap bitmap;
|
yaze_bitmap bitmap;
|
||||||
bitmap.width = 0;
|
bitmap.width = 0;
|
||||||
bitmap.height = 0;
|
bitmap.height = 0;
|
||||||
@@ -213,7 +211,7 @@ yaze_bitmap yaze_load_bitmap(const char *filename) {
|
|||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
snes_color yaze_get_color_from_paletteset(const zelda3_rom *rom,
|
snes_color yaze_get_color_from_paletteset(const zelda3_rom* rom,
|
||||||
int palette_set, int palette,
|
int palette_set, int palette,
|
||||||
int color) {
|
int color) {
|
||||||
snes_color color_struct;
|
snes_color color_struct;
|
||||||
@@ -222,7 +220,7 @@ snes_color yaze_get_color_from_paletteset(const zelda3_rom *rom,
|
|||||||
color_struct.blue = 0;
|
color_struct.blue = 0;
|
||||||
|
|
||||||
if (rom->impl) {
|
if (rom->impl) {
|
||||||
yaze::Rom *internal_rom = static_cast<yaze::Rom *>(rom->impl);
|
yaze::Rom* internal_rom = static_cast<yaze::Rom*>(rom->impl);
|
||||||
auto get_color =
|
auto get_color =
|
||||||
internal_rom->palette_group()
|
internal_rom->palette_group()
|
||||||
.get_group(yaze::gfx::kPaletteGroupAddressesKeys[palette_set])
|
.get_group(yaze::gfx::kPaletteGroupAddressesKeys[palette_set])
|
||||||
@@ -235,21 +233,21 @@ snes_color yaze_get_color_from_paletteset(const zelda3_rom *rom,
|
|||||||
return color_struct;
|
return color_struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
zelda3_overworld *yaze_load_overworld(const zelda3_rom *rom) {
|
zelda3_overworld* yaze_load_overworld(const zelda3_rom* rom) {
|
||||||
if (rom->impl == nullptr) {
|
if (rom->impl == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
yaze::Rom *internal_rom = static_cast<yaze::Rom *>(rom->impl);
|
yaze::Rom* internal_rom = static_cast<yaze::Rom*>(rom->impl);
|
||||||
auto internal_overworld = new yaze::zelda3::Overworld(internal_rom);
|
auto internal_overworld = new yaze::zelda3::Overworld(internal_rom);
|
||||||
if (!internal_overworld->Load(internal_rom).ok()) {
|
if (!internal_overworld->Load(internal_rom).ok()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
zelda3_overworld *overworld = new zelda3_overworld();
|
zelda3_overworld* overworld = new zelda3_overworld();
|
||||||
overworld->impl = internal_overworld;
|
overworld->impl = internal_overworld;
|
||||||
int map_id = 0;
|
int map_id = 0;
|
||||||
for (const auto &ow_map : internal_overworld->overworld_maps()) {
|
for (const auto& ow_map : internal_overworld->overworld_maps()) {
|
||||||
overworld->maps[map_id] = new zelda3_overworld_map();
|
overworld->maps[map_id] = new zelda3_overworld_map();
|
||||||
overworld->maps[map_id]->id = map_id;
|
overworld->maps[map_id]->id = map_id;
|
||||||
map_id++;
|
map_id++;
|
||||||
@@ -257,20 +255,21 @@ zelda3_overworld *yaze_load_overworld(const zelda3_rom *rom) {
|
|||||||
return overworld;
|
return overworld;
|
||||||
}
|
}
|
||||||
|
|
||||||
zelda3_dungeon_room *yaze_load_all_rooms(const zelda3_rom *rom) {
|
zelda3_dungeon_room* yaze_load_all_rooms(const zelda3_rom* rom) {
|
||||||
if (rom->impl == nullptr) {
|
if (rom->impl == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
yaze::Rom *internal_rom = static_cast<yaze::Rom *>(rom->impl);
|
yaze::Rom* internal_rom = static_cast<yaze::Rom*>(rom->impl);
|
||||||
zelda3_dungeon_room *rooms = new zelda3_dungeon_room[256];
|
zelda3_dungeon_room* rooms = new zelda3_dungeon_room[256];
|
||||||
return rooms;
|
return rooms;
|
||||||
}
|
}
|
||||||
|
|
||||||
yaze_status yaze_load_messages(const zelda3_rom* rom, zelda3_message** messages, int* message_count) {
|
yaze_status yaze_load_messages(const zelda3_rom* rom, zelda3_message** messages,
|
||||||
|
int* message_count) {
|
||||||
if (rom == nullptr || messages == nullptr || message_count == nullptr) {
|
if (rom == nullptr || messages == nullptr || message_count == nullptr) {
|
||||||
return YAZE_ERROR_INVALID_ARG;
|
return YAZE_ERROR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rom->impl == nullptr) {
|
if (rom->impl == nullptr) {
|
||||||
return YAZE_ERROR_NOT_INITIALIZED;
|
return YAZE_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
@@ -279,23 +278,25 @@ yaze_status yaze_load_messages(const zelda3_rom* rom, zelda3_message** messages,
|
|||||||
// Use LoadAllTextData from message_data.h
|
// Use LoadAllTextData from message_data.h
|
||||||
std::vector<yaze::editor::MessageData> message_data =
|
std::vector<yaze::editor::MessageData> message_data =
|
||||||
yaze::editor::ReadAllTextData(rom->data, 0);
|
yaze::editor::ReadAllTextData(rom->data, 0);
|
||||||
|
|
||||||
*message_count = static_cast<int>(message_data.size());
|
*message_count = static_cast<int>(message_data.size());
|
||||||
*messages = new zelda3_message[*message_count];
|
*messages = new zelda3_message[*message_count];
|
||||||
|
|
||||||
for (size_t i = 0; i < message_data.size(); ++i) {
|
for (size_t i = 0; i < message_data.size(); ++i) {
|
||||||
const auto& msg = message_data[i];
|
const auto& msg = message_data[i];
|
||||||
(*messages)[i].id = msg.ID;
|
(*messages)[i].id = msg.ID;
|
||||||
(*messages)[i].rom_address = msg.Address;
|
(*messages)[i].rom_address = msg.Address;
|
||||||
(*messages)[i].length = static_cast<uint16_t>(msg.RawString.length());
|
(*messages)[i].length = static_cast<uint16_t>(msg.RawString.length());
|
||||||
|
|
||||||
// Allocate and copy string data
|
// Allocate and copy string data
|
||||||
(*messages)[i].raw_data = new uint8_t[msg.Data.size()];
|
(*messages)[i].raw_data = new uint8_t[msg.Data.size()];
|
||||||
std::memcpy((*messages)[i].raw_data, msg.Data.data(), msg.Data.size());
|
std::memcpy((*messages)[i].raw_data, msg.Data.data(), msg.Data.size());
|
||||||
|
|
||||||
(*messages)[i].parsed_text = new char[msg.ContentsParsed.length() + 1];
|
(*messages)[i].parsed_text = new char[msg.ContentsParsed.length() + 1];
|
||||||
std::strcpy((*messages)[i].parsed_text, msg.ContentsParsed.c_str());
|
// Safe string copy with bounds checking
|
||||||
|
std::memcpy((*messages)[i].parsed_text, msg.ContentsParsed.c_str(), msg.ContentsParsed.length());
|
||||||
|
(*messages)[i].parsed_text[msg.ContentsParsed.length()] = '\0';
|
||||||
|
|
||||||
(*messages)[i].is_compressed = false; // TODO: Detect compression
|
(*messages)[i].is_compressed = false; // TODO: Detect compression
|
||||||
(*messages)[i].encoding_type = 0; // TODO: Detect encoding
|
(*messages)[i].encoding_type = 0; // TODO: Detect encoding
|
||||||
}
|
}
|
||||||
@@ -321,31 +322,36 @@ void yaze_free_bitmap(yaze_bitmap* bitmap) {
|
|||||||
|
|
||||||
yaze_bitmap yaze_create_bitmap(int width, int height, uint8_t bpp) {
|
yaze_bitmap yaze_create_bitmap(int width, int height, uint8_t bpp) {
|
||||||
yaze_bitmap bitmap = {};
|
yaze_bitmap bitmap = {};
|
||||||
|
|
||||||
if (width <= 0 || height <= 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)) {
|
if (width <= 0 || height <= 0 ||
|
||||||
|
(bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)) {
|
||||||
return bitmap; // Return empty bitmap on invalid args
|
return bitmap; // Return empty bitmap on invalid args
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap.width = width;
|
bitmap.width = width;
|
||||||
bitmap.height = height;
|
bitmap.height = height;
|
||||||
bitmap.bpp = bpp;
|
bitmap.bpp = bpp;
|
||||||
bitmap.data = new uint8_t[width * height]();
|
bitmap.data = new uint8_t[width * height]();
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
snes_color yaze_rgb_to_snes_color(uint8_t r, uint8_t g, uint8_t b) {
|
snes_color yaze_rgb_to_snes_color(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
snes_color color = {};
|
snes_color color = {};
|
||||||
color.red = r; // Store full 8-bit values (existing code expects this)
|
color.red = r; // Store full 8-bit values (existing code expects this)
|
||||||
color.green = g;
|
color.green = g;
|
||||||
color.blue = b;
|
color.blue = b;
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
void yaze_snes_color_to_rgb(snes_color color, uint8_t* r, uint8_t* g, uint8_t* b) {
|
void yaze_snes_color_to_rgb(snes_color color, uint8_t* r, uint8_t* g,
|
||||||
if (r != nullptr) *r = static_cast<uint8_t>(color.red);
|
uint8_t* b) {
|
||||||
if (g != nullptr) *g = static_cast<uint8_t>(color.green);
|
if (r != nullptr)
|
||||||
if (b != nullptr) *b = static_cast<uint8_t>(color.blue);
|
*r = static_cast<uint8_t>(color.red);
|
||||||
|
if (g != nullptr)
|
||||||
|
*g = static_cast<uint8_t>(color.green);
|
||||||
|
if (b != nullptr)
|
||||||
|
*b = static_cast<uint8_t>(color.blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version detection functions
|
// Version detection functions
|
||||||
@@ -353,7 +359,7 @@ zelda3_version zelda3_detect_version(const uint8_t* rom_data, size_t size) {
|
|||||||
if (rom_data == nullptr || size < 0x100000) {
|
if (rom_data == nullptr || size < 0x100000) {
|
||||||
return ZELDA3_VERSION_UNKNOWN;
|
return ZELDA3_VERSION_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement proper version detection based on ROM header
|
// TODO: Implement proper version detection based on ROM header
|
||||||
return ZELDA3_VERSION_US; // Default assumption
|
return ZELDA3_VERSION_US; // Default assumption
|
||||||
}
|
}
|
||||||
@@ -375,7 +381,8 @@ const char* zelda3_version_to_string(zelda3_version version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const zelda3_version_pointers* zelda3_get_version_pointers(zelda3_version version) {
|
const zelda3_version_pointers* zelda3_get_version_pointers(
|
||||||
|
zelda3_version version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case ZELDA3_VERSION_US:
|
case ZELDA3_VERSION_US:
|
||||||
return &zelda3_us_pointers;
|
return &zelda3_us_pointers;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ add_executable(
|
|||||||
dungeon_component_unit_test.cc
|
dungeon_component_unit_test.cc
|
||||||
integration/asar_integration_test.cc
|
integration/asar_integration_test.cc
|
||||||
integration/asar_rom_test.cc
|
integration/asar_rom_test.cc
|
||||||
|
editor/tile16_editor_test.cc
|
||||||
zelda3/object_parser_test.cc
|
zelda3/object_parser_test.cc
|
||||||
zelda3/object_parser_structs_test.cc
|
zelda3/object_parser_structs_test.cc
|
||||||
zelda3/test_dungeon_objects.cc
|
zelda3/test_dungeon_objects.cc
|
||||||
|
|||||||
301
test/editor/tile16_editor_test.cc
Normal file
301
test/editor/tile16_editor_test.cc
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
#include "app/editor/overworld/tile16_editor.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/gfx/tilemap.h"
|
||||||
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
#include "app/core/window.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace editor {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class Tile16EditorIntegrationTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
static void SetUpTestSuite() {
|
||||||
|
// Initialize SDL and rendering system once for all tests
|
||||||
|
InitializeTestEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestSuite() {
|
||||||
|
// Clean up SDL
|
||||||
|
if (window_initialized_) {
|
||||||
|
auto shutdown_result = core::ShutdownWindow(test_window_);
|
||||||
|
(void)shutdown_result; // Suppress unused variable warning
|
||||||
|
window_initialized_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!window_initialized_) {
|
||||||
|
GTEST_SKIP() << "Failed to initialize graphics system";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the test ROM
|
||||||
|
rom_ = std::make_unique<Rom>();
|
||||||
|
auto load_result = rom_->LoadFromFile(YAZE_TEST_ROM_PATH);
|
||||||
|
ASSERT_TRUE(load_result.ok()) << "Failed to load test ROM: " << load_result.message();
|
||||||
|
|
||||||
|
// Load overworld data
|
||||||
|
overworld_ = std::make_unique<zelda3::Overworld>(rom_.get());
|
||||||
|
auto overworld_load_result = overworld_->Load(rom_.get());
|
||||||
|
ASSERT_TRUE(overworld_load_result.ok()) << "Failed to load overworld: " << overworld_load_result.message();
|
||||||
|
|
||||||
|
// Create tile16 blockset
|
||||||
|
auto tile16_data = overworld_->tile16_blockset_data();
|
||||||
|
auto palette = overworld_->current_area_palette();
|
||||||
|
|
||||||
|
tile16_blockset_ = std::make_unique<gfx::Tilemap>(
|
||||||
|
gfx::CreateTilemap(tile16_data, 0x80, 0x2000, 16,
|
||||||
|
zelda3::kNumTile16Individual, palette));
|
||||||
|
|
||||||
|
// Create graphics bitmap
|
||||||
|
current_gfx_bmp_ = std::make_unique<gfx::Bitmap>();
|
||||||
|
core::Renderer::Get().CreateAndRenderBitmap(0x80, 512, 0x40,
|
||||||
|
overworld_->current_graphics(),
|
||||||
|
*current_gfx_bmp_, palette);
|
||||||
|
|
||||||
|
// Create tile16 blockset bitmap
|
||||||
|
tile16_blockset_bmp_ = std::make_unique<gfx::Bitmap>();
|
||||||
|
core::Renderer::Get().CreateAndRenderBitmap(0x80, 0x2000, 0x08,
|
||||||
|
tile16_data,
|
||||||
|
*tile16_blockset_bmp_, palette);
|
||||||
|
|
||||||
|
// Initialize the tile16 editor
|
||||||
|
editor_ = std::make_unique<Tile16Editor>(rom_.get(), tile16_blockset_.get());
|
||||||
|
auto init_result = editor_->Initialize(*tile16_blockset_bmp_, *current_gfx_bmp_,
|
||||||
|
*overworld_->mutable_all_tiles_types());
|
||||||
|
ASSERT_TRUE(init_result.ok()) << "Failed to initialize editor: " << init_result.message();
|
||||||
|
|
||||||
|
rom_loaded_ = true;
|
||||||
|
#else
|
||||||
|
// Fallback for non-ROM tests
|
||||||
|
rom_ = std::make_unique<Rom>();
|
||||||
|
tilemap_ = std::make_unique<gfx::Tilemap>();
|
||||||
|
editor_ = std::make_unique<Tile16Editor>(rom_.get(), tilemap_.get());
|
||||||
|
rom_loaded_ = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void InitializeTestEnvironment() {
|
||||||
|
auto window_result = core::CreateWindow(test_window_, SDL_WINDOW_HIDDEN);
|
||||||
|
if (window_result.ok()) {
|
||||||
|
window_initialized_ = true;
|
||||||
|
} else {
|
||||||
|
window_initialized_ = false;
|
||||||
|
// Log the error but don't fail test setup
|
||||||
|
std::cerr << "Failed to initialize test window: " << window_result.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool window_initialized_;
|
||||||
|
static core::Window test_window_;
|
||||||
|
|
||||||
|
bool rom_loaded_ = false;
|
||||||
|
std::unique_ptr<Rom> rom_;
|
||||||
|
std::unique_ptr<gfx::Tilemap> tilemap_;
|
||||||
|
std::unique_ptr<gfx::Tilemap> tile16_blockset_;
|
||||||
|
std::unique_ptr<gfx::Bitmap> current_gfx_bmp_;
|
||||||
|
std::unique_ptr<gfx::Bitmap> tile16_blockset_bmp_;
|
||||||
|
std::unique_ptr<zelda3::Overworld> overworld_;
|
||||||
|
std::unique_ptr<Tile16Editor> editor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Static member definitions
|
||||||
|
bool Tile16EditorIntegrationTest::window_initialized_ = false;
|
||||||
|
core::Window Tile16EditorIntegrationTest::test_window_;
|
||||||
|
|
||||||
|
// Basic validation tests (no ROM required)
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, BasicValidation) {
|
||||||
|
// Test with invalid tile ID
|
||||||
|
EXPECT_FALSE(editor_->IsTile16Valid(-1));
|
||||||
|
EXPECT_FALSE(editor_->IsTile16Valid(9999));
|
||||||
|
|
||||||
|
// Test scratch space operations with invalid slots
|
||||||
|
auto save_invalid = editor_->SaveTile16ToScratchSpace(-1);
|
||||||
|
EXPECT_FALSE(save_invalid.ok());
|
||||||
|
EXPECT_EQ(save_invalid.code(), absl::StatusCode::kInvalidArgument);
|
||||||
|
|
||||||
|
auto load_invalid = editor_->LoadTile16FromScratchSpace(5);
|
||||||
|
EXPECT_FALSE(load_invalid.ok());
|
||||||
|
EXPECT_EQ(load_invalid.code(), absl::StatusCode::kInvalidArgument);
|
||||||
|
|
||||||
|
// Test valid scratch space clearing
|
||||||
|
auto clear_valid = editor_->ClearScratchSpace(0);
|
||||||
|
EXPECT_TRUE(clear_valid.ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROM-dependent tests
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, ValidateTile16DataWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test validation with properly loaded ROM
|
||||||
|
auto status = editor_->ValidateTile16Data();
|
||||||
|
EXPECT_TRUE(status.ok()) << "Validation failed: " << status.message();
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, SetCurrentTileWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setting a valid tile
|
||||||
|
auto valid_tile_result = editor_->SetCurrentTile(0);
|
||||||
|
EXPECT_TRUE(valid_tile_result.ok()) << "Failed to set tile 0: " << valid_tile_result.message();
|
||||||
|
|
||||||
|
auto valid_tile_result2 = editor_->SetCurrentTile(100);
|
||||||
|
EXPECT_TRUE(valid_tile_result2.ok()) << "Failed to set tile 100: " << valid_tile_result2.message();
|
||||||
|
|
||||||
|
// Test invalid ranges still fail
|
||||||
|
auto invalid_low = editor_->SetCurrentTile(-1);
|
||||||
|
EXPECT_FALSE(invalid_low.ok());
|
||||||
|
EXPECT_EQ(invalid_low.code(), absl::StatusCode::kOutOfRange);
|
||||||
|
|
||||||
|
auto invalid_high = editor_->SetCurrentTile(10000);
|
||||||
|
EXPECT_FALSE(invalid_high.ok());
|
||||||
|
EXPECT_EQ(invalid_high.code(), absl::StatusCode::kOutOfRange);
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, FlipOperationsWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a valid tile first
|
||||||
|
auto set_result = editor_->SetCurrentTile(1);
|
||||||
|
ASSERT_TRUE(set_result.ok()) << "Failed to set initial tile: " << set_result.message();
|
||||||
|
|
||||||
|
// Test flip operations
|
||||||
|
auto flip_h_result = editor_->FlipTile16Horizontal();
|
||||||
|
EXPECT_TRUE(flip_h_result.ok()) << "Horizontal flip failed: " << flip_h_result.message();
|
||||||
|
|
||||||
|
auto flip_v_result = editor_->FlipTile16Vertical();
|
||||||
|
EXPECT_TRUE(flip_v_result.ok()) << "Vertical flip failed: " << flip_v_result.message();
|
||||||
|
|
||||||
|
auto rotate_result = editor_->RotateTile16();
|
||||||
|
EXPECT_TRUE(rotate_result.ok()) << "Rotation failed: " << rotate_result.message();
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, UndoRedoWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a tile and perform an operation to create undo state
|
||||||
|
auto set_result = editor_->SetCurrentTile(1);
|
||||||
|
ASSERT_TRUE(set_result.ok());
|
||||||
|
|
||||||
|
auto clear_result = editor_->ClearTile16();
|
||||||
|
ASSERT_TRUE(clear_result.ok()) << "Clear operation failed: " << clear_result.message();
|
||||||
|
|
||||||
|
// Test undo
|
||||||
|
auto undo_result = editor_->Undo();
|
||||||
|
EXPECT_TRUE(undo_result.ok()) << "Undo failed: " << undo_result.message();
|
||||||
|
|
||||||
|
// Test redo
|
||||||
|
auto redo_result = editor_->Redo();
|
||||||
|
EXPECT_TRUE(redo_result.ok()) << "Redo failed: " << redo_result.message();
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, PaletteOperationsWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test palette cycling
|
||||||
|
auto cycle_forward = editor_->CyclePalette(true);
|
||||||
|
EXPECT_TRUE(cycle_forward.ok()) << "Palette cycle forward failed: " << cycle_forward.message();
|
||||||
|
|
||||||
|
auto cycle_backward = editor_->CyclePalette(false);
|
||||||
|
EXPECT_TRUE(cycle_backward.ok()) << "Palette cycle backward failed: " << cycle_backward.message();
|
||||||
|
|
||||||
|
// Test valid palette preview
|
||||||
|
auto valid_palette = editor_->PreviewPaletteChange(3);
|
||||||
|
EXPECT_TRUE(valid_palette.ok()) << "Palette preview failed: " << valid_palette.message();
|
||||||
|
|
||||||
|
// Test invalid palette
|
||||||
|
auto invalid_palette = editor_->PreviewPaletteChange(10);
|
||||||
|
EXPECT_FALSE(invalid_palette.ok());
|
||||||
|
EXPECT_EQ(invalid_palette.code(), absl::StatusCode::kInvalidArgument);
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, CopyPasteOperationsWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a tile first
|
||||||
|
auto set_result = editor_->SetCurrentTile(10);
|
||||||
|
ASSERT_TRUE(set_result.ok());
|
||||||
|
|
||||||
|
// Test copy operation
|
||||||
|
auto copy_result = editor_->CopyTile16ToClipboard(10);
|
||||||
|
EXPECT_TRUE(copy_result.ok()) << "Copy failed: " << copy_result.message();
|
||||||
|
|
||||||
|
// Test paste operation
|
||||||
|
auto paste_result = editor_->PasteTile16FromClipboard();
|
||||||
|
EXPECT_TRUE(paste_result.ok()) << "Paste failed: " << paste_result.message();
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(Tile16EditorIntegrationTest, ScratchSpaceWithROM) {
|
||||||
|
#ifdef YAZE_ENABLE_ROM_TESTS
|
||||||
|
if (!rom_loaded_) {
|
||||||
|
GTEST_SKIP() << "ROM not loaded, skipping integration test";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a tile first
|
||||||
|
auto set_result = editor_->SetCurrentTile(15);
|
||||||
|
ASSERT_TRUE(set_result.ok());
|
||||||
|
|
||||||
|
// Test scratch space save
|
||||||
|
auto save_result = editor_->SaveTile16ToScratchSpace(0);
|
||||||
|
EXPECT_TRUE(save_result.ok()) << "Scratch save failed: " << save_result.message();
|
||||||
|
|
||||||
|
// Test scratch space load
|
||||||
|
auto load_result = editor_->LoadTile16FromScratchSpace(0);
|
||||||
|
EXPECT_TRUE(load_result.ok()) << "Scratch load failed: " << load_result.message();
|
||||||
|
|
||||||
|
// Test scratch space clear
|
||||||
|
auto clear_result = editor_->ClearScratchSpace(0);
|
||||||
|
EXPECT_TRUE(clear_result.ok()) << "Scratch clear failed: " << clear_result.message();
|
||||||
|
#else
|
||||||
|
GTEST_SKIP() << "ROM tests disabled";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace editor
|
||||||
|
} // namespace yaze
|
||||||
27
vcpkg.json
27
vcpkg.json
@@ -1,11 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "yaze",
|
"name": "yaze",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"description": "Yet Another Zelda3 Editor",
|
"description": "Yet Another Zelda3 Editor",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"zlib",
|
{
|
||||||
"libpng",
|
"name": "zlib",
|
||||||
"sdl2"
|
"platform": "!uwp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libpng",
|
||||||
|
"platform": "!uwp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sdl2",
|
||||||
|
"platform": "!uwp",
|
||||||
|
"features": ["vulkan"]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"builtin-baseline": "c8696863d371ab7f46e213d8f5ca923c4aef2a00"
|
"builtin-baseline": "c8696863d371ab7f46e213d8f5ca923c4aef2a00",
|
||||||
|
"overrides": [],
|
||||||
|
"features": {
|
||||||
|
"pkg-config": {
|
||||||
|
"description": "Use pkg-config for dependency detection",
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user