mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-12-05 01:42:54 +01:00
feat: Patch Options CLI implementation (#132)
* feat: Patch Options CLI implementation * fix: remove leftover log message
This commit is contained in:
parent
649d9bdb2a
commit
3f5345af6e
@ -25,11 +25,12 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
|
|
||||||
implementation("app.revanced:revanced-patcher:4.2.2")
|
implementation("app.revanced:revanced-patcher:4.2.3")
|
||||||
implementation("info.picocli:picocli:4.6.3")
|
implementation("info.picocli:picocli:4.6.3")
|
||||||
implementation("com.android.tools.build:apksig:7.2.1")
|
implementation("com.android.tools.build:apksig:7.2.1")
|
||||||
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork
|
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork
|
||||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||||
|
implementation("cc.ekblad:4koma:1.1.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
@ -11,6 +11,7 @@ import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
|||||||
import app.revanced.patcher.extensions.PatchExtensions.description
|
import app.revanced.patcher.extensions.PatchExtensions.description
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
import app.revanced.patcher.util.patch.impl.JarPatchBundle
|
import app.revanced.patcher.util.patch.impl.JarPatchBundle
|
||||||
|
import app.revanced.utils.OptionsLoader
|
||||||
import app.revanced.utils.adb.Adb
|
import app.revanced.utils.adb.Adb
|
||||||
import picocli.CommandLine.*
|
import picocli.CommandLine.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -51,6 +52,9 @@ internal object MainCommand : Runnable {
|
|||||||
@Option(names = ["-b", "--bundles"], description = ["One or more bundles of patches"], required = true)
|
@Option(names = ["-b", "--bundles"], description = ["One or more bundles of patches"], required = true)
|
||||||
var patchBundles = arrayOf<String>()
|
var patchBundles = arrayOf<String>()
|
||||||
|
|
||||||
|
@Option(names = ["--options"], description = ["Configuration file for all patch options"])
|
||||||
|
var options: File = File("options.toml")
|
||||||
|
|
||||||
@ArgGroup(exclusive = false)
|
@ArgGroup(exclusive = false)
|
||||||
var listingArgs: ListingArgs? = null
|
var listingArgs: ListingArgs? = null
|
||||||
|
|
||||||
@ -123,20 +127,17 @@ internal object MainCommand : Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
if (args.patchArgs?.listingArgs?.listOnly == true) {
|
if (args.patchArgs?.listingArgs?.listOnly == true) return printListOfPatches()
|
||||||
printListOfPatches()
|
if (args.uninstall) return uninstall()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.uninstall) {
|
|
||||||
uninstall()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val pArgs = this.args.patchArgs?.patchingArgs ?: return
|
val pArgs = this.args.patchArgs?.patchingArgs ?: return
|
||||||
|
val outputFile = File(pArgs.outputPath) // the file to write to
|
||||||
|
|
||||||
// the file to write to
|
val allPatches = args.patchArgs!!.patchBundles.flatMap { bundle ->
|
||||||
val outputFile = File(pArgs.outputPath)
|
JarPatchBundle(bundle).loadPatches()
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsLoader.init(args.patchArgs!!.options, allPatches)
|
||||||
|
|
||||||
val patcher = app.revanced.patcher.Patcher(
|
val patcher = app.revanced.patcher.Patcher(
|
||||||
PatcherOptions(
|
PatcherOptions(
|
||||||
@ -157,7 +158,7 @@ internal object MainCommand : Runnable {
|
|||||||
val patchedFile = File(pArgs.cacheDirectory).resolve("${outputFile.nameWithoutExtension}_raw.apk")
|
val patchedFile = File(pArgs.cacheDirectory).resolve("${outputFile.nameWithoutExtension}_raw.apk")
|
||||||
|
|
||||||
// start the patcher
|
// start the patcher
|
||||||
Patcher.start(patcher, patchedFile)
|
Patcher.start(patcher, patchedFile, allPatches)
|
||||||
|
|
||||||
val cacheDirectory = File(pArgs.cacheDirectory)
|
val cacheDirectory = File(pArgs.cacheDirectory)
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ package app.revanced.cli.patcher
|
|||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.args
|
import app.revanced.cli.command.MainCommand.args
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
import app.revanced.cli.command.MainCommand.logger
|
||||||
|
import app.revanced.patcher.data.Data
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.utils.filesystem.ZipFileSystemUtils
|
import app.revanced.utils.filesystem.ZipFileSystemUtils
|
||||||
import app.revanced.utils.patcher.addPatchesFiltered
|
import app.revanced.utils.patcher.addPatchesFiltered
|
||||||
import app.revanced.utils.patcher.applyPatchesVerbose
|
import app.revanced.utils.patcher.applyPatchesVerbose
|
||||||
@ -10,14 +12,14 @@ import java.io.File
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
internal object Patcher {
|
internal object Patcher {
|
||||||
internal fun start(patcher: app.revanced.patcher.Patcher, output: File) {
|
internal fun start(patcher: app.revanced.patcher.Patcher, output: File, allPatches: List<Class<out Patch<Data>>>) {
|
||||||
val inputFile = args.inputFile
|
val inputFile = args.inputFile
|
||||||
val args = args.patchArgs?.patchingArgs!!
|
val args = args.patchArgs?.patchingArgs!!
|
||||||
|
|
||||||
// merge files like necessary integrations
|
// merge files like necessary integrations
|
||||||
patcher.mergeFiles()
|
patcher.mergeFiles()
|
||||||
// add patches, but filter incompatible or excluded patches
|
// add patches, but filter incompatible or excluded patches
|
||||||
patcher.addPatchesFiltered()
|
patcher.addPatchesFiltered(allPatches)
|
||||||
// apply patches
|
// apply patches
|
||||||
patcher.applyPatchesVerbose()
|
patcher.applyPatchesVerbose()
|
||||||
|
|
||||||
|
62
src/main/kotlin/app/revanced/utils/OptionsLoader.kt
Normal file
62
src/main/kotlin/app/revanced/utils/OptionsLoader.kt
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package app.revanced.utils
|
||||||
|
|
||||||
|
import app.revanced.cli.command.MainCommand.logger
|
||||||
|
import app.revanced.patcher.data.Data
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.options
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import cc.ekblad.toml.encodeTo
|
||||||
|
import cc.ekblad.toml.model.TomlValue
|
||||||
|
import cc.ekblad.toml.serialization.from
|
||||||
|
import cc.ekblad.toml.tomlMapper
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private typealias PatchList = List<Class<out Patch<Data>>>
|
||||||
|
private typealias OptionsMap = Map<String, Map<String, Any>>
|
||||||
|
|
||||||
|
private const val NULL = "null"
|
||||||
|
|
||||||
|
object OptionsLoader {
|
||||||
|
@JvmStatic
|
||||||
|
private val mapper = tomlMapper {}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun init(file: File, patches: PatchList) {
|
||||||
|
if (!file.exists()) file.createNewFile()
|
||||||
|
val path = file.toPath()
|
||||||
|
val map = mapper.decodeWithDefaults(
|
||||||
|
generateDefaults(patches),
|
||||||
|
TomlValue.from(path)
|
||||||
|
).also { mapper.encodeTo(path, it) }
|
||||||
|
readAndSet(map, patches)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readAndSet(map: OptionsMap, patches: PatchList) {
|
||||||
|
for ((patchName, options) in map) {
|
||||||
|
val patch = patches.find { it.patchName == patchName } ?: continue
|
||||||
|
val patchOptions = patch.options ?: continue
|
||||||
|
for ((key, value) in options) {
|
||||||
|
try {
|
||||||
|
patchOptions[key] = value.let {
|
||||||
|
if (it == NULL) null else it
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.warn("Error while setting option $key for patch $patchName: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateDefaults(patches: PatchList) = buildMap {
|
||||||
|
for (patch in patches) {
|
||||||
|
val options = patch.options ?: continue
|
||||||
|
if (!options.iterator().hasNext()) continue
|
||||||
|
put(patch.patchName, buildMap {
|
||||||
|
for (option in options) {
|
||||||
|
put(option.key, option.value ?: NULL)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,15 +10,13 @@ import app.revanced.patcher.extensions.PatchExtensions.deprecated
|
|||||||
import app.revanced.patcher.extensions.PatchExtensions.include
|
import app.revanced.patcher.extensions.PatchExtensions.include
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.patcher.util.patch.impl.JarPatchBundle
|
|
||||||
|
|
||||||
fun Patcher.addPatchesFiltered() {
|
fun Patcher.addPatchesFiltered(allPatches: List<Class<out Patch<Data>>>) {
|
||||||
val packageName = this.data.packageMetadata.packageName
|
val packageName = this.data.packageMetadata.packageName
|
||||||
val packageVersion = this.data.packageMetadata.packageVersion
|
val packageVersion = this.data.packageMetadata.packageVersion
|
||||||
|
|
||||||
args.patchArgs?.patchBundles!!.forEach { bundle ->
|
|
||||||
val includedPatches = mutableListOf<Class<out Patch<Data>>>()
|
val includedPatches = mutableListOf<Class<out Patch<Data>>>()
|
||||||
JarPatchBundle(bundle).loadPatches().forEach patch@{ patch ->
|
allPatches.forEach patchLoop@{ patch ->
|
||||||
val compatiblePackages = patch.compatiblePackages
|
val compatiblePackages = patch.compatiblePackages
|
||||||
val patchName = patch.patchName
|
val patchName = patch.patchName
|
||||||
|
|
||||||
@ -28,16 +26,16 @@ fun Patcher.addPatchesFiltered() {
|
|||||||
|
|
||||||
if (args.excludedPatches.contains(patchName)) {
|
if (args.excludedPatches.contains(patchName)) {
|
||||||
logger.info("$prefix: manually excluded")
|
logger.info("$prefix: manually excluded")
|
||||||
return@patch
|
return@patchLoop
|
||||||
} else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) {
|
} else if ((!patch.include || args.defaultExclude) && !args.includedPatches.contains(patchName)) {
|
||||||
logger.info("$prefix: excluded by default")
|
logger.info("$prefix: excluded by default")
|
||||||
return@patch
|
return@patchLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.deprecated?.let { (reason, replacement) ->
|
patch.deprecated?.let { (reason, replacement) ->
|
||||||
logger.warn("$prefix: deprecated: $reason")
|
logger.warn("$prefix: deprecated: $reason")
|
||||||
if (replacement != null) logger.warn("Either use ${replacement.java.patchName} instead or include it manually")
|
if (replacement != null) logger.warn("Either use ${replacement.java.patchName} instead or include it manually")
|
||||||
return@patch
|
return@patchLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
|
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
|
||||||
@ -48,7 +46,7 @@ fun Patcher.addPatchesFiltered() {
|
|||||||
", "
|
", "
|
||||||
) { it.name }
|
) { it.name }
|
||||||
}")
|
}")
|
||||||
return@patch
|
return@patchLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
|
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
|
||||||
@ -56,16 +54,16 @@ fun Patcher.addPatchesFiltered() {
|
|||||||
"${_package.name}: ${_package.versions.joinToString(", ")}"
|
"${_package.name}: ${_package.versions.joinToString(", ")}"
|
||||||
}
|
}
|
||||||
logger.warn("$prefix: incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
|
logger.warn("$prefix: incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
|
||||||
return@patch
|
return@patchLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace("Adding $patchName")
|
logger.trace("Adding $patchName")
|
||||||
includedPatches.add(patch)
|
includedPatches.add(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addPatches(includedPatches)
|
this.addPatches(includedPatches)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun Patcher.applyPatchesVerbose() {
|
fun Patcher.applyPatchesVerbose() {
|
||||||
this.applyPatches().forEach { (patch, result) ->
|
this.applyPatches().forEach { (patch, result) ->
|
||||||
|
Loading…
Reference in New Issue
Block a user