Skip to content

Development Diary - March 2026


2026-03-31 (SESSION 101): Refactor OpenALAudioDevice to Core/GameEngineDevice

Moved the OpenALAudioDevice from GeneralsMD/Code/GameEngineDevice/ to Core/GameEngineDevice/ to properly position it as the cross-platform Linux/macOS equivalent of the Windows MilesAudioDevice.

2026-03-31 (SESSION 100): Pin DXVK macOS Remote Source to Immutable SHA

Implemented a safety change on main to avoid breakages from mutable DXVK branch heads during configure/build.

What was changed: - Updated cmake/dx8.cmake macOS remote DXVK path to use an immutable commit ref instead of a branch name. - Added DXVK_REMOTE_REF and wired ExternalProject_Add(... GIT_TAG ...) to that commit.

Pinned ref: - 35965877b85c22c8bd0ce54b099a96bd9954b411 (from fbraz3/dxvk branch generalsx-macos-v2.6 at pin time)

Why: - Prevent main from breaking when the tracked DXVK branch head moves unexpectedly. - Make CI/local configure reproducible and promote DXVK updates explicitly via controlled SHA bumps.

Validation: - cmake --preset macos-vulkan - cmake --build build/macos-vulkan --target z_generals -- -j4 - cmake --build build/macos-vulkan --target g_generals -- -j4

All three commands completed successfully in this session.

2026-03-31 (SESSION 99a): Issue #59 WideChar Portability Fix + macOS Build Unblock

Addressed issue #59 by removing remaining 2-byte WideChar assumptions in shared Unicode serialization paths and validated both macOS game targets build successfully.

What was changed: - Fixed UnicodeString::nextToken() copy size in shared Core code: - replaced memcpy(..., len*2) with memcpy(..., len * sizeof(WideChar)). - Reworked quoted-printable Unicode conversion in both game variants: - GeneralsMD/Code/GameEngine/Source/Common/System/QuotedPrintable.cpp - Generals/Code/GameEngine/Source/Common/System/QuotedPrintable.cpp - encode/decode now operates on explicit UTF-16 code units (low/high byte pair), avoiding host wchar_t width dependency. - Added a lesson entry in docs/WORKDIR/lessons/2026-03-LESSONS.md describing this portability class and prevention guidance.

Build/test status: - macOS configure/build was initially failing in DXVK with unknown type name 'size_t' from the remote DXVK source copy. - Local fork already contained the required #include <cstddef> in references/fbraz3-dxvk/src/util/util_math.h. - Reconfigured with -DSAGE_DXVK_USE_LOCAL_FORK=ON and rebuilt successfully: - z_generals build: OK - g_generals build: OK

Notes: - Build logs still contain existing warnings (deprecated sprintf, duplicate libs, deployment target mismatch warnings), but both targets link successfully.

2026-03-31 (SESSION 99b): Issue #57 Linux LAN IP Enumeration Fix

Implemented the fix for issue #57 where Linux LAN IP selection only exposed hostname/loopback-derived addresses.

What was changed: - Updated Core/GameEngine/Source/GameNetwork/IPEnumeration.cpp to enumerate active IPv4 interfaces on Linux using getifaddrs(). - Applied filtering for AF_INET, interface IFF_UP, and non-loopback (!IFF_LOOPBACK) addresses. - Preserved the legacy hostname-based path as fallback to avoid regressions when interface enumeration fails. - Added duplicate-IP suppression in addNewIP() while keeping deterministic sorted insertion behavior. - Recorded the lesson in docs/WORKDIR/lessons/2026-03-LESSONS.md.

Validation: - Base Generals and Zero Hour manual runtime validation reported as working by user. - Language diagnostics returned no errors for touched files.

2026-03-31 (SESSION 98): PR Review Follow-up - Watermark Lifetime and Label Gate Cleanup

Addressed PR review findings on the main-menu watermark implementation for both Zero Hour and Generals.

What was changed: - Reworked W3DGeneralsXCreditDraw to avoid static managed DisplayString lifetime risk. - Previous path allocated once with TheDisplayStringManager->newDisplayString() and never freed. - New path reuses callback instData text display string (instData->setText(...) + getTextDisplayString()), removing the shutdown-assert leak risk. - Updated initLabelVersion() logic in both game variants: - removed unnecessary TheVersion && TheGlobalData gate for fixed watermark text - now always sets watermark text when optional MainMenu.wnd:LabelVersion exists - Updated stale legacy comments that still described version-hash behavior.

Validation: - No diagnostics errors in touched files after patching. - Diff reviewed for parity across Generals and GeneralsMD.

2026-03-31 (SESSION 97): Main Menu Watermark Finalization + Cleanup

Finalized the GeneralsX watermark rollout for both Zero Hour and Generals main menu paths, including lifecycle fixes and cleanup of temporary fallback draw routes.

What was done: - Added and validated W3DGeneralsXCreditDraw registration in both game variants (W3DFunctionLexicon + W3DGUICallbacks declarations). - Ensured watermark draw works in active menu render paths (W3DMainMenuDraw, W3DMainMenuFourDraw, and clock callback path). - Added robust UI fallback for layouts without LabelVersion and repositioned it to the desired bottom-left placement with transparent background style. - Fixed a build break in menu fallback code by switching to valid APIs: - TheWindowManager->winFindFont(...) - GameMakeColor(...) - Fixed a critical lifecycle bug where fallback watermark persisted into gameplay by: - creating fallback label under parentMainMenu (not root) - destroying it in menu shutdown paths (shutdownComplete and MainMenuShutdown) - Cleaned temporary attempt residue by removing redundant fallback credit draws from map-border and button-drop-shadow draw callbacks.

Validation: - Rebuilt macOS Zero Hour target successfully after each fix iteration. - Confirmed no new compile errors in touched menu/callback files. - User-confirmed final visual behavior in menu and requested cleanup.

Notes: - Warnings from legacy memory-pool macros remain unrelated to this feature and did not block successful link.

2026-03-29 (SESSION 96): Network/LAN Documentation Consolidation

Consolidated the current project position on LAN readiness into a dedicated support document instead of treating inherited upstream networking behavior as immediate blockers without reproduction.

What was documented: - Added a focused network status note at docs/WORKDIR/support/NETWORK_LAN_STATUS_2026-03-29.md. - Recorded the current stance that LAN should be tested after unrelated runtime bugs are resolved. - Separated accepted legacy behavior from real LAN blockers and future online compatibility concerns. - Clarified that GameSpy-related compatibility should be preserved when practical for future Generals Online integration.

Documentation cleanup: - Updated docs/WORKDIR/lessons/2026-02-LESSONS.md to clarify that earlier GameSpy stubbing guidance was a historical Linux bring-up tactic, not a permanent project-wide rule.

Notes: - No gameplay or networking code changed in this session. - Manual two-machine LAN validation remains pending after the current runtime bug backlog is reduced.

2026-03-27 (SESSION 95): TheSuperHackers Upstream Sync (March) + Conflict Reconciliation

