mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 01:02:22 +01:00
feat!: Use Kotlin DSL for patch options (#234)
This commit is contained in:
parent
fcc1de45ed
commit
c299817193
30 changed files with 1055 additions and 724 deletions
|
@ -3,7 +3,6 @@ package app.revanced.patcher.patch.annotations.processor
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import com.google.devtools.ksp.processing.*
|
import com.google.devtools.ksp.processing.*
|
||||||
|
@ -114,7 +113,6 @@ class PatchProcessor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// kotlin poet generate a class for each patch
|
|
||||||
executablePatches.forEach { (patchDeclaration, patchAnnotation) ->
|
executablePatches.forEach { (patchDeclaration, patchAnnotation) ->
|
||||||
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
||||||
|
|
||||||
|
@ -159,7 +157,7 @@ class PatchProcessor(
|
||||||
"dependencies = setOf(%L)",
|
"dependencies = setOf(%L)",
|
||||||
buildList {
|
buildList {
|
||||||
addAll(dependencies)
|
addAll(dependencies)
|
||||||
// Also add the source class of the generated class so that it is also executed
|
// Also add the source class of the generated class so that it is also executed.
|
||||||
add(patchDeclaration.toClassName())
|
add(patchDeclaration.toClassName())
|
||||||
}.joinToString(", ") { dependency ->
|
}.joinToString(", ") { dependency ->
|
||||||
"${(dependencyResolutionMap[dependency] ?: dependency)}::class"
|
"${(dependencyResolutionMap[dependency] ?: dependency)}::class"
|
||||||
|
@ -181,9 +179,18 @@ class PatchProcessor(
|
||||||
.addParameter("context", contextClass)
|
.addParameter("context", contextClass)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.addProperty(
|
.addInitializerBlock(
|
||||||
PropertySpec.builder("options", PatchOptions::class, KModifier.OVERRIDE)
|
CodeBlock.builder()
|
||||||
.initializer("%T.options", patchDeclaration.toClassName())
|
.add(
|
||||||
|
"%T.options.forEach { (key, option) ->",
|
||||||
|
patchDeclaration.toClassName()
|
||||||
|
)
|
||||||
|
.addStatement(
|
||||||
|
"options.register(option)"
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
"}"
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -7,7 +7,6 @@ import com.tschuchort.compiletesting.kspWithCompilation
|
||||||
import com.tschuchort.compiletesting.symbolProcessorProviders
|
import com.tschuchort.compiletesting.symbolProcessorProviders
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class TestPatchAnnotationProcessor {
|
class TestPatchAnnotationProcessor {
|
||||||
|
@ -64,7 +63,7 @@ class TestPatchAnnotationProcessor {
|
||||||
)
|
)
|
||||||
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
||||||
|
|
||||||
assertNotNull(patch.options)
|
assert(patch.options.isNotEmpty())
|
||||||
assertEquals(patch.options["print"].title, "Print message")
|
assertEquals(patch.options["print"].title, "Print message")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
package app.revanced.patcher.patch.annotations.processor.samples.options
|
package app.revanced.patcher.patch.annotations.processor.samples.options
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.PatchOption
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
||||||
|
|
||||||
@Patch(name = "Options patch")
|
@Patch(name = "Options patch")
|
||||||
object OptionsPatch : ResourcePatch() {
|
object OptionsPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {}
|
override fun execute(context: ResourceContext) {}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private val printOption by option(
|
private val printOption by stringPatchOption(
|
||||||
PatchOption.StringOption(
|
"print",
|
||||||
"print",
|
null,
|
||||||
null,
|
"Print message",
|
||||||
"Print message",
|
"The message to print."
|
||||||
"The message to print."
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -332,29 +332,7 @@ public abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/pa
|
||||||
public synthetic fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/IllegalValueException : java/lang/Exception {
|
public abstract class app/revanced/patcher/patch/Patch {
|
||||||
public fun <init> (Ljava/lang/Object;)V
|
|
||||||
public final fun getValue ()Ljava/lang/Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/InvalidTypeException : java/lang/Exception {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
|
||||||
public final fun getExpected ()Ljava/lang/String;
|
|
||||||
public final fun getGot ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/NoSuchOptionException : java/lang/Exception {
|
|
||||||
public fun <init> (Ljava/lang/String;)V
|
|
||||||
public final fun getOption ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/OptionsContainer {
|
|
||||||
public fun <init> ()V
|
|
||||||
public fun getOptions ()Lapp/revanced/patcher/patch/PatchOptions;
|
|
||||||
protected final fun option (Lapp/revanced/patcher/patch/PatchOption;)Lapp/revanced/patcher/patch/PatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/Patch : app/revanced/patcher/patch/OptionsContainer {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
@ -363,6 +341,7 @@ public abstract class app/revanced/patcher/patch/Patch : app/revanced/patcher/pa
|
||||||
public final fun getDependencies ()Ljava/util/Set;
|
public final fun getDependencies ()Ljava/util/Set;
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
public final fun getDescription ()Ljava/lang/String;
|
||||||
public final fun getName ()Ljava/lang/String;
|
public final fun getName ()Ljava/lang/String;
|
||||||
|
public final fun getOptions ()Lapp/revanced/patcher/patch/options/PatchOptions;
|
||||||
public final fun getRequiresIntegrations ()Z
|
public final fun getRequiresIntegrations ()Z
|
||||||
public final fun getUse ()Z
|
public final fun getUse ()Z
|
||||||
public fun hashCode ()I
|
public fun hashCode ()I
|
||||||
|
@ -381,61 +360,9 @@ public final class app/revanced/patcher/patch/PatchException : java/lang/Excepti
|
||||||
public fun <init> (Ljava/lang/Throwable;)V
|
public fun <init> (Ljava/lang/Throwable;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/PatchOption {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
|
||||||
public final fun getKey ()Ljava/lang/String;
|
|
||||||
public final fun getRequired ()Z
|
|
||||||
public final fun getTitle ()Ljava/lang/String;
|
|
||||||
public final fun getValidator ()Lkotlin/jvm/functions/Function1;
|
|
||||||
public final fun getValue ()Ljava/lang/Object;
|
|
||||||
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
|
||||||
public final fun setValue (Ljava/lang/Object;)V
|
|
||||||
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchOption$BooleanOption : app/revanced/patcher/patch/PatchOption {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchOption$IntListOption : app/revanced/patcher/patch/PatchOption$ListOption {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/PatchOption$ListOption : app/revanced/patcher/patch/PatchOption {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun getOptions ()Ljava/lang/Iterable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchOption$StringListOption : app/revanced/patcher/patch/PatchOption$ListOption {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Iterable;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchOption$StringOption : app/revanced/patcher/patch/PatchOption {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchOptions : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
|
||||||
public fun <init> ([Lapp/revanced/patcher/patch/PatchOption;)V
|
|
||||||
public final fun getUntyped (Ljava/lang/String;)Lapp/revanced/patcher/patch/PatchOption;
|
|
||||||
public fun iterator ()Ljava/util/Iterator;
|
|
||||||
public final fun nullify (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchResult {
|
public final class app/revanced/patcher/patch/PatchResult {
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
||||||
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||||
public fun hashCode ()I
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/RequirementNotMetException : java/lang/Exception {
|
|
||||||
public static final field INSTANCE Lapp/revanced/patcher/patch/RequirementNotMetException;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
||||||
|
@ -444,6 +371,167 @@ public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/pa
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
||||||
|
public final fun getDescription ()Ljava/lang/String;
|
||||||
|
public final fun getKey ()Ljava/lang/String;
|
||||||
|
public final fun getRequired ()Z
|
||||||
|
public final fun getTitle ()Ljava/lang/String;
|
||||||
|
public final fun getValidate ()Lkotlin/jvm/functions/Function1;
|
||||||
|
public final fun getValue ()Ljava/lang/Object;
|
||||||
|
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
||||||
|
public final fun setValue (Ljava/lang/Object;)V
|
||||||
|
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/options/PatchOptionException : java/lang/Exception {
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$InvalidValueTypeException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$PatchOptionNotFoundException : java/lang/Exception {
|
||||||
|
public fun <init> (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueRequiredException : java/lang/Exception {
|
||||||
|
public fun <init> (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueValidationException : java/lang/Exception {
|
||||||
|
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptions : java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
|
||||||
|
public fun <init> ()V
|
||||||
|
public fun clear ()V
|
||||||
|
public final fun containsKey (Ljava/lang/Object;)Z
|
||||||
|
public fun containsKey (Ljava/lang/String;)Z
|
||||||
|
public fun containsValue (Lapp/revanced/patcher/patch/options/PatchOption;)Z
|
||||||
|
public final fun containsValue (Ljava/lang/Object;)Z
|
||||||
|
public final fun entrySet ()Ljava/util/Set;
|
||||||
|
public final fun get (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun get (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public fun getEntries ()Ljava/util/Set;
|
||||||
|
public fun getKeys ()Ljava/util/Set;
|
||||||
|
public fun getSize ()I
|
||||||
|
public fun getValues ()Ljava/util/Collection;
|
||||||
|
public fun isEmpty ()Z
|
||||||
|
public final fun keySet ()Ljava/util/Set;
|
||||||
|
public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun put (Ljava/lang/String;Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public fun putAll (Ljava/util/Map;)V
|
||||||
|
public final fun register (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
public final fun remove (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun remove (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun size ()I
|
||||||
|
public final fun values ()Ljava/util/Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption$Companion {
|
||||||
|
public final fun booleanPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
||||||
|
public static synthetic fun booleanPatchOption$default (Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/FloatPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/FloatPatchOption$Companion {
|
||||||
|
public final fun floatPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
||||||
|
public static synthetic fun floatPatchOption$default (Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/IntPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/IntPatchOption$Companion {
|
||||||
|
public final fun intPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
||||||
|
public static synthetic fun intPatchOption$default (Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/LongPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/LongPatchOption$Companion {
|
||||||
|
public final fun longPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
||||||
|
public static synthetic fun longPatchOption$default (Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/StringPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/StringPatchOption$Companion {
|
||||||
|
public final fun stringPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
||||||
|
public static synthetic fun stringPatchOption$default (Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion {
|
||||||
|
public final fun booleanArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
||||||
|
public static synthetic fun booleanArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion {
|
||||||
|
public final fun floatArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
||||||
|
public static synthetic fun floatArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion {
|
||||||
|
public final fun intArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
||||||
|
public static synthetic fun intArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion {
|
||||||
|
public final fun longArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
||||||
|
public static synthetic fun longArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion {
|
||||||
|
public final fun stringArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
||||||
|
public static synthetic fun stringArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/DomFileEditor : java/io/Closeable {
|
public final class app/revanced/patcher/util/DomFileEditor : java/io/Closeable {
|
||||||
public fun <init> (Ljava/io/File;)V
|
public fun <init> (Ljava/io/File;)V
|
||||||
public fun close ()V
|
public fun close ()V
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchClass
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced [Patch] that works on [BytecodeContext].
|
||||||
|
*
|
||||||
|
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
||||||
|
* @param name The name of the patch.
|
||||||
|
* @param description The description of the patch.
|
||||||
|
* @param compatiblePackages The packages the patch is compatible with.
|
||||||
|
* @param dependencies The names of patches this patch depends on.
|
||||||
|
* @param use Weather or not the patch should be used.
|
||||||
|
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||||
|
*/
|
||||||
|
abstract class BytecodePatch(
|
||||||
|
internal val fingerprints: Set<MethodFingerprint> = emptySet(),
|
||||||
|
name: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||||
|
dependencies: Set<PatchClass>? = null,
|
||||||
|
use: Boolean = true,
|
||||||
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
|
requiresIntegrations: Boolean = false,
|
||||||
|
) : Patch<BytecodeContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
@ -1,23 +0,0 @@
|
||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container for patch options.
|
|
||||||
*/
|
|
||||||
abstract class OptionsContainer {
|
|
||||||
/**
|
|
||||||
* A list of [PatchOption]s.
|
|
||||||
* @see PatchOptions
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
open val options = PatchOptions()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a [PatchOption].
|
|
||||||
* @param opt The [PatchOption] to register.
|
|
||||||
* @return The registered [PatchOption].
|
|
||||||
*/
|
|
||||||
protected fun <T> option(opt: PatchOption<T>): PatchOption<T> {
|
|
||||||
options.register(opt)
|
|
||||||
return opt
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,10 +4,8 @@ package app.revanced.patcher.patch
|
||||||
|
|
||||||
import app.revanced.patcher.PatchClass
|
import app.revanced.patcher.PatchClass
|
||||||
import app.revanced.patcher.Patcher
|
import app.revanced.patcher.Patcher
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.data.Context
|
import app.revanced.patcher.data.Context
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.patch.options.PatchOptions
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +30,12 @@ sealed class Patch<out T : Context<*>>(
|
||||||
val use: Boolean = true,
|
val use: Boolean = true,
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
val requiresIntegrations: Boolean = false,
|
val requiresIntegrations: Boolean = false,
|
||||||
) : OptionsContainer() {
|
) {
|
||||||
|
/**
|
||||||
|
* The options of the patch associated by the options key.
|
||||||
|
*/
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The execution function of the patch.
|
* The execution function of the patch.
|
||||||
*
|
*
|
||||||
|
@ -66,44 +69,3 @@ sealed class Patch<out T : Context<*>>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced [Patch] that works on [ResourceContext].
|
|
||||||
*
|
|
||||||
* @param name The name of the patch.
|
|
||||||
* @param description The description of the patch.
|
|
||||||
* @param compatiblePackages The packages the patch is compatible with.
|
|
||||||
* @param dependencies The names of patches this patch depends on.
|
|
||||||
* @param use Weather or not the patch should be used.
|
|
||||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
|
||||||
*/
|
|
||||||
abstract class ResourcePatch(
|
|
||||||
name: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
|
||||||
dependencies: Set<PatchClass>? = null,
|
|
||||||
use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
requiresIntegrations: Boolean = false,
|
|
||||||
) : Patch<ResourceContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced [Patch] that works on [BytecodeContext].
|
|
||||||
*
|
|
||||||
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
|
||||||
* @param name The name of the patch.
|
|
||||||
* @param description The description of the patch.
|
|
||||||
* @param compatiblePackages The packages the patch is compatible with.
|
|
||||||
* @param dependencies The names of patches this patch depends on.
|
|
||||||
* @param use Weather or not the patch should be used.
|
|
||||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
|
||||||
*/
|
|
||||||
abstract class BytecodePatch(
|
|
||||||
internal val fingerprints: Set<MethodFingerprint> = emptySet(),
|
|
||||||
name: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
|
||||||
dependencies: Set<PatchClass>? = null,
|
|
||||||
use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
requiresIntegrations: Boolean = false,
|
|
||||||
) : Patch<BytecodeContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
|
@ -1,230 +0,0 @@
|
||||||
@file:Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST")
|
|
||||||
|
|
||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
|
||||||
class IllegalValueException(val value: Any?) : Exception("Illegal value: $value")
|
|
||||||
class InvalidTypeException(val got: String, val expected: String) :
|
|
||||||
Exception("Invalid option value type: $got, expected $expected")
|
|
||||||
|
|
||||||
object RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A registry for an array of [PatchOption]s.
|
|
||||||
* @param options An array of [PatchOption]s.
|
|
||||||
*/
|
|
||||||
class PatchOptions(vararg options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
|
||||||
private val register = mutableMapOf<String, PatchOption<*>>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
options.forEach { register(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun register(option: PatchOption<*>) {
|
|
||||||
if (register.containsKey(option.key)) {
|
|
||||||
throw IllegalStateException("Multiple options found with the same key")
|
|
||||||
}
|
|
||||||
register[option.key] = option
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a [PatchOption] by its key.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
@JvmName("getUntyped")
|
|
||||||
operator fun get(key: String) = register[key] ?: throw NoSuchOptionException(key)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a [PatchOption] by its key.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
inline operator fun <reified T> get(key: String): PatchOption<T> {
|
|
||||||
val opt = get(key)
|
|
||||||
if (opt.value !is T) throw InvalidTypeException(
|
|
||||||
opt.value?.let { it::class.java.canonicalName } ?: "null",
|
|
||||||
T::class.java.canonicalName
|
|
||||||
)
|
|
||||||
return opt as PatchOption<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of a [PatchOption].
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
* @param value The value you want it to be.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
inline operator fun <reified T> set(key: String, value: T) {
|
|
||||||
val opt = get<T>(key)
|
|
||||||
if (opt.value !is T) throw InvalidTypeException(
|
|
||||||
T::class.java.canonicalName,
|
|
||||||
opt.value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
opt.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of a [PatchOption] to `null`.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
fun nullify(key: String) {
|
|
||||||
get(key).value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator() = register.values.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Patch] option.
|
|
||||||
* @param key Unique identifier of the option. Example: _`settings`_
|
|
||||||
* @param default The default value of the option.
|
|
||||||
* @param title A human-readable title of the option. Example: _Patch Settings_
|
|
||||||
* @param description A human-readable description of the option. Example: _Settings for the patches._
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
sealed class PatchOption<T>(
|
|
||||||
val key: String,
|
|
||||||
default: T?,
|
|
||||||
val title: String,
|
|
||||||
val description: String,
|
|
||||||
val required: Boolean,
|
|
||||||
val validator: (T?) -> Boolean
|
|
||||||
) {
|
|
||||||
var value: T? = default
|
|
||||||
get() {
|
|
||||||
if (field == null && required) {
|
|
||||||
throw RequirementNotMetException
|
|
||||||
}
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
if (value == null && required) {
|
|
||||||
throw RequirementNotMetException
|
|
||||||
}
|
|
||||||
if (!validator(value)) {
|
|
||||||
throw IllegalValueException(value)
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the option.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
@JvmName("getValueTyped")
|
|
||||||
inline operator fun <reified V> getValue(thisRef: Nothing?, property: KProperty<*>): V? {
|
|
||||||
if (value !is V?) throw InvalidTypeException(
|
|
||||||
V::class.java.canonicalName,
|
|
||||||
value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
return value as? V?
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the option.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
@JvmName("setValueTyped")
|
|
||||||
inline operator fun <reified V> setValue(thisRef: Nothing?, property: KProperty<*>, new: V) {
|
|
||||||
if (value !is V) throw InvalidTypeException(
|
|
||||||
V::class.java.canonicalName,
|
|
||||||
value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
value = new as T
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, new: T?) {
|
|
||||||
value = new
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [String].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class StringOption(
|
|
||||||
key: String,
|
|
||||||
default: String?,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (String?) -> Boolean = { true }
|
|
||||||
) : PatchOption<String>(
|
|
||||||
key, default, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Boolean].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class BooleanOption(
|
|
||||||
key: String,
|
|
||||||
default: Boolean?,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Boolean?) -> Boolean = { true }
|
|
||||||
) : PatchOption<Boolean>(
|
|
||||||
key, default, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] with a list of allowed options.
|
|
||||||
* @param options A list of allowed options for the [ListOption].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
sealed class ListOption<E>(
|
|
||||||
key: String,
|
|
||||||
default: E?,
|
|
||||||
val options: Iterable<E>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (E?) -> Boolean = { true }
|
|
||||||
) : PatchOption<E>(
|
|
||||||
key, default, title, description, required, {
|
|
||||||
(it?.let { it in options } ?: true) && validator(it)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
if (default != null && default !in options) {
|
|
||||||
throw IllegalStateException("Default option must be an allowed option")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [ListOption] of type [String].
|
|
||||||
* @see ListOption
|
|
||||||
*/
|
|
||||||
class StringListOption(
|
|
||||||
key: String,
|
|
||||||
default: String?,
|
|
||||||
options: Iterable<String>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (String?) -> Boolean = { true }
|
|
||||||
) : ListOption<String>(
|
|
||||||
key, default, options, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [ListOption] of type [Int].
|
|
||||||
* @see ListOption
|
|
||||||
*/
|
|
||||||
class IntListOption(
|
|
||||||
key: String,
|
|
||||||
default: Int?,
|
|
||||||
options: Iterable<Int>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Int?) -> Boolean = { true }
|
|
||||||
) : ListOption<Int>(
|
|
||||||
key, default, options, title, description, required, validator
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -7,14 +7,4 @@ package app.revanced.patcher.patch
|
||||||
* @param exception The [PatchException] thrown, if any.
|
* @param exception The [PatchException] thrown, if any.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null) {
|
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null)
|
||||||
override fun hashCode() = patch.hashCode()
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as PatchResult
|
|
||||||
|
|
||||||
return patch == other.patch
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchClass
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced [Patch] that works on [ResourceContext].
|
||||||
|
*
|
||||||
|
* @param name The name of the patch.
|
||||||
|
* @param description The description of the patch.
|
||||||
|
* @param compatiblePackages The packages the patch is compatible with.
|
||||||
|
* @param dependencies The names of patches this patch depends on.
|
||||||
|
* @param use Weather or not the patch should be used.
|
||||||
|
* @param requiresIntegrations Weather or not the patch requires integrations.
|
||||||
|
*/
|
||||||
|
abstract class ResourcePatch(
|
||||||
|
name: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||||
|
dependencies: Set<PatchClass>? = null,
|
||||||
|
use: Boolean = true,
|
||||||
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
|
requiresIntegrations: Boolean = false,
|
||||||
|
) : Patch<ResourceContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Patch] option.
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validate The function to validate values of the option.
|
||||||
|
* @param T The value type of the option.
|
||||||
|
*/
|
||||||
|
abstract class PatchOption<T>(
|
||||||
|
val key: String,
|
||||||
|
default: T?,
|
||||||
|
val title: String?,
|
||||||
|
val description: String?,
|
||||||
|
val required: Boolean,
|
||||||
|
val validate: (T?) -> Boolean
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The value of the [PatchOption].
|
||||||
|
*/
|
||||||
|
var value: T? = default
|
||||||
|
set(value) {
|
||||||
|
if (required && value == null) throw PatchOptionException.ValueRequiredException(this)
|
||||||
|
if (!validate(value)) throw PatchOptionException.ValueValidationException(value, this)
|
||||||
|
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||||
|
|
||||||
|
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when using [PatchOption]s.
|
||||||
|
*
|
||||||
|
* @param errorMessage The exception message.
|
||||||
|
*/
|
||||||
|
sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage, null) {
|
||||||
|
/**
|
||||||
|
* An exception thrown when a [PatchOption] is set to an invalid value.
|
||||||
|
*
|
||||||
|
* @param invalidType The type of the value that was passed.
|
||||||
|
* @param expectedType The type of the value that was expected.
|
||||||
|
*/
|
||||||
|
class InvalidValueTypeException(invalidType: String, expectedType: String) :
|
||||||
|
PatchOptionException("Type $expectedType was expected but received type $invalidType")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a value did not satisfy the value conditions specified by the [PatchOption].
|
||||||
|
*
|
||||||
|
* @param value The value that failed validation.
|
||||||
|
*/
|
||||||
|
class ValueValidationException(value: Any?, option: PatchOption<*>) :
|
||||||
|
Exception("The option value \"$value\" failed validation for ${option.key}")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a value is required but null was passed.
|
||||||
|
*
|
||||||
|
* @param option The [PatchOption] that requires a value.
|
||||||
|
*/
|
||||||
|
class ValueRequiredException(option: PatchOption<*>) :
|
||||||
|
Exception("The option ${option.key} requires a value, but null was passed")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a [PatchOption] is not found.
|
||||||
|
*
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
*/
|
||||||
|
class PatchOptionNotFoundException(key: String)
|
||||||
|
: Exception("No option with key $key")
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of [PatchOption]s associated by their keys.
|
||||||
|
*
|
||||||
|
* @param options The [PatchOption]s to initialize with.
|
||||||
|
*/
|
||||||
|
class PatchOptions internal constructor(
|
||||||
|
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf()
|
||||||
|
) : MutableMap<String, PatchOption<*>> by options {
|
||||||
|
/**
|
||||||
|
* Register a [PatchOption]. Acts like [MutableMap.put].
|
||||||
|
* @param value The [PatchOption] to register.
|
||||||
|
*/
|
||||||
|
fun register(value: PatchOption<*>) {
|
||||||
|
options[value.key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an option's value.
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param value The value.
|
||||||
|
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
||||||
|
*/
|
||||||
|
inline operator fun <reified T: Any> set(key: String, value: T?) {
|
||||||
|
val option = this[key]
|
||||||
|
|
||||||
|
if (option.value !is T) throw PatchOptionException.InvalidValueTypeException(
|
||||||
|
T::class.java.name,
|
||||||
|
option.value?.let { it::class.java.name } ?: "null",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(option as PatchOption<T>).value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an option.
|
||||||
|
*/
|
||||||
|
override operator fun get(key: String) =
|
||||||
|
options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Boolean].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class BooleanPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Boolean?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Boolean?) -> Boolean
|
||||||
|
) : PatchOption<Boolean>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [BooleanPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [BooleanPatchOption].
|
||||||
|
*
|
||||||
|
* @see BooleanPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.booleanPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Boolean? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Boolean?) -> Boolean = { true }
|
||||||
|
) = BooleanPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Float].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class FloatPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Float?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Float?) -> Boolean
|
||||||
|
) : PatchOption<Float>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [FloatPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [FloatPatchOption].
|
||||||
|
*
|
||||||
|
* @see FloatPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.floatPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Float? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Float?) -> Boolean = { true }
|
||||||
|
) = FloatPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing an [Integer].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class IntPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Int?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Int?) -> Boolean
|
||||||
|
) : PatchOption<Int>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [IntPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [IntPatchOption].
|
||||||
|
*
|
||||||
|
* @see IntPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.intPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Int? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Int?) -> Boolean = { true }
|
||||||
|
) = IntPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Long].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class LongPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Long?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Long?) -> Boolean
|
||||||
|
) : PatchOption<Long>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [LongPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [LongPatchOption].
|
||||||
|
*
|
||||||
|
* @see LongPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.longPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Long? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Long?) -> Boolean = { true }
|
||||||
|
) = LongPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [String].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class StringPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: String?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (String?) -> Boolean
|
||||||
|
) : PatchOption<String>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [StringPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [StringPatchOption].
|
||||||
|
*
|
||||||
|
* @see StringPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.stringPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: String? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (String?) -> Boolean = { true }
|
||||||
|
) = StringPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types.array
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Boolean] array.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class BooleanArrayPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Array<Boolean>?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Array<Boolean>?) -> Boolean
|
||||||
|
) : PatchOption<Array<Boolean>>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [BooleanArrayPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [BooleanArrayPatchOption].
|
||||||
|
*
|
||||||
|
* @see BooleanArrayPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.booleanArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Boolean>? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Array<Boolean>?) -> Boolean = { true }
|
||||||
|
) = BooleanArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types.array
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Float] array.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class FloatArrayPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Array<Float>?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Array<Float>?) -> Boolean
|
||||||
|
) : PatchOption<Array<Float>>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [FloatArrayPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [FloatArrayPatchOption].
|
||||||
|
*
|
||||||
|
* @see FloatArrayPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.floatArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Float>? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Array<Float>?) -> Boolean = { true }
|
||||||
|
) = FloatArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types.array
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing an [Integer] array.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class IntArrayPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Array<Int>?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Array<Int>?) -> Boolean
|
||||||
|
) : PatchOption<Array<Int>>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [IntArrayPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [IntArrayPatchOption].
|
||||||
|
*
|
||||||
|
* @see IntArrayPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.intArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Int>? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Array<Int>?) -> Boolean = { true }
|
||||||
|
) = IntArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types.array
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Long] array.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class LongArrayPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Array<Long>?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Array<Long>?) -> Boolean
|
||||||
|
) : PatchOption<Array<Long>>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [LongArrayPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [LongArrayPatchOption].
|
||||||
|
*
|
||||||
|
* @see LongArrayPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.longArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Long>? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Array<Long>?) -> Boolean = { true }
|
||||||
|
) = LongArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package app.revanced.patcher.patch.options.types.array
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [String] array.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class StringArrayPatchOption private constructor(
|
||||||
|
key: String,
|
||||||
|
default: Array<String>?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
required: Boolean,
|
||||||
|
validator: (Array<String>?) -> Boolean
|
||||||
|
) : PatchOption<Array<String>>(key, default, title, description, required, validator) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Create a new [StringArrayPatchOption] and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @return The created [StringArrayPatchOption].
|
||||||
|
*
|
||||||
|
* @see StringArrayPatchOption
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <T : Patch<*>> T.stringArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<String>? = null,
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Array<String>?) -> Boolean = { true }
|
||||||
|
) = StringArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
package app.revanced.patcher.issues
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.PatchOption
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
internal class Issue98 {
|
|
||||||
companion object {
|
|
||||||
var key1: String? by PatchOption.StringOption(
|
|
||||||
"key1", null, "title", "description"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should infer nullable type correctly`() {
|
|
||||||
assertNull(key1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.usage.ExampleBytecodePatch
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertNotEquals
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
|
|
||||||
internal class PatchOptionsTest {
|
|
||||||
private val options = ExampleBytecodePatch.options
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should not throw an exception`() {
|
|
||||||
for (option in options) {
|
|
||||||
when (option) {
|
|
||||||
is PatchOption.StringOption -> {
|
|
||||||
option.value = "Hello World"
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.BooleanOption -> {
|
|
||||||
option.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.StringListOption -> {
|
|
||||||
option.value = option.options.first()
|
|
||||||
for (choice in option.options) {
|
|
||||||
assertNotNull(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.IntListOption -> {
|
|
||||||
option.value = option.options.first()
|
|
||||||
for (choice in option.options) {
|
|
||||||
assertNotNull(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val option = options.get<String>("key1")
|
|
||||||
// or: val option: String? by options["key1"]
|
|
||||||
// then you won't need `.value` every time
|
|
||||||
assertEquals("Hello World", option.value)
|
|
||||||
options["key1"] = "Hello, world!"
|
|
||||||
assertEquals("Hello, world!", option.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return a different value when changed`() {
|
|
||||||
var value: String? by options["key1"]
|
|
||||||
val current = value + "" // force a copy
|
|
||||||
value = "Hello, world!"
|
|
||||||
assertNotEquals(current, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should be able to set value to null`() {
|
|
||||||
// Sadly, doing:
|
|
||||||
// > options["key2"] = null
|
|
||||||
// is not possible because Kotlin
|
|
||||||
// cannot reify the type "Nothing?".
|
|
||||||
// So we have to do this instead:
|
|
||||||
options["key2"] = null as Any?
|
|
||||||
// This is a cleaner replacement for the above:
|
|
||||||
options.nullify("key2")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because the option does not exist`() {
|
|
||||||
assertThrows<NoSuchOptionException> {
|
|
||||||
options["this option does not exist"] = 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of invalid value type when setting an option`() {
|
|
||||||
assertThrows<InvalidTypeException> {
|
|
||||||
options["key1"] = 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of invalid value type when getting an option`() {
|
|
||||||
assertThrows<InvalidTypeException> {
|
|
||||||
options.get<Int>("key1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of an illegal value`() {
|
|
||||||
assertThrows<IllegalValueException> {
|
|
||||||
options["key3"] = "this value is not an allowed option"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because the requirement is not met`() {
|
|
||||||
assertThrows<RequirementNotMetException> {
|
|
||||||
options.nullify("key1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because getting a non-initialized option is illegal`() {
|
|
||||||
assertThrows<RequirementNotMetException> {
|
|
||||||
options["key5"].value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.options.types.BooleanPatchOption.Companion.booleanPatchOption
|
||||||
|
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
||||||
|
import app.revanced.patcher.patch.options.types.array.StringArrayPatchOption.Companion.stringArrayPatchOption
|
||||||
|
import org.junit.jupiter.api.assertDoesNotThrow
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class PatchOptionsTest {
|
||||||
|
@Test
|
||||||
|
fun `should not fail because default value is unvalidated`() {
|
||||||
|
assertDoesNotThrow {
|
||||||
|
OptionsTestPatch.options["required"].value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should throw due to incorrect type`() {
|
||||||
|
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
||||||
|
OptionsTestPatch.options["bool"] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should be nullable`() {
|
||||||
|
OptionsTestPatch.options["bool"] = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `option should not be found`() {
|
||||||
|
assertThrows<PatchOptionException.PatchOptionNotFoundException> {
|
||||||
|
OptionsTestPatch.options["this option does not exist"] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should be able to add options manually`() {
|
||||||
|
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
||||||
|
OptionsTestPatch.options["array"] = OptionsTestPatch.stringArrayOption
|
||||||
|
}
|
||||||
|
assertDoesNotThrow {
|
||||||
|
OptionsTestPatch.options.register(OptionsTestPatch.stringArrayOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object OptionsTestPatch : BytecodePatch() {
|
||||||
|
private var stringOption by stringPatchOption("string", "default")
|
||||||
|
private var booleanOption by booleanPatchOption("bool", true)
|
||||||
|
private var requiredStringOption by stringPatchOption("required", "default", required = true)
|
||||||
|
private var nullDefaultRequiredOption by stringPatchOption("null", null, required = true)
|
||||||
|
|
||||||
|
val stringArrayOption = stringArrayPatchOption("array", arrayOf("1", "2"))
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
package app.revanced.patcher.patch.usage
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.annotations.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Format
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
||||||
|
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
||||||
|
import com.android.tools.smali.dexlib2.util.Preconditions
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@Patch(
|
||||||
|
name = "Example bytecode patch",
|
||||||
|
description = "Example demonstration of a bytecode patch.",
|
||||||
|
dependencies = [ExampleResourcePatch::class],
|
||||||
|
compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))]
|
||||||
|
)
|
||||||
|
object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
||||||
|
// Entry point of a patch. Supplied fingerprints are resolved at this point.
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
ExampleFingerprint.result?.let { result ->
|
||||||
|
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
||||||
|
// Get the start index of our opcode pattern.
|
||||||
|
// This will be the index of the instruction with the opcode CONST_STRING.
|
||||||
|
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
|
result.mutableMethod.apply {
|
||||||
|
replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
||||||
|
|
||||||
|
// Store the fields initial value into the first virtual register.
|
||||||
|
replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
||||||
|
|
||||||
|
// Now let's create a new call to our method and print the return value!
|
||||||
|
// You can also use the smali compiler to create instructions.
|
||||||
|
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
||||||
|
//
|
||||||
|
// Control flow instructions are not supported as of now.
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
startIndex + 2,
|
||||||
|
"""
|
||||||
|
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||||
|
move-result-object v1
|
||||||
|
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the class in which the method matching our fingerprint is defined in.
|
||||||
|
context.findClass(result.classDef.type)!!.mutableClass.apply {
|
||||||
|
// Add a new method that returns a string.
|
||||||
|
methods.add(
|
||||||
|
ImmutableMethod(
|
||||||
|
result.classDef.type,
|
||||||
|
"returnHello",
|
||||||
|
null,
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ImmutableMethodImplementation(
|
||||||
|
1,
|
||||||
|
ImmutableList.of(
|
||||||
|
BuilderInstruction21c(
|
||||||
|
Opcode.CONST_STRING,
|
||||||
|
0,
|
||||||
|
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
||||||
|
),
|
||||||
|
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
).toMutable()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add a field in the main class.
|
||||||
|
// We will use this field in our method below to call println on.
|
||||||
|
// The field holds the Ljava/io/PrintStream->out; field.
|
||||||
|
fields.add(
|
||||||
|
ImmutableField(
|
||||||
|
type,
|
||||||
|
"dummyField",
|
||||||
|
"Ljava/io/PrintStream;",
|
||||||
|
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
ImmutableFieldEncodedValue(
|
||||||
|
ImmutableFieldReference(
|
||||||
|
"Ljava/lang/System;",
|
||||||
|
"out",
|
||||||
|
"Ljava/io/PrintStream;"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).toMutable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: throw PatchException("Fingerprint failed to resolve.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace an existing instruction with a new one containing a reference to a new string.
|
||||||
|
* @param index The index of the instruction to replace.
|
||||||
|
* @param string The replacement string.
|
||||||
|
*/
|
||||||
|
private fun MutableMethod.replaceStringAt(index: Int, string: String) {
|
||||||
|
val instruction = getInstruction(index)
|
||||||
|
|
||||||
|
// Utility method of dexlib2.
|
||||||
|
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
||||||
|
|
||||||
|
// Cast this to an instruction of the format 21c.
|
||||||
|
// The instruction format can be found in the docs at
|
||||||
|
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
||||||
|
val strInstruction = instruction as Instruction21c
|
||||||
|
|
||||||
|
// In our case we want an instruction with the opcode CONST_STRING
|
||||||
|
// The format is 21c, so we create a new BuilderInstruction21c
|
||||||
|
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
||||||
|
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
||||||
|
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
||||||
|
replaceInstruction(
|
||||||
|
index,
|
||||||
|
"const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package app.revanced.patcher.usage
|
package app.revanced.patcher.patch.usage
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
@ -12,8 +12,8 @@ object ExampleFingerprint : MethodFingerprint(
|
||||||
listOf("[L"),
|
listOf("[L"),
|
||||||
listOf(
|
listOf(
|
||||||
Opcode.SGET_OBJECT,
|
Opcode.SGET_OBJECT,
|
||||||
null, // Testing unknown opcodes.
|
null, // Matching unknown opcodes.
|
||||||
Opcode.INVOKE_STATIC, // This is intentionally wrong to test the Fuzzy resolver.
|
Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching.
|
||||||
Opcode.RETURN_VOID
|
Opcode.RETURN_VOID
|
||||||
),
|
),
|
||||||
null
|
null
|
|
@ -1,4 +1,4 @@
|
||||||
package app.revanced.patcher.usage
|
package app.revanced.patcher.patch.usage
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
|
@ -1,193 +0,0 @@
|
||||||
package app.revanced.patcher.usage
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.PatchOption
|
|
||||||
import app.revanced.patcher.patch.annotations.CompatiblePackage
|
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Format
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
|
||||||
import com.android.tools.smali.dexlib2.util.Preconditions
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@Patch(
|
|
||||||
name = "Example bytecode patch",
|
|
||||||
description = "Example demonstration of a bytecode patch.",
|
|
||||||
dependencies = [ExampleResourcePatch::class],
|
|
||||||
compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))]
|
|
||||||
)
|
|
||||||
object ExampleBytecodePatch : BytecodePatch(
|
|
||||||
setOf(ExampleFingerprint)
|
|
||||||
) {
|
|
||||||
|
|
||||||
// This function will be executed by the patcher.
|
|
||||||
// You can treat it as a constructor
|
|
||||||
override fun execute(context: BytecodeContext) {
|
|
||||||
// Get the resolved method by its fingerprint from the resolver cache
|
|
||||||
val result = ExampleFingerprint.result!!
|
|
||||||
|
|
||||||
// Patch options
|
|
||||||
assertNotNull(key1)
|
|
||||||
key2 = false
|
|
||||||
|
|
||||||
// Get the implementation for the resolved method
|
|
||||||
val method = result.mutableMethod
|
|
||||||
val implementation = method.implementation!!
|
|
||||||
|
|
||||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
|
||||||
// Get the start index of our opcode pattern.
|
|
||||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
|
||||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
|
||||||
|
|
||||||
implementation.replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
|
||||||
|
|
||||||
// Get the class in which the method matching our fingerprint is defined in.
|
|
||||||
val mainClass = context.findClass {
|
|
||||||
it.type == result.classDef.type
|
|
||||||
}!!.mutableClass
|
|
||||||
|
|
||||||
// Add a new method returning a string
|
|
||||||
mainClass.methods.add(
|
|
||||||
ImmutableMethod(
|
|
||||||
result.classDef.type,
|
|
||||||
"returnHello",
|
|
||||||
null,
|
|
||||||
"Ljava/lang/String;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
1,
|
|
||||||
ImmutableList.of(
|
|
||||||
BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
0,
|
|
||||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
|
||||||
),
|
|
||||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add a field in the main class
|
|
||||||
// We will use this field in our method below to call println on
|
|
||||||
// The field holds the Ljava/io/PrintStream->out; field
|
|
||||||
mainClass.fields.add(
|
|
||||||
ImmutableField(
|
|
||||||
mainClass.type,
|
|
||||||
"dummyField",
|
|
||||||
"Ljava/io/PrintStream;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
ImmutableFieldEncodedValue(
|
|
||||||
ImmutableFieldReference(
|
|
||||||
"Ljava/lang/System;",
|
|
||||||
"out",
|
|
||||||
"Ljava/io/PrintStream;"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
|
|
||||||
// store the fields initial value into the first virtual register
|
|
||||||
method.replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
|
||||||
|
|
||||||
// Now let's create a new call to our method and print the return value!
|
|
||||||
// You can also use the smali compiler to create instructions.
|
|
||||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
|
||||||
//
|
|
||||||
// Control flow instructions are not supported as of now.
|
|
||||||
method.addInstructionsWithLabels(
|
|
||||||
startIndex + 2,
|
|
||||||
"""
|
|
||||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
|
||||||
move-result-object v1
|
|
||||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace the string for an instruction at the given index with a new one.
|
|
||||||
* @param index The index of the instruction to replace the string for
|
|
||||||
* @param string The replacing string
|
|
||||||
*/
|
|
||||||
private fun MutableMethodImplementation.replaceStringAt(index: Int, string: String) {
|
|
||||||
val instruction = this.instructions[index]
|
|
||||||
|
|
||||||
// Utility method of dexlib2
|
|
||||||
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
|
||||||
|
|
||||||
// Cast this to an instruction of the format 21c
|
|
||||||
// The instruction format can be found in the docs at
|
|
||||||
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
|
||||||
val strInstruction = instruction as Instruction21c
|
|
||||||
|
|
||||||
// In our case we want an instruction with the opcode CONST_STRING
|
|
||||||
// The format is 21c, so we create a new BuilderInstruction21c
|
|
||||||
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
|
||||||
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
|
||||||
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
|
||||||
this.replaceInstruction(
|
|
||||||
index,
|
|
||||||
BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
strInstruction.registerA,
|
|
||||||
ImmutableStringReference(string)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var key1 by option(
|
|
||||||
PatchOption.StringOption(
|
|
||||||
"key1", "default", "title", "description", true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private var key2 by option(
|
|
||||||
PatchOption.BooleanOption(
|
|
||||||
"key2", true, "title", "description" // required defaults to false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private var key3 by option(
|
|
||||||
PatchOption.StringListOption(
|
|
||||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private var key4 by option(
|
|
||||||
PatchOption.IntListOption(
|
|
||||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private var key5 by option(
|
|
||||||
PatchOption.StringOption(
|
|
||||||
"key5", null, "title", "description", true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue