Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f1fe8d8
Pin recomp-ui for reproducible CI builds
Zection6V Aug 17, 2026
ecabcc1
Add Nightly release payload verifier
Zection6V Aug 17, 2026
fef5e8f
Add portable Linux AppImage packager
Zection6V Aug 17, 2026
44a7b99
Add ROM-free runner fallback patch
Zection6V Aug 17, 2026
95d511c
Fix ROM-free patch idempotent anchors
Zection6V Aug 17, 2026
fcb87fb
Add redistributable FreeBIOS bank builder
Zection6V Aug 17, 2026
05fa97c
Document portable optimization cache directory
Zection6V Aug 17, 2026
6be471d
Use portable-first cache root in Linux package
Zection6V Aug 17, 2026
4e722e5
Add ROM-free Windows Nightly packager
Zection6V Aug 17, 2026
f4c11be
Add ROM-free Windows build workflow
Zection6V Aug 17, 2026
bf87a3e
Add ROM-free Linux build workflow
Zection6V Aug 17, 2026
2dc1dc3
Add cross-platform ROM-free build CI
Zection6V Aug 17, 2026
c5a90ea
Add ROM-free Nightly release workflow
Zection6V Aug 17, 2026
1b1c409
Document local optimization cache architecture
Zection6V Aug 17, 2026
095e1b5
Document ROM-free Nightly execution model
Zection6V Aug 17, 2026
86823e7
Require portable cache contract in Windows Nightly
Zection6V Aug 17, 2026
e946c1e
Ignore local optimization cache
Zection6V Aug 17, 2026
901d4f0
Add ROM-free source policy check
Zection6V Aug 17, 2026
1da2470
Gate CI on ROM-free release policy
Zection6V Aug 17, 2026
0fcfd9c
Record ROM-free Nightly architecture
Zection6V Aug 17, 2026
27fed60
Restore README and append ROM-free Nightly notes
Zection6V Aug 17, 2026
5ce8a46
Restore bring-up ledger and append ROM-free Nightly notes
Zection6V Aug 17, 2026
d8c9683
Fix ROM-free patch idempotency
Zection6V Aug 17, 2026
3acd616
Preserve existing ignores for local cache
Zection6V Aug 17, 2026
ceafcc7
Run Linux packager through bash
Zection6V Aug 17, 2026
4b9ad7c
Fix Windows Nightly PowerShell packaging
Zection6V Aug 17, 2026
dc95926
Gate Nightly helper syntax in build CI
Zection6V Aug 17, 2026
c19611d
Fix delegated multi-ROM status in launcher UI
Zection6V Aug 17, 2026
89731ae
Delegate launcher ROM verification to runtime detector
Zection6V Aug 17, 2026
cd8d6f5
Keep profile defaults while patching real launcher UI
Zection6V Aug 17, 2026
6cf124f
Show runtime-detected region in generic Nightly launcher
Zection6V Aug 17, 2026
66bdd9c
Gate launcher multi-ROM UI patch syntax
Zection6V Aug 17, 2026
1291373
Fix launcher default ROM selection when file is absent
Zection6V Aug 17, 2026
ae79be2
Require readable ROM before delegated validation display
Zection6V Aug 17, 2026
13a2634
Assert launcher starts with no absent default ROM
Zection6V Aug 17, 2026
e516c3b
Add launcher startup ROM selection regression check
Zection6V Aug 17, 2026
e46806c
Document fresh-launch ROM selection assertion
Zection6V Aug 17, 2026
06baba5
Reuse cached MSYS2 on Windows CI
Zection6V Aug 17, 2026
cc95024
Add persistent ccache for Windows CI
Zection6V Aug 17, 2026
15e0173
Avoid full MSYS2 upgrade in Windows CI
Zection6V Aug 17, 2026
d617f75
Trim Windows CI MinGW dependency install
Zection6V Aug 17, 2026
35bc28c
Add persistent ccache to Linux CI
Zection6V Aug 17, 2026
892e108
Add MPH runtime diagnostics and ROM-free multi-ROM gate
Zection6V Aug 17, 2026
cece5e3
Apply MPH diagnostics after runtime patch stack
Zection6V Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Build Linux

on:
workflow_call:
workflow_dispatch:

permissions:
contents: read

jobs:
linux:
name: Linux ROM-free build
runs-on: ubuntu-24.04
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 750M
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache

steps:
- name: Check out title sources
uses: actions/checkout@v4