Performed a full upstream sync from thesuperhackers/main into thesuperhackers-sync-03-27-2026 and completed manual conflict reconciliation focused on preserving the GeneralsX cross-platform stack.

What was done: - Created sync branch thesuperhackers-sync-03-27-2026 and merged thesuperhackers/main. - Resolved merge conflicts in camera and FFmpeg-adjacent runtime paths with a hybrid strategy: - keep upstream API/consistency improvements where safe - preserve GeneralsX SDL3/DXVK/OpenAL/FFmpeg runtime behavior where required for macOS/Linux - Documented a detailed conflict-resolution plan and risk map in: - docs/WORKDIR/planning/PLAN-THESUPERHACKERS_SYNC_2026-03-27.md

Important merge decisions: - Retained local camera defaults flow where it protects existing GeneralsX UX/runtime expectations. - Kept cross-platform media/runtime guards and integration hooks that upstream does not maintain as a priority. - Avoided blanket “ours/theirs” picks in unified Core/ areas; decisions were made per conflict based on platform strategy impact.

Validation status: - macOS configure/build workflow was rechecked during the merge cycle. - Manual runtime verification was reported as successful before finalizing the branch for Linux manual test handoff.

Notes: - This sync intentionally prioritizes preserving working cross-platform architecture while importing useful upstream fixes. - Follow-up validation on Linux runtime behavior remains part of the post-push checklist.

2026-03-27 (SESSION 95): GitHub Actions Artifact Layout Cleanup (revised)

Tightened artifact uploads further based on code review: archives now preserve POSIX permissions and the Linux artifact name is preset-aware.

What was changed (revision): - Linux and macOS workflows now create a tar (uncompressed) archive before upload so POSIX executable bits and symlinks are preserved after download. - Linux: tar -C /tmp -cf GeneralsX-<game>-<preset>.tar GeneralsX-<game>-deploy/ - macOS: tar -C <artifact_dir> -cf <AppName>.tar <AppName>.app - Linux artifact name now includes inputs.preset so builds from linux64-testing produce a distinct artifact name and no collision with deploy artifacts: - linux-generalsxzh-linux64-deploy-bundle / linux-generalsxzh-linux64-testing-bundle - linux-generalsx-linux64-deploy-bundle / linux-generalsx-linux64-testing-bundle - Opened follow-up GitHub issue #53 to remove the internal GeneralsMD naming from future Zero Hour user-facing paths.

Why: - actions/upload-artifact@v4 does not preserve executable bits or symlinks when uploading a raw directory; a tar archive ensures the bundle rehydrates correctly for end users. - Hardcoding linux64 in the artifact name caused ambiguity when the workflow is triggered with the linux64-testing preset.

Validation: - Verified workflow YAML remains error-free after the artifact path/name changes.

2026-03-24 (SESSION 94): macOS Load/Replay Crash Investigation + Runtime Path Unification

Investigated a macOS crash reported when opening Load Game / Replay and consolidated user data/runtime paths for both games.

What was changed: - Updated user data directory defaults in both game variants to a unified tree under ~/Library/Application Support/GeneralsX/ (macOS) and ~/.local/share/GeneralsX/ (Linux fallback). - Base game now writes to GeneralsX/Generals and Zero Hour writes to GeneralsX/GeneralsZH. - Hardened HOME/XDG fallback handling to avoid null-path usage when environment variables are missing. - Updated macOS ZH deploy and bundle scripts to ship Fontconfig configuration (fonts.conf + conf.d) and export FONTCONFIG_FILE/FONTCONFIG_PATH in generated launchers.

Why: - Save/Load and Replay paths should be deterministic and not depend on legacy folder names. - Runtime logs showed repeated Fontconfig configuration failures during UI layout creation; packaging explicit Fontconfig config removes this host-dependent behavior.

Validation performed: - macOS builds for both Generals and Zero Hour completed successfully. - ZH deploy completed and launcher now reports bundled Fontconfig path in runtime summary.

Notes: - The crash flow around Menus/SaveLoad.wnd now has cleaner runtime environment setup for follow-up testing.

2026-03-24 (SESSION 93): TheSuperHackers Upstream Sync + macOS Build Recovery

Performed a full upstream sync merge from thesuperhackers/main into the dedicated sync branch and resolved the resulting conflict set manually.

What was merged/resolved: - Merged upstream changes across CI workflows, Core engine headers/sources, shared networking/game client paths, and both game variants. - Resolved localized merge conflicts in: - Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp - Generals/Code/GameEngine/Source/Common/RTS/Player.cpp - Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp - GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp - GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp

Important merge decisions: - Preserved GeneralsX tactical camera-height default behavior instead of taking upstream setDefaultView(...) change blindly. - Combined upstream battle-plan safety fix (invert a local copy instead of mutating source bonus data) with GeneralsX portability fix (std::max instead of __max). - Preserved existing GeneralsX DX8 present-parameter policy in dx8wrapper.cpp for cross-platform stability.

macOS follow-up fix: - Build initially failed because GameEngine now requires createParticleSystemManager(Bool dummy) after upstream sync, while SDL3 backends still implemented the old void signature. - Updated SDL3GameEngine headers/implementations in both GeneralsMD and Generals to match the new pure-virtual signature.

Validation: - [macOS] Configure completed successfully. - cmake --build build/macos-vulkan --target z_generals -j 8 returned EXIT:0 after the SDL3GameEngine signature fix.

Notes: - Added planning/report context in docs/WORKDIR/planning/PLAN-THESUPERHACKERS-SYNC-2026-03-24.md. - Added a lessons-learned note about pure-virtual signature drift after upstream merges.

2026-03-24 (SESSION 92): macOS Bundle Dependency Resolver + OpenAL Audio Restore

Focused on two macOS runtime regressions found during app bundle testing.

What was changed: - Fixed false unresolved dependency warnings in macOS bundle scripts by resolving @rpath entries against staged sibling dylibs before LC_RPATH traversal. - Applied the same fix to both bundle scripts: - scripts/build/macos/bundle-macos-zh.sh - scripts/build/macos/bundle-macos-generals.sh - Restored audio on macOS by scoping PipeWire/OpenAL environment workaround to Linux only. - Updated both game entrypoints for parity: - GeneralsMD/Code/Main/SDL3Main.cpp - Generals/Code/Main/SDL3Main.cpp

Root causes: - Bundle resolver treated some staged @rpath deps as unresolved because it skipped direct same-directory lookup. - FilterPipeWireOpenAL() exported Linux-only ALSOFT_DRIVERS (pulse,alsa,oss,jack,null,wave) on macOS, which can exclude CoreAudio and lead to silent audio.

Validation: - Re-ran ./scripts/build/macos/bundle-macos-zh.sh with zero WARNING: Unable to resolve dependency lines. - Rebuilt/deployed ZH and verified runtime log reports OpenAL: keeping default driver selection on non-Linux platform. - User-confirmed audio playback works again on macOS.

2026-03-23 (SESSION 91): Backport Camera Aspect-Ratio Default Fixes to Generals Base

Backported the camera default-height integration from ZH to Generals base game paths after validating file-by-file equivalence.

