diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 29a08cf4420..76655859ced 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -10,9 +10,33 @@ on: jobs: build-windows: - name: GeneralsMD x86 + name: ${{ matrix.game.id }} x86 runs-on: windows-2022 timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + game: + - id: generalsxzh + name: Zero Hour + target: z_generals + folder: GeneralsMD + exe: GeneralsXZH.exe + pdb: GeneralsXZH.pdb + build_zh: "ON" + build_generals: "OFF" + artifact_name: generalsxzh-windows-x86 + ci_artifact_name: generalsxzh-windows-x86-replay-test-only + - id: generalsx + name: Generals Base + target: g_generals + folder: Generals + exe: GeneralsX.exe + pdb: GeneralsX.pdb + build_zh: "OFF" + build_generals: "ON" + artifact_name: generalsx-windows-x86 + ci_artifact_name: generalsx-windows-x86-ci-only steps: - name: Checkout @@ -28,27 +52,28 @@ jobs: run: | $env:VCPKG_ROOT = $env:VCPKG_INSTALLATION_ROOT cmake --preset win32-vcpkg ` - -DRTS_BUILD_GENERALS=OFF ` - -DRTS_BUILD_ZEROHOUR=ON ` + -DRTS_BUILD_GENERALS=${{ matrix.game.build_generals }} ` + -DRTS_BUILD_ZEROHOUR=${{ matrix.game.build_zh }} ` -DRTS_BUILD_CORE_TOOLS=OFF ` -DRTS_BUILD_ZEROHOUR_TOOLS=OFF ` + -DRTS_BUILD_GENERALS_TOOLS=OFF ` -DRTS_BUILD_OPTION_SAGE_PATCH=OFF ` -DSAGE_USE_DX8=ON ` -DSAGE_USE_DETERMINISTIC_MATH=ON - name: Build shell: pwsh - run: cmake --build --preset win32-vcpkg + run: cmake --build --preset win32-vcpkg --target ${{ matrix.game.target }} - name: Collect runtime shell: pwsh run: | New-Item -ItemType Directory -Force artifacts | Out-Null New-Item -ItemType Directory -Force ci-artifacts | Out-Null - Copy-Item build/win32-vcpkg/GeneralsMD/Release/GeneralsXZH.exe artifacts/ - Copy-Item build/win32-vcpkg/GeneralsMD/Release/GeneralsXZH.pdb artifacts/ + Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.exe }} artifacts/ + Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.pdb }} artifacts/ Copy-Item build/win32-vcpkg/vcpkg_installed/x86-windows/bin/zlib1.dll artifacts/ - Copy-Item build/win32-vcpkg/GeneralsMD/Release/GeneralsXZH.exe ci-artifacts/ + Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.exe }} ci-artifacts/ Copy-Item build/win32-vcpkg/vcpkg_installed/x86-windows/bin/zlib1.dll ci-artifacts/ foreach ($name in @("binkw32.dll", "mss32.dll")) { @@ -61,7 +86,7 @@ jobs: } @" - Copy these files into an existing licensed Zero Hour installation. + Copy these files into an existing licensed ${{ matrix.game.name }} installation. Keep the installation's original binkw32.dll and mss32.dll; the build-time stub DLLs do not provide retail video or audio playback. "@ | Set-Content artifacts/INSTALL.txt @@ -69,13 +94,13 @@ jobs: - name: Upload runtime uses: actions/upload-artifact@v7 with: - name: generalsxzh-windows-x86 + name: ${{ matrix.game.artifact_name }} path: artifacts retention-days: 7 - name: Upload replay-test runtime uses: actions/upload-artifact@v7 with: - name: generalsxzh-windows-x86-replay-test-only + name: ${{ matrix.game.ci_artifact_name }} path: ci-artifacts retention-days: 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 664afde4e72..86305f77fe1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: build-windows: name: Build Windows needs: detect-changes - if: needs.detect-changes.outputs.zh-should-build == 'true' || github.event_name == 'workflow_dispatch' + if: needs.detect-changes.outputs.zh-should-build == 'true' || needs.detect-changes.outputs.base-should-build == 'true' || github.event_name == 'workflow_dispatch' uses: ./.github/workflows/build-windows.yml secrets: inherit diff --git a/AGENTS.md b/AGENTS.md index 854ac51eb36..848914ad491 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -151,6 +151,7 @@ Docker is the recommended build method on Linux hosts to ensure all toolchain re - **SDL3 from source**: Fetched via CMake FetchContent. No system package needed. - **Manual memory**: Always delete/delete[]. Use STLPort for VC6 legacy builds. - **Debug options break replays**: Use `RTS_BUILD_OPTION_DEBUG=OFF` for replay tests. +- **Windows executable icons (.ico)**: Windows builds embed `Generals/Code/Main/Generals.ico` and `GeneralsMD/Code/Main/Generals.ico` via `RTS.RC`. If source PNG icon assets (`assets/generalsx_icon.png` or `assets/generalsx-zh_icon.png`) are updated, regenerate the multi-resolution `.ico` files (sizes 16, 24, 32, 48, 64, 128, 256) to keep Windows executables in sync. ## Testing & Validation ### Smoke test diff --git a/Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h b/Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h index de5a39ab408..db992c268c4 100644 --- a/Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h +++ b/Core/GameEngineDevice/Include/VideoDevice/Bink/BinkVideoPlayer.h @@ -46,12 +46,11 @@ //---------------------------------------------------------------------------- #include "GameClient/VideoPlayer.h" -// GeneralsX @TheSuperHackers @build BenderAI 11/02/2026 Stub Bink for Phase 1 (proprietary SDK) -// TODO Phase 3: Replace with FFmpeg or skip videos gracefully -// #include "bink.h" - -// Bink type stubs (proprietary SDK not available) +#ifdef _WIN32 +#include "bink.h" +#else typedef struct BINK* HBINK; +#endif //---------------------------------------------------------------------------- // Forward References diff --git a/Generals/Code/CMakeLists.txt b/Generals/Code/CMakeLists.txt index e3d7bb8cd06..1e287bc3795 100644 --- a/Generals/Code/CMakeLists.txt +++ b/Generals/Code/CMakeLists.txt @@ -8,7 +8,9 @@ add_library(gi_always_no_pch INTERFACE) # Use this for Shared Libs with MFC AFX target_include_directories(gi_gameengine_include INTERFACE "GameEngine/Include") target_include_directories(gi_libraries_source_wwvegas INTERFACE "Libraries/Source/WWVegas") target_include_directories(gi_main INTERFACE "Main") -target_include_directories(gi_always INTERFACE "CompatLib/Include") +if(UNIX) + target_include_directories(gi_always INTERFACE "CompatLib/Include") +endif() target_compile_definitions(gi_always INTERFACE RTS_GENERALS=1 diff --git a/Generals/Code/CompatLib/Include/comip.h b/Generals/Code/CompatLib/Include/comip.h deleted file mode 100644 index 092614780fb..00000000000 --- a/Generals/Code/CompatLib/Include/comip.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// GeneralsX @build fbraz 11/02/2026 Bender -// COM Interface Pointer header wrapper - redirects to platform-specific implementation -// Used by dx8webbrowser.cpp (DirectX web browser embedding) - -#ifdef _WIN32 -// Windows: Use real COM IP header from Windows SDK -#include_next -#else -// Linux: Use our compatibility layer (minimal stubs for compilation) -#include "comip_compat.h" -#endif diff --git a/Generals/Code/CompatLib/Include/comutil.h b/Generals/Code/CompatLib/Include/comutil.h deleted file mode 100644 index 25fe9fec6a6..00000000000 --- a/Generals/Code/CompatLib/Include/comutil.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// GeneralsX @build fbraz 10/02/2026 Bender -// COM utility header wrapper - redirects to platform-specific implementation - -#ifdef _WIN32 -// Windows: Use real COM utility header from Windows SDK -#include_next -#else -// Linux: Use our compatibility layer -#include "comutil_compat.h" -#endif diff --git a/Generals/Code/CompatLib/Include/d3dx8math.h b/Generals/Code/CompatLib/Include/d3dx8math.h index 7fe6cdfcc07..b3d1b431c46 100644 --- a/Generals/Code/CompatLib/Include/d3dx8math.h +++ b/Generals/Code/CompatLib/Include/d3dx8math.h @@ -18,6 +18,20 @@ extern "C" typedef struct D3DXMATRIX : D3DMATRIX { #ifdef __cplusplus + D3DXMATRIX() = default; + + D3DXMATRIX( + FLOAT m00, FLOAT m01, FLOAT m02, FLOAT m03, + FLOAT m10, FLOAT m11, FLOAT m12, FLOAT m13, + FLOAT m20, FLOAT m21, FLOAT m22, FLOAT m23, + FLOAT m30, FLOAT m31, FLOAT m32, FLOAT m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + } + D3DXMATRIX operator *(const D3DXMATRIX &other) const; D3DXMATRIX operator *= (const D3DXMATRIX& other); #endif diff --git a/Generals/Code/CompatLib/Include/windows.h b/Generals/Code/CompatLib/Include/windows.h index b3eff7e4d5f..892efdecfaa 100644 --- a/Generals/Code/CompatLib/Include/windows.h +++ b/Generals/Code/CompatLib/Include/windows.h @@ -7,11 +7,8 @@ // Our types take precedence via PCH (PreRTS.h includes windows_compat.h early). #ifdef _WIN32 -// Windows: use real Windows.h from SDK (will be found in system paths first) -// This header is only reached if no SDK windows.h exists -#include "windows_compat.h" +#include #else -// Linux: Our compatibility layer only (NO DXVK headers here!) -// DXVK's windows_base.h will be included by d3d8.h when needed +// Linux/macOS: Our compatibility layer only (prevents DXVK unknwn.h redefinition) #include "windows_compat.h" #endif diff --git a/Generals/Code/CompatLib/Include/windows_wrapper.h b/Generals/Code/CompatLib/Include/windows_wrapper.h index b3eff7e4d5f..82c4d833a3f 100644 --- a/Generals/Code/CompatLib/Include/windows_wrapper.h +++ b/Generals/Code/CompatLib/Include/windows_wrapper.h @@ -9,7 +9,7 @@ #ifdef _WIN32 // Windows: use real Windows.h from SDK (will be found in system paths first) // This header is only reached if no SDK windows.h exists -#include "windows_compat.h" +#include #else // Linux: Our compatibility layer only (NO DXVK headers here!) // DXVK's windows_base.h will be included by d3d8.h when needed diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt index 86601ea7d99..f14b320f7e8 100644 --- a/Generals/Code/GameEngine/CMakeLists.txt +++ b/Generals/Code/GameEngine/CMakeLists.txt @@ -1079,6 +1079,17 @@ target_sources(g_gameengine PRIVATE ${GAMEENGINE_SRC}) target_include_directories(g_gameengine PUBLIC Include ) +if(UNIX) + target_include_directories(g_gameengine PUBLIC ../CompatLib/Include) +endif() + +# Ensure DXVK headers are available when building on Linux +if(NOT SAGE_USE_DX8 AND dxvk_SOURCE_DIR) + target_include_directories(g_gameengine PUBLIC + ${dxvk_SOURCE_DIR}/usr/include + ${dxvk_SOURCE_DIR}/usr/include/dxvk + ) +endif() target_include_directories(g_gameengine PRIVATE Include/Precompiled @@ -1092,6 +1103,7 @@ target_link_libraries(g_gameengine PRIVATE target_link_libraries(g_gameengine PUBLIC corei_gameengine_public g_wwvegas + d3d8lib # Ensure DirectX8 headers available (Windows SDK or DXVK) ) target_precompile_headers(g_gameengine PRIVATE diff --git a/Generals/Code/GameEngine/Source/Common/GameEngine.cpp b/Generals/Code/GameEngine/Source/Common/GameEngine.cpp index 6361bc18904..ccba389b1cb 100644 --- a/Generals/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/Generals/Code/GameEngine/Source/Common/GameEngine.cpp @@ -1159,3 +1159,14 @@ void updateTGAtoDDS() system(CONVERT_EXEC1); } + +// If we're using the Wide character version of MessageBox, then there's no additional +// processing necessary. Please note that this is a sleazy way to get this information, +// but pending a better one, this'll have to do. +// TheSuperHackers @build fighter19 11/02/2026 MessageBox detection (Windows-only) +#ifdef _WIN32 +extern const Bool TheSystemIsUnicode = (((void*) (::MessageBox)) == ((void*) (::MessageBoxW))); +#else +extern const Bool TheSystemIsUnicode = true; // Linux: Always Unicode (UTF-8) +#endif + diff --git a/Generals/Code/GameEngine/Source/Common/System/registry.cpp b/Generals/Code/GameEngine/Source/Common/System/registry.cpp index db11ec0ac87..ef3c2b556c4 100644 --- a/Generals/Code/GameEngine/Source/Common/System/registry.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/registry.cpp @@ -29,7 +29,7 @@ #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine #include "Common/Registry.h" -#include "registryini.h" +#include "WWLib/registryini.h" // TheSuperHackers @build felipebraz 11/02/2026 Phase 1.5 - Linux port // Windows Registry types not available on Linux - define stub types diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 48454578fbf..3963194da34 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -46,7 +46,7 @@ #include "Common/GameLOD.h" #include "Common/GameState.h" -#if DEEP_CRC_TO_MEMORY +#if DEEP_CRC_TO_MEMORY && !defined(_WIN32) #include #include #endif @@ -5065,6 +5065,9 @@ void GameLogic::writeCRCBuffersToDisk(UnsignedInt frame) const AsciiString str; // GeneralsX: Generate OS/Arch header AsciiString headerStr; +#ifdef _WIN32 + headerStr = "GeneralsX: Windows x86\n\n"; +#else struct utsname sysInfo; if (uname(&sysInfo) == 0) { headerStr.format("GeneralsX: %s %s (%s)\nArch: %s\nCPU Cores: %d\nRAM: %d MB\n\n", @@ -5073,6 +5076,7 @@ void GameLogic::writeCRCBuffersToDisk(UnsignedInt frame) const } else { headerStr = "GeneralsX: Unknown OS/Arch\n\n"; } +#endif // Format filename as deep_crc_YYYY-MM-DD-HH-MM-SS.bin inside user data Debug dir time_t t = time(nullptr); diff --git a/Generals/Code/GameEngineDevice/CMakeLists.txt b/Generals/Code/GameEngineDevice/CMakeLists.txt index 34be188af06..209448af2cb 100644 --- a/Generals/Code/GameEngineDevice/CMakeLists.txt +++ b/Generals/Code/GameEngineDevice/CMakeLists.txt @@ -175,13 +175,13 @@ if(WIN32) list(APPEND GAMEENGINEDEVICE_SRC # Source/Win32Device/Common/Win32BIGFile.cpp # Source/Win32Device/Common/Win32BIGFileSystem.cpp - Source/Win32Device/Common/Win32CDManager.cpp - Source/Win32Device/Common/Win32GameEngine.cpp + ${CMAKE_SOURCE_DIR}/Core/GameEngineDevice/Source/Win32Device/Common/Win32GameEngine.cpp # Source/Win32Device/Common/Win32LocalFile.cpp # Source/Win32Device/Common/Win32LocalFileSystem.cpp - Source/Win32Device/Common/Win32OSDisplay.cpp - Source/Win32Device/GameClient/Win32DIKeyboard.cpp - Source/Win32Device/GameClient/Win32Mouse.cpp + ${CMAKE_SOURCE_DIR}/Core/GameEngineDevice/Source/Win32Device/Common/Win32OSDisplay.cpp + ${CMAKE_SOURCE_DIR}/Core/GameEngineDevice/Source/Win32Device/GameClient/Win32DIKeyboard.cpp +# Source/Win32Device/GameClient/Win32DIMouse.cpp + ${CMAKE_SOURCE_DIR}/Core/GameEngineDevice/Source/Win32Device/GameClient/Win32Mouse.cpp ) else() # Linux-specific files (SDL3, DXVK) diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DProjectedShadow.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DProjectedShadow.cpp index 98afa662f7c..e55879480f8 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DProjectedShadow.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DProjectedShadow.cpp @@ -2111,7 +2111,6 @@ void W3DProjectedShadow::init() if (m_type & SHADOW_PROJECTION) { - float minx,maxx; m_shadowProjector = NEW_REF(TexProjectClass,()); m_shadowProjector->Set_Intensity(0.4f,true); m_shadowProjector->Set_Texture(m_shadowTexture[0]->getTexture()); diff --git a/Generals/Code/Main/Generals.ico b/Generals/Code/Main/Generals.ico index 9112002400e..7462af28c46 100644 Binary files a/Generals/Code/Main/Generals.ico and b/Generals/Code/Main/Generals.ico differ diff --git a/GeneralsMD/Code/CompatLib/Include/windows.h b/GeneralsMD/Code/CompatLib/Include/windows.h new file mode 100644 index 00000000000..892efdecfaa --- /dev/null +++ b/GeneralsMD/Code/CompatLib/Include/windows.h @@ -0,0 +1,14 @@ +#pragma once + +// Linux/Unix compatibility shim for Windows.h +// GeneralsX @build BenderAI 11/02/2026 Windows API compatibility layer +// STRATEGY: Provide our own complete Windows types/functions FIRST. +// DXVK headers (d3d8.h) will include their own windows_base.h later. +// Our types take precedence via PCH (PreRTS.h includes windows_compat.h early). + +#ifdef _WIN32 +#include +#else +// Linux/macOS: Our compatibility layer only (prevents DXVK unknwn.h redefinition) +#include "windows_compat.h" +#endif diff --git a/GeneralsMD/Code/Main/Generals.ico b/GeneralsMD/Code/Main/Generals.ico index 1d8102c799a..3d33add138a 100644 Binary files a/GeneralsMD/Code/Main/Generals.ico and b/GeneralsMD/Code/Main/Generals.ico differ diff --git a/GeneralsMD/Code/Main/RTS.RC b/GeneralsMD/Code/Main/RTS.RC index faa4ee857fa..07e26ff29ae 100644 --- a/GeneralsMD/Code/Main/RTS.RC +++ b/GeneralsMD/Code/Main/RTS.RC @@ -54,7 +54,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ApplicationIcon ICON DISCARDABLE "GENERALS.ICO" +IDI_ApplicationIcon ICON DISCARDABLE "Generals.ico" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/docs/WORKLOG/2026-08-DIARY.md b/docs/WORKLOG/2026-08-DIARY.md index a7f74e4cfc4..d89294a8d54 100644 --- a/docs/WORKLOG/2026-08-DIARY.md +++ b/docs/WORKLOG/2026-08-DIARY.md @@ -1,6 +1,15 @@ # August 2026 ## 19/08/2026 +### Enable Windows Build Matrix for Generals Base Game +- Backported native MSVC Windows build support to the Generals base game (`Generals/`): + - Pointed Win32 device sources to shared `Core/GameEngineDevice/Source/Win32Device/` implementations (`Win32GameEngine.cpp`, `Win32OSDisplay.cpp`, `Win32DIKeyboard.cpp`, `Win32Mouse.cpp`). + - Synced include guards and `d3d8lib` linkage across `Generals/Code/GameEngine/CMakeLists.txt` and `Generals/Code/CMakeLists.txt`. + - Removed obsolete shadowing CompatLib headers (`comip.h`, `comutil.h`, `windows.h`) and synced `windows_wrapper.h` / `d3dx8math.h`. +- Expanded `.github/workflows/build-windows.yml` to a matrix building both `generalsxzh` (`GeneralsMD` -> `GeneralsXZH.exe`) and `generalsx` (`Generals` -> `GeneralsX.exe`). +- Updated `ci.yml` to trigger `build-windows` when either Zero Hour or Generals base game changes are detected. +- Converted `assets/generalsx_icon.png` and `assets/generalsx-zh_icon.png` into high-resolution multi-size Windows icon files (`Generals.ico`), packaging crisp icons (16x16 to 256x256) into Windows executables. + ### Independent Windows CI Asset Cache & Executable Standardization - Decoupled Windows replay tests from Linux build/replay workflows by establishing dedicated Windows asset caching (`game-assets-cache-windows-v1`) with self-contained fallback extraction. - Passed `ASSETS_KEY` secret directly to the Windows replay test workflow.