- name: Install build dependencies
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential cmake ninja-build git curl ca-certificates ccache \
libsdl2-dev libgl1-mesa-dev libx11-dev libxext-dev libxrandr-dev \
libxcursor-dev libxi-dev libxinerama-dev libwayland-dev

- name: Restore compiler cache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.ccache
key: >-
mph-linux-x86_64-ccache-v1-${{ hashFiles(
'ndsrecomp.pin',
'config/mph_rom_profiles.json',
'tools/patch_ndsrecomp_mph_runtime.py',
'tools/patch_ndsrecomp_rom_free_release.py'
) }}
restore-keys: |
mph-linux-x86_64-ccache-v1-

- name: Configure compiler cache
run: |
set -euo pipefail
ccache --version
ccache --set-config=max_size="$CCACHE_MAXSIZE"
ccache --zero-stats

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Fetch pinned ndsrecomp
run: |
set -euo pipefail
nds_pin="$(tr -d '\r\n' < ndsrecomp.pin)"
git clone --filter=blob:none https://github.com/mstan/ndsrecomp.git ../ndsrecomp
git -C ../ndsrecomp checkout --detach "$nds_pin"
git -C ../ndsrecomp submodule update --init --recursive
test "$(git -C ../ndsrecomp rev-parse HEAD)" = "$nds_pin"

- name: Apply MPH and ROM-free runner integration
run: |
set -euo pipefail
python tools/patch_ndsrecomp_mph_runtime.py \
--framework-root ../ndsrecomp \
--profiles config/mph_rom_profiles.json
python tools/patch_ndsrecomp_rom_free_release.py \
--framework-root ../ndsrecomp
python tools/patch_ndsrecomp_mph_runtime.py \
--framework-root ../ndsrecomp \
--profiles config/mph_rom_profiles.json
python tools/patch_ndsrecomp_rom_free_release.py \
--framework-root ../ndsrecomp

- name: Generate redistributable FreeBIOS banks
run: |
set -euo pipefail
python tools/ci/prepare_freebios_banks.py \
--framework-root ../ndsrecomp \
--build-dir ../ndsrecomp/build-freebios-recompiler
test ! -e ../ndsrecomp/generated/arm9_bios.c
test ! -e ../ndsrecomp/generated/arm7_bios.c

- name: Build ROM-free runner
run: |
set -euo pipefail
cmake -S ../ndsrecomp/runner -B ../ndsrecomp/runner/build-mph-nightly \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DNDS_BOOTSTRAP_FIRMWARE=ON \
-DNDS_RETAIL_BIOS_BANKS=OFF \
-DNDS_ENABLE_COMPUTE_RENDERER=ON
cmake --build ../ndsrecomp/runner/build-mph-nightly
test -x ../ndsrecomp/runner/build-mph-nightly/nds_runner
if grep -a -E -q 'g_dispatch_mph_arm(9|7)|mph_arm9_fmv_runtime' \
../ndsrecomp/runner/build-mph-nightly/nds_runner; then
echo 'ROM-derived MPH title bank leaked into ROM-free runner' >&2
exit 1
fi

- name: Install pinned AppImage packaging tools
run: |
set -euo pipefail
mkdir -p .ci-tools
curl -fL --retry 3 \
https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage \
-o .ci-tools/appimagetool
echo 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 .ci-tools/appimagetool' | sha256sum -c -
curl -fL --retry 3 \
https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage \
-o .ci-tools/linuxdeploy
echo '421ca71d5c69ea97c6309276232990d43df1dcece0edfaa26bbf926ff96ed12e .ci-tools/linuxdeploy' | sha256sum -c -
chmod +x .ci-tools/appimagetool .ci-tools/linuxdeploy

- name: Package Linux Nightly payload
run: |
set -euo pipefail
version="$(sed -n 's/^project(MetroidPrimeHuntersRecomp VERSION \([0-9.]*\).*/\1/p' CMakeLists.txt)"
test -n "$version"
bash tools/package-linux-appimage.sh \
--version "$version" \
--runner ../ndsrecomp/runner/build-mph-nightly/nds_runner \
--appimage-tool "$PWD/.ci-tools/appimagetool" \
--linuxdeploy "$PWD/.ci-tools/linuxdeploy"
image="release-stage/MetroidPrimeHuntersRecomp-linux-v${version}-x86_64.AppImage"
test -s "$image"
mkdir -p /tmp/mph-appimage-audit
(cd /tmp/mph-appimage-audit && "$GITHUB_WORKSPACE/$image" --appimage-extract >/dev/null)
if find /tmp/mph-appimage-audit/squashfs-root -type f \( \
-iname '*.nds' -o -iname '*.sav' -o -iname '*.dsv' -o \
-iname 'biosnds9.rom' -o -iname 'biosnds7.rom' -o -iname 'firmware.bin' \
\) -print -quit | grep -q .; then
echo 'Forbidden ROM/save/BIOS/firmware material found inside AppImage' >&2
exit 1
fi