What was changed in Generals: - Replaced direct setDefaultView(0.0f, 0.0f, 1.0f) usage in InGameUI::init() and InGameUI::reset() with setCameraHeightAboveGroundLimitsToDefault(). - Updated scripted camera default action to use height-scale-aware flow (setCameraHeightAboveGroundLimitsToDefault, setPitch, setAngle) and synced header signature. - Updated GameLogic::startNewGame() to initialize camera height limits before default angle/pitch/zoom and force setZoomToMax(). - Updated OptionsMenu::saveOptions() to refresh shell-map camera limits and zoom after runtime resolution changes. - Added contextual note in GlobalData.h documenting that max/min camera height are treated as 4:3 baseline values and scaled for other aspect ratios.

Validation: - Confirmed pending patch scope is limited to 6 Generals files. - Checked for accidental literal \\n artifacts in edited files; none found. - Language service reports no syntax errors on changed files.

2026-03-23 (SESSION 90): Issue #16 Skirmish Instant-End + Neutral/White Enemy on Linux

Problem observed: - Starting a Skirmish match opened SkirmishGameOptionsMenu and then immediately pushed ScoreScreen. - In runs that did not hard-end immediately, enemy sides could appear as neutral/white due to broken script/player setup.

Root causes identified: 1. Player::initFromDict consumed skirmish qualification logic before loading m_mpStartIndex, corrupting qualified player/team/script naming in MP/skirmish flows. 2. Runtime script loading for SkirmishScripts.scb and MultiplayerScripts.scb failed on Linux because loose-file lookups were resolved from binary CWD only. 3. Linux runtime layout uses separate directories for binary CWD (~/GeneralsX/GeneralsMD/) and asset root (~/GeneralsX/data/GeneralsMD/), unlike classic Windows install assumptions.

Fixes implemented: - Reordered m_mpStartIndex initialization in both Generals and GeneralsMD Player.cpp before skirmish qualification logic. - Corrected script references to Data\\Scripts\\SkirmishScripts.scb and Data\\Scripts\\MultiplayerScripts.scb where required. - Added targeted SKIRMISH_DIAG stderr probes in ZH runtime paths to confirm open/parse status and score-screen trigger path. - Added LocalFileSystem::setAssetRootPath() (default no-op) and implemented fallback support in StdLocalFileSystem. - Wired StdBIGFileSystem::init() to propagate resolved primary asset directory to local filesystem fallback so relative loose-file paths resolve from asset root when CWD lookup fails.

Validation: - Linux linux64-deploy Docker build completed successfully. - Deploy completed successfully. - Diagnostic logs clearly showed previous open failures and enabled direct confirmation of the filesystem root-cause chain.

Result: - Issue #16 received root-cause-level fixes across script loading and player initialization paths, with platform-safe handling for Linux split runtime/data layouts and no Win32 behavior regression by interface defaulting.

2026-03-21 (SESSION 89): Issue #15 Shadow Artifacts - Major Visual Improvement + Commit-Worthy Stabilization

Worked another focused pass on animated shadow artifacts in Zero Hour (especially rotor-driven cases).

What improved in this session: - Restored matrix conversion parity in volumetric shadow world transforms (To_D3DMATRIX path), aligning Zero Hour behavior with the Generals baseline. - Fixed shadow type routing and projected-shadow update logic to respect bitmask flags (SHADOW_DYNAMIC_PROJECTION), improving animated shadow refresh behavior. - Stabilized volumetric strip construction by removing unsafe edge-pair swaps that used Int* reinterpret casts on Short silhouette indices (64-bit/ARM UB risk). - Added a hard clamp for volumetric extrusion length before volume build to avoid runaway spikes under shallow light angles.

Validation: - macOS target GeneralsXZH built successfully. - macOS deploy completed successfully. - Runtime feedback confirmed major lighting/shadow quality improvement, although a residual rotor-line artifact can still appear in specific intro camera/angle situations.

Decision: - Changes were considered commit-worthy due to substantial visual improvement and clear root-cause hardening, even without full artifact elimination yet.

2026-03-20 (SESSION 88): macOS CI Bundle Crash — Root Cause: Missing DXVK_WSI_DRIVER + SDL3 WSI Not Compiled

Problem: CI-built .app bundle crashes immediately with ERROR: TheDisplay->init() threw unknown exception.

Root cause analysis (multi-session investigation): 1. DXVK_WSI_DRIVER env var is required on non-Win32 platforms by DXVK's wsi_platform.cpp. When unset, DXVK throws DxvkError("DXVK_WSI_DRIVER environment variable unset"). 2. DxvkError did not inherit std::exception, so the game's catch (std::exception& e) never caught it — always "unknown exception". 3. On CI, SDL3 was not installed via Homebrew, so DXVK's meson build found only SDL2 (from ffmpeg dependency) → compiled with -DDXVK_WSI_SDL2 only. Setting DXVK_WSI_DRIVER=SDL3 at runtime then fails with "Failed to initialize WSI." because SDL3 WSI backend is not compiled in. 4. VK_ICD_FILENAMES deprecated in Vulkan Loader 1.3.236+ — must use VK_DRIVER_FILES instead.

Fixes applied: - references/fbraz3-dxvk/src/util/util_error.h: DxvkError now inherits std::exception with what() override — exposes actual error messages. - references/fbraz3-dxvk/src/wsi/wsi_platform.cpp: Auto-select best available WSI backend when DXVK_WSI_DRIVER is unset (SDL3 > SDL2 > GLFW priority). - All macOS run scripts: added export DXVK_WSI_DRIVER="SDL3". - All macOS bundle run.sh generators: added DXVK_WSI_DRIVER="SDL3" + VK_DRIVER_FILES alongside VK_ICD_FILENAMES. - .github/workflows/build-macos.yml: added brew install sdl3 so DXVK meson finds SDL3 via pkg-config and compiles SDL3 WSI backend. - Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp: improved error logging around Direct3DCreate8 call.

Key insight: DXVK's WSI selection is compile-time (which backends are compiled via -DDXVK_WSI_SDL3/-DDXVK_WSI_SDL2), but the selection of WHICH one to use is runtime (via DXVK_WSI_DRIVER). Both must be correct.

Files changed: - references/fbraz3-dxvk/src/util/util_error.h - references/fbraz3-dxvk/src/wsi/wsi_platform.cpp - scripts/build/macos/run-macos-zh.sh - scripts/build/macos/bundle-macos-zh.sh - scripts/build/macos/bundle-macos-generals.sh - scripts/build/macos/deploy-macos-zh.sh - scripts/build/macos/deploy-macos-generals.sh - .github/workflows/build-macos.yml - Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp


2026-03-17 (SESSION 87): macOS CI Bundle Parity with Local Deploy (Generals + Zero Hour)

What was implemented: - Updated .github/workflows/build-macos.yml bundle step to align runtime contract with local deploy scripts for both games. - Added DXVK config packaging from resources/dxvk/dxvk.conf into CI runtime bundle. - Replaced generic wrapper copy with generated macOS-specific run.sh that: - sets DYLD_LIBRARY_PATH to runtime root - exports VK_ICD_FILENAMES when MoltenVK_icd.json is present - supports both GeneralsX and GeneralsXZH - preserves optional CNC_GENERALS_INSTALLPATH auto-detection for sibling data layout

