mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 09:08:04 +01:00
feat: merge classes on addition (#127)
This commit is contained in:
parent
77bbf6be1f
commit
a925650044
1 changed files with 78 additions and 25 deletions
|
@ -8,6 +8,10 @@ import app.revanced.patcher.extensions.nullOutputStream
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.util.VersionReader
|
import app.revanced.patcher.util.VersionReader
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import brut.androlib.Androlib
|
import brut.androlib.Androlib
|
||||||
import brut.androlib.meta.UsesFramework
|
import brut.androlib.meta.UsesFramework
|
||||||
import brut.androlib.options.BuildOptions
|
import brut.androlib.options.BuildOptions
|
||||||
|
@ -22,7 +26,9 @@ import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
import lanchon.multidexlib2.DexIO
|
import lanchon.multidexlib2.DexIO
|
||||||
import lanchon.multidexlib2.MultiDexIO
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
import org.jf.dexlib2.Opcodes
|
import org.jf.dexlib2.Opcodes
|
||||||
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
import org.jf.dexlib2.iface.DexFile
|
import org.jf.dexlib2.iface.DexFile
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
@ -65,40 +71,85 @@ class Patcher(private val options: PatcherOptions) {
|
||||||
/**
|
/**
|
||||||
* 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 process The callback for [files] which are being added.
|
||||||
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
|
|
||||||
*/
|
*/
|
||||||
fun addFiles(
|
fun addFiles(
|
||||||
files: List<File>,
|
files: List<File>,
|
||||||
allowedOverwrites: Iterable<String> = emptyList(),
|
process: (File) -> Unit
|
||||||
throwOnDuplicates: Boolean = false,
|
|
||||||
callback: (File) -> Unit
|
|
||||||
) {
|
) {
|
||||||
|
with(context.bytecodeContext.classes) {
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
var modified = false
|
process(file)
|
||||||
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
|
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
|
||||||
val type = classDef.type
|
val type = classDef.type
|
||||||
|
|
||||||
val existingClass = context.bytecodeContext.classes.classes.findIndexed { it.type == type }
|
val result = classes.findIndexed { it.type == type }
|
||||||
if (existingClass == null) {
|
if (result == null) {
|
||||||
if (throwOnDuplicates) throw Exception("Class $type has already been added to the patcher")
|
logger.trace("Merging type $type")
|
||||||
|
classes.add(classDef)
|
||||||
|
} else {
|
||||||
|
val (existingClass, existingClassIndex) = result
|
||||||
|
|
||||||
logger.trace("Merging $type")
|
logger.trace("Type $type exists. Adding missing methods and fields.")
|
||||||
context.bytecodeContext.classes.classes.add(classDef)
|
|
||||||
modified = true
|
|
||||||
|
|
||||||
continue
|
/**
|
||||||
|
* Add missing fields and methods from [from].
|
||||||
|
*
|
||||||
|
* @param from The class to add methods and fields from.
|
||||||
|
*/
|
||||||
|
fun ClassDef.addMissingFrom(from: ClassDef) {
|
||||||
|
var changed = false
|
||||||
|
fun <T> ClassDef.transformClass(transform: (MutableClass) -> T): T {
|
||||||
|
fun toMutableClass() =
|
||||||
|
if (this@transformClass is MutableClass) this else this.toMutable()
|
||||||
|
return transform(toMutableClass())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowedOverwrites.contains(type)) continue
|
fun ClassDef.addMissingMethods(): ClassDef {
|
||||||
|
fun getMissingMethods() = from.methods.filterNot {
|
||||||
logger.trace("Overwriting $type")
|
this@addMissingMethods.methods.any { original ->
|
||||||
|
MethodUtil.methodSignaturesMatch(original, it)
|
||||||
val index = existingClass.second
|
}
|
||||||
context.bytecodeContext.classes.classes[index] = classDef
|
}
|
||||||
modified = true
|
|
||||||
|
return getMissingMethods()
|
||||||
|
.apply {
|
||||||
|
if (isEmpty()) return@addMissingMethods this@addMissingMethods else changed =
|
||||||
|
true
|
||||||
|
}
|
||||||
|
.map { it.toMutable() }
|
||||||
|
.let { missingMethods ->
|
||||||
|
this@addMissingMethods.transformClass { classDef ->
|
||||||
|
classDef.apply { methods.addAll(missingMethods) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ClassDef.addMissingFields(): ClassDef {
|
||||||
|
fun getMissingFields() = from.fields.filterNot {
|
||||||
|
this@addMissingFields.fields.any { original -> original.name == it.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMissingFields()
|
||||||
|
.apply {
|
||||||
|
if (isEmpty()) return@addMissingFields this@addMissingFields else changed = true
|
||||||
|
}
|
||||||
|
.map { it.toMutable() }
|
||||||
|
.let { missingFields ->
|
||||||
|
this@addMissingFields.transformClass { classDef ->
|
||||||
|
classDef.apply { fields.addAll(missingFields) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes[existingClassIndex] = addMissingMethods().addMissingFields()
|
||||||
|
.apply { if (!changed) return }
|
||||||
|
}
|
||||||
|
|
||||||
|
existingClass.addMissingFrom(classDef)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (modified) callback(file)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +205,7 @@ class Patcher(private val options: PatcherOptions) {
|
||||||
cacheDirectory.close()
|
cacheDirectory.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> logger.info("Not compiling resources because resource patching is not required")
|
else -> logger.info("Not compiling resources because resource patching is not required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +292,7 @@ class Patcher(private val options: PatcherOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceDecodingMode.MANIFEST_ONLY -> {
|
ResourceDecodingMode.MANIFEST_ONLY -> {
|
||||||
logger.info("Decoding AndroidManifest.xml only, because resources are not needed")
|
logger.info("Decoding AndroidManifest.xml only, because resources are not needed")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue