diff --git a/toolchain/Containerfile b/toolchain/Containerfile new file mode 100644 index 0000000..32e5ba0 --- /dev/null +++ b/toolchain/Containerfile @@ -0,0 +1,6 @@ +FROM registry.fedoraproject.org/fedora:43 +RUN dnf -y install --setopt=install_weak_deps=False \ + mingw32-gcc mingw32-gcc-c++ mingw32-winpthreads-static mingw32-headers mingw32-crt \ + mingw32-binutils meson ninja-build glslang git python3 make cmake file diffutils patch which findutils \ + && dnf clean all +WORKDIR /work diff --git a/toolchain/build.sh b/toolchain/build.sh new file mode 100755 index 0000000..b875eb6 --- /dev/null +++ b/toolchain/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Reproducible mingw build of the TRL VR mod (DXVK fork + d3d9 proxy + launcher). +# usage: build.sh [dxvk|proxy|all] +# source-tree: TRLVR layout (src/, dxvk-trlvr/, third_party/), mounted read-only. +# output-dir : receives dist/{d3d9.dll,d3d9_dxvk.dll,trlvr_launcher.exe}, work/, logs/ +# Env: FIXES= +# JOBS, EXTRA_CXXFLAGS, EXTRA_LIBS. Needs podman and image legendframe-mingw +# (podman build -t legendframe-mingw ). +set -euo pipefail +HERE=$(cd "$(dirname "$0")" && pwd) +SRC=$(readlink -f "${1:?source tree}"); OUT=$(readlink -m "${2:?output dir}"); WHAT=${3:-all} +mkdir -p "$OUT" +FIXMNT=() +if [ -n "${FIXES:-}" ]; then FIXMNT=(-v "$(readlink -f "$FIXES"):/fixes:ro,Z"); fi +podman image exists localhost/legendframe-mingw || podman build -t legendframe-mingw "$HERE" +exec podman run --rm -e JOBS="${JOBS:-}" -e WHAT="$WHAT" -e EXTRA_CXXFLAGS="${EXTRA_CXXFLAGS:-}" -e EXTRA_LIBS="${EXTRA_LIBS:-}" \ + -v "$SRC:/src:ro,Z" -v "$OUT:/out:Z" -v "$HERE/shims:/shims:ro,Z" -v "$HERE/container-build.sh:/container-build.sh:ro,Z" \ + "${FIXMNT[@]}" localhost/legendframe-mingw bash /container-build.sh diff --git a/toolchain/container-build.sh b/toolchain/container-build.sh new file mode 100755 index 0000000..abdf58a --- /dev/null +++ b/toolchain/container-build.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# Runs INSIDE the legendframe-mingw container. Invoked by build.sh. +# /src read-only source tree (TRLVR layout: src/, dxvk-trlvr/, third_party/) +# /out writable output dir +# /shims read-only toolchain/shims +# /fixes optional read-only dir of *.patch files applied to the COPY (patch -p1) +# Env: JOBS, WHAT=all|dxvk|proxy +set -euo pipefail +JOBS=${JOBS:-$(nproc)} +WHAT=${WHAT:-all} +W=/out/work +mkdir -p "$W" /out/dist /out/logs + +ts() { date +%s; } + +if [ "$WHAT" = all ] || [ "$WHAT" = dxvk ]; then + t0=$(ts) + echo "=== DXVK: copy tree" + rm -rf "$W/dxvk-trlvr" "$W/b-dxvk" + cp -r /src/dxvk-trlvr "$W/dxvk-trlvr"; rm -rf "$W/dxvk-trlvr/build-win32" + for p in /fixes/dxvk-*.patch; do [ -f "$p" ] && (cd "$W/dxvk-trlvr" && patch -p1 < "$p"); done || true + cd "$W/dxvk-trlvr" + # Same options as build_dxvk.bat (d3d9 only), mingw cross file, asserts off. + meson setup --backend ninja --cross-file build-win32.txt --buildtype release \ + -Denable_d3d8=false -Denable_d3d10=false -Denable_d3d11=false -Denable_dxgi=false \ + -Db_ndebug=if-release "$W/b-dxvk" . + ninja -C "$W/b-dxvk" -j"$JOBS" + i686-w64-mingw32-strip -o /out/dist/d3d9_dxvk.dll "$W/b-dxvk/src/d3d9/d3d9.dll" + echo "DXVK_SECONDS=$(( $(ts) - t0 ))" +fi + +if [ "$WHAT" = all ] || [ "$WHAT" = proxy ]; then + t0=$(ts) + echo "=== proxy + launcher: copy tree" + rm -rf "$W/src" "$W/obj" "$W/third_party"; mkdir -p "$W/obj" "$W/third_party" + cp -r /src/src "$W/src" + for d in openvr openxr imgui; do [ -d /src/third_party/$d ] && cp -r /src/third_party/$d "$W/third_party/"; done + for p in /fixes/proxy-*.patch; do [ -f "$p" ] && (cd "$W" && patch -p1 < "$p"); done || true + cd "$W" + # Struct-returning OpenVR virtuals: MSVC and GCC disagree on thiscall+sret, so route the + # calls through trlvr_abi::call_sret (see shims/seh_compat.h). Mechanical rewrite, diffed. + mkdir -p "$W/abi-orig"; rm -rf "$W/abi-orig/src"; cp -r "$W/src" "$W/abi-orig/src" + SRET='GetEyeToHeadTransform|GetProjectionMatrix|GetSeatedZeroPoseToStandingAbsoluteTrackingPose|GetRawZeroPoseToStandingAbsoluteTrackingPose|GetMatrix34TrackedDeviceProperty|GetHiddenAreaMesh|GetCurrentFadeColor|GetHeadsetViewMode' + find "$W/src" -name '*.cpp' -o -name '*.h' | xargs sed -i -E \ + "s/\b([A-Za-z_][A-Za-z0-9_]*)->($SRET)\(\)/trlvr_abi::call_sret(\1, \&std::remove_pointer_t::\2)/g; \ + s/\b([A-Za-z_][A-Za-z0-9_]*)->($SRET)\(/trlvr_abi::call_sret(\1, \&std::remove_pointer_t::\2, /g" + (cd "$W" && diff -ru abi-orig/src src > /out/abi-fix.diff) || true + echo "ABI rewrite sites: $(grep -c '^+.*call_sret' /out/abi-fix.diff || true)" + # Source list, defines and include dirs come straight from build.bat. + SRCS=$(grep -E '^set SRC=%SRC% "%ROOT%[^"]+\.cpp"' /src/build.bat | sed -E 's/.*%ROOT%([^"]+)"/\1/; s#\\#/#g') + IMGUI_SRCS="" + if [ -d "$W/third_party/imgui" ]; then + for f in $(grep -oE 'for %%F in \(([a-z0-9_ ]+)\)' /src/build.bat | sed -E 's/.*\((.*)\)/\1/'); do IMGUI_SRCS="$IMGUI_SRCS third_party/imgui/$f.cpp"; done + fi + DEFS=$(grep -E '^set CFLAGS=' /src/build.bat | grep -oE '/D[A-Za-z_0-9]+' | sed 's#^/D#-D#' | tr '\n' ' ') + INC="-I$W/third_party/openvr/headers -I$W/third_party/openxr/include -I$W/third_party/imgui -I/src/dxvk-trlvr/src/d3d9 -I/src/dxvk-trlvr/include/vulkan/include" + # -include seh_compat.h: MSVC __try/__except shim + _ReturnAddress + sret helper; sources stay unmodified. + CXXFLAGS="-std=gnu++17 -O2 $DEFS -fno-strict-aliasing -include /shims/seh_compat.h $INC ${EXTRA_CXXFLAGS:-}" + echo "sources: $(echo $SRCS $IMGUI_SRCS | wc -w); defs: $DEFS" + objs=(); pids=() + for s in $SRCS $IMGUI_SRCS; do + s=${s%.cpp} + o="$W/obj/$(echo $s | tr / _).o"; objs+=("$o") + f="$W/$s.cpp"; [ -f "$f" ] || f="$W/src/${s#src/}.cpp" + i686-w64-mingw32-g++ $CXXFLAGS -c "$f" -o "$o" > "${o%.o}.log" 2>&1 & + pids+=($!) + while [ "$(jobs -r | wc -l)" -ge "$JOBS" ]; do sleep 0.2; done + done + rc=0; for p in "${pids[@]}"; do wait "$p" || rc=1; done + cat "$W"/obj/*.log | grep -E "error|Error" | head -80 || true + [ $rc = 0 ] || { echo "PROXY COMPILE FAILED"; exit 1; } + # GNU ld wants a name after a bare LIBRARY keyword (MSVC link does not). + sed 's/^LIBRARY[[:space:]]*$/LIBRARY "d3d9.dll"/' "$W/src/proxy/d3d9.def" > "$W/d3d9.mingw.def" + XRLIB=""; [ -f "$W/third_party/openxr/lib/win32/openxr_loader.lib" ] && XRLIB="$W/third_party/openxr/lib/win32/openxr_loader.lib" + # Not linking d3d9: this DLL is d3d9.dll. --kill-at = undecorated stdcall exports like MSVC. + i686-w64-mingw32-g++ -shared -static -static-libgcc -static-libstdc++ -s \ + -o /out/dist/d3d9.dll "${objs[@]}" "$W/d3d9.mingw.def" $XRLIB \ + -Wl,--kill-at -Wl,--enable-stdcall-fixup -Wl,--subsystem,windows -Wl,--large-address-aware \ + -Wl,-Map,/out/dist/d3d9.map \ + -ldxguid -luser32 -lkernel32 -ladvapi32 -lgdi32 -ldwmapi ${EXTRA_LIBS:-} + # Launcher: its own exe (large-address-aware bit must be in the file). + i686-w64-mingw32-g++ -std=gnu++17 -O2 -DWIN32 -DNDEBUG -D_CRT_SECURE_NO_WARNINGS \ + -include /shims/seh_compat.h -static -static-libgcc -static-libstdc++ -s \ + -o /out/dist/trlvr_launcher.exe "$W/src/launcher/main.cpp" \ + -mwindows -municode -Wl,--large-address-aware -luser32 -lkernel32 -lshell32 + # d3d9.dll links openxr_loader statically (MSVC delay-loads it): the loader must ship beside it. + if [ -f /src/third_party/openxr/bin/win32/openxr_loader.dll ]; then + cp /src/third_party/openxr/bin/win32/openxr_loader.dll /out/dist/ + echo "NOTE: dist/openxr_loader.dll must be installed next to d3d9.dll (static import)" + fi + echo "PROXY_SECONDS=$(( $(ts) - t0 ))" +fi +ls -l /out/dist diff --git a/toolchain/run-trio.sh b/toolchain/run-trio.sh new file mode 100755 index 0000000..1cdf833 --- /dev/null +++ b/toolchain/run-trio.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# usage: run-trio.sh