Quest: make Quest 1 builds reproducible

This commit is contained in:
darthcircuit committed 2026-09-22 11:14:31 -06:00
1 parent 83fe04d160
commit 6e6406c200
9 files changed
+124 -17

No files matched your search

+11 -4
View File
@@ -1,7 +1,7 @@
# Builds the standalone Meta Quest APK on a Windows host.
#
# powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 [-Generated <dir>]
# [-Configuration debug|release] [-Install]
# [-Headset modern|quest1] [-Configuration debug|release] [-Install]
#
# One app offers both games: the APK carries a kit for the base game, and for Retro Rewind too
# when -Generated holds a translation that includes the mod.
@@ -20,6 +20,7 @@ param(
[string]$Generated = '',
[string]$Dependencies = '',
[string]$CMakeDir = '',
[ValidateSet('modern', 'quest1')] [string]$Headset = 'modern',
[ValidateSet('debug', 'release')] [string]$Configuration = 'debug',
[switch]$Install
)
@@ -73,7 +74,8 @@ if (-not $Dependencies) {
}
$variant = (Get-Culture).TextInfo.ToTitleCase($Configuration)
$task = "app:assemble$variant"
$flavour = if ($Headset -eq 'quest1') { 'Quest1' } else { 'ModernQuest' }
$task = "app:assemble$flavour$variant"
$gradleArgs = @(
'--project-dir', $root,
"-PmkwGeneratedDir=$Generated"
@@ -84,7 +86,8 @@ Write-Host "gradlew $($gradleArgs -join ' ')"
& (Join-Path $root 'gradlew.bat') @gradleArgs
if ($LASTEXITCODE -ne 0) { throw "Gradle failed ($LASTEXITCODE)" }
$apkDir = Join-Path $root "app\build\outputs\apk\$Configuration"
$flavourDir = if ($Headset -eq 'quest1') { 'quest1' } else { 'modernQuest' }
$apkDir = Join-Path $root "app\build\outputs\apk\$flavourDir\$Configuration"
$apk = Get-ChildItem -Path $apkDir -Filter '*.apk' | Select-Object -First 1
if (-not $apk) { throw "No APK under $apkDir" }
Write-Host "APK: $($apk.FullName)"
@@ -106,5 +109,9 @@ if ($Install) {
$adb = Join-Path $sdkRoot 'platform-tools\adb.exe'
& $adb install -r $apk.FullName
if ($LASTEXITCODE -ne 0) { throw "adb install failed ($LASTEXITCODE)" }
Write-Host 'Installed. Build a game with Build on this Quest in the launcher, or on this PC with android/Build-QuestGame.ps1 -Install.'
if ($Headset -eq 'quest1') {
Write-Host 'Installed. Use WiiCompiled Settings for setup, then launch WiiCompiled VR directly from the library.'
} else {
Write-Host 'Installed. Build a game with Build on this Quest in the launcher, or on this PC with android/Build-QuestGame.ps1 -Install.'
}
}
+26
View File
@@ -139,6 +139,32 @@ android {
}
}
flavorDimensions += "headset"
productFlavors {
create("modernQuest") {
dimension = "headset"
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest2|quest3|quest3s|questpro"
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "false")
externalNativeBuild {
cmake {
// Snapdragon XR2 and newer. Keep this as the default build target.
arguments += "-DMKW_ANDROID_CPU=cortex-a77"
}
}
}
create("quest1") {
dimension = "headset"
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest|quest2"
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "true")
externalNativeBuild {
cmake {
// Snapdragon 835. cortex-a77 binaries terminate with SIGILL on Quest 1.
arguments += "-DMKW_ANDROID_CPU=kryo"
}
}
}
}
externalNativeBuild {
cmake {
// aurora-main needs CMake 3.25+, newer than the SDK's bundled 3.22.1;
+3 -11
View File
@@ -37,12 +37,13 @@
android:label="@string/app_name"
android:theme="@style/Theme.WiiCompiled.Quest">
<meta-data android:name="com.oculus.supportedDevices" android:value="quest2|quest3|quest3s|questpro" />
<meta-data android:name="com.oculus.supportedDevices" android:value="${mkwQuestSupportedDevices}" />
<meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
<!-- Let the runtime move CPU budget to the GPU; Aurora's replay is GPU-bound. -->
<meta-data android:name="com.oculus.trade_cpu_for_gpu_amount" android:value="-1" />
<!-- The app opens on this 2D panel: Play starts QuestActivity, Settings edits Config.toml. -->
<!-- The setup panel: Play starts QuestActivity on modern Horizon OS; the
Quest 1 flavour exposes it as a separate Settings library entry. -->
<activity
android:name=".launcher.LauncherActivity"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|density"
@@ -55,10 +56,6 @@
android:defaultHeight="640dp"
android:minWidth="760dp"
android:minHeight="520dp" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Extracts the player's disc image and imports games built on a PC (launcher process). -->
@@ -84,11 +81,6 @@
<property
android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
@@ -585,6 +585,10 @@ class LauncherActivity : Activity() {
}
private fun play() {
if (BuildConfig.QUEST1_DIRECT_LAUNCH) {
Toast.makeText(this, R.string.home_quest1_launch_from_library, Toast.LENGTH_LONG).show()
return
}
if (launching) {
return
}
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WiiCompiled VR</string>
<string name="quest1_settings_name">WiiCompiled Settings</string>
<string name="missing_game_data_title">Game data not found</string>
<string name="missing_game_data_message">Copy the extracted DATA directory of your own PAL RMCP01 Mario Kart Wii disc to:\n\n%1$s\n\nFor example with adb: adb push DATA /sdcard/Android/data/org.wiicompiled.quest/files/WiiCompiledOpenXRVR/DATA\n\nThen start the app again.</string>
@@ -77,6 +78,7 @@
<string name="home_replace_title">Replace the game files?</string>
<string name="home_replace_message">Your current DATA folder stays until the new disc image has been checked and completely extracted.</string>
<string name="home_launch_failed">The game could not be started.</string>
<string name="home_quest1_launch_from_library">Quest 1 cannot switch this settings panel into VR. Close it, then launch WiiCompiled VR from your library.</string>
<string name="home_close_game_first">Close the game first.</string>
<string name="home_reset">Reset installation</string>
<string name="home_reset_title">Reset the installation?</string>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- Modern Horizon OS can move from this 2D setup panel into immersive VR. -->
<activity android:name=".launcher.LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".QuestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- Quest 1's final Horizon OS cannot promote a running 2D panel to an
immersive activity. Put the game itself in the library instead. -->
<activity android:name=".QuestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".SettingsLauncher"
android:exported="true"
android:label="@string/quest1_settings_name"
android:targetActivity=".launcher.LauncherActivity">
<intent-filter android:priority="-1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>
</manifest>
+8 -1
View File
@@ -154,6 +154,13 @@ elseif (_aurora_dawn_provider STREQUAL "package")
endif ()
set(AURORA_DAWN_PACKAGE_URL
"https://github.com/theofficialgman/dawn-build/releases/download/${AURORA_DAWN_VERSION}/dawn-${_dawn_system}-${_dawn_arch}.tar.gz")
# Meta's Quest 1 firmware advertises VK_EXT_debug_utils but rejects it at
# vkCreateInstance. This pinned Android package disables that extension on
# Android; it is also the package validated on modern Quest headsets.
if (_dawn_system STREQUAL "android" AND _dawn_arch STREQUAL "aarch64")
set(AURORA_DAWN_PACKAGE_URL
"https://github.com/encounter/dawn-build/releases/download/${AURORA_DAWN_VERSION}/dawn-android-aarch64.tar.gz")
endif ()
# A release asset is mutable: the same tag has already served two different windows-amd64 archives,
# and a cached extraction is never re-verified. Pin the digest for the combinations we ship.
@@ -181,7 +188,7 @@ elseif (_aurora_dawn_provider STREQUAL "package")
"SHA256=f97701d26fd1f25bbcc260b4c31736ede134c730c12556029e2470fde967f424")
elseif (_dawn_system STREQUAL "android" AND _dawn_arch STREQUAL "aarch64")
set(AURORA_DAWN_PACKAGE_URL_HASH
"SHA256=0e63e8cbf53551f703f582d1306f4257c0380353f66b53369d96952ce6d9f934")
"SHA256=27d910dee1201fd1e5b6ac567f0ba2306ebf2135e9f40b6929976c365d38b09b")
endif ()
endif ()
endif ()
+21 -1
View File
@@ -107,6 +107,22 @@ the result. Live RGB5A3 menu copies use a minimal RGBA passthrough shader for
the same reason. Other Android devices retain the normal filtered and
format-converting paths.
Quest 1 must be built with the `quest1` flavour (`Build-Quest.ps1 -Headset
quest1`). It targets the Snapdragon 835's Kryo CPU; the modern flavour targets
`cortex-a77`, whose instructions can terminate the game with `SIGILL` on Quest
1. Android builds use a pinned Dawn package which does not enable
`VK_EXT_debug_utils`: the old Quest firmware advertises that extension but
rejects it when Dawn creates the Vulkan instance.
The final Quest 1 firmware also cannot reliably promote this app's 2D setup
panel into an immersive activity. Its APK therefore exposes two library
entries: **WiiCompiled VR** starts the game directly in VR, while
**WiiCompiled Settings** opens the setup/settings panel. After changing a
setting, close the panel and start WiiCompiled VR from the library. The Play
button in the settings panel explains this limitation instead of opening a
black panel. Modern Quest builds keep the normal single launcher, whose Play
button starts VR directly.
The workaround was isolated on a physical Quest 1: it changed the boot/menu
output from flickering black or white frames to a complete menu with character
and vehicle previews. It does intentionally skip Wii-era RGB5A3 quantization on
@@ -459,7 +475,10 @@ Android facts this design rests on, all measured on a Quest 3:
also learned `--target-os windows|macos|linux|android` for
`generate-data-init` and `translate-mod`, for pipelines that generate on
another host.
- `android/`: the Gradle project, one app with no flavours.
- `android/`: the Gradle project, with `modernQuest` (the default script
target) and `quest1` headset flavours. They share the application ID and
storage, but select the appropriate CPU baseline, supported-device manifest,
and launcher behavior.
`app/src/main/cpp/CMakeLists.txt` adds the repository's `runtime/` as a
subdirectory with those Android choices and builds both game kit probes
(the Retro Rewind one only when the translation includes the mod), which
@@ -481,6 +500,7 @@ installer's `BuildWorkspace/generated`, produced by the normal Windows pipeline)
```powershell
powershell -ExecutionPolicy Bypass -File android/Prepare-QuestDependencies.ps1 # SDL3 3.4.4 AAR into android/app/libs
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Install # the app, its game kit and toolchain, debug-signed
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Headset quest1 -Install # Quest 1: Kryo CPU and direct-VR library entry
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Install # your game, against that kit, into Import (or WheelWizard VR's Build for Quest)
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Product retro_rewind -Mod <RetroRewind6> -Install # the mod and its pack (needs translate-mod output)
adb push MarioKart.iso /sdcard/Download/ # then Select disc image in the launcher