mirror of
https://github.com/ReVanced/revanced-cli.git
synced 2026-01-18 00:43:58 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37cc77dfc5 | ||
|
|
075bf406fd | ||
|
|
71c81510f7 | ||
|
|
51fd16409f | ||
|
|
3f5345af6e |
@@ -1,3 +1,12 @@
|
|||||||
|
## [2.10.1](https://github.com/revanced/revanced-cli/compare/v2.10.0...v2.10.1) (2022-09-09)
|
||||||
|
|
||||||
|
# [2.10.0](https://github.com/revanced/revanced-cli/compare/v2.9.10...v2.10.0) (2022-09-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Patch Options CLI implementation ([#132](https://github.com/revanced/revanced-cli/issues/132)) ([3f5345a](https://github.com/revanced/revanced-cli/commit/3f5345af6e45bfb6c91d52fc089ab18d81fdc998))
|
||||||
|
|
||||||
## [2.9.10](https://github.com/revanced/revanced-cli/compare/v2.9.9...v2.9.10) (2022-09-08)
|
## [2.9.10](https://github.com/revanced/revanced-cli/compare/v2.9.9...v2.9.10) (2022-09-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.4.0")
|
||||||
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 {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 2.9.10
|
version = 2.10.1
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
77
src/main/kotlin/app/revanced/utils/OptionsLoader.kt
Normal file
77
src/main/kotlin/app/revanced/utils/OptionsLoader.kt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package app.revanced.utils
|
||||||
|
|
||||||
|
import app.revanced.cli.command.MainCommand.logger
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
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.encodeToString
|
||||||
|
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 = MutableMap<String, MutableMap<String, Any>>
|
||||||
|
|
||||||
|
object OptionsLoader {
|
||||||
|
@JvmStatic
|
||||||
|
private val mapper = tomlMapper {}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun init(file: File, patches: PatchList) {
|
||||||
|
if (!file.exists()) file.createNewFile()
|
||||||
|
val map = mapper.decodeWithDefaults(generateDefaults(patches), TomlValue.from(file.toPath()))
|
||||||
|
readAndSet(map, patches)
|
||||||
|
save(map, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (value == "null") { // backwards compatibility, subject to removal
|
||||||
|
options.remove(key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
patchOptions[key] = value
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Error while setting option $key for patch $patchName: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun save(map: OptionsMap, file: File) {
|
||||||
|
val toml = mapper.encodeToString(map)
|
||||||
|
file.writeText(
|
||||||
|
"""
|
||||||
|
# A list of options for each patch.
|
||||||
|
# This file does not contain all options by default.
|
||||||
|
# Run the CLI with the "--list --with-options" flags to see all available options.
|
||||||
|
# You can also run the CLI with the aforementioned flags and a patch name to see all available options for that patch.
|
||||||
|
# To set an option, add a line with the format "option = value".
|
||||||
|
# To remove or reset an option to its default value, delete the line.
|
||||||
|
# To reset all options to their default values, delete this file.
|
||||||
|
#
|
||||||
|
# This file was generated by ReVanced Patcher version ${Patcher.version}.
|
||||||
|
""".trimIndent() + "\n\n$toml"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ?: continue)
|
||||||
|
}
|
||||||
|
} as MutableMap)
|
||||||
|
}
|
||||||
|
} as MutableMap
|
||||||
|
}
|
||||||
@@ -10,61 +10,59 @@ 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>>>()
|
allPatches.forEach patchLoop@{ patch ->
|
||||||
JarPatchBundle(bundle).loadPatches().forEach patch@{ patch ->
|
val compatiblePackages = patch.compatiblePackages
|
||||||
val compatiblePackages = patch.compatiblePackages
|
val patchName = patch.patchName
|
||||||
val patchName = patch.patchName
|
|
||||||
|
|
||||||
val prefix = "Skipping $patchName, reason"
|
val prefix = "Skipping $patchName, reason"
|
||||||
|
|
||||||
val args = MainCommand.args.patchArgs?.patchingArgs!!
|
val args = MainCommand.args.patchArgs?.patchingArgs!!
|
||||||
|
|
||||||
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) ->
|
|
||||||
logger.warn("$prefix: deprecated: $reason")
|
|
||||||
if (replacement != null) logger.warn("Either use ${replacement.java.patchName} instead or include it manually")
|
|
||||||
return@patch
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
|
|
||||||
else {
|
|
||||||
if (!compatiblePackages.any { it.name == packageName }) {
|
|
||||||
logger.warn("$prefix: incompatible with $packageName. This patch is only compatible with ${
|
|
||||||
compatiblePackages.joinToString(
|
|
||||||
", "
|
|
||||||
) { it.name }
|
|
||||||
}")
|
|
||||||
return@patch
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
|
|
||||||
val compatibleWith = compatiblePackages.joinToString(";") { _package ->
|
|
||||||
"${_package.name}: ${_package.versions.joinToString(", ")}"
|
|
||||||
}
|
|
||||||
logger.warn("$prefix: incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
|
|
||||||
return@patch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trace("Adding $patchName")
|
|
||||||
includedPatches.add(patch)
|
|
||||||
}
|
}
|
||||||
this.addPatches(includedPatches)
|
|
||||||
|
patch.deprecated?.let { (reason, replacement) ->
|
||||||
|
logger.warn("$prefix: deprecated: $reason")
|
||||||
|
if (replacement != null) logger.warn("Either use ${replacement.java.patchName} instead or include it manually")
|
||||||
|
return@patchLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatiblePackages == null) logger.warn("$prefix: Missing compatibility annotation. Continuing.")
|
||||||
|
else {
|
||||||
|
if (!compatiblePackages.any { it.name == packageName }) {
|
||||||
|
logger.warn("$prefix: incompatible with $packageName. This patch is only compatible with ${
|
||||||
|
compatiblePackages.joinToString(
|
||||||
|
", "
|
||||||
|
) { it.name }
|
||||||
|
}")
|
||||||
|
return@patchLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
|
||||||
|
val compatibleWith = compatiblePackages.joinToString(";") { _package ->
|
||||||
|
"${_package.name}: ${_package.versions.joinToString(", ")}"
|
||||||
|
}
|
||||||
|
logger.warn("$prefix: incompatible with version $packageVersion. This patch is only compatible with version $compatibleWith")
|
||||||
|
return@patchLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Adding $patchName")
|
||||||
|
includedPatches.add(patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addPatches(includedPatches)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Patcher.applyPatchesVerbose() {
|
fun Patcher.applyPatchesVerbose() {
|
||||||
|
|||||||
Reference in New Issue
Block a user