mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 08:00:25 +02:00
Quest: address flavor safety review
This commit is contained in:
1 parent
6e6406c200
commit
02dd9bf8cc
15 files changed
+162
-67
No files matched your search
@@ -75,6 +75,7 @@ if (-not $Dependencies) {
|
||||
|
||||
$variant = (Get-Culture).TextInfo.ToTitleCase($Configuration)
|
||||
$flavour = if ($Headset -eq 'quest1') { 'Quest1' } else { 'ModernQuest' }
|
||||
$expectedCpu = if ($Headset -eq 'quest1') { 'kryo' } else { 'cortex-a77' }
|
||||
$task = "app:assemble$flavour$variant"
|
||||
$gradleArgs = @(
|
||||
'--project-dir', $root,
|
||||
@@ -98,11 +99,16 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$archive = [IO.Compression.ZipFile]::OpenRead($apk.FullName)
|
||||
try {
|
||||
$gameLibraries = @($archive.Entries | Where-Object { $_.FullName -match '^lib/[^/]+/libmain[^/]*\.so$' })
|
||||
$hasKit = @($archive.Entries | Where-Object { $_.FullName -eq 'assets/game_kit/kit.json' }).Count -gt 0
|
||||
$kitEntry = @($archive.Entries | Where-Object { $_.FullName -eq 'assets/game_kit/kit.json' }) | Select-Object -First 1
|
||||
$kitCpu = if ($kitEntry) {
|
||||
$reader = New-Object IO.StreamReader($kitEntry.Open())
|
||||
try { ($reader.ReadToEnd() | ConvertFrom-Json).androidCpu } finally { $reader.Dispose() }
|
||||
} else { '' }
|
||||
$hasToolchain = @($archive.Entries | Where-Object { $_.FullName -eq 'assets/quest_toolchain/files.zip' }).Count -gt 0
|
||||
} finally { $archive.Dispose() }
|
||||
if ($gameLibraries.Count -gt 0) { throw "The APK contains a translated game library: $($gameLibraries.FullName -join ', ')" }
|
||||
if (-not $hasKit) { throw 'The APK has no game kit (assets/game_kit/kit.json)' }
|
||||
if (-not $kitEntry) { throw 'The APK has no game kit (assets/game_kit/kit.json)' }
|
||||
if ($kitCpu -ne $expectedCpu) { throw "The $Headset APK contains a game kit for CPU '$kitCpu', expected '$expectedCpu'" }
|
||||
if (-not $hasToolchain) { throw 'The APK has no build toolchain (assets/quest_toolchain/files.zip)' }
|
||||
|
||||
if ($Install) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# SDK's NDK and ninja:
|
||||
#
|
||||
# powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 [-Generated <dir>] [-Kit <dir>]
|
||||
# [-Headset modern|quest1] [-Configuration debug|release]
|
||||
# [-Data <extracted disc dir>] [-Output <file.wcgame>] [-Install]
|
||||
#
|
||||
# WiiCompiled Setup's --build-quest runs the copy staged in an installation's BuildWorkspace\android,
|
||||
@@ -32,6 +33,8 @@ param(
|
||||
[string]$Sysroot = '',
|
||||
[string]$Ninja = '',
|
||||
[string]$BuiltBy = 'android/Build-QuestGame.ps1',
|
||||
[ValidateSet('modern', 'quest1')] [string]$Headset = 'modern',
|
||||
[ValidateSet('debug', 'release')] [string]$Configuration = 'debug',
|
||||
[ValidateSet('base', 'retro_rewind')] [string]$Product = 'base',
|
||||
[string]$Mod = '',
|
||||
[int]$TranslatedJobs = 0,
|
||||
@@ -44,12 +47,13 @@ $root = $PSScriptRoot
|
||||
$workspace = (Resolve-Path (Join-Path $root '..')).Path
|
||||
if (-not $Generated) { $Generated = Join-Path $workspace '.scratch\vr-build-workspace\BuildWorkspace\generated' }
|
||||
if (-not $Manifest) { $Manifest = Join-Path $workspace 'projects\mkwii\recomp.yml' }
|
||||
if (-not $BuildDir) { $BuildDir = Join-Path $root "app\build\questGame\$Product" }
|
||||
$variant = if ($Headset -eq 'quest1') { 'quest1' } else { 'modernQuest' }
|
||||
$variant += (Get-Culture).TextInfo.ToTitleCase($Configuration)
|
||||
if (-not $BuildDir) { $BuildDir = Join-Path $root "app\build\questGame\$variant\$Product" }
|
||||
if (-not $Kit) {
|
||||
# The kit the last APK build packaged, so the game matches the app it will be imported into.
|
||||
$Kit = Get-ChildItem -Path (Join-Path $root 'app\build\generated') -Filter kit.json -Recurse -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Directory.Name -eq 'game_kit' } |
|
||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1 | ForEach-Object { $_.DirectoryName }
|
||||
# Select the requested variant explicitly. Modification time is unsafe now that flavours use
|
||||
# different -mcpu targets and an up-to-date native probe is not necessarily the newest one.
|
||||
$Kit = Join-Path $root "app\build\generated\assets\questGameKit\$variant\game_kit"
|
||||
}
|
||||
if (-not $Kit -or -not (Test-Path (Join-Path $Kit 'kit.json'))) {
|
||||
throw 'No game kit; run Build-Quest.ps1 first, or pass -Kit'
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
param(
|
||||
[Parameter(Mandatory)] [string]$CMakeBinaryDir,
|
||||
[Parameter(Mandatory)] [string]$OutputDir,
|
||||
[Parameter(Mandatory)] [string]$LlvmStrip
|
||||
[Parameter(Mandatory)] [string]$LlvmStrip,
|
||||
[Parameter(Mandatory)] [string]$AndroidCpu
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Import-Module (Join-Path $PSScriptRoot 'QuestGameKit.psm1') -Force
|
||||
$repo = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
||||
$kit = Export-QuestGameKit -CMakeBinaryDir $CMakeBinaryDir -OutputDir $OutputDir -RepoRoot $repo -LlvmStrip $LlvmStrip
|
||||
Write-Host "Quest game kit $($kit.Fingerprint) [$($kit.Products -join ', ')] -> $OutputDir"
|
||||
$kit = Export-QuestGameKit -CMakeBinaryDir $CMakeBinaryDir -OutputDir $OutputDir -RepoRoot $repo -LlvmStrip $LlvmStrip -AndroidCpu $AndroidCpu
|
||||
Write-Host "Quest game kit $($kit.Fingerprint) [$($kit.Products -join ', '), CPU $AndroidCpu] -> $OutputDir"
|
||||
@@ -268,10 +268,16 @@ function Export-QuestGameKit {
|
||||
[Parameter(Mandatory)] [string]$CMakeBinaryDir,
|
||||
[Parameter(Mandatory)] [string]$OutputDir,
|
||||
[Parameter(Mandatory)] [string]$RepoRoot,
|
||||
[Parameter(Mandatory)] [string]$LlvmStrip
|
||||
[Parameter(Mandatory)] [string]$LlvmStrip,
|
||||
[Parameter(Mandatory)] [string]$AndroidCpu
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$binary = ConvertTo-ForwardPath $CMakeBinaryDir
|
||||
$cache = [IO.File]::ReadAllText("$binary/CMakeCache.txt")
|
||||
$configuredCpu = [regex]::Match($cache, '(?m)^MKW_ANDROID_CPU(?::[^=\r\n]*)?=([^\r\n]+)$').Groups[1].Value.Trim()
|
||||
if ($configuredCpu -ne $AndroidCpu) {
|
||||
throw "CMake tree $binary targets Android CPU '$configuredCpu', expected '$AndroidCpu'"
|
||||
}
|
||||
$runtimeInclude = ConvertTo-ForwardPath (Join-Path $RepoRoot 'runtime/include')
|
||||
$ninja = [IO.File]::ReadAllText("$binary/build.ninja")
|
||||
$commands = Read-CompileCommands "$binary/compile_commands.json"
|
||||
@@ -405,6 +411,7 @@ function Export-QuestGameKit {
|
||||
|
||||
$recipe = [ordered]@{
|
||||
schema = $script:KitSchema
|
||||
androidCpu = $AndroidCpu
|
||||
runtimeIncludeFingerprint = Get-RuntimeIncludeFingerprint (Join-Path $OutputDir 'include')
|
||||
products = $products
|
||||
}
|
||||
@@ -445,10 +452,16 @@ function Invoke-QuestGameBuild {
|
||||
$workspace = ConvertTo-ForwardPath (Split-Path -Parent $generated)
|
||||
$recipe = [IO.File]::ReadAllText("$kit/kit.json") | ConvertFrom-Json
|
||||
if ($recipe.schema -ne $script:KitSchema) { throw "Unsupported game kit schema $($recipe.schema)" }
|
||||
if ([string]::IsNullOrWhiteSpace($recipe.androidCpu)) { throw 'The game kit does not name its Android CPU target' }
|
||||
if (-not $recipe.products.PSObject.Properties.Name.Contains($Product)) {
|
||||
throw "This Quest app's game kit cannot build $Product (it has: $($recipe.products.PSObject.Properties.Name -join ', '))"
|
||||
}
|
||||
$productRecipe = $recipe.products.$Product
|
||||
foreach ($kind in 'translated', 'product', 'runtime', 'asm') {
|
||||
if (@($productRecipe.compile.$kind) -notcontains "-mcpu=$($recipe.androidCpu)") {
|
||||
throw "The game kit says CPU '$($recipe.androidCpu)' but its $kind compile flags do not match"
|
||||
}
|
||||
}
|
||||
# The translation must come from the same release as the kit: its code is compiled against the
|
||||
# kit's runtime headers and linked with the kit's runtime objects.
|
||||
$workspaceInclude = Join-Path $workspace 'runtime/include'
|
||||
@@ -460,6 +473,20 @@ function Invoke-QuestGameBuild {
|
||||
}
|
||||
New-Item -ItemType Directory -Force $BuildDir | Out-Null
|
||||
$build = ConvertTo-ForwardPath $BuildDir
|
||||
# Ninja sees the response-file path in each command, not changes to that file's contents. A
|
||||
# different kit can therefore otherwise reuse objects compiled with another flavour's -mcpu.
|
||||
# Keep an explicit identity next to the build and discard every reusable native output when it
|
||||
# changes. WiiCompiled Setup deliberately reuses one BuildDir across APK selections.
|
||||
$identity = "$($recipe.fingerprint) $Product"
|
||||
$identityFile = "$build/kit-identity.txt"
|
||||
$previousIdentity = if (Test-Path $identityFile) { [IO.File]::ReadAllText($identityFile).Trim() } else { '' }
|
||||
if ($previousIdentity -ne $identity) {
|
||||
foreach ($stale in 'obj', 'libmain.so', '.ninja_deps', '.ninja_log') {
|
||||
$path = "$build/$stale"
|
||||
if (Test-Path $path) { Remove-Item -Recurse -Force $path }
|
||||
}
|
||||
}
|
||||
[IO.File]::WriteAllText($identityFile, $identity, (New-Object Text.UTF8Encoding $false))
|
||||
$expand = { param([string]$s) $s.Replace('{kit}', $kit).Replace('{sysroot}', (ConvertTo-ForwardPath $Sysroot)).Replace('{workspace}', $workspace) }
|
||||
|
||||
foreach ($kind in 'translated', 'product', 'runtime', 'asm') {
|
||||
@@ -583,6 +610,7 @@ function New-QuestGamePackage {
|
||||
dolSha256 = $DolSha256
|
||||
relSha256 = $RelSha256
|
||||
kitFingerprint = $recipe.fingerprint
|
||||
androidCpu = $recipe.androidCpu
|
||||
library = $recipe.products.$Product.output
|
||||
librarySha256 = Get-Sha256Hex $Library
|
||||
includesData = [bool]$DataDir
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
# WiiCompiled VR for Meta Quest (Android)
|
||||
|
||||
Standalone Android/OpenXR build of the Mario Kart Wii recompilation for Quest 2,
|
||||
Quest 3, Quest 3S and Quest Pro. The full design, build walkthrough and current
|
||||
Standalone Android/OpenXR build of the Mario Kart Wii recompilation for Quest 1,
|
||||
Quest 2, Quest 3, Quest 3S and Quest Pro. The full design, build walkthrough and current
|
||||
status live in [docs/quest-port.md](../docs/quest-port.md); this directory only
|
||||
holds the Gradle project, its helper scripts, the game kit tooling
|
||||
(`QuestGameKit.psm1`, `Build-QuestGame.ps1`), the on-headset build toolchain
|
||||
@@ -10,6 +10,7 @@ holds the Gradle project, its helper scripts, the game kit tooling
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File android/Prepare-QuestDependencies.ps1 # stages the SDL3 3.4.4 AAR once
|
||||
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Install # the app, debug-signed, installs over adb
|
||||
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Headset quest1 -Install # Quest 1 flavour
|
||||
```
|
||||
|
||||
One app offers both games, with a toggle on Home: Mario Kart Wii and, when your
|
||||
@@ -23,6 +24,7 @@ half an hour) or on a PC and imported:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Product base -Install
|
||||
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Headset quest1 -Product base -Install
|
||||
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Product retro_rewind -Mod <RetroRewind6> -Install
|
||||
```
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
# diagnostics for one session.
|
||||
#
|
||||
# powershell -ExecutionPolicy Bypass -File android/Run-Quest.ps1 [-Apk <path>] [-Data <extracted disc dir>]
|
||||
# [-Seconds 60] [-NoLaunch] [-SkipInstall]
|
||||
# [-Headset modern|quest1] [-Configuration debug|release]
|
||||
# [-Seconds 60] [-NoLaunch] [-SkipInstall]
|
||||
#
|
||||
# -Data names the extracted disc partition (the directory holding sys/, files/,
|
||||
# disc/ ...). It is pushed once to a staging folder, then moved into the app's
|
||||
@@ -24,6 +25,8 @@
|
||||
param(
|
||||
[string]$Apk = '',
|
||||
[string]$Data = '',
|
||||
[ValidateSet('modern', 'quest1')] [string]$Headset = 'modern',
|
||||
[ValidateSet('debug', 'release')] [string]$Configuration = 'debug',
|
||||
[int]$Seconds = 60,
|
||||
[switch]$NoLaunch,
|
||||
[switch]$SkipInstall
|
||||
@@ -49,8 +52,10 @@ if (-not $devices) { throw 'No device in "device" state; check the Quest is conn
|
||||
|
||||
if (-not $SkipInstall) {
|
||||
if (-not $Apk) {
|
||||
$Apk = Get-ChildItem -Path (Join-Path $root 'app\build\outputs\apk') -Recurse -Filter '*.apk' |
|
||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1 | ForEach-Object FullName
|
||||
$flavour = if ($Headset -eq 'quest1') { 'quest1' } else { 'modernQuest' }
|
||||
$apkDir = Join-Path $root "app\build\outputs\apk\$flavour\$Configuration"
|
||||
$Apk = Get-ChildItem -Path $apkDir -Filter '*.apk' -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 1 | ForEach-Object FullName
|
||||
}
|
||||
if (-not $Apk -or -not (Test-Path $Apk)) { throw 'No APK found; run Build-Quest.ps1 first or pass -Apk' }
|
||||
Write-Host "Installing $Apk"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.provider.Property
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
@@ -145,6 +146,7 @@ android {
|
||||
dimension = "headset"
|
||||
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest2|quest3|quest3s|questpro"
|
||||
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "false")
|
||||
buildConfigField("String", "ANDROID_CPU", "\"cortex-a77\"")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
// Snapdragon XR2 and newer. Keep this as the default build target.
|
||||
@@ -156,6 +158,7 @@ android {
|
||||
dimension = "headset"
|
||||
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest|quest2"
|
||||
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "true")
|
||||
buildConfigField("String", "ANDROID_CPU", "\"kryo\"")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
// Snapdragon 835. cortex-a77 binaries terminate with SIGILL on Quest 1.
|
||||
@@ -221,6 +224,12 @@ abstract class ExportQuestGameKit : DefaultTask() {
|
||||
@get:Internal
|
||||
abstract val llvmStrip: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val androidCpu: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val cmakeBuildType: Property<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val execOperations: ExecOperations
|
||||
|
||||
@@ -232,18 +241,39 @@ abstract class ExportQuestGameKit : DefaultTask() {
|
||||
@TaskAction
|
||||
fun export() {
|
||||
val app = appDir.get().asFile
|
||||
val probe = File(app, "build/intermediates/cxx").walkTopDown()
|
||||
data class Candidate(val probe: File, val binaryDir: File, val cpu: String?, val buildType: String?)
|
||||
fun cacheValue(cache: String, name: String): String? =
|
||||
Regex("(?m)^${Regex.escape(name)}(?::[^=\\r\\n]*)?=([^\\r\\n]*)$")
|
||||
.find(cache)?.groupValues?.get(1)?.trim()
|
||||
|
||||
val candidates = File(app, "build/intermediates/cxx").walkTopDown()
|
||||
.filter { it.name == "libmkw_quest_kit_probe.so" && it.parentFile.name == "arm64-v8a" }
|
||||
.maxByOrNull { it.lastModified() }
|
||||
?: throw GradleException("No libmkw_quest_kit_probe.so; the native build did not produce the game kit probe")
|
||||
val configuration = probe.parentFile.parentFile.parentFile // <BuildType>/<hash>
|
||||
val binaryDir = File(app, ".cxx/${configuration.parentFile.name}/${configuration.name}/arm64-v8a")
|
||||
if (!File(binaryDir, "build.ninja").isFile) throw GradleException("No CMake tree at $binaryDir")
|
||||
.map { probe ->
|
||||
val configuration = probe.parentFile.parentFile.parentFile // <Variant>/<hash>
|
||||
val binaryDir = File(app, ".cxx/${configuration.parentFile.name}/${configuration.name}/arm64-v8a")
|
||||
val cacheFile = File(binaryDir, "CMakeCache.txt")
|
||||
val cache = cacheFile.takeIf { it.isFile }?.readText().orEmpty()
|
||||
Candidate(probe, binaryDir, cacheValue(cache, "MKW_ANDROID_CPU"), cacheValue(cache, "CMAKE_BUILD_TYPE"))
|
||||
}
|
||||
.toList()
|
||||
val expectedCpu = androidCpu.get()
|
||||
val expectedBuildType = cmakeBuildType.get()
|
||||
val matching = candidates.filter {
|
||||
it.cpu == expectedCpu && it.buildType.equals(expectedBuildType, ignoreCase = true) &&
|
||||
File(it.binaryDir, "build.ninja").isFile
|
||||
}
|
||||
val chosen = matching.maxByOrNull { it.probe.lastModified() }
|
||||
?: throw GradleException(
|
||||
"No $expectedBuildType CMake tree for Android CPU $expectedCpu. Found: " +
|
||||
candidates.joinToString { "${it.binaryDir} (cpu=${it.cpu}, type=${it.buildType})" }
|
||||
)
|
||||
val binaryDir = chosen.binaryDir
|
||||
val kitDir = File(outputDir.get().asFile, "game_kit")
|
||||
execOperations.exec {
|
||||
commandLine(
|
||||
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script.get().asFile.path,
|
||||
"-CMakeBinaryDir", binaryDir.path, "-OutputDir", kitDir.path, "-LlvmStrip", llvmStrip.get().asFile.path,
|
||||
"-AndroidCpu", expectedCpu,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -293,6 +323,9 @@ androidComponents {
|
||||
dependsOn("merge${capitalized}NativeLibs")
|
||||
appDir.set(layout.projectDirectory)
|
||||
script.set(rootProject.layout.projectDirectory.file("Export-QuestGameKit.ps1"))
|
||||
androidCpu.set(if (variant.name.startsWith("quest1", ignoreCase = true)) "kryo" else "cortex-a77")
|
||||
cmakeBuildType.set(variant.buildType)
|
||||
outputDir.set(layout.buildDirectory.dir("generated/assets/questGameKit/${variant.name}"))
|
||||
llvmStrip.set(sdkComponents.ndkDirectory.map {
|
||||
it.file("toolchains/llvm/prebuilt/$host/bin/llvm-strip" + if (host.startsWith("windows")) ".exe" else "")
|
||||
})
|
||||
|
||||
@@ -50,6 +50,8 @@ object GameLibrary {
|
||||
if (manifest == null || !library(context, profile).isFile) {
|
||||
return Status.Missing
|
||||
}
|
||||
return if (manifest.optString("kitFingerprint") == kitFingerprint(context)) Status.Ready else Status.Stale
|
||||
return if (manifest.optString("kitFingerprint") == kitFingerprint(context) &&
|
||||
manifest.optString("androidCpu") == BuildConfig.ANDROID_CPU
|
||||
) Status.Ready else Status.Stale
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,9 @@ object GameBuild {
|
||||
if (kitJson.optInt("schema") != KIT_SCHEMA) {
|
||||
return "This app's game kit is version ${kitJson.optInt("schema")}, which this builder does not know."
|
||||
}
|
||||
if (kitJson.optString("androidCpu") != BuildConfig.ANDROID_CPU) {
|
||||
return "This app contains a game kit for ${kitJson.optString("androidCpu")}, but this build requires ${BuildConfig.ANDROID_CPU}."
|
||||
}
|
||||
val recipe = kitJson.getJSONObject("products").optJSONObject(profile.id)
|
||||
?: return "This app's game kit cannot build ${profile.id}."
|
||||
report(40, Step.Prepare)
|
||||
@@ -570,6 +573,7 @@ object GameBuild {
|
||||
.put("dolSha256", BuildConfig.DISC_DOL_SHA256)
|
||||
.put("relSha256", BuildConfig.DISC_REL_SHA256)
|
||||
.put("kitFingerprint", kitFingerprint)
|
||||
.put("androidCpu", BuildConfig.ANDROID_CPU)
|
||||
.put("library", GameLibrary.LIBRARY_NAME)
|
||||
.put("librarySha256", BuildRecipe.hex(digest.digest()))
|
||||
.put("includesData", false)
|
||||
|
||||
@@ -157,6 +157,9 @@ object GamePackageImport {
|
||||
if (manifest.optString("kitFingerprint") != GameLibrary.kitFingerprint(context)) {
|
||||
return "This game file was built for a different version of this app. Build it again against the version installed on the headset."
|
||||
}
|
||||
if (manifest.optString("androidCpu") != BuildConfig.ANDROID_CPU) {
|
||||
return "This game file targets ${manifest.optString("androidCpu")}, but this app requires ${BuildConfig.ANDROID_CPU}. Build it again for the correct headset."
|
||||
}
|
||||
val expected = manifest.optString("librarySha256")
|
||||
if (librarySha256 == null) {
|
||||
return "This game file has no game library (libmain.so)."
|
||||
|
||||
Reference in new issue
Block a user