- name: Show compiler cache stats
if: always()
run: |
if command -v ccache >/dev/null 2>&1; then
ccache --show-stats || true
fi

- name: Upload Linux Nightly payload
uses: actions/upload-artifact@v4
with:
name: mph-nightly-linux
path: release-stage/MetroidPrimeHuntersRecomp-linux-v*-x86_64.AppImage
if-no-files-found: error
retention-days: 14
204 changes: 204 additions & 0 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
name: Build Windows

on:
workflow_call:
workflow_dispatch:

permissions:
contents: read

jobs:
windows:
name: Windows ROM-free build
runs-on: windows-2025
env:
# Keep compiler output reusable across PR iterations. ccache validates the
# compiler command/source content itself; this directory is only a cache,
# never a source of build truth.
CCACHE_DIR: ${{ github.workspace }}\.ccache
CCACHE_MAXSIZE: 750M
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
# Hosted CMake is a native Windows binary. Point it at MinGW's package
# prefix so SDL2's CMake package remains discoverable without installing
# a second copy of CMake inside MSYS2.
CMAKE_PREFIX_PATH: C:\msys64\mingw64

steps:
- name: Check out title sources
uses: actions/checkout@v4

- name: Install MinGW build dependencies
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
# windows-2025 already includes C:\msys64 plus native Git, CMake,
# Ninja and Python. Inherit the hosted PATH and install only the
# MinGW-specific compiler/runtime dependencies we actually need.
path-type: inherit
release: false
update: false
cache: true
install: >-
mingw-w64-x86_64-gcc
mingw-w64-x86_64-SDL2
mingw-w64-x86_64-ccache

- name: Restore compiler cache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}\.ccache
key: >-
mph-windows-mingw64-ccache-v1-${{ hashFiles(
'ndsrecomp.pin',
'recomp-ui.pin',
'launcher/recomp-ui/**',
'tools/patch_ndsrecomp_mph_runtime.py',
'tools/patch_ndsrecomp_rom_free_release.py',
'tools/patch_recomp_ui_mph_multirom.py'
) }}
restore-keys: |
mph-windows-mingw64-ccache-v1-

- name: Verify hosted build tools
shell: msys2 {0}
run: |
set -euo pipefail
command -v git
command -v python
command -v cmake
command -v ninja
command -v gcc
command -v ccache
git --version
python --version
cmake --version | head -n1
ninja --version
gcc --version | head -n1

- name: Configure compiler cache
shell: msys2 {0}
run: |
set -euo pipefail
ccache --version
ccache --set-config=max_size="$CCACHE_MAXSIZE"
ccache --zero-stats

- name: Fetch pinned ndsrecomp and recomp-ui
shell: msys2 {0}
run: |
set -euo pipefail
nds_pin="$(tr -d '\r\n' < ndsrecomp.pin)"
ui_pin="$(tr -d '\r\n' < recomp-ui.pin)"
git clone --filter=blob:none https://github.com/mstan/ndsrecomp.git ../ndsrecomp
git -C ../ndsrecomp checkout --detach "$nds_pin"
git -C ../ndsrecomp submodule update --init --recursive
git clone --filter=blob:none https://github.com/mstan/recomp-ui.git ../recomp-ui
git -C ../recomp-ui checkout --detach "$ui_pin"
test "$(git -C ../ndsrecomp rev-parse HEAD)" = "$nds_pin"
test "$(git -C ../recomp-ui rev-parse HEAD)" = "$ui_pin"

- name: Apply MPH and ROM-free runner integration
shell: msys2 {0}
run: |
set -euo pipefail
python tools/patch_ndsrecomp_mph_runtime.py \
--framework-root ../ndsrecomp \
--profiles config/mph_rom_profiles.json
python tools/patch_ndsrecomp_rom_free_release.py \
--framework-root ../ndsrecomp
# Both patch stacks are required to be idempotent.
python tools/patch_ndsrecomp_mph_runtime.py \
--framework-root ../ndsrecomp \
--profiles config/mph_rom_profiles.json
python tools/patch_ndsrecomp_rom_free_release.py \
--framework-root ../ndsrecomp

