You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-10 05:30:49 +02:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c3db23d3c7 | ||
![]() |
c28584736e | ||
![]() |
6b909c1ee6 | ||
![]() |
0e8446516e | ||
![]() |
aa46b953db | ||
![]() |
a562e476c0 | ||
![]() |
75d2be8803 | ||
![]() |
d6308e126c | ||
![]() |
bb97af4d86 | ||
![]() |
392164862c | ||
![]() |
53e807dec1 | ||
![]() |
288d50a8b4 | ||
![]() |
131dedd4b0 | ||
![]() |
5a92d5c29d | ||
![]() |
4b81318710 | ||
![]() |
44f6a3ebc5 | ||
![]() |
7882a8d928 | ||
![]() |
cc3d32748b | ||
![]() |
f9da2ad531 |
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,3 +1,57 @@
|
|||||||
|
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Improve Patch Options ([6b909c1](https://github.com/revanced/revanced-patcher/commit/6b909c1ee6b8c2ea08bbca059df755e2e5f31656))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
|
||||||
|
|
||||||
|
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add tests for PathOption ([d6308e1](https://github.com/revanced/revanced-patcher/commit/d6308e126c6217b098192c51b6e98bc85a8656bd))
|
||||||
|
* PathOption should be open, not sealed ([a562e47](https://github.com/revanced/revanced-patcher/commit/a562e476c085841efbc7ee98b01d8e6bb18ed757))
|
||||||
|
* typo in ListOption ([3921648](https://github.com/revanced/revanced-patcher/commit/392164862c83d6e76b2a2113d6f6d59fef0020d1))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* make exception an object ([75d2be8](https://github.com/revanced/revanced-patcher/commit/75d2be88037c9cf5436ab69d92abea575409a865))
|
||||||
|
|
||||||
|
# [3.5.0](https://github.com/revanced/revanced-patcher/compare/v3.4.1...v3.5.0) (2022-09-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* default value for `Package.versions` annotation parameter ([131dedd](https://github.com/revanced/revanced-patcher/commit/131dedd4b021fe1c3b0be49ccba4764b325770ea))
|
||||||
|
|
||||||
|
## [3.4.1](https://github.com/revanced/revanced-patcher/compare/v3.4.0...v3.4.1) (2022-09-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove default param from Package.versions ([4b81318](https://github.com/revanced/revanced-patcher/commit/4b813187107e85dc267dbc2d353884b2cc671cc4))
|
||||||
|
|
||||||
|
# [3.4.0](https://github.com/revanced/revanced-patcher/compare/v3.3.3...v3.4.0) (2022-08-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* nullable parameters ([7882a8d](https://github.com/revanced/revanced-patcher/commit/7882a8d928cad8de8cfea711947fc02659549d20))
|
||||||
|
|
||||||
|
## [3.3.3](https://github.com/revanced/revanced-patcher/compare/v3.3.2...v3.3.3) (2022-08-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* show error message if cause is null ([f9da2ad](https://github.com/revanced/revanced-patcher/commit/f9da2ad531644617ad5a2cc6a1819d530e18ba22))
|
||||||
|
|
||||||
## [3.3.2](https://github.com/revanced/revanced-patcher/compare/v3.3.1...v3.3.2) (2022-08-06)
|
## [3.3.2](https://github.com/revanced/revanced-patcher/compare/v3.3.1...v3.3.2) (2022-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -26,6 +26,7 @@ dependencies {
|
|||||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||||
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
|
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(kotlin("reflect"))
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 3.3.2
|
version = 4.0.0
|
||||||
|
@@ -276,7 +276,8 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
|
|
||||||
if (result.isSuccess()) return@forEach
|
if (result.isSuccess()) return@forEach
|
||||||
|
|
||||||
val errorMessage = result.error()!!.cause
|
val error = result.error()!!
|
||||||
|
val errorMessage = error.cause ?: error.message
|
||||||
return PatchResultError("'$patchName' depends on '${patchDependency.patchName}' but the following error was raised: $errorMessage")
|
return PatchResultError("'$patchName' depends on '${patchDependency.patchName}' but the following error was raised: $errorMessage")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,5 +24,5 @@ annotation class Compatibility(
|
|||||||
@MustBeDocumented
|
@MustBeDocumented
|
||||||
annotation class Package(
|
annotation class Package(
|
||||||
val name: String,
|
val name: String,
|
||||||
val versions: Array<String>
|
val versions: Array<String> = [],
|
||||||
)
|
)
|
@@ -6,8 +6,10 @@ import app.revanced.patcher.annotation.Name
|
|||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.Data
|
import app.revanced.patcher.data.Data
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively find a given annotation on a class.
|
* Recursively find a given annotation on a class.
|
||||||
@@ -43,6 +45,9 @@ object PatchExtensions {
|
|||||||
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
||||||
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
|
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
|
||||||
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||||
|
val Class<out Patch<Data>>.options get() = kotlin.companionObjectInstance?.let {
|
||||||
|
(it as? OptionsContainer)?.options
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {
|
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {
|
||||||
|
@@ -22,10 +22,10 @@ import org.jf.dexlib2.iface.Method
|
|||||||
* A `null` opcode is equals to an unknown opcode.
|
* A `null` opcode is equals to an unknown opcode.
|
||||||
*/
|
*/
|
||||||
abstract class MethodFingerprint(
|
abstract class MethodFingerprint(
|
||||||
internal val returnType: String?,
|
internal val returnType: String? = null,
|
||||||
internal val access: Int?,
|
internal val access: Int? = null,
|
||||||
internal val parameters: Iterable<String>?,
|
internal val parameters: Iterable<String>? = null,
|
||||||
internal val opcodes: Iterable<Opcode?>?,
|
internal val opcodes: Iterable<Opcode?>? = null,
|
||||||
internal val strings: Iterable<String>? = null,
|
internal val strings: Iterable<String>? = null,
|
||||||
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
|
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
|
||||||
) : Fingerprint {
|
) : Fingerprint {
|
||||||
|
@@ -17,9 +17,18 @@ abstract class Patch<out T : Data> {
|
|||||||
* The main function of the [Patch] which the patcher will call.
|
* The main function of the [Patch] which the patcher will call.
|
||||||
*/
|
*/
|
||||||
abstract fun execute(data: @UnsafeVariance T): PatchResult
|
abstract fun execute(data: @UnsafeVariance T): PatchResult
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class OptionsContainer {
|
||||||
/**
|
/**
|
||||||
* A list of [PatchOption]s.
|
* A list of [PatchOption]s.
|
||||||
|
* @see PatchOptions
|
||||||
*/
|
*/
|
||||||
open val options = PatchOptions()
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
|
protected fun option(opt: PatchOption<*>): PatchOption<*> {
|
||||||
|
options.register(opt)
|
||||||
|
return opt
|
||||||
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package app.revanced.patcher.patch
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
||||||
@@ -9,20 +11,24 @@ class IllegalValueException(val value: Any?) : Exception("Illegal value: $value"
|
|||||||
class InvalidTypeException(val got: String, val expected: String) :
|
class InvalidTypeException(val got: String, val expected: String) :
|
||||||
Exception("Invalid option value type: $got, expected $expected")
|
Exception("Invalid option value type: $got, expected $expected")
|
||||||
|
|
||||||
class RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
object RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A registry for an array of [PatchOption]s.
|
* A registry for an array of [PatchOption]s.
|
||||||
* @param options An array of [PatchOption]s.
|
* @param options An array of [PatchOption]s.
|
||||||
*/
|
*/
|
||||||
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
||||||
private val register = buildMap {
|
private val register = mutableMapOf<String, PatchOption<*>>()
|
||||||
for (option in options) {
|
|
||||||
if (containsKey(option.key)) {
|
init {
|
||||||
throw IllegalStateException("Multiple options found with the same key")
|
options.forEach { register(it) }
|
||||||
}
|
}
|
||||||
put(option.key, option)
|
|
||||||
|
internal fun register(option: PatchOption<*>) {
|
||||||
|
if (register.containsKey(option.key)) {
|
||||||
|
throw IllegalStateException("Multiple options found with the same key")
|
||||||
}
|
}
|
||||||
|
register[option.key] = option
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +83,7 @@ sealed class PatchOption<T>(
|
|||||||
var value: T? = default
|
var value: T? = default
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value == null && required) {
|
if (value == null && required) {
|
||||||
throw RequirementNotMetException()
|
throw RequirementNotMetException
|
||||||
}
|
}
|
||||||
if (!validator(value)) {
|
if (!validator(value)) {
|
||||||
throw IllegalValueException(value)
|
throw IllegalValueException(value)
|
||||||
@@ -89,7 +95,7 @@ sealed class PatchOption<T>(
|
|||||||
* Gets the value of the option.
|
* Gets the value of the option.
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
* Please note that using the wrong value type results in a runtime error.
|
||||||
*/
|
*/
|
||||||
operator fun <T> getValue(thisRef: Nothing?, property: KProperty<*>) = value as T
|
operator fun <T> getValue(thisRef: Any?, property: KProperty<*>) = value as T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the option.
|
* Gets the value of the option.
|
||||||
@@ -153,7 +159,7 @@ sealed class PatchOption<T>(
|
|||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
if (default !in options) {
|
if (default !in options) {
|
||||||
throw IllegalStateException("Default option must be an allowed options")
|
throw IllegalStateException("Default option must be an allowed option")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,4 +195,36 @@ sealed class PatchOption<T>(
|
|||||||
) : ListOption<Int>(
|
) : ListOption<Int>(
|
||||||
key, default, options, title, description, required, validator
|
key, default, options, title, description, required, validator
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Path].
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
open class PathOption(
|
||||||
|
key: String,
|
||||||
|
default: Path?,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Path?) -> Boolean = { true }
|
||||||
|
) : PatchOption<Path>(
|
||||||
|
key, default, title, description, required, validator
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PathOption] of type [File].
|
||||||
|
* @see PathOption
|
||||||
|
*/
|
||||||
|
class FileOption(
|
||||||
|
key: String,
|
||||||
|
default: File?,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (File?) -> Boolean = { true }
|
||||||
|
) : PathOption(
|
||||||
|
key, default?.toPath(), title, description, required, {
|
||||||
|
validator(it?.toFile())
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
@@ -3,10 +3,11 @@ package app.revanced.patcher.patch
|
|||||||
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
|
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.assertThrows
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import java.io.File
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
internal class PatchOptionsTest {
|
internal class PatchOptionsTest {
|
||||||
private val options = ExampleBytecodePatch().options
|
private val options = ExampleBytecodePatch.options
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should not throw an exception`() {
|
fun `should not throw an exception`() {
|
||||||
@@ -15,21 +16,28 @@ internal class PatchOptionsTest {
|
|||||||
is PatchOption.StringOption -> {
|
is PatchOption.StringOption -> {
|
||||||
option.value = "Hello World"
|
option.value = "Hello World"
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.BooleanOption -> {
|
is PatchOption.BooleanOption -> {
|
||||||
option.value = false
|
option.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.StringListOption -> {
|
is PatchOption.StringListOption -> {
|
||||||
option.value = option.options.first()
|
option.value = option.options.first()
|
||||||
for (choice in option.options) {
|
for (choice in option.options) {
|
||||||
println(choice)
|
println(choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.IntListOption -> {
|
is PatchOption.IntListOption -> {
|
||||||
option.value = option.options.first()
|
option.value = option.options.first()
|
||||||
for (choice in option.options) {
|
for (choice in option.options) {
|
||||||
println(choice)
|
println(choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is PatchOption.PathOption -> {
|
||||||
|
option.value = File("test.txt").toPath()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val option = options["key1"]
|
val option = options["key1"]
|
||||||
|
@@ -7,8 +7,8 @@ import app.revanced.patcher.data.impl.BytecodeData
|
|||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
import app.revanced.patcher.patch.PatchOption
|
import app.revanced.patcher.patch.PatchOption
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
@@ -32,6 +32,8 @@ import org.jf.dexlib2.immutable.reference.ImmutableFieldReference
|
|||||||
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
||||||
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
||||||
import org.jf.dexlib2.util.Preconditions
|
import org.jf.dexlib2.util.Preconditions
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("example-bytecode-patch")
|
@Name("example-bytecode-patch")
|
||||||
@@ -46,6 +48,10 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|||||||
// Get the resolved method by its fingerprint from the resolver cache
|
// Get the resolved method by its fingerprint from the resolver cache
|
||||||
val result = ExampleFingerprint.result!!
|
val result = ExampleFingerprint.result!!
|
||||||
|
|
||||||
|
// Patch options
|
||||||
|
println(key1)
|
||||||
|
key2 = false
|
||||||
|
|
||||||
// Get the implementation for the resolved method
|
// Get the implementation for the resolved method
|
||||||
val method = result.mutableMethod
|
val method = result.mutableMethod
|
||||||
val implementation = method.implementation!!
|
val implementation = method.implementation!!
|
||||||
@@ -164,18 +170,31 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val options = PatchOptions(
|
companion object : OptionsContainer() {
|
||||||
PatchOption.StringOption(
|
private var key1: String by option(
|
||||||
"key1", "default", "title", "description", true
|
PatchOption.StringOption(
|
||||||
),
|
"key1", "default", "title", "description", true
|
||||||
PatchOption.BooleanOption(
|
)
|
||||||
"key2", true, "title", "description" // required defaults to false
|
)
|
||||||
),
|
private var key2: Boolean by option(
|
||||||
PatchOption.StringListOption(
|
PatchOption.BooleanOption(
|
||||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
"key2", true, "title", "description" // required defaults to false
|
||||||
),
|
)
|
||||||
PatchOption.IntListOption(
|
)
|
||||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
private var key3: List<String> by option(
|
||||||
),
|
PatchOption.StringListOption(
|
||||||
)
|
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private var key4: List<Int> by option(
|
||||||
|
PatchOption.IntListOption(
|
||||||
|
"key4", 1, listOf(1, 2, 3), "title", "description"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private var key5: Path by option(
|
||||||
|
PatchOption.PathOption(
|
||||||
|
"key5", File("test.txt").toPath(), "title", "description"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user