Root cause found: - CI packed dylibs at bundle root, but old wrapper expected ${SCRIPT_DIR}/lib. - This mismatch made runtime behavior diverge from local deploy and could break launch despite successful builds.

Additional notes: - Relaxed required-library check in CI bundle validation by removing hard requirement on libopenal.1.dylib for current runtime package shape. - Documented the lesson in docs/WORKDIR/lessons/2026-03-LESSONS.md to avoid future drift between CI and local runtime packaging.

Result: - CI macOS bundle now mirrors local deploy expectations for library lookup and Vulkan ICD setup, reducing "build green, runtime broken" regressions.

2026-03-16 (SESSION 86): Asset Root Overrides for Packaged Layouts + Language Detection Follow-up

What was implemented: - Added runtime asset root resolution in BIG filesystem initialization for both backends: - Core/GameEngineDevice/Source/StdDevice/Common/StdBIGFileSystem.cpp - Core/GameEngineDevice/Source/Win32Device/Common/Win32BIGFileSystem.cpp - Resolution priority now follows: 1. Environment variables 2. Options.ini 3. Default install-path resolution 4. Current working directory - Adopted preferred env names: - CNC_GENERALS_PATH - CNC_GENERALS_ZH_PATH - Kept compatibility fallback for older env variable names.

Diagnostics hardening: - Added startup logs indicating selected asset root source (env, ini, default, cwd). - Added path sanitization (trim/quote removal) for ENV/INI values.

Language detection follow-up: - Fixed Linux registry language auto-detection so it checks localized BIG files in configured asset roots, not only current working directory: - Generals/Code/GameEngine/Source/Common/System/registry.cpp - GeneralsMD/Code/GameEngine/Source/Common/System/registry.cpp - Added case-tolerant checks for localized BIG filenames.

Documentation: - Updated README.md with a new runtime asset path section describing env vars, Options.ini keys, and final precedence.

Result: - Packaged/disconnected executable layouts now resolve asset roots deterministically via env/ini overrides. - Runtime logs clearly show which source was selected.

2026-03-16 (SESSION 85): README English Proofreading and Clarity Pass

What was updated: - Revised README.md for native English grammar, spelling, and phrasing. - Corrected multiple typos and non-native sentence constructions. - Kept the structure concise and avoided reintroducing previously removed redundant sections.

Notable wording improvements: - Standardized status bullets for Linux, macOS, and Windows. - Rewrote project-difference and naming-origin sections for clarity. - Fixed capitalization and wording consistency (for example, "Windows only" and "minor issues").

Result: - README is now cleaner and more natural for native English readers while preserving the streamlined content scope.


2026-03-16 (SESSION 84-HOTFIX): Add Linux Generals Base Build Job

What was fixed: - Realized that build-linux-generals job was missing from ci.yml orchestrator. - After granular change detection refactor, base-should-build filter was created but only build-macos-generals was added. - Result: Generals base changes→macOS build only; no Linux Generals base build triggered.

Root cause: - When refactoring to granular detection, split outputs per platform variant but didn't add all platform jobs for base game. - Copy-paste incomplete: added macOS base job, forgot Linux parity.

What was added: - build-linux-generals job (mirrors build-macos-generals pattern) - Updated ci-summary needs: array to include build-linux-generals - Updated summary status table with new Linux Generals Base row - Updated Fail if Any Build Failed condition to check build-linux-generals.result

Result: - Now have complete coverage: Generals base builds on both Linux and macOS when base-should-build filter triggers - ZH variants (GeneralsMD) continue to build on Linux, macOS, Windows when zh-should-build filter triggers

Commit: eb5598562


2026-03-16 (SESSION 84): CI Granular Change Detection Refactor

