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.
This commit is contained in:
Kelsi 2026-02-18 17:38:08 -08:00
parent 02fd0166e9
commit cb01821d89
6 changed files with 125 additions and 16 deletions

View file

@ -64,3 +64,47 @@ jobs:
name: wowee-${{ matrix.arch }} name: wowee-${{ matrix.arch }}
path: build/bin/ path: build/bin/
if-no-files-found: error 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

View file

@ -22,8 +22,25 @@ find_package(GLEW REQUIRED)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(PkgConfig REQUIRED) if(WIN32)
pkg_check_modules(FFMPEG REQUIRED libavformat libavcodec libswscale libavutil) 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) # Unicorn Engine (x86 emulator for cross-platform Warden module execution)
find_library(UNICORN_LIBRARY NAMES unicorn) find_library(UNICORN_LIBRARY NAMES unicorn)

View file

@ -3,11 +3,16 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <sstream> #include <sstream>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#endif
namespace wowee { namespace wowee {
namespace core { namespace core {
#ifndef _WIN32
namespace { namespace {
size_t readMemAvailableBytesFromProc() { size_t readMemAvailableBytesFromProc() {
std::ifstream meminfo("/proc/meminfo"); std::ifstream meminfo("/proc/meminfo");
@ -26,6 +31,7 @@ size_t readMemAvailableBytesFromProc() {
return 0; return 0;
} }
} // namespace } // namespace
#endif
MemoryMonitor& MemoryMonitor::getInstance() { MemoryMonitor& MemoryMonitor::getInstance() {
static MemoryMonitor instance; static MemoryMonitor instance;
@ -33,6 +39,16 @@ MemoryMonitor& MemoryMonitor::getInstance() {
} }
void MemoryMonitor::initialize() { void MemoryMonitor::initialize() {
#ifdef _WIN32
ULONGLONG totalKB = 0;
if (GetPhysicallyInstalledSystemMemory(&totalKB)) {
totalRAM_ = static_cast<size_t>(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; struct sysinfo info;
if (sysinfo(&info) == 0) { if (sysinfo(&info) == 0) {
totalRAM_ = static_cast<size_t>(info.totalram) * info.mem_unit; totalRAM_ = static_cast<size_t>(info.totalram) * info.mem_unit;
@ -42,9 +58,18 @@ void MemoryMonitor::initialize() {
totalRAM_ = 16ull * 1024 * 1024 * 1024; totalRAM_ = 16ull * 1024 * 1024 * 1024;
LOG_WARNING("Could not detect system RAM, assuming 16GB"); LOG_WARNING("Could not detect system RAM, assuming 16GB");
} }
#endif
} }
size_t MemoryMonitor::getAvailableRAM() const { size_t MemoryMonitor::getAvailableRAM() const {
#ifdef _WIN32
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
if (GlobalMemoryStatusEx(&status)) {
return static_cast<size_t>(status.ullAvailPhys);
}
return totalRAM_ / 2;
#else
// Best source on Linux for reclaimable memory headroom. // Best source on Linux for reclaimable memory headroom.
if (size_t memAvailable = readMemAvailableBytesFromProc(); memAvailable > 0) { if (size_t memAvailable = readMemAvailableBytesFromProc(); memAvailable > 0) {
return memAvailable; return memAvailable;
@ -59,6 +84,7 @@ size_t MemoryMonitor::getAvailableRAM() const {
return (totalRAM_ > 0 && available > totalRAM_) ? totalRAM_ : available; return (totalRAM_ > 0 && available > totalRAM_) ? totalRAM_ : available;
} }
return totalRAM_ / 2; // Fallback: assume 50% available return totalRAM_ / 2; // Fallback: assume 50% available
#endif
} }
size_t MemoryMonitor::getRecommendedCacheBudget() const { size_t MemoryMonitor::getRecommendedCacheBudget() const {

View file

@ -2397,11 +2397,15 @@ bool GameHandler::loadWardenCRFile(const std::string& moduleHashHex) {
wardenCREntries_.clear(); wardenCREntries_.clear();
// Look for .cr file in warden cache // Look for .cr file in warden cache
std::string homeDir; std::string cacheBase;
if (const char* h = std::getenv("HOME")) homeDir = h; #ifdef _WIN32
else homeDir = "."; if (const char* h = std::getenv("APPDATA")) cacheBase = std::string(h) + "\\wowee\\warden_cache";
else cacheBase = ".\\warden_cache";
std::string crPath = homeDir + "/.local/share/wowee/warden_cache/" + moduleHashHex + ".cr"; #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); std::ifstream crFile(crPath, std::ios::binary);
if (!crFile) { if (!crFile) {
@ -2581,10 +2585,15 @@ void GameHandler::handleWardenData(network::Packet& packet) {
// Cache raw module to disk // Cache raw module to disk
{ {
std::string homeDir; #ifdef _WIN32
if (const char* h = std::getenv("HOME")) homeDir = h; std::string cacheDir;
else homeDir = "."; if (const char* h = std::getenv("APPDATA")) cacheDir = std::string(h) + "\\wowee\\warden_cache";
std::string cacheDir = homeDir + "/.local/share/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::filesystem::create_directories(cacheDir);
std::string hashHex; std::string hashHex;

View file

@ -906,13 +906,18 @@ bool WardenModule::initializeModule() {
// ============================================================================ // ============================================================================
WardenModuleManager::WardenModuleManager() { WardenModuleManager::WardenModuleManager() {
// Set cache directory: ~/.local/share/wowee/warden_cache/ // Set cache directory
const char* home = std::getenv("HOME"); #ifdef _WIN32
if (home) { 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"; cacheDirectory_ = std::string(home) + "/.local/share/wowee/warden_cache";
} else { else
cacheDirectory_ = "./warden_cache"; cacheDirectory_ = "./warden_cache";
} #endif
// Create cache directory if it doesn't exist // Create cache directory if it doesn't exist
std::filesystem::create_directories(cacheDirectory_); std::filesystem::create_directories(cacheDirectory_);

View file

@ -3,6 +3,7 @@
#include <exception> #include <exception>
#include <csignal> #include <csignal>
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#ifdef __linux__
#include <X11/Xlib.h> #include <X11/Xlib.h>
// Keep a persistent X11 connection for emergency mouse release in signal handlers. // Keep a persistent X11 connection for emergency mouse release in signal handlers.
@ -16,6 +17,9 @@ static void releaseMouseGrab() {
XFlush(g_emergencyDisplay); XFlush(g_emergencyDisplay);
} }
} }
#else
static void releaseMouseGrab() {}
#endif
static void crashHandler(int sig) { static void crashHandler(int sig) {
releaseMouseGrab(); releaseMouseGrab();
@ -24,7 +28,9 @@ static void crashHandler(int sig) {
} }
int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
#ifdef __linux__
g_emergencyDisplay = XOpenDisplay(nullptr); g_emergencyDisplay = XOpenDisplay(nullptr);
#endif
std::signal(SIGSEGV, crashHandler); std::signal(SIGSEGV, crashHandler);
std::signal(SIGABRT, crashHandler); std::signal(SIGABRT, crashHandler);
std::signal(SIGFPE, crashHandler); std::signal(SIGFPE, crashHandler);
@ -46,7 +52,9 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
app.shutdown(); app.shutdown();
LOG_INFO("Application exited successfully"); LOG_INFO("Application exited successfully");
#ifdef __linux__
if (g_emergencyDisplay) { XCloseDisplay(g_emergencyDisplay); g_emergencyDisplay = nullptr; } if (g_emergencyDisplay) { XCloseDisplay(g_emergencyDisplay); g_emergencyDisplay = nullptr; }
#endif
return 0; return 0;
} }
catch (const std::exception& e) { catch (const std::exception& e) {