From cb01821d890720ebab42bce1cca9014fa4c9c181 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 18 Feb 2026 17:38:08 -0800 Subject: [PATCH] Add Windows build support via MSYS2 and fix platform-specific code Guard X11 display crash handler with __linux__, add Windows GlobalMemoryStatusEx path in memory_monitor, guard warden cache paths with APPDATA on Windows, and make pkg-config optional in CMakeLists with a find_library fallback. Add Windows x86-64 CI job using MSYS2 MINGW64 to the build workflow. --- .github/workflows/build.yml | 44 +++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 21 ++++++++++++++++-- src/core/memory_monitor.cpp | 26 ++++++++++++++++++++++ src/game/game_handler.cpp | 27 +++++++++++++++-------- src/game/warden_module.cpp | 15 ++++++++----- src/main.cpp | 8 +++++++ 6 files changed, 125 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bd2cb883..946273e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,3 +64,47 @@ jobs: name: wowee-${{ matrix.arch }} path: build/bin/ if-no-files-found: error + + build-windows: + name: Build (windows-x86-64) + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: false + install: >- + mingw-w64-x86_64-cmake + mingw-w64-x86_64-gcc + mingw-w64-x86_64-pkgconf + mingw-w64-x86_64-SDL2 + mingw-w64-x86_64-glew + mingw-w64-x86_64-glm + mingw-w64-x86_64-openssl + mingw-w64-x86_64-zlib + mingw-w64-x86_64-ffmpeg + mingw-w64-x86_64-unicorn + git + + - name: Clone ImGui + shell: msys2 {0} + run: git clone --depth 1 https://github.com/ocornut/imgui.git extern/imgui + + - name: Configure + shell: msys2 {0} + run: cmake -S . -B build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release + + - name: Build + shell: msys2 {0} + run: cmake --build build --parallel $(nproc) + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: wowee-windows-x86-64 + path: build/bin/ + if-no-files-found: error diff --git a/CMakeLists.txt b/CMakeLists.txt index 0547f99c..6bc26920 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,8 +22,25 @@ find_package(GLEW REQUIRED) find_package(OpenSSL REQUIRED) find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) -find_package(PkgConfig REQUIRED) -pkg_check_modules(FFMPEG REQUIRED libavformat libavcodec libswscale libavutil) +if(WIN32) + find_package(PkgConfig QUIET) +else() + find_package(PkgConfig REQUIRED) +endif() +if(PkgConfig_FOUND) + pkg_check_modules(FFMPEG REQUIRED libavformat libavcodec libswscale libavutil) +else() + # Fallback for MSVC/vcpkg — find FFmpeg libraries manually + find_path(FFMPEG_INCLUDE_DIRS libavformat/avformat.h) + find_library(AVFORMAT_LIB NAMES avformat) + find_library(AVCODEC_LIB NAMES avcodec) + find_library(AVUTIL_LIB NAMES avutil) + find_library(SWSCALE_LIB NAMES swscale) + set(FFMPEG_LIBRARIES ${AVFORMAT_LIB} ${AVCODEC_LIB} ${AVUTIL_LIB} ${SWSCALE_LIB}) + if(NOT AVFORMAT_LIB) + message(FATAL_ERROR "FFmpeg not found. On Windows install via MSYS2: mingw-w64-x86_64-ffmpeg") + endif() +endif() # Unicorn Engine (x86 emulator for cross-platform Warden module execution) find_library(UNICORN_LIBRARY NAMES unicorn) diff --git a/src/core/memory_monitor.cpp b/src/core/memory_monitor.cpp index 4064d49b..e4245b29 100644 --- a/src/core/memory_monitor.cpp +++ b/src/core/memory_monitor.cpp @@ -3,11 +3,16 @@ #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif namespace wowee { namespace core { +#ifndef _WIN32 namespace { size_t readMemAvailableBytesFromProc() { std::ifstream meminfo("/proc/meminfo"); @@ -26,6 +31,7 @@ size_t readMemAvailableBytesFromProc() { return 0; } } // namespace +#endif MemoryMonitor& MemoryMonitor::getInstance() { static MemoryMonitor instance; @@ -33,6 +39,16 @@ MemoryMonitor& MemoryMonitor::getInstance() { } void MemoryMonitor::initialize() { +#ifdef _WIN32 + ULONGLONG totalKB = 0; + if (GetPhysicallyInstalledSystemMemory(&totalKB)) { + totalRAM_ = static_cast(totalKB) * 1024ull; + LOG_INFO("System RAM detected: ", totalRAM_ / (1024 * 1024 * 1024), " GB"); + } else { + totalRAM_ = 16ull * 1024 * 1024 * 1024; + LOG_WARNING("Could not detect system RAM, assuming 16GB"); + } +#else struct sysinfo info; if (sysinfo(&info) == 0) { totalRAM_ = static_cast(info.totalram) * info.mem_unit; @@ -42,9 +58,18 @@ void MemoryMonitor::initialize() { totalRAM_ = 16ull * 1024 * 1024 * 1024; LOG_WARNING("Could not detect system RAM, assuming 16GB"); } +#endif } size_t MemoryMonitor::getAvailableRAM() const { +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (GlobalMemoryStatusEx(&status)) { + return static_cast(status.ullAvailPhys); + } + return totalRAM_ / 2; +#else // Best source on Linux for reclaimable memory headroom. if (size_t memAvailable = readMemAvailableBytesFromProc(); memAvailable > 0) { return memAvailable; @@ -59,6 +84,7 @@ size_t MemoryMonitor::getAvailableRAM() const { return (totalRAM_ > 0 && available > totalRAM_) ? totalRAM_ : available; } return totalRAM_ / 2; // Fallback: assume 50% available +#endif } size_t MemoryMonitor::getRecommendedCacheBudget() const { diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index d636b351..892336a6 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -2397,11 +2397,15 @@ bool GameHandler::loadWardenCRFile(const std::string& moduleHashHex) { wardenCREntries_.clear(); // Look for .cr file in warden cache - std::string homeDir; - if (const char* h = std::getenv("HOME")) homeDir = h; - else homeDir = "."; - - std::string crPath = homeDir + "/.local/share/wowee/warden_cache/" + moduleHashHex + ".cr"; + std::string cacheBase; +#ifdef _WIN32 + if (const char* h = std::getenv("APPDATA")) cacheBase = std::string(h) + "\\wowee\\warden_cache"; + else cacheBase = ".\\warden_cache"; +#else + if (const char* h = std::getenv("HOME")) cacheBase = std::string(h) + "/.local/share/wowee/warden_cache"; + else cacheBase = "./warden_cache"; +#endif + std::string crPath = cacheBase + "/" + moduleHashHex + ".cr"; std::ifstream crFile(crPath, std::ios::binary); if (!crFile) { @@ -2581,10 +2585,15 @@ void GameHandler::handleWardenData(network::Packet& packet) { // Cache raw module to disk { - std::string homeDir; - if (const char* h = std::getenv("HOME")) homeDir = h; - else homeDir = "."; - std::string cacheDir = homeDir + "/.local/share/wowee/warden_cache"; +#ifdef _WIN32 + std::string cacheDir; + if (const char* h = std::getenv("APPDATA")) cacheDir = std::string(h) + "\\wowee\\warden_cache"; + else cacheDir = ".\\warden_cache"; +#else + std::string cacheDir; + if (const char* h = std::getenv("HOME")) cacheDir = std::string(h) + "/.local/share/wowee/warden_cache"; + else cacheDir = "./warden_cache"; +#endif std::filesystem::create_directories(cacheDir); std::string hashHex; diff --git a/src/game/warden_module.cpp b/src/game/warden_module.cpp index 90caef2c..5df03aa4 100644 --- a/src/game/warden_module.cpp +++ b/src/game/warden_module.cpp @@ -906,13 +906,18 @@ bool WardenModule::initializeModule() { // ============================================================================ WardenModuleManager::WardenModuleManager() { - // Set cache directory: ~/.local/share/wowee/warden_cache/ - const char* home = std::getenv("HOME"); - if (home) { + // Set cache directory +#ifdef _WIN32 + if (const char* appdata = std::getenv("APPDATA")) + cacheDirectory_ = std::string(appdata) + "\\wowee\\warden_cache"; + else + cacheDirectory_ = ".\\warden_cache"; +#else + if (const char* home = std::getenv("HOME")) cacheDirectory_ = std::string(home) + "/.local/share/wowee/warden_cache"; - } else { + else cacheDirectory_ = "./warden_cache"; - } +#endif // Create cache directory if it doesn't exist std::filesystem::create_directories(cacheDirectory_); diff --git a/src/main.cpp b/src/main.cpp index 52757c81..cbd1c305 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#ifdef __linux__ #include // Keep a persistent X11 connection for emergency mouse release in signal handlers. @@ -16,6 +17,9 @@ static void releaseMouseGrab() { XFlush(g_emergencyDisplay); } } +#else +static void releaseMouseGrab() {} +#endif static void crashHandler(int sig) { releaseMouseGrab(); @@ -24,7 +28,9 @@ static void crashHandler(int sig) { } int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { +#ifdef __linux__ g_emergencyDisplay = XOpenDisplay(nullptr); +#endif std::signal(SIGSEGV, crashHandler); std::signal(SIGABRT, crashHandler); std::signal(SIGFPE, crashHandler); @@ -46,7 +52,9 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { app.shutdown(); LOG_INFO("Application exited successfully"); +#ifdef __linux__ if (g_emergencyDisplay) { XCloseDisplay(g_emergencyDisplay); g_emergencyDisplay = nullptr; } +#endif return 0; } catch (const std::exception& e) {