mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-06-02 19:36:07 +02:00
Merge remote-tracking branch 'upstream/dev' into insan
This commit is contained in:
commit
5ef10b05a5
|
@ -1,3 +1,10 @@
|
|||
# [2.21.0-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.0-dev.1...v2.21.0-dev.2) (2023-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use working JADB dependency version ([#222](https://github.com/revanced/revanced-cli/issues/222)) ([94a71ef](https://github.com/revanced/revanced-cli/commit/94a71ef2779aacae0458edff96b5fbac0e410dc2))
|
||||
|
||||
# [2.21.0-dev.1](https://github.com/revanced/revanced-cli/compare/v2.20.2-dev.1...v2.21.0-dev.1) (2023-04-30)
|
||||
|
||||
|
||||
|
|
|
@ -28,10 +28,11 @@ dependencies {
|
|||
|
||||
implementation("app.revanced:revanced-patcher:7.1.0-dev.1")
|
||||
implementation("info.picocli:picocli:4.7.1")
|
||||
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork
|
||||
implementation("com.github.revanced:jadb:2531a28109") // updated fork
|
||||
implementation("com.android.tools.build:apksig:8.1.0-alpha09")
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
implementation("cc.ekblad:4koma:1.1.0")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.+")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
kotlin.code.style = official
|
||||
version = 2.21.0-dev.1
|
||||
version = 2.21.0-dev.2
|
||||
|
|
|
@ -10,8 +10,11 @@ import app.revanced.patcher.apk.ApkBundle
|
|||
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
||||
import app.revanced.patcher.extensions.PatchExtensions.include
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchClass
|
||||
import app.revanced.patcher.util.patch.PatchBundle
|
||||
import app.revanced.utils.OptionsLoader
|
||||
import app.revanced.utils.Options
|
||||
import app.revanced.utils.Options.setOptions
|
||||
import app.revanced.utils.adb.Adb
|
||||
import app.revanced.utils.apk.ApkSigner
|
||||
import picocli.CommandLine.*
|
||||
|
@ -19,6 +22,11 @@ import java.io.File
|
|||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
/**
|
||||
* Alias for a list of patches.
|
||||
*/
|
||||
internal typealias PatchList = List<PatchClass>
|
||||
|
||||
private class CLIVersionProvider : IVersionProvider {
|
||||
override fun getVersion() = arrayOf(
|
||||
MainCommand::class.java.`package`.implementationVersion ?: "unknown"
|
||||
|
@ -75,8 +83,8 @@ internal object MainCommand : Runnable {
|
|||
@Option(names = ["-o", "--out"], description = ["Output folder path"], required = true)
|
||||
var outputPath: File = File("revanced")
|
||||
|
||||
@Option(names = ["--options"], description = ["Configuration file for all patch options"])
|
||||
var options: File? = null
|
||||
@Option(names = ["--options"], description = ["Path to patch options JSON file"])
|
||||
var optionsFile: File = File("options.json")
|
||||
|
||||
@Option(names = ["-e", "--exclude"], description = ["Explicitly exclude patches"])
|
||||
var excludedPatches = arrayOf<String>()
|
||||
|
@ -173,11 +181,15 @@ internal object MainCommand : Runnable {
|
|||
val apkArgs = patchingArgs.apkArgs!!
|
||||
|
||||
val apkBundle = ApkBundle(
|
||||
if (apkArgs.apkDir != null) apkArgs.apkDir!!.listFiles()!!.filter { it.extension == "apk" } else apkArgs.apks)
|
||||
if (apkArgs.apkDir != null) apkArgs.apkDir!!.listFiles()!!
|
||||
.filter { it.extension == "apk" } else apkArgs.apks)
|
||||
|
||||
// prepare the patches
|
||||
val allPatches = patchArgs.patchBundles.flatMap { bundle -> PatchBundle.Jar(bundle).readPatches() }.also {
|
||||
OptionsLoader.init(patchingArgs.options ?: patchingArgs.outputPath.resolve("options.toml"), it)
|
||||
val allPatches = patchArgs.patchBundles.flatMap { bundle -> PatchBundle.Jar(bundle).readPatches() }
|
||||
|
||||
patchingArgs.optionsFile.let {
|
||||
if (it.exists()) allPatches.setOptions(it, logger)
|
||||
else Options.serialize(allPatches, prettyPrint = true).let(it::writeText)
|
||||
}
|
||||
|
||||
// prepare the patcher
|
||||
|
|
103
src/main/kotlin/app/revanced/utils/Options.kt
Normal file
103
src/main/kotlin/app/revanced/utils/Options.kt
Normal file
|
@ -0,0 +1,103 @@
|
|||
package app.revanced.utils
|
||||
|
||||
import app.revanced.cli.command.PatchList
|
||||
import app.revanced.cli.logging.CliLogger
|
||||
import app.revanced.patcher.extensions.PatchExtensions.options
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.patch.NoSuchOptionException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import java.io.File
|
||||
|
||||
|
||||
internal object Options {
|
||||
private var mapper = jacksonObjectMapper()
|
||||
|
||||
/**
|
||||
* Serializes the options for the patches in the list.
|
||||
*
|
||||
* @param patches The list of patches to serialize.
|
||||
* @param prettyPrint Whether to pretty print the JSON.
|
||||
* @return The JSON string containing the options.
|
||||
* @see PatchList
|
||||
*/
|
||||
fun serialize(patches: PatchList, prettyPrint: Boolean = false): String = patches
|
||||
.filter { it.options?.any() == true }
|
||||
.map { patch ->
|
||||
PatchOption(
|
||||
patch.patchName,
|
||||
patch.options!!.map { option -> PatchOption.Option(option.key, option.value) }
|
||||
)
|
||||
}.let {
|
||||
if (prettyPrint)
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it)
|
||||
else
|
||||
mapper.writeValueAsString(it)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the options for the patches in the list.
|
||||
*
|
||||
* @param json The JSON string containing the options.
|
||||
* @return The list of [PatchOption]s.
|
||||
* @see PatchOption
|
||||
* @see PatchList
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun deserialize(json: String): Array<PatchOption> = mapper.readValue(json, Array<PatchOption>::class.java)
|
||||
|
||||
/**
|
||||
* Sets the options for the patches in the list.
|
||||
*
|
||||
* @param json The JSON string containing the options.
|
||||
* @param logger The logger to use for logging.
|
||||
*/
|
||||
fun PatchList.setOptions(json: String, logger: CliLogger? = null) {
|
||||
filter { it.options?.any() == true }.let { patches ->
|
||||
if (patches.isEmpty()) return
|
||||
|
||||
val patchOptions = deserialize(json)
|
||||
|
||||
patches.forEach { patch ->
|
||||
patchOptions.find { option -> option.patchName == patch.patchName }?.let {
|
||||
it.options.forEach { option ->
|
||||
try {
|
||||
patch.options?.set(option.key, option.value)
|
||||
?: logger?.warn("${patch.patchName} has no options")
|
||||
} catch (e: NoSuchOptionException) {
|
||||
logger?.error(e.message ?: "Unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options for the patches in the list.
|
||||
*
|
||||
* @param file The file containing the JSON string containing the options.
|
||||
* @param logger The logger to use for logging.
|
||||
* @see setOptions
|
||||
*/
|
||||
fun PatchList.setOptions(file: File, logger: CliLogger? = null) = setOptions(file.readText(), logger)
|
||||
|
||||
/**
|
||||
* Data class for a patch and its [Option]s.
|
||||
*
|
||||
* @property patchName The name of the patch.
|
||||
* @property options The [Option]s for the patch.
|
||||
*/
|
||||
internal data class PatchOption(
|
||||
val patchName: String,
|
||||
val options: List<Option>
|
||||
) {
|
||||
|
||||
/**
|
||||
* Data class for patch option.
|
||||
*
|
||||
* @property key The name of the option.
|
||||
* @property value The value of the option.
|
||||
*/
|
||||
internal data class Option(val key: String, val value: Any?)
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package app.revanced.utils
|
||||
|
||||
import app.revanced.cli.command.MainCommand.logger
|
||||
import app.revanced.patcher.Patcher
|
||||
import app.revanced.patcher.Context
|
||||
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<Context>>>
|
||||
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" or set the value if the option already exists.
|
||||
# To 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
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package app.revanced.patcher.options
|
||||
|
||||
import app.revanced.patcher.BytecodeContext
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.utils.Options
|
||||
import app.revanced.utils.Options.setOptions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class PatchOptionsTestPatch : BytecodePatch() {
|
||||
override fun execute(context: BytecodeContext) {}
|
||||
|
||||
companion object : OptionsContainer() {
|
||||
var key1 by option(
|
||||
PatchOption.StringOption(
|
||||
"key1", null, "title1", "description1"
|
||||
)
|
||||
)
|
||||
|
||||
var key2 by option(
|
||||
PatchOption.BooleanOption(
|
||||
"key2", true, "title2", "description2"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal object PatchOptionOptionsTest {
|
||||
private var patches = listOf(PatchOptionsTestPatch::class.java as PatchClass)
|
||||
|
||||
@Test
|
||||
fun serializeTest() {
|
||||
assert(SERIALIZED_JSON == Options.serialize(patches))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadOptionsTest() {
|
||||
patches.setOptions(CHANGED_JSON)
|
||||
|
||||
assert(PatchOptionsTestPatch.key1 == "test")
|
||||
assert(PatchOptionsTestPatch.key2 == false)
|
||||
}
|
||||
|
||||
private const val SERIALIZED_JSON =
|
||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\",\"value\":true}]}]"
|
||||
|
||||
private const val CHANGED_JSON =
|
||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":false}]}]"
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user