Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Set patch options via CLI #336

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 81 additions & 10 deletions docs/1_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ java -jar revanced-cli.jar -h
## 📃 List patches

```bash
java -jar revanced-cli.jar list-patches --with-descriptions --with-packages --with-versions --with-options --with-universal-patches revanced-patches.rvp
java -jar revanced-cli.jar list-patches --with-packages --with-versions --with-options revanced-patches.rvp
```

## 💉 Patch an app with the default list of patches
## 💉 Patch an app

To patch an app using the default list of patches, use the `patch` command:

```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp input.apk
Expand All @@ -28,22 +30,37 @@ You can also use multiple patch bundles:
java -jar revanced-cli.jar patch -b revanced-patches.rvp -b another-patches.rvp input.apk
```

To manually include or exclude patches, use the options `-i` and `-e`.
Keep in mind the name of the patch must be an exact match.
You can also use the options `--ii` and `--ie` to include or exclude patches by their index
if two patches have the same name.
To know the indices of patches, use the option `--with-indices` when listing patches:
To change the default set of used patches, use the option `-i` or `-e` to use or disuse specific patches.
You can use the `list-patches` command to see which patches are used by default.

To only use specific patches, you can use the option `--exclusive` combined with `-i`.
Remember that the options `-i` and `-e` match the patch's name exactly. Here is an example:

```bash
java -jar revanced-cli.jar list-patches --with-indices revanced-patches.rvp
java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" -i "Another patch name" input.apk
```

Then you can use the indices to include or exclude patches:
You can also use the options `--ii` and `--ie` to use or disuse patches by their index.
This is useful, if two patches happen to have the same name.
To know the indices of patches, use the command `list-patches`:

```bash
java -jar revanced-cli.jar list-patches revanced-patches.rvp
```

Then you can use the indices to use or disuse patches:

```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp --ii 123 --ie 456 input.apk
```

You can combine the option `-i`, `-e`, `--ii`, `--ie` and `--exclusive`. Here is an example:

```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" --ii 123 input.apk
```


