You've already forked revanced-patcher
							
							
				mirror of
				https://github.com/revanced/revanced-patcher
				synced 2025-11-02 07:30:52 +01:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			feat/impro
			...
			feat/kmp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c2e0f57579 | ||
| 
						 | 
					242e244b18 | ||
| 
						 | 
					f338ebff6c | ||
| 
						 | 
					00e5950cf2 | 
@@ -150,7 +150,7 @@ public final class app/revanced/patcher/patch/BytecodePatchContext : app/revance
 | 
			
		||||
	public final fun getValue (Lapp/revanced/patcher/Fingerprint;Ljava/lang/Void;Lkotlin/reflect/KProperty;)Lapp/revanced/patcher/Match;
 | 
			
		||||
	public final fun match (Lapp/revanced/patcher/Fingerprint;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
 | 
			
		||||
	public final fun match (Lapp/revanced/patcher/Fingerprint;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
 | 
			
		||||
	public final fun navigate (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/MethodNavigator;
 | 
			
		||||
	public final fun navigate (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)Lapp/revanced/patcher/util/MethodNavigator;
 | 
			
		||||
	public final fun proxy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/ClassProxy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -386,18 +386,13 @@ public final class app/revanced/patcher/patch/ResourcePatchBuilder : app/revance
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public final class app/revanced/patcher/patch/ResourcePatchContext : app/revanced/patcher/patch/PatchContext {
 | 
			
		||||
	public final fun delete (Ljava/lang/String;)Z
 | 
			
		||||
	public final fun document (Ljava/io/InputStream;)Lapp/revanced/patcher/util/Document;
 | 
			
		||||
	public final fun document (Ljava/lang/String;)Lapp/revanced/patcher/util/Document;
 | 
			
		||||
	public fun get ()Lapp/revanced/patcher/PatcherResult$PatchedResources;
 | 
			
		||||
	public synthetic fun get ()Ljava/lang/Object;
 | 
			
		||||
	public final fun get (Ljava/lang/String;Z)Ljava/io/File;
 | 
			
		||||
	public static synthetic fun get$default (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;ZILjava/lang/Object;)Ljava/io/File;
 | 
			
		||||
	public final fun getDocument ()Lapp/revanced/patcher/patch/ResourcePatchContext$DocumentOperatable;
 | 
			
		||||
	public final fun stageDelete (Lkotlin/jvm/functions/Function1;)Z
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public final class app/revanced/patcher/patch/ResourcePatchContext$DocumentOperatable {
 | 
			
		||||
	public fun <init> (Lapp/revanced/patcher/patch/ResourcePatchContext;)V
 | 
			
		||||
	public final fun get (Ljava/io/InputStream;)Lapp/revanced/patcher/util/Document;
 | 
			
		||||
	public final fun get (Ljava/lang/String;)Lapp/revanced/patcher/util/Document;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public final class app/revanced/patcher/util/Document : java/io/Closeable, org/w3c/dom/Document {
 | 
			
		||||
@@ -476,8 +471,9 @@ public final class app/revanced/patcher/util/MethodNavigator {
 | 
			
		||||
	public final fun at (ILkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/MethodNavigator;
 | 
			
		||||
	public final fun at ([I)Lapp/revanced/patcher/util/MethodNavigator;
 | 
			
		||||
	public static synthetic fun at$default (Lapp/revanced/patcher/util/MethodNavigator;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/util/MethodNavigator;
 | 
			
		||||
	public final fun immutable ()Lcom/android/tools/smali/dexlib2/iface/Method;
 | 
			
		||||
	public final fun mutable ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
 | 
			
		||||
	public final fun getValue (Ljava/lang/Void;Lkotlin/reflect/KProperty;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
 | 
			
		||||
	public final fun original ()Lcom/android/tools/smali/dexlib2/iface/Method;
 | 
			
		||||
	public final fun stop ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public final class app/revanced/patcher/util/ProxyClassList : java/util/List, kotlin/jvm/internal/markers/KMutableList {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										101
									
								
								docs/4_apis.md
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								docs/4_apis.md
									
									
									
									
									
								
							@@ -6,14 +6,105 @@ A handful of APIs are available to make patch development easier and more effici
 | 
			
		||||
 | 
			
		||||
1. 👹 Create mutable replacements of classes with `proxy(ClassDef)`
 | 
			
		||||
2. 🔍 Find and create mutable replaces with `classBy(Predicate)`
 | 
			
		||||
3. 🏃 Navigate method calls recursively by index with `navigate(Method).at(index)`
 | 
			
		||||
4. 💾 Read and write resource files with `get(Path, Boolean)`
 | 
			
		||||
5. 📃 Read and write DOM files using `document`
 | 
			
		||||
3. 🏃 Navigate method calls recursively by index with `navigate(Method)`
 | 
			
		||||
4. 💾 Read and write resource files with `get(String, Boolean)` and `delete(String)`
 | 
			
		||||
5. 📃 Read and write DOM files using `document(String)` and  `document(InputStream)`
 | 
			
		||||
 | 
			
		||||
### 🧰 APIs
 | 
			
		||||
 | 
			
		||||
> [!WARNING]
 | 
			
		||||
> This section is still under construction and may be incomplete.
 | 
			
		||||
#### 👹 `proxy(ClassDef)`
 | 
			
		||||
 | 
			
		||||
By default, the classes are immutable, meaning they cannot be modified.
 | 
			
		||||
To make a class mutable, use the `proxy(ClassDef)` function.
 | 
			
		||||
This function creates a lazy mutable copy of the class definition.
 | 
			
		||||
Accessing the property will replace the original class definition with the mutable copy,
 | 
			
		||||
thus allowing you to make changes to the class. Subsequent accesses will return the same mutable copy.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    val mutableClass = proxy(classDef)
 | 
			
		||||
    mutableClass.methods.add(Method())
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 🔍 `classBy(Predicate)`
 | 
			
		||||
 | 
			
		||||
The `classBy(Predicate)` function is an alternative to finding and creating mutable classes by a predicate.
 | 
			
		||||
It automatically proxies the class definition, making it mutable.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    // Alternative to proxy(classes.find { it.name == "Lcom/example/MyClass;" })?.classDef
 | 
			
		||||
    val classDef = classBy { it.name == "Lcom/example/MyClass;" }?.classDef
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 🏃 `navigate(Method).at(index)`
 | 
			
		||||
 | 
			
		||||
The `navigate(Method)` function allows you to navigate method calls recursively by index.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    // Sequentially navigate to the instructions at index 1 within 'someMethod'.
 | 
			
		||||
    val method = navigate(someMethod).at(1).original() // original() returns the original immutable method.
 | 
			
		||||
    
 | 
			
		||||
    // Further navigate to the second occurrence where the instruction's opcode is 'INVOKEVIRTUAL'.
 | 
			
		||||
    // stop() returns the mutable copy of the method.
 | 
			
		||||
    val method = navigate(someMethod).at(2) { instruction -> instruction.opcode == Opcode.INVOKEVIRTUAL }.stop()
 | 
			
		||||
    
 | 
			
		||||
    // Alternatively, to stop(), you can delegate the method to a variable.
 | 
			
		||||
    val method by navigate(someMethod).at(1)
 | 
			
		||||
    
 | 
			
		||||
    // You can chain multiple calls to at() to navigate deeper into the method.
 | 
			
		||||
    val method by navigate(someMethod).at(1).at(2, 3, 4).at(5)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 💾 `get(String, Boolean)` and `delete(String)`
 | 
			
		||||
 | 
			
		||||
The `get(String, Boolean)` function returns a `File` object that can be used to read and write resource files.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    val file = get("res/values/strings.xml")
 | 
			
		||||
    val content = file.readText()
 | 
			
		||||
    file.writeText(content)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `delete` function can mark files for deletion when the APK is rebuilt.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    delete("res/values/strings.xml")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 📃 `document(String)`  and  `document(InputStream)`
 | 
			
		||||
 | 
			
		||||
The `document` function is used to read and write DOM files.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute { 
 | 
			
		||||
    document("res/values/strings.xml").use { document ->
 | 
			
		||||
        val element = doc.createElement("string").apply {
 | 
			
		||||
            textContent = "Hello, World!"
 | 
			
		||||
        }
 | 
			
		||||
        document.documentElement.appendChild(element)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can also read documents from an `InputStream`:
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
execute {
 | 
			
		||||
    val inputStream = classLoader.getResourceAsStream("some.xml")
 | 
			
		||||
    document(inputStream).use { document ->
 | 
			
		||||
        // ...
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 🎉 Afterword
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,12 @@ class PatcherResult internal constructor(
 | 
			
		||||
     * @param resourcesApk The compiled resources.apk file.
 | 
			
		||||
     * @param otherResources The directory containing other resources files.
 | 
			
		||||
     * @param doNotCompress List of files that should not be compressed.
 | 
			
		||||
     * @param deleteResources List of predicates about resources that should be deleted.
 | 
			
		||||
     * @param deleteResources List of resources that should be deleted.
 | 
			
		||||
     */
 | 
			
		||||
    class PatchedResources internal constructor(
 | 
			
		||||
        val resourcesApk: File?,
 | 
			
		||||
        val otherResources: File?,
 | 
			
		||||
        val doNotCompress: Set<String>,
 | 
			
		||||
        val deleteResources: Set<(String) -> Boolean>,
 | 
			
		||||
        val deleteResources: Set<String>,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.DexFile
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.Method
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
 | 
			
		||||
import lanchon.multidexlib2.BasicDexFileNamer
 | 
			
		||||
import lanchon.multidexlib2.DexIO
 | 
			
		||||
@@ -147,7 +148,7 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
 | 
			
		||||
     *
 | 
			
		||||
     * @return A [MethodNavigator] for the method.
 | 
			
		||||
     */
 | 
			
		||||
    fun navigate(method: Method) = MethodNavigator(this@BytecodePatchContext, method)
 | 
			
		||||
    fun navigate(method: MethodReference) = MethodNavigator(this@BytecodePatchContext, method)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Compile bytecode from the [BytecodePatchContext].
 | 
			
		||||
 
 | 
			
		||||
@@ -32,14 +32,19 @@ class ResourcePatchContext internal constructor(
 | 
			
		||||
    private val logger = Logger.getLogger(ResourcePatchContext::class.java.name)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Read and write documents in the [PatcherConfig.apkFiles].
 | 
			
		||||
     * Read a document from an [InputStream].
 | 
			
		||||
     */
 | 
			
		||||
    val document = DocumentOperatable()
 | 
			
		||||
    fun document(inputStream: InputStream) = Document(inputStream)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Predicate to delete resources from [PatcherConfig.apkFiles].
 | 
			
		||||
     * Read and write documents in the [PatcherConfig.apkFiles].
 | 
			
		||||
     */
 | 
			
		||||
    private val deleteResources = mutableSetOf<(String) -> Boolean>()
 | 
			
		||||
    fun document(path: String) = Document(get(path))
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set of resources from [PatcherConfig.apkFiles] to delete.
 | 
			
		||||
     */
 | 
			
		||||
    private val deleteResources = mutableSetOf<String>()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decode resources of [PatcherConfig.apkFile].
 | 
			
		||||
@@ -201,11 +206,11 @@ class ResourcePatchContext internal constructor(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stage a file to be deleted from [PatcherConfig.apkFile].
 | 
			
		||||
     * Mark a file for deletion when the APK is rebuilt.
 | 
			
		||||
     *
 | 
			
		||||
     * @param shouldDelete The predicate to stage the file for deletion given its name.
 | 
			
		||||
     * @param name The name of the file to delete.
 | 
			
		||||
     */
 | 
			
		||||
    fun stageDelete(shouldDelete: (String) -> Boolean) = deleteResources.add(shouldDelete)
 | 
			
		||||
    fun delete(name: String) = deleteResources.add(name)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * How to handle resources decoding and compiling.
 | 
			
		||||
@@ -227,10 +232,4 @@ class ResourcePatchContext internal constructor(
 | 
			
		||||
         */
 | 
			
		||||
        NONE,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class DocumentOperatable {
 | 
			
		||||
        operator fun get(inputStream: InputStream) = Document(inputStream)
 | 
			
		||||
 | 
			
		||||
        operator fun get(path: String) = Document(this@ResourcePatchContext[path])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
 | 
			
		||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
 | 
			
		||||
import com.android.tools.smali.dexlib2.util.MethodUtil
 | 
			
		||||
import kotlin.reflect.KProperty
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A navigator for methods.
 | 
			
		||||
@@ -27,7 +28,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
 | 
			
		||||
class MethodNavigator internal constructor(private val context: BytecodePatchContext, private var startMethod: MethodReference) {
 | 
			
		||||
    private var lastNavigatedMethodReference = startMethod
 | 
			
		||||
 | 
			
		||||
    private val lastNavigatedMethodInstructions get() = with(immutable()) {
 | 
			
		||||
    private val lastNavigatedMethodInstructions get() = with(original()) {
 | 
			
		||||
        instructionsOrNull ?: throw NavigateException("Method $definingClass.$name does not have an implementation.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -76,15 +77,22 @@ class MethodNavigator internal constructor(private val context: BytecodePatchCon
 | 
			
		||||
     *
 | 
			
		||||
     * @return The last navigated method mutably.
 | 
			
		||||
     */
 | 
			
		||||
    fun mutable() = context.classBy(matchesCurrentMethodReferenceDefiningClass)!!.mutableClass.firstMethodBySignature
 | 
			
		||||
    fun stop() = context.classBy(matchesCurrentMethodReferenceDefiningClass)!!.mutableClass.firstMethodBySignature
 | 
			
		||||
        as MutableMethod
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the last navigated method mutably.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The last navigated method mutably.
 | 
			
		||||
     */
 | 
			
		||||
    operator fun getValue(nothing: Nothing?, property: KProperty<*>) = stop()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the last navigated method immutably.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The last navigated method immutably.
 | 
			
		||||
     */
 | 
			
		||||
    fun immutable() = context.classes.first(matchesCurrentMethodReferenceDefiningClass).firstMethodBySignature
 | 
			
		||||
    fun original() = context.classes.first(matchesCurrentMethodReferenceDefiningClass).firstMethodBySignature
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Predicate to match the class defining the current method reference.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user