Add multi-ROM base detection and EU1.1 bring-up infrastructure #65
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: MPH Multi-ROM Static Checks | |
| on: | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| static-multirom: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout project | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Check Python syntax | |
| run: | | |
| python -m py_compile \ | |
| tools/mph_profile.py \ | |
| tools/prepare_mph.py \ | |
| tools/check_mph_multirom_profiles.py \ | |
| tools/patch_ndsrecomp_mph_runtime.py \ | |
| tools/promote_mph_static_coverage.py \ | |
| tools/promote_mph_runtime_coverage.py \ | |
| tools/benchmark_mph_fmv.py \ | |
| tools/fuzz_mph_gameplay.py \ | |
| tools/capture_mph_checkpoints.py | |
| - name: Check shell syntax | |
| run: bash -n tools/build-linux.sh | |
| - name: Check PowerShell syntax | |
| shell: pwsh | |
| run: | | |
| $failed = $false | |
| foreach ($path in @('tools/build-windows.ps1', 'tools/make_release.ps1')) { | |
| $tokens = $null | |
| $errors = $null | |
| [System.Management.Automation.Language.Parser]::ParseFile( | |
| (Resolve-Path $path), [ref]$tokens, [ref]$errors) | Out-Null | |
| if ($errors.Count -ne 0) { | |
| $failed = $true | |
| Write-Error "$path has PowerShell parser errors:`n$($errors | Out-String)" | |
| } | |
| } | |
| if ($failed) { exit 1 } | |
| - name: Cross-check runtime addresses against melonPrimeDS | |
| run: | | |
| curl -fsSL --retry 3 \ | |
| https://raw.githubusercontent.com/ag-advania/melonPrimeDS/main/src/frontend/qt_sdl/MelonPrimeGameRomAddrTable.h \ | |
| -o /tmp/MelonPrimeGameRomAddrTable.h | |
| python tools/check_mph_multirom_profiles.py \ | |
| --melonprime-table /tmp/MelonPrimeGameRomAddrTable.h | |
| - name: Test profile-aware coverage promotion | |
| run: | | |
| mkdir -p /tmp/eu-inputs | |
| cat > /tmp/eu-inputs/arm9.toml <<'EOF' | |
| [program] | |
| id = "mph_amhp1_arm9" | |
| load_address = 0x02004000 | |
| size = 0x00001000 | |
| entry_pc = 0x02004000 | |
| EOF | |
| cat > /tmp/eu-inputs/arm7.toml <<'EOF' | |
| [program] | |
| id = "mph_amhp1_arm7" | |
| load_address = 0x02380000 | |
| size = 0x00001000 | |
| entry_pc = 0x02380000 | |
| EOF | |
| cat > /tmp/eu-trace.json <<'EOF' | |
| { | |
| "mph_profile": "EU1_1", | |
| "rom_sha1": "bdcd1dea293e24c98d4c481430e90d21198985a5", | |
| "scenario": "scenarios/adventure_start.json", | |
| "static_coverage": {"tier3_entries9": 3}, | |
| "tier3_coverage": {"entries": [ | |
| {"cpu": 9, "pc": 33570816, "thumb": 0, "kind": 2, "hits": 99}, | |
| {"cpu": 9, "pc": 33570848, "thumb": 0, "kind": 2, "hits": 7}, | |
| {"cpu": 9, "pc": 33579008, "thumb": 0, "kind": 2, "hits": 5}, | |
| {"cpu": 7, "pc": 37224480, "thumb": 0, "kind": 3, "hits": 4} | |
| ]} | |
| } | |
| EOF | |
| python tools/promote_mph_static_coverage.py \ | |
| --version EU1_1 \ | |
| --inputs /tmp/eu-inputs \ | |
| --trace /tmp/eu-trace.json \ | |
| --out /tmp/eu-coverage.json \ | |
| --runner-commit 0123456789abcdef0123456789abcdef01234567 | |
| python - <<'PY' | |
| import json | |
| p = json.load(open('/tmp/eu-coverage.json', encoding='utf-8')) | |
| assert p['profile'] == 'EU1_1' | |
| assert p['game_sha1'] == 'bdcd1dea293e24c98d4c481430e90d21198985a5' | |
| assert p['scenario'] == 'scenarios/adventure_start.json' | |
| assert p['main_image']['arm9']['end'] == '0x02005000' | |
| assert [e['addr'] for e in p['entry_points']['arm9']] == ['0x02004020'] | |
| assert [e['addr'] for e in p['entry_points']['arm7']] == ['0x02380020'] | |
| PY | |
| truncate -s $((0x00408000)) /tmp/eu-runtime.bin | |
| cat > /tmp/eu-before.json <<'EOF' | |
| { | |
| "mph_profile": "EU1_1", | |
| "rom_sha1": "bdcd1dea293e24c98d4c481430e90d21198985a5", | |
| "tier3_coverage": {"entries": [ | |
| {"cpu": 9, "pc": 33525760, "thumb": 0, "kind": 2, "caller": 1, "hits": 1} | |
| ]} | |
| } | |
| EOF | |
| cat > /tmp/eu-after.json <<'EOF' | |
| { | |
| "mph_profile": "EU1_1", | |
| "rom_sha1": "bdcd1dea293e24c98d4c481430e90d21198985a5", | |
| "runtime_capture": { | |
| "sha1": "2dacada9962037d4e1fa3099d9de8cdf616c318c", | |
| "bytes": 4227072 | |
| }, | |
| "tier3_coverage": {"entries": [ | |
| {"cpu": 9, "pc": 33525760, "thumb": 0, "kind": 2, "caller": 1, "hits": 2}, | |
| {"cpu": 9, "pc": 33525792, "thumb": 0, "kind": 3, "caller": 2, "hits": 3} | |
| ]} | |
| } | |
| EOF | |
| python tools/promote_mph_runtime_coverage.py \ | |
| --version EU1_1 \ | |
| --before-benchmark /tmp/eu-before.json \ | |
| --benchmark /tmp/eu-after.json \ | |
| --image /tmp/eu-runtime.bin \ | |
| --out /tmp/eu-runtime.toml | |
| grep -q 'Profile: EU1_1' /tmp/eu-runtime.toml | |
| grep -q 'Metroid Prime Hunters (Europe rev 1) ARM9 FMV runtime' /tmp/eu-runtime.toml | |
| grep -q 'id = "mph_amhp1_arm9_fmv_runtime"' /tmp/eu-runtime.toml | |
| ! grep -q 'Metroid Prime Hunters (USA) ARM9 FMV runtime' /tmp/eu-runtime.toml | |
| - name: Verify registry-driven profile and FMV routing | |
| run: | | |
| grep -q 'MPH_PROFILE_FMV_RUNTIME_BANK' CMakeLists.txt | |
| grep -q 'config/${MPH_PROFILE_FMV_RUNTIME_BANK}.toml' CMakeLists.txt | |
| grep -q 'capture/${MPH_PROFILE_FMV_RUNTIME_BANK}.bin' CMakeLists.txt | |
| ! grep -q -- '--bank mph_arm9_fmv_runtime' CMakeLists.txt | |
| grep -q 'MPH_PROFILE_KEYS' CMakeLists.txt | |
| ! grep -q 'PROPERTY STRINGS US1_0 EU1_1' CMakeLists.txt | |
| ! grep -q "ValidateSet('US1_0', 'EU1_1')" tools/build-windows.ps1 | |
| grep -q 'FMV_RUNTIME_BANK' tools/build-linux.sh | |
| grep -q 'FmvRuntimeBank' tools/build-windows.ps1 | |
| grep -q 'FmvRuntimeBank' tools/make_release.ps1 | |
| - name: Fetch exact pinned ndsrecomp revision | |
| run: | | |
| pin="$(tr -d '\r\n' < ndsrecomp.pin)" | |
| test "${#pin}" -eq 40 | |
| git init /tmp/ndsrecomp | |
| git -C /tmp/ndsrecomp remote add origin https://github.com/mstan/ndsrecomp.git | |
| git -C /tmp/ndsrecomp fetch --depth 1 origin "$pin" | |
| git -C /tmp/ndsrecomp checkout --detach FETCH_HEAD | |
| test "$(git -C /tmp/ndsrecomp rev-parse HEAD)" = "$pin" | |
| - name: Render US1.0 and EU1.1 launcher profiles | |
| run: | | |
| mkdir -p /tmp/fake-prefix/lib/cmake/SDL2 /tmp/fake-recomp-ui | |
| cat > /tmp/fake-prefix/lib/cmake/SDL2/SDL2Config.cmake <<'EOF' | |
| if(NOT TARGET SDL2::SDL2) | |
| add_library(SDL2::SDL2 INTERFACE IMPORTED) | |
| endif() | |
| EOF | |
| cat > /tmp/fake-recomp-ui/recomp_ui.cmake <<'EOF' | |
| function(recomp_target_launcher_ui) | |
| endfunction() | |
| EOF | |
| cmake -S launcher/recomp-ui -B /tmp/mph-launcher-us10 \ | |
| -DNDSRECOMP_ROOT=/tmp/ndsrecomp \ | |
| -DRECOMP_UI_ROOT=/tmp/fake-recomp-ui \ | |
| -DCMAKE_PREFIX_PATH=/tmp/fake-prefix | |
| cmake -S launcher/recomp-ui -B /tmp/mph-launcher-eu11 \ | |
| -DNDSRECOMP_ROOT=/tmp/ndsrecomp \ | |
| -DRECOMP_UI_ROOT=/tmp/fake-recomp-ui \ | |
| -DCMAKE_PREFIX_PATH=/tmp/fake-prefix \ | |
| -DMPH_LAUNCHER_ROM_SHA1=bdcd1dea293e24c98d4c481430e90d21198985a5 \ | |
| -DMPH_LAUNCHER_REGION=Europe \ | |
| '-DMPH_LAUNCHER_DEFAULT_ROM=Metroid Prime Hunters (Europe Rev 1).nds' \ | |
| -DMPH_LAUNCHER_ADAPTIVE_WIDESCREEN=OFF | |
| us=/tmp/mph-launcher-us10/launcher_main_profile.cpp | |
| eu=/tmp/mph-launcher-eu11/launcher_main_profile.cpp | |
| test -f "$us" -a -f "$eu" | |
| grep -q '90164d1ac127ee5f9815ea4ae7de798c7b5fc629' "$us" | |
| grep -q 'game.region = "USA";' "$us" | |
| grep -q 'bool adaptive_widescreen = true;' "$us" | |
| grep -A1 'int mod_feature_count' "$us" | grep -q 'return 2;' | |
| grep -q 'adaptive = adaptive && true;' "$us" | |
| grep -q 'bdcd1dea293e24c98d4c481430e90d21198985a5' "$eu" | |
| grep -q 'game.region = "Europe";' "$eu" | |
| grep -q 'exe / "Metroid Prime Hunters (Europe Rev 1).nds";' "$eu" | |
| grep -q 'bool adaptive_widescreen = false;' "$eu" | |
| grep -A1 'int mod_feature_count' "$eu" | grep -q 'return 1;' | |
| grep -q 'if (!false) ++index;' "$eu" | |
| grep -q 'adaptive = adaptive && false;' "$eu" | |
| - name: Verify Linux profile-owned launch policy | |
| run: | | |
| grep -q 'launcher_default_rom' tools/build-linux.sh | |
| grep -q 'fmv_runtime_bank' tools/build-linux.sh | |
| grep -q 'cp "$GAME_CONFIG" "$APPDIR/usr/share/mph-recomp/game.toml"' \ | |
| tools/build-linux.sh | |
| grep -q -- '--config "$HERE/usr/share/mph-recomp/game.toml"' \ | |
| tools/build-linux.sh | |
| ! grep -q -- '--adaptive-widescreen' tools/build-linux.sh | |
| - name: Verify ndsrecomp runtime patch is idempotent | |
| run: | | |
| python tools/patch_ndsrecomp_mph_runtime.py \ | |
| --framework-root /tmp/ndsrecomp \ | |
| --profiles config/mph_rom_profiles.json | |
| sha256sum \ | |
| /tmp/ndsrecomp/runner/src/mph_runtime_profiles.generated.h \ | |
| /tmp/ndsrecomp/runner/src/title_patches.h \ | |
| /tmp/ndsrecomp/runner/src/title_patches.cpp \ | |
| /tmp/ndsrecomp/runner/src/frontend.cpp \ | |
| /tmp/ndsrecomp/runner/src/main.cpp \ | |
| > /tmp/first.sha256 | |
| python tools/patch_ndsrecomp_mph_runtime.py \ | |
| --framework-root /tmp/ndsrecomp \ | |
| --profiles config/mph_rom_profiles.json | |
| sha256sum \ | |
| /tmp/ndsrecomp/runner/src/mph_runtime_profiles.generated.h \ | |
| /tmp/ndsrecomp/runner/src/title_patches.h \ | |
| /tmp/ndsrecomp/runner/src/title_patches.cpp \ | |
| /tmp/ndsrecomp/runner/src/frontend.cpp \ | |
| /tmp/ndsrecomp/runner/src/main.cpp \ | |
| > /tmp/second.sha256 | |
| diff -u /tmp/first.sha256 /tmp/second.sha256 | |
| grep -q '0x020DB138u, 0x020DEE46u, 0x020DEE4Eu' \ | |
| /tmp/ndsrecomp/runner/src/mph_runtime_profiles.generated.h | |
| grep -q 'bdcd1dea293e24c98d4c481430e90d21198985a5' \ | |
| /tmp/ndsrecomp/runner/src/mph_runtime_profiles.generated.h | |
| grep -q 'nds_title_patches_select_mph_runtime_profile' \ | |
| /tmp/ndsrecomp/runner/src/main.cpp | |
| grep -q 'nds_title_patches_mph_in_ball' \ | |
| /tmp/ndsrecomp/runner/src/frontend.cpp | |
| ! grep -q 'kMphUs10MorphState' /tmp/ndsrecomp/runner/src/frontend.cpp | |
| ! grep -q 'kMphUs10AimX' /tmp/ndsrecomp/runner/src/title_patches.cpp | |
| ! grep -q 'kMphUs10AimY' /tmp/ndsrecomp/runner/src/title_patches.cpp | |
| - name: Compile patched runner translation units | |
| run: | | |
| mkdir -p /tmp/ndsrecomp/generated | |
| for source in \ | |
| arm9_bios.c arm9_bios_dispatch.c \ | |
| arm7_bios.c arm7_bios_dispatch.c \ | |
| freebios_arm9.c freebios_arm9_dispatch.c \ | |
| freebios_arm7.c freebios_arm7_dispatch.c; do | |
| : > "/tmp/ndsrecomp/generated/$source" | |
| done | |
| cmake -S /tmp/ndsrecomp/runner -B /tmp/nds-runner-profile-check \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DNDS_BOOTSTRAP_FIRMWARE=ON \ | |
| -DNDS_ENABLE_COMPUTE_RENDERER=OFF \ | |
| -DNDS_ENABLE_PCAP_BACKEND=OFF | |
| cmake --build /tmp/nds-runner-profile-check --target \ | |
| src/title_patches.o -j2 | |
| cmake --build /tmp/nds-runner-profile-check --target \ | |
| src/frontend.o -j2 | |
| cmake --build /tmp/nds-runner-profile-check --target \ | |
| src/main.o -j2 | |
| - name: Test exact-ROM runtime address dispatch | |
| run: | | |
| c++ -std=c++20 -Wall -Wextra -Wno-unused-parameter \ | |
| -I/tmp/ndsrecomp/runner/src \ | |
| -I/tmp/ndsrecomp/recompiler/armv4t \ | |
| tools/tests/mph_runtime_profile_test.cpp \ | |
| /tmp/ndsrecomp/runner/src/title_patches.cpp \ | |
| -o /tmp/mph-runtime-profile-test | |
| /tmp/mph-runtime-profile-test | |
| - name: Compile US1.0 and EU1.1 ROM checkers | |
| run: | | |
| for profile in US1_0 EU1_1; do | |
| build="/tmp/mph-romcheck-$profile" | |
| cmake -S . -B "$build" \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DNDSRECOMP_ROOT=/tmp/ndsrecomp \ | |
| -DMPH_VERSION="$profile" \ | |
| -DMPH_ROM="/tmp/nonexistent-$profile.nds" | |
| cmake --build "$build" --target mph_romcheck -j2 | |
| test -x "$build/MetroidPrimeHuntersRecomp" | |
| done | |
| strings /tmp/mph-romcheck-EU1_1/MetroidPrimeHuntersRecomp | \ | |
| grep -q bdcd1dea293e24c98d4c481430e90d21198985a5 | |
| strings /tmp/mph-romcheck-EU1_1/MetroidPrimeHuntersRecomp | \ | |
| grep -q EU1_1 | |
| - name: Diff sanity | |
| run: git diff --check |