> [!TIP]
> You can use the option `-d` to automatically install the patched app after patching.
> Make sure ADB is working:
Expand All @@ -62,7 +79,61 @@ java -jar revanced-cli.jar patch -b revanced-patches.rvp --ii 123 --ie 456 input
> adb install input.apk
> ```

## 📦 Install an app manually
Patches can have options you can set using the option `-O` alongside the option to include the patch by name or index.
To know the options of a patch, use the option `--with-options` when listing patches:

```bash
java -jar revanced-cli.jar list-patches --with-options revanced-patches.rvp
```

Each patch can have multiple options. You can set them using the option `-O`.
For example, to set the options for the patch with the name `Patch name`
with the key `key1` and `key2` to `value1` and `value2` respectively, use the following command:

```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1=value1 -Okey2=value2 input.apk
```

If you want to set a value to `null`, you can omit the value:

```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1 input.apk
```

> [!WARNING]
> Option values are usually typed. If you set a value with the wrong type, the patch can fail.
> Option value types can be seen when listing patches with the option `--with-options`.
>
> Example option values:
>
> - String: `string`
> - Boolean: `true`, `false`
> - Integer: `123`
> - Double: `1.0`
> - Float: `1.0f`
> - Long: `1234567890`, `1L`
> - List: `[item1,item2,item3]`
> - List of type `Any`: `[item1,123,true,1.0]`
> - Empty list of type `Any`: `[]`
> - Typed empty list: `int[]`
> - Typed and nested empty list: `[int[]]`
> - List with null value and two empty strings: `[null,\'\',\"\"]`
>
> Quotes and commas escaped in strings (`\"`, `\'`, `\,`) are parsed as part of the string.
> List items are recursively parsed, so you can escape values in lists:
>
> - Escaped integer as a string: `[\'123\']`
> - Escaped boolean as a string: `[\'true\']`
> - Escaped list as a string: `[\'[item1,item2]\']`
> - Escaped null value as a string: `[\'null\']`
> - List with an integer, an integer as a string and a string with a comma, and an escaped list: [`123,\'123\',str\,ing`,`\'[]\'`]
>
> Example command with an escaped integer as a string:
>
> ```bash
> java -jar revanced-cli.jar -b revanced-patches.rvp -i "Patch name" -OstringKey=\'1\' input.apk
> ```
## 📦 Install an app manually

```bash
java -jar revanced-cli.jar utility install -a input.apk
Expand Down
105 changes: 105 additions & 0 deletions src/main/kotlin/app/revanced/cli/command/CommandUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package app.revanced.cli.command

import picocli.CommandLine

class OptionKeyConverter : CommandLine.ITypeConverter<String> {
override fun convert(value: String): String = value
}

class OptionValueConverter : CommandLine.ITypeConverter<Any?> {
override fun convert(value: String?): Any? {
value ?: return null

return when {
value.startsWith("[") && value.endsWith("]") -> {
val innerValue = value.substring(1, value.length - 1)

buildList {
var nestLevel = 0
var insideQuote = false
var escaped = false

val item = buildString {
for (char in innerValue) {
when (char) {
'\\' -> {
if (escaped || nestLevel != 0) {
append(char)
}

escaped = !escaped
}

'"', '\'' -> {
if (!escaped) {
insideQuote = !insideQuote
} else {
escaped = false
}

append(char)
}

'[' -> {
if (!insideQuote) {
nestLevel++
}

append(char)
}

']' -> {
if (!insideQuote) {
nestLevel--

if (nestLevel == -1) {
return value
}
}

append(char)
}

',' -> if (nestLevel == 0) {
if (insideQuote) {
append(char)
} else {
add(convert(toString()))
setLength(0)
}
} else {
append(char)
}

else -> append(char)
}
}
}

if (item.isNotEmpty()) {
add(convert(item))
}
}
}

value.startsWith("\"") && value.endsWith("\"") -> value.substring(1, value.length - 1)
value.startsWith("'") && value.endsWith("'") -> value.substring(1, value.length - 1)
value.endsWith("f") -> value.dropLast(1).toFloat()
value.endsWith("L") -> value.dropLast(1).toLong()
value.equals("true", ignoreCase = true) -> true
value.equals("false", ignoreCase = true) -> false
value.toIntOrNull() != null -> value.toInt()
value.toLongOrNull() != null -> value.toLong()
value.toDoubleOrNull() != null -> value.toDouble()
value.toFloatOrNull() != null -> value.toFloat()
value == "null" -> null
value == "int[]" -> emptyList<Int>()
value == "long[]" -> emptyList<Long>()
value == "double[]" -> emptyList<Double>()
value == "float[]" -> emptyList<Float>()
value == "boolean[]" -> emptyList<Boolean>()
value == "string[]" -> emptyList<String>()
else -> value
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package app.revanced.cli.command

import app.revanced.library.PackageName
import app.revanced.library.PatchUtils
import app.revanced.library.VersionMap
import app.revanced.library.mostCommonCompatibleVersions
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine
import java.io.File
Expand All @@ -12,11 +12,11 @@ import java.util.logging.Logger
name = "list-versions",
description = [
"List the most common compatible versions of apps that are compatible " +
"with the patches in the supplied patch bundles.",
"with the patches in the supplied patch bundles.",
],
)
internal class ListCompatibleVersions : Runnable {
private val logger = Logger.getLogger(ListCompatibleVersions::class.java.name)
private val logger = Logger.getLogger(this::class.java.name)

@CommandLine.Parameters(
description = ["Paths to patch bundles."],
Expand Down Expand Up @@ -58,8 +58,7 @@ internal class ListCompatibleVersions : Runnable {

val patches = loadPatchesFromJar(patchBundles)

PatchUtils.getMostCommonCompatibleVersions(
patches,
patches.mostCommonCompatibleVersions(
packageNames,
countUnusedPatches,
).entries.joinToString("\n", transform = ::buildString).let(logger::info)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import app.revanced.patcher.patch.Option as PatchOption
description = ["List patches from supplied patch bundles."],
)
internal object ListPatchesCommand : Runnable {
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
private val logger = Logger.getLogger(this::class.java.name)

@Parameters(
description = ["Paths to patch bundles."],
Expand Down Expand Up @@ -95,9 +95,11 @@ internal object ListPatchesCommand : Runnable {
} ?: append("Key: $key")

values?.let { values ->
appendLine("\nValid values:")
appendLine("\nPossible values:")
append(values.map { "${it.value} (${it.key})" }.joinToString("\n").prependIndent("\t"))
}

append("\nType: $type")
}

fun IndexedValue<Patch<*>>.buildString() =
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/app/revanced/cli/command/MainCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ private object CLIVersionProvider : IVersionProvider {
versionProvider = CLIVersionProvider::class,
subcommands = [
PatchCommand::class,
OptionsCommand::class,
ListPatchesCommand::class,
ListCompatibleVersions::class,
UtilityCommand::class,
Expand Down
62 changes: 0 additions & 62 deletions src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt

This file was deleted.

Loading
Loading