What was implemented: - Refactored .github/workflows/ci.yml to use granular change detection instead of single boolean filter. - Split detect-changes job outputs into two independent selectors: - zh-should-build: Triggered on GeneralsMD/**, Core/**, cmake/** changes → builds Zero Hour variants (Linux, macOS, Windows) - base-should-build: Triggered on Generals/**, Core/**, cmake/** changes → builds Generals base variant (macOS)

Pre-refactor problem: - Single should-build boolean fired on ANY change to either game, causing wasteful multi-platform CI builds when only one variant was modified. - PR #36 had macOS Generals job added but wasn't selectively triggered.

Solution implemented: - Each build job now checks only its relevant output: - build-linux (GeneralsMD) checks zh-should-build - build-macos (GeneralsMD) checks zh-should-build - build-macos-generals (Generals base) checks base-should-build - build-windows (GeneralsMD) checks zh-should-build - workflow_dispatch still overrides with || github.event_name == 'workflow_dispatch' (force-run always available) - Summary job displays granular status table showing which variant detected changes

Benefits: - Faster CI feedback (only relevant platforms build) - Reduced CI minutes for feature branches targeting specific game variant - Clearer workflow visibility (summary shows per-variant build triggers)

Commit: 99a46314d on feat/macos-generals-base-build-deploy


2026-03-15 (SESSION 83): macOS Generals Base Build/Deploy Parity

What was implemented: - Created dedicated macOS base-game scripts: - scripts/build/macos/build-macos-generals.sh - scripts/build/macos/deploy-macos-generals.sh - Added VS Code tasks in .vscode/tasks.json: - [macOS] Build GeneralsX - [macOS] Deploy GeneralsX - [macOS] Pipeline: Configure + Build + Deploy Generals

Backport scope from macOS Zero Hour flow: - Vulkan SDK prerequisite validation. - Build using preset macos-vulkan and target g_generals. - Deploy of runtime dependencies (SDL3, SDL3_image, GameSpy, DXVK d3d8/d3d9, libvulkan, libMoltenVK). - MoltenVK ICD manifest generation (MoltenVK_icd.json). - Runtime wrapper generation (run.sh) for local dylib loading via DYLD_LIBRARY_PATH. - DXVK dylib fallback lookup (installed path and Meson output path).

Validation: - Build completed successfully for g_generals on macOS. - Deploy completed successfully to ~/GeneralsX/Generals. - No runtime auto-launch was performed by pipeline (manual execution retained by request).

2026-03-14 (SESSION 82): Generals Base Fixes - Shell Map and Skirmish SIGSEGV

What was fixed: - Restored Linux hardware fallback in Generals/Code/GameEngine/Source/Common/GameLOD.cpp so base Generals no longer disables shell map because Windows-only RAM/CPU detection returns zeros on non-Windows builds. - Ported 64-bit safe pointer cast in Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupPlayerInfo.cpp for battle honor listbox item data. - Fixed root cause of skirmish crash in shared text renderer (Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp) by normalizing incoming WCHAR to 16-bit code units before unicode font array growth/indexing.

Crash details validated: - User-provided GDB trace showed SIGSEGV in FontCharsClass::Get_Char_Spacing during SkirmishGameOptionsMenuInit map display text update. - Backtrace text payload contained packed UTF-16-like values, matching out-of-range indexing risk on Linux where wchar_t is 32-bit.

Result: - Base Generals shell map path and skirmish text-render path are now aligned with Linux runtime constraints and 64-bit safety expectations.

2026-03-12 (SESSION 81): Save/Replay Path Separator Fix + New Compat Layer Issue

What was fixed: - Fixed Linux path separator handling for save/replay runtime directories in both Zero Hour and Generals. - Updated affected runtime call sites so non-Windows builds use / where paths are assembled from user data roots.

Result: - Save/replay paths no longer create literal backslash file names like Save\\00000002.sav on Linux.

New issue found: - Opened ISSUE-013_findfirstfile_pattern_ignored_on_linux.md. - Root problem: Linux compat FindFirstFile implementation currently opens . and ignores wildcard/path filtering semantics.

Status: - Issue documented as OPEN for follow-up implementation in compat layer.

2026-03-13 (SESSION 81): Fix macOS Terrain Texture Failure (ISSUE-009 / GH #12)

Status: Manually validated by user as fixed in runtime test.

Problem Found: - macOS runtime repeatedly failed during terrain-related shader pipeline creation. - Logs showed recurring MoltenVK shader compile errors with undeclared shadow sampler symbols such as s0_2d_shadowSmplr.

Root Cause: - DXVK shader generation path emitted a shadow-compare sampler wrapper branch that produced invalid symbol usage for this workload on macOS/MoltenVK.

Fix Applied: - Updated DXVK fork on branch generalsx-macos-v2.6 in: - references/fbraz3-dxvk/src/dxso/dxso_compiler.cpp - Added a macOS-guarded path that avoids the problematic shadow-compare branch for the affected emission block and uses the color sampling path.

Build/Source Policy Hardening: - Updated cmake/dx8.cmake to use remote DXVK fork tracking by default for macOS (generalsx-macos-v2.6) with updates enabled. - Added explicit local development mode via -DSAGE_DXVK_USE_LOCAL_FORK=ON. - Documented source-of-truth rules in: - .github/instructions/generalsx.instructions.md - .github/copilot-instructions.md

Validation: - User performed manual runtime test after rebuild/deploy and confirmed success (FUNCIONOU!).


2026-03-12 (SESSION 81): Save/Replay Path Separator Fix + New Compat Layer Issue

What was fixed: - Fixed Linux path separator handling for save/replay runtime directories in both Zero Hour and Generals. - Updated affected runtime call sites so non-Windows builds use / where paths are assembled from user data roots.

Result: - Save/replay paths no longer create literal backslash file names like Save\\00000002.sav on Linux.

New issue found: - Opened ISSUE-013_findfirstfile_pattern_ignored_on_linux.md. - Root problem: Linux compat FindFirstFile implementation currently opens . and ignores wildcard/path filtering semantics.

Status: - Issue documented as OPEN for follow-up implementation in compat layer.

2026-03-12 (SESSION 80): Correction — macOS terrain textures issue still persists

Correction: Earlier February diary entries stated that missing terrain textures on macOS had been fixed and were rendering correctly. That conclusion was incorrect or incomplete.

Current understanding: - Terrain textures are still failing to load on macOS - The issue remains open and is tracked in ISSUE-009_macos_terrain_textures_missing.md - Previous notes likely captured a partial fix, a non-representative test result, or an incomplete end-to-end validation

Action taken: - Updated known issue documentation to treat this as an active open bug - Removed speculative wording that framed it as merely a regression candidate - Recorded this correction so future sessions do not assume the problem was solved


2026-03-10 (SESSION 79): Fix CI Bundle Wrapper Path (macOS + Linux)

Problem Found: GitHub Actions bundling step failed on both macOS and Linux workflows with:

cp: scripts/run-bundled-game.sh: No such file or directory

Root Cause: Workflow YAML still referenced the old wrapper location (scripts/run-bundled-game.sh) after script directory reorganization. The current wrapper path is scripts/qa/smoke/run-bundled-game.sh.

Fix: - Updated wrapper copy path in .github/workflows/build-macos.yml. - Updated wrapper copy path in .github/workflows/build-linux.yml.

Validation: - Verified no stale references remain: rg "scripts/run-bundled-game.sh" .github/workflows scripts returns no matches.


2026-03-10 (SESSION 78): Fix macOS CI Build -- OpenAL via FetchContent

Problem Found:

fatal error: 'AL/al.h' file not found
CI macOS build failed because: Homebrew openal-soft was intentionally NOT installed (Intel Homebrew installs x86_64-only binaries that fail on ARM64). Without Homebrew, CMake fell back to Apple's deprecated OpenAL.framework which uses <OpenAL/al.h> instead of the standard <AL/al.h> expected throughout the codebase.

Root Cause: cmake/openal.cmake had Homebrew detection but no FetchContent fallback. When Homebrew openal-soft was absent (CI), find_package(OpenAL) silently picked up the Apple system framework.

Fix (porting pattern from windows-sdl branch): - Rewrote cmake/openal.cmake to use FetchContent for all platforms unconditionally (no Homebrew detection). - Included openal.cmake from the root CMakeLists.txt before Core subdirectory so OpenAL::OpenAL target is available when Core/GameEngineDevice processes. - Updated Core/GameEngineDevice/CMakeLists.txt: uses if(TARGET OpenAL::OpenAL) CMake-native guard. - Updated GeneralsMD/Code/GameEngineDevice/CMakeLists.txt: uses if(NOT TARGET OpenAL::OpenAL) guard before find_package.

Validation: - Local configure passes: Configuring OpenAL Soft (v1.24.2) with FetchContent...OpenAL Soft configured: target OpenAL::OpenAL available. - No Apple OpenAL.framework or Homebrew path in configure output.


2026-03-09 (SESSION 77): Stabilize macOS DXVK Source and Deploy Flow

Status: Validated locally. Game now passes Direct3DCreate8 startup path and enters runtime loop.

Work completed: - Switched macOS DXVK source in cmake/dx8.cmake from upstream repo to forked pinned commit (fbraz3/dxvk, commit ffcdbcaf...) to avoid patch drift. - Disabled dynamic patch step in CMake for macOS DXVK source (PATCH_COMMAND "") because patch 6 (util_env.cpp) was not being applied reliably. - Fixed macOS deploy/bundle scripts to resolve DXVK libraries from both possible outputs: - install copy: build/macos-vulkan/libdxvk_*.dylib - meson output: build/macos-vulkan/_deps/dxvk-build-macos/src/*/libdxvk_*.dylib - Fixed PROJECT_ROOT resolution for build scripts under scripts/build/linux/ and scripts/build/macos/ using BASH_SOURCE[0] + ../../...

Root cause resolved: - Runtime SIGTRAP on macOS came from missing __APPLE__ implementation in DXVK src/util/util_env.cpp (getExePath/getExeName path).

Validation: - Configure/build/deploy succeeded. - Runtime no longer traps at Direct3DCreate8; process continues into game initialization loop.

2026-03-03 (SESSION 76): Fix macOS Build — Install FFmpeg and Missing Dependencies in CI