- name: Generate redistributable FreeBIOS banks
shell: msys2 {0}
run: |
set -euo pipefail
python tools/ci/prepare_freebios_banks.py \
--framework-root ../ndsrecomp \
--build-dir ../ndsrecomp/build-freebios-recompiler
test ! -e ../ndsrecomp/generated/arm9_bios.c
test ! -e ../ndsrecomp/generated/arm7_bios.c

- name: Build ROM-free runner
shell: msys2 {0}
run: |
set -euo pipefail
cmake -S ../ndsrecomp/runner -B ../ndsrecomp/runner/build-mph-nightly \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DNDS_BOOTSTRAP_FIRMWARE=ON \
-DNDS_RETAIL_BIOS_BANKS=OFF \
-DNDS_ENABLE_COMPUTE_RENDERER=ON
cmake --build ../ndsrecomp/runner/build-mph-nightly
test -s ../ndsrecomp/runner/build-mph-nightly/nds_runner.exe
if grep -a -E -q 'g_dispatch_mph_arm(9|7)|mph_arm9_fmv_runtime' \
../ndsrecomp/runner/build-mph-nightly/nds_runner.exe; then
echo 'ROM-derived MPH title bank leaked into ROM-free runner' >&2
exit 1
fi

- name: Build and test launcher
shell: msys2 {0}
run: |
set -euo pipefail
cmake -S launcher/recomp-ui -B launcher/recomp-ui/build-nightly \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DNDSRECOMP_ROOT="$PWD/../ndsrecomp" \
-DRECOMP_UI_ROOT="$PWD/../recomp-ui" \
'-DMPH_LAUNCHER_REGION=Auto (runtime detected)'
cmake --build launcher/recomp-ui/build-nightly
ctest --test-dir launcher/recomp-ui/build-nightly --output-on-failure
test -s launcher/recomp-ui/build-nightly/mph-recomp-ui.exe
grep -q 'game.region = "Auto (runtime detected)";' \
launcher/recomp-ui/build-nightly/launcher_main_profile.cpp
grep -q 'game.known_sha1_hex = nullptr;' \
launcher/recomp-ui/build-nightly/launcher_main_profile.cpp
# Fresh ROM-free extraction must start with no ROM selected. The
# conventional filename is auto-selected only when the file exists.
grep -q 'std::filesystem::is_regular_file(default_rom, initial_rom_error)' \
launcher/recomp-ui/build-nightly/launcher_main_profile.cpp
grep -q 'exe.string().c_str(), initial_rom.c_str(),' \
launcher/recomp-ui/build-nightly/launcher_main_profile.cpp
grep -q 'MPH_MULTIROM_DELEGATED_VERIFY' \
../recomp-ui/src/common/backends/imgui/launcher_imgui.cpp
grep -q 'const bool readable = m->rom_present && std::strcmp(m->rom_size, "--") != 0;' \
../recomp-ui/src/common/backends/imgui/launcher_imgui.cpp

- name: Package Windows Nightly payload
shell: msys2 {0}
run: |
set -euo pipefail
version="$(sed -n 's/^project(MetroidPrimeHuntersRecomp VERSION \([0-9.]*\).*/\1/p' CMakeLists.txt)"
test -n "$version"
runtime_bin="$(cygpath -w /mingw64/bin)"
ps_exe="$(cygpath -u "${SYSTEMROOT}")/System32/WindowsPowerShell/v1.0/powershell.exe"
script_path="$(cygpath -w "$PWD/tools/package-windows-nightly.ps1")"
test -x "$ps_exe"
"$ps_exe" -NoProfile -ExecutionPolicy Bypass -File "$script_path" \
-Version "$version" \
-RunnerBuildDir '..\ndsrecomp\runner\build-mph-nightly' \
-LauncherBuildDir 'launcher\recomp-ui\build-nightly' \
-RuntimeBinDir "$runtime_bin"
test -s "release-stage/MetroidPrimeHuntersRecomp-windows-x64-v${version}.zip"

- name: Show compiler cache stats
if: always()
shell: msys2 {0}
run: |
if command -v ccache >/dev/null 2>&1; then
ccache --show-stats || true
fi

- name: Upload Windows Nightly payload
uses: actions/upload-artifact@v4
with:
name: mph-nightly-windows
path: release-stage/MetroidPrimeHuntersRecomp-windows-x64-v*.zip
if-no-files-found: error
retention-days: 14
Loading
Loading