diff --git a/build.gradle.kts b/build.gradle.kts index 78f6682..5294530 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,11 +25,13 @@ repositories { dependencies { implementation(kotlin("stdlib")) implementation("app.revanced:revanced-patcher:+") + implementation("app.revanced:revanced-patches:+") + implementation("info.picocli:picocli:+") - implementation("me.tongfei:progressbar:+") implementation("com.github.li-wjohnson:jadb:master-SNAPSHOT") // using a fork instead. implementation("org.bouncycastle:bcpkix-jdk15on:+") + implementation(kotlin("reflect")) } java { diff --git a/src/main/kotlin/app/revanced/cli/MainCommand.kt b/src/main/kotlin/app/revanced/cli/MainCommand.kt index 0bed6d2..13440dd 100644 --- a/src/main/kotlin/app/revanced/cli/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/MainCommand.kt @@ -1,6 +1,8 @@ package app.revanced.cli import app.revanced.patch.Patches +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.extensions.findAnnotationRecursively import app.revanced.utils.adb.Adb import picocli.CommandLine.* import java.io.File @@ -47,36 +49,36 @@ internal object MainCommand : Runnable { override fun run() { if (listOnly) { - patchBundles.forEach { - Patches.load(it).forEach { - println(it().metadata) - } - } + for (patchBundle in patchBundles) for (it in Patches.load(patchBundle)) println( + "[available] ${ + it.javaClass.findAnnotationRecursively( + Name::class.java + )?.name ?: Name::class.java.name + }" + ) return } + val outputFile = File(outputPath) + val patcher = app.revanced.patcher.Patcher( - inputFile, - cacheDirectory, - patchResources + inputFile, cacheDirectory, patchResources ) + var adb: Adb? = null + deploy?.let { + adb = Adb( + outputFile, patcher.packageName, deploy!! + ) + } + Patcher.start(patcher) if (clean) { File(cacheDirectory).deleteRecursively() + outputFile.delete() } - val outputFile = File(outputPath) - - deploy?.let { - Adb( - outputFile, - patcher.packageName, - deploy!! - ).deploy() - } - - if (clean) outputFile.delete() + adb?.deploy() } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/Patcher.kt b/src/main/kotlin/app/revanced/cli/Patcher.kt index c20d796..5459122 100644 --- a/src/main/kotlin/app/revanced/cli/Patcher.kt +++ b/src/main/kotlin/app/revanced/cli/Patcher.kt @@ -1,7 +1,10 @@ package app.revanced.cli import app.revanced.patch.Patches +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Name import app.revanced.patcher.data.base.Data +import app.revanced.patcher.extensions.findAnnotationRecursively import app.revanced.patcher.patch.base.Patch import app.revanced.utils.filesystem.FileSystemUtils import app.revanced.utils.signing.Signer @@ -15,10 +18,8 @@ internal class Patcher { // add patches, but filter incompatible or excluded patches patcher.addPatchesFiltered() // apply patches - for ((meta, result) in patcher.applyPatches { - println("Applying $it.") - }) { - println("Applied ${meta.name}. The result was $result.") + for ((patch, result) in patcher.applyPatches()) { + println("[error: ${result.isFailure}] $patch") } // write output file @@ -34,7 +35,7 @@ internal class Patcher { } if (MainCommand.patchResources) { - for (file in File(MainCommand.cacheDirectory).resolve("build/").listFiles().first().listFiles()) { + for (file in File(MainCommand.cacheDirectory).resolve("build/").listFiles()?.first()?.listFiles()!!) { if (!file.isDirectory) { zipFileSystem.replaceFile(file.name, file.readBytes()) continue @@ -48,6 +49,8 @@ internal class Patcher { // and sign the apk file Signer.signApk(outFile) + + println("[done]") } private fun app.revanced.patcher.Patcher.addPatchesFiltered() { @@ -58,25 +61,34 @@ internal class Patcher { MainCommand.patchBundles.forEach { bundle -> val includedPatches = mutableListOf>() - Patches.load(bundle).forEach patch@{ - val patch = it() + Patches.load(bundle).forEach patch@{ it -> + val patch = it.getDeclaredConstructor().newInstance() val filterOutPatches = true - if (filterOutPatches && !patch.metadata.compatiblePackages.any { packageMetadata -> - packageMetadata.name == packageName && packageMetadata.versions.any { - it == packageVersion - } - }) { - println("Skipping ${patch.metadata.name} due to incompatibility with current package $packageName.") + val compatibilityAnnotation = patch.javaClass.findAnnotationRecursively(Compatibility::class.java) + + val patchName = + patch.javaClass.findAnnotationRecursively(Name::class.java)?.name ?: Name::class.java.name + + if (checkInclude && !MainCommand.includedPatches.contains(patchName)) { return@patch } - if (checkInclude && !MainCommand.includedPatches.contains(patch.metadata.shortName)) { - return@patch + if (filterOutPatches) { + if (compatibilityAnnotation == null || !(compatibilityAnnotation.compatiblePackages.any { packageMetadata -> + packageMetadata.name == packageName && packageMetadata.versions.any { + it == packageVersion + } + })) { + // TODO: misleading error message + println("[Skipped] $patchName: Incompatible with current package.") + return@patch + } } - println("Adding ${patch.metadata.name}.") + + println("[loaded] $patchName") includedPatches.add(patch) } diff --git a/src/main/kotlin/app/revanced/patch/Patches.kt b/src/main/kotlin/app/revanced/patch/Patches.kt index 4b24a81..8ed9f2c 100644 --- a/src/main/kotlin/app/revanced/patch/Patches.kt +++ b/src/main/kotlin/app/revanced/patch/Patches.kt @@ -1,26 +1,35 @@ package app.revanced.patch -import app.revanced.patcher.data.base.Data import app.revanced.patcher.patch.base.Patch import java.io.File import java.net.URLClassLoader +import java.util.jar.JarFile internal object Patches { /** - * This method loads patches from a given patch file - * @return the loaded patches represented as a list of functions returning instances of [Patch] + * This method loads patches from a given jar file containing [Patch]es + * @return the loaded patches represented as a list of [Patch] classes */ - internal fun load(patchesJar: File): List<() -> Patch> { - val url = patchesJar.toURI().toURL() - val classLoader = URLClassLoader(arrayOf(url)) + internal fun load(patchesJar: File) = buildList { + val jarFile = JarFile(patchesJar) + val classLoader = URLClassLoader(arrayOf(patchesJar.toURI().toURL())) - val indexClass = classLoader.loadClass("app.revanced.patches.Index") + val entries = jarFile.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + if (!entry.name.endsWith(".class") || entry.name.contains("$")) continue - val index = indexClass.declaredFields.last() - index.isAccessible = true + val clazz = classLoader.loadClass(entry.realName.replace('/', '.').replace(".class", "")) - @Suppress("UNCHECKED_CAST") - return index.get(null) as List<() -> Patch> + if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) continue + + @Suppress("UNCHECKED_CAST") + val patch = clazz as Class> + + // TODO: include declared classes from patch + + this.add(patch) + } } } diff --git a/src/main/kotlin/app/revanced/utils/adb/Constants.kt b/src/main/kotlin/app/revanced/utils/adb/Constants.kt index 04b4ff3..1b27e3d 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Constants.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Constants.kt @@ -8,7 +8,7 @@ internal object Constants { private const val COMMAND_CHMOD_MOUNT = "chmod +x" internal const val COMMAND_PID_OF = "pidof -s" internal const val COMMAND_CREATE_DIR = "mkdir -p" - internal const val COMMAND_LOGCAT = "logcat -c && logcat --pid=$($COMMAND_PID_OF $PLACEHOLDER)" + internal const val COMMAND_LOGCAT = "logcat -c && logcat | grep AndroidRuntime" internal const val COMMAND_RESTART = "monkey -p $PLACEHOLDER 1 && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)" // default mount file name