forked from ReVanced/revanced-patcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Patcher.kt
101 lines (92 loc) · 3.25 KB
/
Patcher.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package app.revanced.patcher
import app.revanced.patcher.cache.Cache
import app.revanced.patcher.extensions.replace
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.signature.resolver.SignatureResolver
import app.revanced.patcher.signature.MethodSignature
import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO
import lanchon.multidexlib2.MultiDexIO
import org.jf.dexlib2.Opcodes
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.DexFile
import java.io.File
val NAMER = BasicDexFileNamer()
/**
* ReVanced Patcher.
* @param input The input file (an apk or any other multi dex container).
* @param output The output folder.
* @param signatures An array of method signatures for the patches
*/
class Patcher(
input: File,
private val output: File,
signatures: Array<MethodSignature>,
) {
private val cache: Cache
private val patches = mutableSetOf<Patch>()
private val opcodes: Opcodes
init {
val dexFile = MultiDexIO.readDexFile(true, input, NAMER, null, null)
opcodes = dexFile.opcodes
cache = Cache(dexFile.classes.toMutableSet(), SignatureResolver(dexFile.classes, signatures).resolve())
}
/**
* Save the patched dex file.
*/
fun save() {
val newDexFile = object : DexFile {
override fun getClasses(): Set<ClassDef> {
// this is a slow workaround for now
cache.methodMap.values.forEach {
if (it.definingClassProxy.proxyUsed) {
cache.classes.replace(it.definingClassProxy.originalIndex, it.definingClassProxy.mutatedClass)
}
}
cache.classProxy.filter { it.proxyUsed }.forEach { proxy ->
cache.classes.replace(proxy.originalIndex, proxy.mutatedClass)
}
return cache.classes
}
override fun getOpcodes(): Opcodes {
return this@Patcher.opcodes
}
}
MultiDexIO.writeDexFile(
true, -1, // core count
output, NAMER, newDexFile,
DexIO.DEFAULT_MAX_DEX_POOL_SIZE,
null
)
}
/**
* Add a patch to the patcher.
* @param patches The patches to add.
*/
fun addPatches(vararg patches: Patch) {
this.patches.addAll(patches)
}
/**
* Apply patches loaded into the patcher.
* @param stopOnError If true, the patches will stop on the first error.
*/
fun applyPatches(stopOnError: Boolean = false): Map<String, Result<PatchResult>> {
return buildMap {
for (patch in patches) {
val result: Result<PatchResult> = try {
val pr = patch.execute(cache)
if (!pr.isSuccess()) {
Result.success(pr)
} else {
Result.failure(Exception(pr.error()?.errorMessage() ?: "Unknown error"))
}
} catch (e: Exception) {
Result.failure(e)
}
this[patch.patchName] = result
if (result.isFailure && stopOnError) break
}
}
}
}