Problem Found:

CMake Error: The following required packages were not found:
  - libavcodec
  - libavformat
  - libavutil
  - libswscale

Root Cause: CI workflow only installed cmake ninja meson but was missing: - ffmpeg — required by Core/GameEngineDevice/CMakeLists.txt via pkg_check_modules(FFMPEG REQUIRED ...) - pkg-config — required to find FFmpeg modules - freetype fontconfig — required by font rendering (locally available, missing in CI)

Complete dependency audit of macOS build (macos-vulkan preset): | Dependency | Source | Status | |---|---|---| | cmake, ninja, meson | Homebrew | Already in workflow | | pkg-config | Homebrew | Added | | ffmpeg | Homebrew | Added | | freetype, fontconfig | Homebrew | Added | | Vulkan SDK + MoltenVK | LunarG ZIP | Already handled | | SDL3, SDL3_image | CMake FetchContent | Auto-fetched | | OpenAL | Xcode SDK (Apple framework) | Auto-found | | PNG, zlib | Xcode SDK | Auto-found |

Fix Applied: Updated Install Dependencies step with all required packages + added verification for libavcodec via pkg-config --modversion.


2026-03-03 (SESSION 75): Fix macOS Build — Vulkan SDK Path Normalization

Status: find_package(Vulkan) now succeeds locally and CI should pass.

Problem Found:

CMake Error: Could NOT find Vulkan (missing: Vulkan_LIBRARY Vulkan_INCLUDE_DIR MoltenVK)

Root Cause:

The LunarG installer places libraries at $INSTALL_TARGET/macOS/lib/libvulkan.dylib, but CMake's FindVulkan.cmake searches in $VULKAN_SDK/lib/ and $VULKAN_SDK/include/.

  • Locally: VULKAN_SDK=/Users/felipebraz/VulkanSDK/1.4.341.0/macOS (already normalized, works)
  • CI: VULKAN_SDK=/Users/runner/VulkanSDK/1.4.341.1 (version root, not platform dir — fails)

The SDK directory structure is:

$HOME/VulkanSDK/1.4.341.1/
└── macOS/              ← CMake FindVulkan expects VULKAN_SDK to point HERE
    ├── lib/
    │   ├── libvulkan.dylib
    │   └── libMoltenVK.dylib
    └── include/
        └── vulkan/

