diff --git a/build.gradle.kts b/build.gradle.kts index 6474d12..3def5e0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) implementation(libs.picocli) + implementation(files("libs/APKEditor-1.4.7.jar")) testImplementation(libs.kotlin.test) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 322442e..ec5907b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,8 +5,10 @@ kotlinx = "1.9.0" picocli = "4.7.7" morphe-patcher = "1.1.0" morphe-library = "1.1.0" +arsclib = "1.3.8" [libraries] +arsclib = { module = "io.github.reandroid:ARSCLib", version.ref = "arsclib" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx" } diff --git a/libs/APKEditor-1.4.7.jar b/libs/APKEditor-1.4.7.jar new file mode 100644 index 0000000..f1a0d81 Binary files /dev/null and b/libs/APKEditor-1.4.7.jar differ diff --git a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt index c730844..994fad9 100644 --- a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt @@ -14,6 +14,8 @@ import app.morphe.patcher.Patcher import app.morphe.patcher.PatcherConfig import app.morphe.patcher.patch.Patch import app.morphe.patcher.patch.loadPatchesFromJar +import com.reandroid.apkeditor.merge.Merger +import com.reandroid.apkeditor.merge.MergerOptions import kotlinx.coroutines.runBlocking import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json @@ -260,7 +262,7 @@ internal object PatchCommand : Runnable { val outputFilePath = outputFilePath ?: File("").absoluteFile.resolve( - "${apk.nameWithoutExtension}-patched.${apk.extension}", + "${apk.nameWithoutExtension}-patched.apk", ) val temporaryFilesPath = @@ -312,12 +314,35 @@ internal object PatchCommand : Runnable { val patcherTemporaryFilesPath = temporaryFilesPath.resolve("patcher") + // Checking if the file is in apkm format (like reddit) + var mergedApkToCleanup: File? = null + val inputApk = if (apk.extension.equals("apkm", ignoreCase = true)) { + logger.info("Detected APKM file, converting to APK...") + + // Save merged APK to output directory (will be cleaned up after patching) + val outputApk = outputFilePath.parentFile.resolve("${apk.nameWithoutExtension}-merged.apk") + + // Use APKEditor's Merger directly (handles extraction and merging) + val mergerOptions = MergerOptions().apply { + inputFile = apk // Original APKM file + outputFile = outputApk + cleanMeta = true + } + Merger(mergerOptions).run() + + logger.info("Conversion complete: ${outputApk.path}") + mergedApkToCleanup = outputApk + outputApk + } else { + apk + } + val patchingResult = PatchingResult() try { val (packageName, patcherResult) = Patcher( PatcherConfig( - apk, + inputApk, patcherTemporaryFilesPath, aaptBinaryPath?.path, patcherTemporaryFilesPath.absolutePath, @@ -377,7 +402,7 @@ internal object PatchCommand : Runnable { // region Save. - apk.copyTo(temporaryFilesPath.resolve(apk.name), overwrite = true).apply { + inputApk.copyTo(temporaryFilesPath.resolve(inputApk.name), overwrite = true).apply { patchingResult.addStepResult( PatchingStep.REBUILDING, { @@ -406,6 +431,7 @@ internal object PatchCommand : Runnable { patchedApkFile.copyTo(outputFilePath, overwrite = true) } } + logger.info("Saved to $outputFilePath") // endregion @@ -448,6 +474,13 @@ internal object PatchCommand : Runnable { logger.info("Purging temporary files") purge(temporaryFilesPath) } + + // Clean up merged APK if we created one from APKM + mergedApkToCleanup?.let { + if (it.delete()) { + logger.info("Cleaned up merged APK: ${it.path}") + } + } } /**