diff --git a/README.md b/README.md index c9d0f9b..0269dc2 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ If you have any issues regarding game compatibility, please, enable verbose logg # About legality -Think of this app like an emulator: you need to legally own the game files to use it. +Think of this app like an emulator: you need to legally obtain the game files to use it. Obviously, to get a Quest game running on a different headset, the app has to strip away some entitlement checks. However, the game still won't launch on a Quest unless you’ve actually bought it. 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 48cb4b0..4a515c7 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 @@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import moe.crx.overport.app.decodeBitmap import moe.crx.overport.app.util.ModifierUtil.rounded @@ -25,43 +24,53 @@ import overportapp.composeapp.generated.resources.Res import overportapp.composeapp.generated.resources.unknown_package import java.util.* +val iconCache = mutableMapOf() +val coverCache = mutableMapOf() + @Composable fun ApplicationInfo( - applicationName: String? = "Application", - applicationPackage: String? = "com.company.application", - applicationVersion: String? = "1.0.0", + applicationName: String, + applicationPackage: String, + applicationVersion: String, 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 iconIsLoading by remember(applicationPackage) { mutableStateOf(true) } + var onlineIcon by remember(applicationPackage) { mutableStateOf(iconCache[applicationPackage]) } + var onlineCover by remember(applicationPackage) { mutableStateOf(coverCache[applicationPackage]) } + var onlineLabel by rememberSaveable(applicationPackage) { mutableStateOf(null) } - val scope = rememberCoroutineScope() LaunchedEffect(applicationPackage) { - scope.launch { - withContext(Dispatchers.IO) { - runCatching { - val info = applicationPackage?.let { getApplicationInfo(it) } - onlineLabel = info?.displayName - - val icon = info?.run { - val associated = images.associateBy { it.imageType } - associated["APP_IMG_ICON"] ?: associated["APP_IMG_COVER_SQUARE"] - } - - 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 + withContext(Dispatchers.IO) { + if (iconCache.containsKey(applicationPackage)) { + return@withContext } + + val info = getApplicationInfo(applicationPackage) + onlineLabel = info?.displayName + + val icon = info?.run { + val associated = images.associateBy { it.imageType } + associated["APP_IMG_ICON"] ?: associated["APP_IMG_COVER_SQUARE"] + } + + val cover = info?.run { + val associated = images.associateBy { it.imageType } + associated["APP_IMG_COVER_SQUARE"] ?: associated["APP_IMG_ICON"] + } + + val iconBitmap = icon?.uri?.let { Base64.getDecoder().decode(it) }?.decodeBitmap() + val coverBitmap = cover?.uri?.let { Base64.getDecoder().decode(it) }?.decodeBitmap() + + iconCache[applicationPackage] = iconBitmap + coverCache[applicationPackage] = coverBitmap + + onlineIcon = iconBitmap + onlineCover = coverBitmap + + iconIsLoading = false } } diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt index 18ee753..29f10d9 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/ApplicationInfoContent.kt @@ -21,9 +21,9 @@ import overportapp.composeapp.generated.resources.* @Composable @Preview fun ApplicationInfoContent( - applicationName: String? = "Application", - applicationPackage: String? = "", - applicationVersion: String? = "1.0.0", + applicationName: String, + applicationPackage: String, + applicationVersion: String, applicationIcon: ImageBitmap? = null, onCancel: () -> Unit = {}, onConfirm: (Map>) -> Unit = {} 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 4d916c0..e18af47 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 @@ -35,30 +35,26 @@ fun PatcherScreen( val isPatcherVisible = !viewModel.working && viewModel.isApkLoaded() LaunchedEffect(openFlow) { - scope.launch { - openFlow.collect { - inputFile = it - } + openFlow.collect { + inputFile = it } } LaunchedEffect(saveFlow) { - scope.launch { - saveFlow.collect { stream -> - val outputStream = stream ?: return@collect - try { - viewModel.process(patches) - viewModel.export(outputStream) - scope.launch { - snackbarHostState.showSnackbar(getString(Res.string.apk_file_exported)) - } - } catch (ex: Throwable) { - ex.printStackTrace() - errorMessage = getString(Res.string.unknown_error) to ex.stackTraceToString() - } finally { - viewModel.cancel() - outputStream.close() + saveFlow.collect { stream -> + val outputStream = stream ?: return@collect + try { + viewModel.process(patches) + viewModel.export(outputStream) + scope.launch { + snackbarHostState.showSnackbar(getString(Res.string.apk_file_exported)) } + } catch (ex: Throwable) { + ex.printStackTrace() + errorMessage = getString(Res.string.unknown_error) to ex.stackTraceToString() + } finally { + viewModel.cancel() + //outputStream.close() } } } @@ -71,9 +67,9 @@ fun PatcherScreen( FadeVisibility(isPatcherVisible) { ApplicationInfoContent( - viewModel.currentAppName(), - viewModel.currentAppPackage(), - viewModel.currentAppVersion(), + viewModel.currentAppName() ?: "", + viewModel.currentAppPackage() ?: "", + viewModel.currentAppVersion() ?: "", viewModel.currentAppIcon(), onCancel = { scope.launch { diff --git a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/VersionManagerSheet.kt b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/VersionManagerSheet.kt index 8b43522..390617d 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/VersionManagerSheet.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/overport/app/composables/VersionManagerSheet.kt @@ -27,10 +27,8 @@ fun VersionManagerSheet(viewModel: MainViewModel, onSelected: (String) -> Unit, var available by remember { mutableStateOf?>(null) } LaunchedEffect(Unit) { - scope.launch { - withContext(Dispatchers.IO) { - available = viewModel.versionManager.available() - } + withContext(Dispatchers.IO) { + available = viewModel.versionManager.available() } } diff --git a/gradle.properties b/gradle.properties index 7b34f53..a4b2dc2 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.2 \ No newline at end of file +appVersion=1.2.3 \ No newline at end of file diff --git a/overportcli/build.gradle.kts b/overportcli/build.gradle.kts index 190d29e..7ea4a74 100644 --- a/overportcli/build.gradle.kts +++ b/overportcli/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "moe.crx" -version = "1.2.2" +version = "1.2.3" dependencies { implementation(libs.android.tools.build) diff --git a/overportcli/libs/ARSCLib-1.3.8.jar b/overportcli/libs/ARSCLib-1.4.0.jar similarity index 79% rename from overportcli/libs/ARSCLib-1.3.8.jar rename to overportcli/libs/ARSCLib-1.4.0.jar index 976a869..657263c 100644 Binary files a/overportcli/libs/ARSCLib-1.3.8.jar and b/overportcli/libs/ARSCLib-1.4.0.jar differ 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 8aa97b8..171fa0b 100644 --- a/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt +++ b/overportcli/src/main/java/moe/crx/overport/patches/VRMetadataPatch.kt @@ -31,7 +31,7 @@ fun createMetadataFloat(name: String, value: Float): JSONObject { ).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) + .put("value_type", "FLOAT").put("data", value.toRawBits()) ) ) } @@ -91,6 +91,14 @@ fun createProperty(key: String, value: String): JSONObject { val PATCH_VR_METADATA = Patch("patch_vr_metadata") { selectManifestJson { takeNodesEach({ named("manifest") }) { + takeNodes { + this?.put(createUsesFeature("android.software.xr.api.openxr", true)) + ?.put(createUsesFeature("android.software.xr.api.spatial", true)) + ?.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("application") }) { takeNodesEach({ named("activity") }) { takeNodes { @@ -107,15 +115,6 @@ val PATCH_VR_METADATA = Patch("patch_vr_metadata") { ) } } - 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 } @@ -131,6 +130,7 @@ val PATCH_VR_METADATA = Patch("patch_vr_metadata") { ?.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")) + ?.put(createUsesLibrary("libopenxr.google.so", false)) } } } diff --git a/overportcli/src/main/java/moe/crx/overport/versions/VersionManager.kt b/overportcli/src/main/java/moe/crx/overport/versions/VersionManager.kt index fe8b4a8..d8b8400 100644 --- a/overportcli/src/main/java/moe/crx/overport/versions/VersionManager.kt +++ b/overportcli/src/main/java/moe/crx/overport/versions/VersionManager.kt @@ -10,7 +10,7 @@ class VersionManager(val workingDirectory: File) { companion object { val json = Json { ignoreUnknownKeys = true } - const val VERSION = "1.2.2" + const val VERSION = "1.2.3" private fun isVersionLower(left: String, right: String): Boolean { val (lY, lM, lD) = left.substringBeforeLast('-').split('.').map { it.toInt() }