Fixes Applied:

  1. .github/workflows/build-macos.yml — Normalize VULKAN_SDK before export:
  2. After install: if $INSTALL_TARGET/macOS exists, set VULKAN_SDK=$INSTALL_TARGET/macOS
  3. Final normalization block: if VULKAN_SDK/macOS/lib/libvulkan.dylib exists, append /macOS
  4. Simplified MoltenVK check: now only one path to check (lib/, not macOS/lib/)

  5. cmake/dx8.cmake — Simplified and unified Vulkan SDK detection:

  6. If $ENV{VULKAN_SDK} points to version root (has macOS/lib/libMoltenVK.dylib), normalize to macOS/ subdir
  7. Home dir search now looks for ~/VulkanSDK/*/macOS directly
  8. Single final check: ${VULKAN_SDK_ENV}/lib/libMoltenVK.dylib
  9. Removed dual-path (macOS/lib vs lib) complexity in favor of normalized path

Verified: cmake --preset macos-vulkan locally completes:

-- Found Vulkan: .../macOS/lib/libvulkan.dylib (found version "1.4.341") found components: MoltenVK
-- MoltenVK (Vulkan on macOS) detected and enabled
-- Configuring done (229.2s)


2026-03-03 (SESSION 74): Fix macOS Build Failure — CMake else Without Parentheses

Status: CMake configuration now completes successfully locally.

Problem Found:

CMake kept failing with the same error at line 119:

CMake Error at cmake/dx8.cmake:119:
  Parse error. Expected "(", got newline with text "".

Root Cause:

In SESSION 71, when adding the else block for the MoltenVK fallback, else was written without parentheses:

  elseif(VULKAN_SDK_ENV AND EXISTS "...")
    set(VULKAN_SDK_ENV_VAR "VULKAN_SDK=...")
  else                ERROR: must be else()
    message(WARNING "...")
  endif()

CMake requires else() with empty parentheses (unlike bash/C). Line 118 with bare else (no ()) caused a parse error at line 119 where message() was being parsed.

Fix Applied:

Changed elseelse() in cmake/dx8.cmake.

Also:

Consolidated ExternalProject_Add(dxvk_macos_build) to single-line per argument key (avoids future parse issues with empty variable expansions in multiline format).

Verified: cmake --preset macos-vulkan completes successfully:

-- Configuring done (183.0s)
-- Generating done (1.4s)
-- Build files have been written to: .../build/macos-vulkan


2026-03-03 (SESSION 73): Fix macOS Build Failure — CMake Parse Error in dx8.cmake

Status: Fixed CMake syntax error in ExternalProject_Add configure command.

Problem Found:

CMake config failed with:

CMake Error at cmake/dx8.cmake:119:
  Parse error.  Expected "(", got newline with text "".

Root Cause:

In ExternalProject_Add(dxvk_macos_build) CONFIGURE_COMMAND, the variable ${VULKAN_SDK_ENV_VAR} was on its own line:

CONFIGURE_COMMAND
  ${CMAKE_COMMAND} -E env
    CC=clang CXX=clang++
    "CFLAGS=..."
    "CXXFLAGS=..."
    "LDFLAGS=..."
    ${VULKAN_SDK_ENV_VAR}     On own line!
  ${MESON_EXECUTABLE} setup ...

When this variable expands to an empty string, CMake's parser sees:

${CMAKE_COMMAND} -E env ... 
                        ← Empty line (just spaces)
${MESON_EXECUTABLE} setup ...

This confuses CMake's argument parser, which expects the next token after -E env arguments, not a newline.

Solution Applied:

Moved ${VULKAN_SDK_ENV_VAR} to the same line as the env command:

CONFIGURE_COMMAND
  ${CMAKE_COMMAND} -E env CC=clang CXX=clang++ "CFLAGS=..." "CXXFLAGS=..." "LDFLAGS=..." ${VULKAN_SDK_ENV_VAR}
  ${MESON_EXECUTABLE} setup ${DXVK_BUILD_DIR} ${DXVK_SOURCE_DIR}
    ...

When VULKAN_SDK_ENV_VAR is empty, the line becomes:

${CMAKE_COMMAND} -E env CC=clang CXX=clang++ "CFLAGS=..." "CXXFLAGS=..." "LDFLAGS=..."
This is valid and CMake continues to the next line without parse errors.

Expected Outcome: - CMake parses dx8.cmake successfully - DXVK Meson build configuration can proceed


2026-03-03 (SESSION 72): Fix macOS Build Failure — vcpkg Toolchain + Ninja + Compilers

Status: Fixed CMake preset inheritance and toolchain issues. Compiler now explicitly available.

Problems Found in SESSION 71:

CMake config failed with:

Could not find toolchain file: "/scripts/buildsystems/vcpkg.cmake"
CMAKE_MAKE_PROGRAM not set
CMAKE_C_COMPILER not set, after EnableLanguage

Root Causes:

  1. Incorrect preset inheritance: macos-vulkan inherited from default-vcpkg, which required:

    "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
    
    Since VCPKG_ROOT is unset in CI, CMake tried to find /scripts/buildsystems/vcpkg.cmake (absolute path with empty prefix).

  2. Homebrew formula caching: brew install may hit stale cache; needs brew update first.

  3. Compilers not exported to CMake: Even though clang --version worked in shell, compilername was not passed to CMake environment.

Fixes Applied:

  1. CMakePresets.json — Changed macos-vulkan:
  2. Before: "inherits": "default-vcpkg" (requires VCPKG_ROOT)
  3. After: "inherits": "default" (no vcpkg dependency)
  4. Removed "VCPKG_OVERLAY_TRIPLETS" (not needed for macOS brew-based build)

  5. .github/workflows/build-macos.yml — Install Dependencies step:

  6. Added brew update before brew install (ensures fresh formulas)
  7. Prevents stale Ninja formula causing CMAKE_MAKE_PROGRAM not set

  8. .github/workflows/build-macos.yml — Configure CMake step:

  9. Explicitly set CC=clang and CXX=clang++ as environment variables
  10. Added debug output: echo CC=$CC, echo CXX=$CXX
  11. CMake will now use these compilers instead of default detection

  12. .github/workflows/build-macos.yml — Deploy Bundle step:

  13. Updated MoltenVK copy logic to check both macOS/lib and lib paths
  14. Added informative messages about which path was used

Expected Outcome: - CMake finds Ninja (via fresh brew formula) - CMake finds clang/clang++ (via environment variables) - No invalid toolchain path error - Configuration should complete successfully


2026-03-03 (SESSION 71): Fix macOS Build Failure — Compiler + MoltenVK Paths

Status: CI workflow now gets further — Vulkan SDK installs successfully, but CMake configuration fails due to: 1. Missing clang/clang++ compiler (even though xcode selected) 2. MoltenVK path mismatch: installer deploys to macOS/lib/libMoltenVK.dylib, not lib/libMoltenVK.dylib

Problems Found:

CI log excerpt:

⚠️ MoltenVK not found at expected location; build may still work if system Vulkan is available
Checking for alternate locations...
/Users/runner/VulkanSDK/1.4.341.1/macOS/lib/libMoltenVK.dylib  ← FOUND HERE
/Users/runner/VulkanSDK/1.4.341.1/macOS/lib/MoltenVK.xcframework/...

CMake Error at /opt/homebrew/share/cmake/Modules/CMakeDetermineSystem.cmake:159 (message):
  Could not find toolchain file: "/scripts/buildsystems/vcpkg.cmake"
CMake Error: CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage

Root Causes:

  1. Compiler not detected: .github/workflows/build-macos.yml does not verify xcode-select installation; macOS runner may not have xcode-command-line-tools enabled.
  2. MoltenVK path off by one: Vulkan SDK 1.4.341.1 installer puts MoltenVK at $VULKAN_SDK/macOS/lib/libMoltenVK.dylib, but:
  3. cmake/dx8.cmake checks only ${VULKAN_SDK_ENV}/lib/libMoltenVK.dylib (no macOS component)
  4. .github/workflows/build-macos.yml verification also checks only lib/libMoltenVK.dylib

Fixes Applied:

  1. .github/workflows/build-macos.yml — Ensure compiler is available:
  2. Added explicit xcode-select --install (harmless if already installed)
  3. Added path check: xcode-select -p with error message if fails
  4. Added verification: clang --version to confirm compiler available
  5. Added verification: meson --version to confirm Meson available

  6. .github/workflows/build-macos.yml — Check both MoltenVK path variants:

  7. Primary: $VULKAN_SDK/macOS/lib/libMoltenVK.dylib
  8. Fallback: $VULKAN_SDK/lib/libMoltenVK.dylib
  9. If neither found: still proceed (may use system Vulkan)

  10. cmake/dx8.cmake — Update Vulkan SDK detection for both macOS/lib paths:

  11. VULKAN_HOME_DIRS search now checks both ${POTENTIAL_SDK}/macOS/lib/libMoltenVK.dylib AND ${POTENTIAL_SDK}/lib/libMoltenVK.dylib
  12. Final check after MoltenVK lookup: try macOS/lib first, then lib fallback
  13. Enhanced logging: show which paths were checked and what was found

Expected Outcome: - Compiler available → CMake preset configuration should work - MoltenVK found → Meson can properly configure DXVK build with VULKAN_SDK env var


2026-03-03 (SESSION 70): Fix macOS Build Failure — Vulkan SDK Download (FINAL FIX)

Third iteration: Manual download verification revealed actual structure.

Discovery: - Manual download + extraction on local macOS: ZIP extracts correctly to .app bundle - GitHub Actions extraction: Shows only executable file (not .app bundle structure) - Root cause: unzip -q (quiet mode) was silencing critical extraction errors - Possible triggers: ZIP corruption during download, I/O errors, or permission issues in CI

Correct SDK Structure:

vulkansdk-macOS-1.4.341.1.zip
└── vulkansdk-macOS-1.4.341.1.app/
    └── Contents/
        ├── MacOS/
        │   └── vulkansdk-macOS-1.4.341.1 (executable)
        ├── Resources/
        │   ├── installer.dat
        │   └── vulkansdk-macOS-1.4.341.1.icns
        ├── Info.plist
        ├── PkgInfo
        └── _CodeSignature/
            └── CodeResources

Final Fixes Applied:

  1. .github/workflows/build-macos.yml — Enhanced extraction diagnostics:
  2. ZIP integrity check: unzip -t before extraction (catches corrupted files early)
  3. Verbose extraction: Removed -q flag; show all extraction output for debugging
  4. Pre-extraction listing: unzip -l to show contents before extraction
  5. Post-extraction tree: find to print directory structure (first 30 entries)
  6. Revised installer search strategy:

    • Strategy 1 (Primary): Look for .app bundle → Contents/MacOS/vulkansdk-macOS-1.4.341.1
    • Strategy 2 (Fallback): Direct executable vulkansdk-macOS-* if no .app
    • Strategy 3: Make non-executable variants executable with chmod +x
  7. Improved error handling:

  8. If ZIP corrupted: show test results and skip extraction
  9. If extraction fails: detailed directory listing printed
  10. Each strategy logs success/failure and path discovered
  11. Installer executable name corrected from InstallVulkan to vulkansdk-macOS-1.4.341.1

  12. Diagnostics output (for troubleshooting):

  13. Shows first 20 lines of ZIP contents
  14. Shows first 30 entries of extracted directory tree
  15. Lists .app structure if found
  16. Full error messages from unzip -t if file corrupted

Key Learnings: 1. -q (quiet) flag in CI hides critical errors that developers need to see 2. ZIP integrity test (unzip -t) must run BEFORE extraction to catch issues early 3. GitHub Actions CI environment may have different I/O behavior than local macOS 4. SHA256 validation catches download corruption, but not extraction issues 5. Verbose logging in CI is essential for debugging ephemeral failures

Testing Path: - Next CI run will show detailed extraction output - If ZIP corrupts: error caught at integrity test stage - If structure differs: directory tree printed for analysis - If installer not found: detailed path dump provided for debugging


2026-03-03 (SESSION 70, ITERATION 1): Original Analysis

Root Cause Analysis: - Original URL https://sdk.lunarg.com/download/latest/mac/vulkan-sdk.dmg was returning redirects or HTTP errors (~1426 bytes HTML). - LunarG's SDK delivery changed: latest version (1.4.341.1) is now distributed as .zip containing a macOS app installer bundle. - Correct direct URL: https://sdk.lunarg.com/sdk/download/1.4.341.1/mac/vulkansdk-macos-1.4.341.1.zip (318 MB). - SHA256 hash for validation: 632cbe96c8ed6ed00c6ce25e3a7738c466134f76586e1c51f1419410d7f9042e.

Architecture of Vulkan SDK 1.4.341.1: - ZIP contains: vulkansdk-macOS-1.4.341.1.app (macOS application bundle) - Inside .app: /Contents/MacOS/InstallVulkan (executable installer script) - Installer accepts command-line parameters for unattended deployment - Target install path: $HOME/VulkanSDK/1.4.341.1/ (version-specific directory)

Fixes Applied:

  1. .github/workflows/build-macos.yml — Updated Vulkan SDK installation strategy (REVERSED PRIORITY):
  2. Strategy 1 (PREFERRED): Direct download of versioned SDK (1.4.341.1) with SHA256 validation
    • Download from: https://sdk.lunarg.com/sdk/download/1.4.341.1/mac/vulkansdk-macos-1.4.341.1.zip
    • Validate SHA256: 632cbe96c8ed6ed00c6ce25e3a7738c466134f76586e1c51f1419410d7f9042e
    • Extract ZIP to find .app bundle
    • Execute installer: ./vulkansdk-macOS-1.4.341.1.app/Contents/MacOS/InstallVulkan
    • Install to: $HOME/VulkanSDK/1.4.341.1/
    • Retry up to 3 times with 15s delays
  3. Strategy 2 (FALLBACK): Homebrew (brew install --cask vulkan-sdk)
    • Only attempted if versioned download fails
    • May install older version than 1.4.341.1, but guarantees working installation
    • Better than nothing if LunarG CDN is unreachable
  4. Why reversed: Versioned download is more reliable (direct URL, SHA256 validated, specific version known to work); Homebrew is fallback in case LunarG CDN is down

  5. Installation parameters:

  6. --root $HOME/VulkanSDK/1.4.341.1 — version-specific path (supports coexistence)
  7. --accept-licenses — non-interactive (skip dialog)
  8. --default-answer — use presets for all prompts
  9. --confirm-command install — execute install (not just preview)

  10. cmake/dx8.cmake — Updated Vulkan SDK detection paths:

  11. First: Check $VULKAN_SDK environment variable
  12. Second: Search $HOME/VulkanSDK/*/ directories (find latest version)
  13. Third: Try Homebrew locations (Intel: /usr/local/Caskroom/vulkan-sdk, ARM64: /opt/homebrew/Caskroom/vulkan-sdk)
  14. Validate MoltenVK: lib/libMoltenVK.dylib must exist
  15. Pass detected path to Meson via VULKAN_SDK env var

  16. Diagnostics & Error Handling:

  17. SHA256 mismatch detected early (prevent .app corruption)
  18. File size checks reveal HTML error responses (< 10MB)
  19. First 20 lines of invalid files printed for debugging
  20. Installer failures reported with exit status
  21. Graceful fallback if primary strategy fails
  22. Warns if MoltenVK not found (system Vulkan may still work)

