From 474f7bf12aee054f002ed661a64987885ef0fcd9 Mon Sep 17 00:00:00 2001 From: Mathew Tkachuk Date: Sun, 21 Dec 2025 22:18:18 +0300 Subject: [PATCH] Release 1.2.2 (#28) * Fix version detection edge case * Fix VrApi detection * Delete empty icon file * Remove leftover code * Animate icon and label on patch switching * Fix forced replace icon/label patch * Rename patch * Create Unity stub if UE4 smali is present only * Remove unused VrApi after patching * Add line breaks for appends * Add OculusXRPlugin binary patch and improve binary patching * Mark frida cleanup patch as not recommended * Change frida cleanup behavior * Filter version with existing directories * Show cover if selected * Remove VrApi library patch * Android XR metadata * Huawei VR metadata * More metadata * Uses-library for Android XR * Bump version * Rename patch --- .../composeResources/values/strings.xml | 7 +- .../overport/app/composables/AppContent.kt | 14 --- .../app/composables/ApplicationInfo.kt | 28 +++--- ...nInfoCard.kt => ApplicationInfoContent.kt} | 52 ++++++---- .../overport/app/composables/PatcherScreen.kt | 2 +- .../crx/overport/app/util/PatchLocalizer.kt | 1 + gradle.properties | 2 +- overportcli/build.gradle.kts | 2 +- .../crx/overport/patches/CleanUpFridaPatch.kt | 10 -- .../patches/CopyOVRPluginVrApiPatch.kt | 4 +- .../overport/patches/FixUnrealCrashPatch.kt | 16 +-- .../overport/patches/LauncherEntryPatch.kt | 1 + .../crx/overport/patches/OculusUnityPatch.kt | 11 +++ .../patches/RemoveUsesLibraryPatch.kt | 9 +- .../crx/overport/patches/RemoveVrApiPatch.kt | 16 +++ .../overport/patches/ReplaceIconLabelPatch.kt | 6 ++ .../crx/overport/patches/VRMetadataPatch.kt | 98 +++++++++++++++++++ .../crx/overport/patching/FileTransformer.kt | 33 ++++++- .../moe/crx/overport/patching/PatchStore.kt | 1 + .../crx/overport/versions/VersionManager.kt | 5 +- 20 files changed, 236 insertions(+), 82 deletions(-) rename composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/{ApplicationInfoCard.kt => ApplicationInfoContent.kt} (81%) create mode 100644 overportcli/src/main/java/moe/crx/overport/patches/RemoveVrApiPatch.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 1279ce2..ed3b099 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -39,15 +39,15 @@ Copy overport libraries Copy OVRPlugin if VrApi is present Fix minimal Android SDK - Generate ovrport config + Generate overport config Remove localized app names - Clean up Frida leftovers + Clean up Frida leftovers in smali Patch Oculus detection for Unity Patch Oculus detection for Unreal Pico/YVR/Quest metadata Fix launcher icon entry Remove uses-library - Fix Unreal Engine 4 crash + Fix UE4 crash with Unity stub Patch Meta XR Audio Mark application as debuggable Mark application to allow backup @@ -56,4 +56,5 @@ Force enable passthrough Disable application space warp if used Disable controller tracking offset + Remove VrApi library \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/AppContent.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/AppContent.kt index 9b82d53..8295047 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/AppContent.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/AppContent.kt @@ -46,7 +46,6 @@ fun AppContent( } var versionToUpdate by remember { mutableStateOf(null) } - var showAboutDialog by remember { mutableStateOf(false) } val urlHandler = LocalUriHandler.current LaunchedEffect(Unit) { @@ -95,18 +94,5 @@ fun AppContent( navigationItems = listOf(patcherNavItem), itemsEnabled = !viewModel.working && !viewModel.isApkLoaded() ) - - if (showAboutDialog) { - AlertDialog( - onDismissRequest = { showAboutDialog = false }, - title = { Text("overport") }, - text = { Text("about overport") }, // TODO - confirmButton = { - TextButton(onClick = { showAboutDialog = false }) { - Text("Close") - } - }, - ) - } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfo.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfo.kt index 70f42ff..48cb4b0 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfo.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfo.kt @@ -31,9 +31,12 @@ fun ApplicationInfo( applicationPackage: String? = "com.company.application", applicationVersion: String? = "1.0.0", applicationIcon: ImageBitmap? = null, + forceLocalInfo: Boolean = false, + useCover: Boolean = false, ) { var iconIsLoading by rememberSaveable(applicationPackage) { mutableStateOf(true) } var onlineIcon by rememberSaveable(applicationPackage) { mutableStateOf(null) } + var onlineCover by rememberSaveable(applicationPackage) { mutableStateOf(null) } var onlineLabel by rememberSaveable(applicationPackage) { mutableStateOf(null) } val scope = rememberCoroutineScope() @@ -44,12 +47,18 @@ fun ApplicationInfo( val info = applicationPackage?.let { getApplicationInfo(it) } onlineLabel = info?.displayName - val image = info?.run { + val icon = info?.run { val associated = images.associateBy { it.imageType } associated["APP_IMG_ICON"] ?: associated["APP_IMG_COVER_SQUARE"] } - onlineIcon = image?.uri?.let { Base64.getDecoder().decode(it) }?.decodeBitmap() + val cover = info?.run { + val associated = images.associateBy { it.imageType } + associated["APP_IMG_COVER_SQUARE"] ?: associated["APP_IMG_ICON"] + } + + onlineIcon = icon?.uri?.let { Base64.getDecoder().decode(it) }?.decodeBitmap() + onlineCover = cover?.uri?.let { Base64.getDecoder().decode(it) }?.decodeBitmap() } iconIsLoading = false } @@ -61,14 +70,9 @@ fun ApplicationInfo( ) { Box { FadeVisibility(!iconIsLoading) { - onlineIcon?.let { - Image( - it, - contentDescription = null, - modifier = Modifier.size(48.dp).rounded(8.dp) - ) - } ?: run { - if (applicationIcon == null) { + AnimatedContent((onlineCover.takeIf { useCover } ?: onlineIcon) + .takeIf { !forceLocalInfo } ?: applicationIcon) { + if (it == null) { Icon( Icons.Default.Widgets, contentDescription = null, @@ -76,7 +80,7 @@ fun ApplicationInfo( ) } else { Image( - applicationIcon, + it, contentDescription = null, modifier = Modifier.size(48.dp).rounded(8.dp) ) @@ -91,7 +95,7 @@ fun ApplicationInfo( modifier = Modifier.height(48.dp), verticalArrangement = Arrangement.Center ) { - AnimatedContent(onlineLabel ?: applicationName) { + AnimatedContent(onlineLabel.takeIf { !forceLocalInfo } ?: applicationName) { Text("$it ($applicationVersion)") } Text( diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoCard.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt similarity index 81% rename from composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoCard.kt rename to composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt index e581a03..18ee753 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoCard.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt @@ -20,7 +20,7 @@ import overportapp.composeapp.generated.resources.* @OptIn(ExperimentalLayoutApi::class) @Composable @Preview -fun ApplicationInfoCard( +fun ApplicationInfoContent( applicationName: String? = "Application", applicationPackage: String? = "", applicationVersion: String? = "1.0.0", @@ -28,6 +28,26 @@ fun ApplicationInfoCard( onCancel: () -> Unit = {}, onConfirm: (Map>) -> Unit = {} ) { + val enabledPatches = remember(applicationPackage) { + mutableStateMapOf( + *PatchStore.recommended().map { it.name to listOf() }.toTypedArray() + ) + } + + var patchIconArgument by remember(applicationPackage) { + mutableStateOf("icon") + } + + fun togglePatch(patch: Patch) { + val checked = enabledPatches.containsKey(patch.name) + + if (checked) { + enabledPatches.remove(patch.name) + } else { + enabledPatches[patch.name] = listOf() + } + } + Column( verticalArrangement = Arrangement.spacedBy(8.dp), ) { @@ -36,27 +56,15 @@ fun ApplicationInfoCard( horizontalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.spacedBy(8.dp), ) { - ApplicationInfo(applicationName, applicationPackage, applicationVersion, applicationIcon) - } - - val enabledPatches = remember { - mutableStateMapOf( - *PatchStore.recommended().map { it.name to listOf() }.toTypedArray() + ApplicationInfo( + applicationName, + applicationPackage, + applicationVersion, + applicationIcon, + !enabledPatches.containsKey(PATCH_REPLACE_ICON_LABEL.name), + patchIconArgument != "icon", ) } - var patchIconArgument by remember { - mutableStateOf("icon") - } - - fun togglePatch(patch: Patch) { - val checked = enabledPatches.containsKey(patch.name) - - if (checked) { - enabledPatches.remove(patch.name) - } else { - enabledPatches[patch.name] = listOf() - } - } Box(modifier = Modifier.weight(1f)) { LazyColumn { @@ -129,7 +137,9 @@ fun ApplicationInfoCard( Button( modifier = Modifier.weight(1f), onClick = { - enabledPatches[PATCH_REPLACE_ICON_LABEL.name] = listOf(patchIconArgument) + if (enabledPatches.containsKey(PATCH_REPLACE_ICON_LABEL.name)) { + enabledPatches[PATCH_REPLACE_ICON_LABEL.name] = listOf(patchIconArgument) + } onConfirm(enabledPatches) } ) { diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/PatcherScreen.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/PatcherScreen.kt index 46efca2..4d916c0 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/PatcherScreen.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/PatcherScreen.kt @@ -70,7 +70,7 @@ fun PatcherScreen( } FadeVisibility(isPatcherVisible) { - ApplicationInfoCard( + ApplicationInfoContent( viewModel.currentAppName(), viewModel.currentAppPackage(), viewModel.currentAppVersion(), diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/util/PatchLocalizer.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/util/PatchLocalizer.kt index a71c14b..5566c33 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/util/PatchLocalizer.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/util/PatchLocalizer.kt @@ -24,6 +24,7 @@ object PatchLocalizer { PATCH_META_XR_AUDIO to Res.string.patch_meta_xr_audio, PATCH_MARK_AS_DEBUGGABLE to Res.string.patch_mark_as_debuggable, PATCH_MARK_ALLOW_BACKUP to Res.string.patch_mark_allow_backup, + PATCH_REMOVE_VRAPI to Res.string.patch_remove_vrapi, PATCH_REMOVE_UNREAL_FORCE_QUIT to Res.string.patch_remove_unreal_force_quit, PATCH_FORCE_PASSTHROUGH to Res.string.patch_force_passthrough, PATCH_DISABLE_SPACE_WARP to Res.string.patch_disable_space_warp, diff --git a/gradle.properties b/gradle.properties index 492bfae..7b34f53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 android.nonTransitiveRClass=true android.useAndroidX=true #Application -appVersion=1.2.1 \ No newline at end of file +appVersion=1.2.2 \ No newline at end of file diff --git a/overportcli/build.gradle.kts b/overportcli/build.gradle.kts index 5718164..190d29e 100644 --- a/overportcli/build.gradle.kts +++ b/overportcli/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "moe.crx" -version = "1.2.1" +version = "1.2.2" dependencies { implementation(libs.android.tools.build) diff --git a/overportcli/src/main/java/moe/crx/overport/patches/CleanUpFridaPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/CleanUpFridaPatch.kt index d8bfbb1..fc4a803 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/CleanUpFridaPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/CleanUpFridaPatch.kt @@ -6,16 +6,6 @@ import moe.crx.overport.utils.named import moe.crx.overport.utils.takeNodesEach val PATCH_CLEAN_UP_FRIDA = Patch("patch_clean_up_frida") { - selectLibrary("libfrda.so") { - file.delete() - } - selectLibrary("libfrda.config.so") { - file.delete() - } - selectLibrary("libscript.so") { - file.delete() - } - selectManifestJson { takeNodesEach({ named("manifest") }) { takeNodesEach({ named("application") }) { diff --git a/overportcli/src/main/java/moe/crx/overport/patches/CopyOVRPluginVrApiPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/CopyOVRPluginVrApiPatch.kt index a178e3e..da5ce93 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/CopyOVRPluginVrApiPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/CopyOVRPluginVrApiPatch.kt @@ -9,8 +9,8 @@ val PATCH_COPY_OVRPLUGIN_VRAPI = Patch("patch_copy_ovrplugin_vrapi") { val arch = getLibraries().resolve("lib/${archDirectory.name}") if (arch.exists()) { - val hasVrApi = arch.listFiles().find { it.name.lowercase() == "libvrapi.so" } != null - if (hasVrApi) { + val vrApi = archDirectory.listFiles().find { it.name.lowercase() == "libvrapi.so" } + if (vrApi != null) { arch.listFiles().filter { it.name == "libOVRPlugin.so" }.forEach { library -> library.copyTo(File(archDirectory, library.name), true) } diff --git a/overportcli/src/main/java/moe/crx/overport/patches/FixUnrealCrashPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/FixUnrealCrashPatch.kt index b9ad178..1885fbe 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/FixUnrealCrashPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/FixUnrealCrashPatch.kt @@ -3,18 +3,18 @@ package moe.crx.overport.patches import moe.crx.overport.patching.Patch val PATCH_FIX_UNREAL_CRASH = Patch("patch_fix_unreal_crash") { - createSmali("com/unity3d/player/UnityPlayer") { - if (readText().isBlank()) { - append(".class public Lcom/unity3d/player/UnityPlayer;") - append(".super Ljava/lang/Object;") - append(".field public static currentActivity:Landroid/app/Activity;") - } - } - selectSmali("com/epicgames/ue4/GameActivity") { replace( "sput-object (\\w+), Lcom\\/epicgames\\/ue4\\/GameActivity;->\\w+:Lcom\\/epicgames\\/ue4\\/GameActivity;", "$0\nsput-object $1, Lcom/unity3d/player/UnityPlayer;->currentActivity:Landroid/app/Activity;" ) + + createSmali("com/unity3d/player/UnityPlayer") { + if (readText().isBlank()) { + appendLine(".class public Lcom/unity3d/player/UnityPlayer;") + appendLine(".super Ljava/lang/Object;") + appendLine(".field public static currentActivity:Landroid/app/Activity;") + } + } } } \ No newline at end of file diff --git a/overportcli/src/main/java/moe/crx/overport/patches/LauncherEntryPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/LauncherEntryPatch.kt index 892d9f7..49c1d5c 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/LauncherEntryPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/LauncherEntryPatch.kt @@ -45,6 +45,7 @@ val PATCH_LAUNCHER_ENTRY = Patch("patch_launcher_entry") { .put(createCategory("android.intent.category.LAUNCHER")) .put(createCategory("com.oculus.intent.category.VR")) .put(createCategory("com.yvr.intent.category.VR")) + .put(createCategory("org.khronos.openxr.intent.category.IMMERSIVE_HMD")) } } } diff --git a/overportcli/src/main/java/moe/crx/overport/patches/OculusUnityPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/OculusUnityPatch.kt index 1616581..24d9f89 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/OculusUnityPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/OculusUnityPatch.kt @@ -8,4 +8,15 @@ val PATCH_OCULUS_UNITY = Patch("patch_oculus_unity") { "(getIsOnOculusHardware\\(\\)Z.*?)return (\\w+)", "$1\nconst/4 $2, 0x1\nreturn $2" ) } + + selectLibrary("libOculusXRPlugin.so") { + replaceHex( + "1F 1C 00 72 ?? ?? ?? ?? E9 07 9F 1A C0 00 80 52 20 00 A0 72 ?? ?? ?? ??", + "1F 1C 00 72 ?? ?? ?? ?? 29 00 80 52 C0 00 80 52 20 00 A0 72 ?? ?? ?? ??" + ) + replaceHex( + "00 28 18 BF 01 20 ?? ?? 08 70 06 20 C0 F2 01 00", + "01 20 00 BF 00 BF ?? ?? 08 70 06 20 C0 F2 01 00" + ) + } } \ No newline at end of file diff --git a/overportcli/src/main/java/moe/crx/overport/patches/RemoveUsesLibraryPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/RemoveUsesLibraryPatch.kt index 7787e6b..d473b4b 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/RemoveUsesLibraryPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/RemoveUsesLibraryPatch.kt @@ -1,6 +1,7 @@ package moe.crx.overport.patches import moe.crx.overport.patching.Patch +import moe.crx.overport.utils.nameAttribute import moe.crx.overport.utils.named import moe.crx.overport.utils.takeNodesEach @@ -8,8 +9,12 @@ val PATCH_REMOVE_USES_LIBRARY = Patch("patch_remove_uses_library") { selectManifestJson { takeNodesEach({ named("manifest") }) { takeNodesEach({ named("application") }) { - takeNodesEach({ named("uses-library") }) { null } - takeNodesEach({ named("uses-native-library") }) { null } + takeNodesEach({ named("uses-library") }) { + if (nameAttribute() != "libopenxr.google.so") null else this + } + takeNodesEach({ named("uses-native-library") }) { + if (nameAttribute() != "libopenxr.google.so") null else this + } } } } diff --git a/overportcli/src/main/java/moe/crx/overport/patches/RemoveVrApiPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/RemoveVrApiPatch.kt new file mode 100644 index 0000000..b155803 --- /dev/null +++ b/overportcli/src/main/java/moe/crx/overport/patches/RemoveVrApiPatch.kt @@ -0,0 +1,16 @@ +package moe.crx.overport.patches + +import moe.crx.overport.patching.Patch + +val PATCH_REMOVE_VRAPI = Patch("patch_remove_vrapi", false) { + selectWorkspace { + file.resolve("root/lib").listFiles()?.forEach { archDirectory -> + val arch = getLibraries().resolve("lib/${archDirectory.name}") + + if (arch.exists()) { + val vrApi = archDirectory.listFiles().find { it.name.lowercase() == "libvrapi.so" } + vrApi?.delete() + } + } + } +} \ No newline at end of file diff --git a/overportcli/src/main/java/moe/crx/overport/patches/ReplaceIconLabelPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/ReplaceIconLabelPatch.kt index 107880a..9e0e79a 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/ReplaceIconLabelPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/ReplaceIconLabelPatch.kt @@ -55,6 +55,12 @@ val PATCH_REPLACE_ICON_LABEL = Patch("patch_replace_icon_label") { arguments -> } } + if (!iconDownloaded) { + selectFile("root/res/drawable/custom_icon_ovrp.png") { + delete() + } + } + if (iconDownloaded) { selectPackageBlock { getOrCreate("", "drawable", "custom_icon_ovrp").apply { diff --git a/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt b/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt index 972cf97..8aa97b8 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt @@ -22,10 +22,100 @@ fun createMetadata(name: String, value: String): JSONObject { ) } +fun createMetadataFloat(name: String, value: Float): JSONObject { + return JSONObject().put("node_type", "element").put("name", "meta-data").put( + "attributes", JSONArray().put( + JSONObject().put("name", "name").put("id", 16842755) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", name) + ).put( + JSONObject().put("name", "value").put("id", 16842788) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "FLOAT").put("data", value) + ) + ) +} + +fun createUsesFeature(name: String, required: Boolean): JSONObject { + return JSONObject().put("node_type", "element").put("name", "uses-feature").put( + "attributes", JSONArray().put( + JSONObject().put("name", "name").put("id", 16842755) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", name) + ).put( + JSONObject().put("name", "required").put("id", 16843406) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "BOOLEAN").put("data", required) + ) + ) +} + +fun createUsesLibrary(name: String, required: Boolean): JSONObject { + return JSONObject().put("node_type", "element").put("name", "uses-library").put( + "attributes", JSONArray().put( + JSONObject().put("name", "name").put("id", 16842755) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", name) + ).put( + JSONObject().put("name", "required").put("id", 16843406) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "BOOLEAN").put("data", required) + ) + ) +} + +fun createUsesPermission(name: String): JSONObject { + return JSONObject().put("node_type", "element").put("name", "uses-permission").put( + "attributes", JSONArray().put( + JSONObject().put("name", "name").put("id", 16842755) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", name) + ) + ) +} + +fun createProperty(key: String, value: String): JSONObject { + return JSONObject().put("node_type", "element").put("name", "property").put( + "attributes", JSONArray().put( + JSONObject().put("name", "name").put("id", 16842755) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", key) + ).put( + JSONObject().put("name", "value").put("id", 16842788) + .put("uri", "http://schemas.android.com/apk/res/android").put("prefix", "android") + .put("value_type", "STRING").put("data", value) + ) + ) +} + val PATCH_VR_METADATA = Patch("patch_vr_metadata") { selectManifestJson { takeNodesEach({ named("manifest") }) { takeNodesEach({ named("application") }) { + takeNodesEach({ named("activity") }) { + takeNodes { + this?.put( + createProperty( + "android.window.PROPERTY_XR_ACTIVITY_START_MODE", + "XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED" + ) + )?.put( + createProperty( + "android.window.PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED", + "XR_BOUNDARY_TYPE_LARGE" + ) + ) + } + } + takeNodes { + this?.put(createUsesFeature("android.software.xr.api.openxr", true)) + this?.put(createUsesFeature("android.software.xr.api.spatial", true)) + this?.put(createUsesLibrary("libopenxr.google.so", false)) + this?.put(createUsesFeature("android.software.xr.input.controller", false)) + ?.put(createUsesPermission("org.khronos.openxr.permission.OPENXR")) + ?.put(createUsesPermission("org.khronos.openxr.permission.OPENXR_SYSTEM")) + ?.put(createUsesPermission("com.huawei.android.permission.VR")) + } takeNodesEach({ named("meta-data") }) { if (nameAttribute() == "com.oculus.supportedDevices") null else this } @@ -33,6 +123,14 @@ val PATCH_VR_METADATA = Patch("patch_vr_metadata") { this?.put(createMetadata("pvr.app.type", "vr")) ?.put(createMetadata("com.yvr.intent.category.VR", "vr_only")) ?.put(createMetadata("com.oculus.supportedDevices", "all")) + ?.put(createMetadata("com.huawei.android.vr.application.mode", "vr_only")) + ?.put(createMetadata("com.huawei.android.vr.application.type", "game")) + ?.put(createMetadata("com.huawei.vr.application.freeDegree", "3dof|6dof")) + ?.put(createMetadataFloat("android.max_aspect", 2.1f)) + ?.put(createMetadata("minWaveSDKVersion", "1")) + ?.put(createMetadata("com.htc.vr.content.NumController", "1,2")) + ?.put(createMetadata("com.htc.vr.content.NumDoFController", "3,6DoF")) + ?.put(createMetadata("com.htc.vr.content.NumDoFHmd", "3,6DoF")) } } } diff --git a/overportcli/src/main/java/moe/crx/overport/patching/FileTransformer.kt b/overportcli/src/main/java/moe/crx/overport/patching/FileTransformer.kt index 2f14506..f9adbb4 100644 --- a/overportcli/src/main/java/moe/crx/overport/patching/FileTransformer.kt +++ b/overportcli/src/main/java/moe/crx/overport/patching/FileTransformer.kt @@ -8,6 +8,10 @@ class FileTransformer(val file: File) { file.writeText(Regex(input, RegexOption.DOT_MATCHES_ALL).replace(file.readText(), output)) } + fun delete(): Boolean { + return file.deleteRecursively() + } + fun readText(): String { return file.readText() } @@ -16,6 +20,11 @@ class FileTransformer(val file: File) { file.appendText(line) } + fun appendLine(line: String) { + append(line) + append("\n") + } + fun readJson(): JSONObject? { return runCatching { JSONObject(file.readText()) }.getOrNull() } @@ -35,6 +44,10 @@ class FileTransformer(val file: File) { fun replaceHex(input: String, output: String) { val bytes = file.readBytes() val inputBytes = input.split(' ') + val outputBytes = output.split(' ') + var startIndex = -1 + + check(inputBytes.size == outputBytes.size) for (i in 0..