Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.picocli)
implementation(libs.arsclib)

testImplementation(libs.kotlin.test)
}
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ kotlinx = "1.9.0"
picocli = "4.7.7"
morphe-patcher = "1.0.1"
morphe-library = "1.0.1"
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" }
Expand Down
56 changes: 54 additions & 2 deletions src/main/kotlin/app/morphe/cli/command/PatchCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.apk.ApkBundle
import java.util.zip.ZipFile
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -312,12 +314,54 @@ 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...")
temporaryFilesPath.mkdirs()

// Extract APKM to temp directory
val extractedDir = temporaryFilesPath.resolve("apkm_extracted")
extractedDir.mkdirs()

ZipFile(apk).use { zip ->
zip.entries().asSequence().forEach { entry ->
val outFile = extractedDir.resolve(entry.name)
if (entry.isDirectory) {
outFile.mkdirs()
} else {
outFile.parentFile?.mkdirs()
zip.getInputStream(entry).use { input ->
outFile.outputStream().use { output ->
input.copyTo(output)
}
}
}
}
}

// Save merged APK to output directory (outside temp structure for testing, but ideally this is in the temp folder too and gets cleaned)
val outputApk = outputFilePath.parentFile.resolve("${apk.nameWithoutExtension}-merged.apk")

// Use ARSCLib to merge split APKs
val bundle = ApkBundle()
bundle.loadApkDirectory(extractedDir)
val mergedModule = bundle.mergeModules()
mergedModule.writeApk(outputApk)

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,
Expand Down Expand Up @@ -377,7 +421,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,
{
Expand Down Expand Up @@ -406,6 +450,7 @@ internal object PatchCommand : Runnable {
patchedApkFile.copyTo(outputFilePath, overwrite = true)
}
}

logger.info("Saved to $outputFilePath")

// endregion
Expand Down Expand Up @@ -448,6 +493,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}")
}
}
}

/**
Expand Down
Loading