Testing in CI: 1. GitHub Actions will retry 3 times on download failure (15s between attempts) 2. SHA256 validation ensures downloaded file integrity 3. Logs show which strategy succeeded (Homebrew vs manual installer) 4. MoltenVK verification confirms complete installation

Lessons Learned: - LunarG changed distribution format (DMG→ZIP with app installer) — needed URL and extraction method updates. - macOS app installers need explicit --accept-licenses --confirm-command for headless operation. - Version-specific paths ($HOME/VulkanSDK/1.4.341.1/) better than generic ($HOME/VulkanSDK/) for multi-version coexistence. - SHA256 validation critical for supply-chain security (detect corrupted/HTML responses immediately).



2026-03-03 (SESSION 69): PR Review Feedback — macOS Build Cleanup

Objective: Address code review feedback from PR #3 macOS build.

Changes Applied:

  1. README.md: Replaced UTF-8 replacement character (U+FFFD) with plain text on macOS status line.
  2. CMakePresets.json: Updated macos-vulkan display name from "Universal Binary" to "arm64 Native" to accurately reflect single-arch build.
  3. cmake/dxvk-macos-patches.py: Removed versioned libvulkan.1.4.341.dylib from the macOS Vulkan loader patch; use stable names only (libvulkan.dylib, libvulkan.1.dylib, libMoltenVK.dylib).
  4. cmake/dx8.cmake: Pinned DXVK GIT_TAG to immutable commit SHA ad253b8a (v2.6) instead of mutable tag v2.6 for supply-chain security.
  5. cmake/ccache.cmake: Replaced ccache -o sloppiness=... (global config mutation) with set(ENV{CCACHE_SLOPPINESS} ...) scoped to the build.
  6. cmake/config-build.cmake: Fixed Vulkan SDK error message to point to LunarG installer, not brew install --cask.
  7. .github/workflows/build-macos.yml: Dropped pull-requests: write to read (least privilege); removed sudo from VulkanSDK copy under $HOME; fixed $? check after pipe to use ${PIPESTATUS[0]}.
  8. scripts/build-macos-zh.sh: Added pipefail to set -e so failing cmake pipes are not silently masked.
  9. scripts/run-macos-zh.sh: Replaced slow find over $HOME to locate MoltenVK_icd.json with direct lookup in game dir and Vulkan SDK.
  10. scripts/test_ccache.sh: Replaced hard-coded local path with script-relative PROJECT_DIR; added pipefail.
  11. scripts/setup_ccache.sh: Removed hard-coded /Users/felipebraz path and unconditional global config overwrite; now shows recommended config and asks for confirmation.
  12. scripts/monitor-dxvk-build.py: Replaced hard-coded path with CLI argument (--log); replaced bare except: with specific OSError catches.
  13. Core/GameEngine/Source/Common/System/Debug.cpp: Added explicit #include "thread_compat.h" for non-Windows builds.
  14. Documentation translations: Translated MACOS_PORT_ANALYSIS.md, TERRAIN_SHADER_FIX_PLAN.md, PLANO_B_WORKAROUND.md, CCACHE_DIAGNOSIS.md from Portuguese to English.
  15. Documentation corrections: Updated binary paths in macOS-Workflow-Before-After.md and GitHub-Actions-macOS-Improvements.md; updated DXVK patch count from 10 to 13 in MACOS_BUILD_INSTRUCTIONS.md.