mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-05 06:44:25 +02:00
feat: allow classes to be overwritten in addFiles and resolve signatures when applyPatches is called
This commit is contained in:
parent
996c4acb20
commit
1db735b1e2
@ -25,7 +25,6 @@ dependencies {
|
|||||||
implementation(kotlin("stdlib"))
|
implementation(kotlin("stdlib"))
|
||||||
|
|
||||||
api("app.revanced:multidexlib2:2.5.2.r2")
|
api("app.revanced:multidexlib2:2.5.2.r2")
|
||||||
@Suppress("GradlePackageUpdate")
|
|
||||||
api("org.smali:smali:2.5.2")
|
api("org.smali:smali:2.5.2")
|
||||||
|
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patcher
|
package app.revanced.patcher
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
import app.revanced.patcher.cache.Cache
|
||||||
|
import app.revanced.patcher.cache.findIndexed
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.signature.resolver.SignatureResolver
|
import app.revanced.patcher.signature.resolver.SignatureResolver
|
||||||
@ -24,31 +25,41 @@ val NAMER = BasicDexFileNamer()
|
|||||||
*/
|
*/
|
||||||
class Patcher(
|
class Patcher(
|
||||||
input: File,
|
input: File,
|
||||||
signatures: Iterable<MethodSignature>,
|
private val signatures: Iterable<MethodSignature>,
|
||||||
) {
|
) {
|
||||||
private val cache: Cache
|
private val cache: Cache
|
||||||
private val patches = mutableSetOf<Patch>()
|
private val patches = mutableSetOf<Patch>()
|
||||||
private val opcodes: Opcodes
|
private val opcodes: Opcodes
|
||||||
|
private var sigsResolved = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dexFile = MultiDexIO.readDexFile(true, input, NAMER, null, null)
|
val dexFile = MultiDexIO.readDexFile(true, input, NAMER, null, null)
|
||||||
opcodes = dexFile.opcodes
|
opcodes = dexFile.opcodes
|
||||||
cache = Cache(dexFile.classes.toMutableList(), SignatureResolver(dexFile.classes, signatures).resolve())
|
cache = Cache(dexFile.classes.toMutableList())
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Add additional dex file container to the patcher.
|
* Add additional dex file container to the patcher.
|
||||||
* @param files The dex file containers to add to the patcher.
|
* @param files The dex file containers to add to the patcher.
|
||||||
|
* @param allowedOverwrites A list of class types that are allowed to be overwritten.
|
||||||
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
|
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
|
||||||
*/
|
*/
|
||||||
fun addFiles(vararg files: File, throwOnDuplicates: Boolean = false) {
|
fun addFiles(
|
||||||
|
files: Iterable<File>,
|
||||||
|
allowedOverwrites: Iterable<String> = emptyList(),
|
||||||
|
throwOnDuplicates: Boolean = false
|
||||||
|
) {
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val dexFile = MultiDexIO.readDexFile(true, files[0], NAMER, null, null)
|
val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null)
|
||||||
for (classDef in dexFile.classes) {
|
for (classDef in dexFile.classes) {
|
||||||
if (cache.classes.any { it.type == classDef.type }) {
|
val e = cache.classes.findIndexed { it.type == classDef.type }
|
||||||
// TODO: Use logger and warn about duplicate classes
|
if (e != null) {
|
||||||
if (throwOnDuplicates)
|
if (throwOnDuplicates) {
|
||||||
throw Exception("Class ${classDef.type} has already been added to the patcher.")
|
throw Exception("Class ${classDef.type} has already been added to the patcher.")
|
||||||
|
}
|
||||||
|
val (_, idx) = e
|
||||||
|
if (allowedOverwrites.contains(classDef.type)) {
|
||||||
|
cache.classes[idx] = classDef
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cache.classes.add(classDef)
|
cache.classes.add(classDef)
|
||||||
@ -61,7 +72,6 @@ class Patcher(
|
|||||||
fun save(): Map<String, MemoryDataStore> {
|
fun save(): Map<String, MemoryDataStore> {
|
||||||
val newDexFile = object : DexFile {
|
val newDexFile = object : DexFile {
|
||||||
override fun getClasses(): Set<ClassDef> {
|
override fun getClasses(): Set<ClassDef> {
|
||||||
// this is a slow workaround for now
|
|
||||||
cache.methodMap.values.forEach {
|
cache.methodMap.values.forEach {
|
||||||
if (it.definingClassProxy.proxyUsed) {
|
if (it.definingClassProxy.proxyUsed) {
|
||||||
cache.classes[it.definingClassProxy.originalIndex] = it.definingClassProxy.mutatedClass
|
cache.classes[it.definingClassProxy.originalIndex] = it.definingClassProxy.mutatedClass
|
||||||
@ -104,6 +114,10 @@ class Patcher(
|
|||||||
* If the patch failed to apply, an Exception will always be returned in the wrapping Result object.
|
* If the patch failed to apply, an Exception will always be returned in the wrapping Result object.
|
||||||
*/
|
*/
|
||||||
fun applyPatches(stopOnError: Boolean = false, callback: (String) -> Unit = {}): Map<String, Result<PatchResultSuccess>> {
|
fun applyPatches(stopOnError: Boolean = false, callback: (String) -> Unit = {}): Map<String, Result<PatchResultSuccess>> {
|
||||||
|
if (!sigsResolved) {
|
||||||
|
SignatureResolver(cache.classes, signatures).resolve(cache.methodMap)
|
||||||
|
sigsResolved = true
|
||||||
|
}
|
||||||
return buildMap {
|
return buildMap {
|
||||||
for (patch in patches) {
|
for (patch in patches) {
|
||||||
callback(patch.patchName)
|
callback(patch.patchName)
|
||||||
|
@ -6,7 +6,7 @@ import org.jf.dexlib2.iface.ClassDef
|
|||||||
|
|
||||||
class Cache(
|
class Cache(
|
||||||
internal val classes: MutableList<ClassDef>,
|
internal val classes: MutableList<ClassDef>,
|
||||||
val methodMap: MethodMap
|
val methodMap: MethodMap = MethodMap()
|
||||||
) {
|
) {
|
||||||
// TODO: currently we create ClassProxies at multiple places, which is why we could have merge conflicts
|
// TODO: currently we create ClassProxies at multiple places, which is why we could have merge conflicts
|
||||||
// this can be solved by creating a dedicated method for creating class proxies,
|
// this can be solved by creating a dedicated method for creating class proxies,
|
||||||
|
@ -10,14 +10,11 @@ import org.jf.dexlib2.iface.ClassDef
|
|||||||
import org.jf.dexlib2.iface.Method
|
import org.jf.dexlib2.iface.Method
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
// TODO: add logger back
|
|
||||||
internal class SignatureResolver(
|
internal class SignatureResolver(
|
||||||
private val classes: Set<ClassDef>,
|
private val classes: List<ClassDef>,
|
||||||
private val methodSignatures: Iterable<MethodSignature>
|
private val methodSignatures: Iterable<MethodSignature>
|
||||||
) {
|
) {
|
||||||
fun resolve(): MethodMap {
|
fun resolve(methodMap: MethodMap) {
|
||||||
val methodMap = MethodMap()
|
|
||||||
|
|
||||||
for ((index, classDef) in classes.withIndex()) {
|
for ((index, classDef) in classes.withIndex()) {
|
||||||
for (signature in methodSignatures) {
|
for (signature in methodSignatures) {
|
||||||
if (methodMap.containsKey(signature.name)) {
|
if (methodMap.containsKey(signature.name)) {
|
||||||
@ -37,8 +34,6 @@ internal class SignatureResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// These functions do not require the constructor values, so they can be static.
|
// These functions do not require the constructor values, so they can be static.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user