Merge remote-tracking branch 'upstream/dev' into insan

This commit is contained in:
Ax333l 2023-05-03 21:34:09 +02:00
commit 5ef10b05a5
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
8 changed files with 181 additions and 86 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -1,2 +1,2 @@
kotlin.code.style = official
version = 2.21.0-dev.1
version = 2.21.0-dev.2

View File

@ -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

View 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?)
}
}

View File

@ -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
}

View File

@ -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}]}]"
}