You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-06 16:38:50 +02:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e802141df5 | ||
![]() |
abebc0862c | ||
![]() |
96ef150e89 | ||
![]() |
c5de9e2988 | ||
![]() |
c391ca648b | ||
![]() |
7cf79e68e0 | ||
![]() |
f07db3c214 | ||
![]() |
88bb3a8845 | ||
![]() |
b9e6bd6775 | ||
![]() |
cd1b72e078 | ||
![]() |
6b889557ab | ||
![]() |
4b1be8c647 | ||
![]() |
73c893c6e7 | ||
![]() |
75b36823b8 | ||
![]() |
d2d93cd075 | ||
![]() |
26b8621ac8 | ||
![]() |
f365a41741 | ||
![]() |
9ec720e983 | ||
![]() |
0f432b3fdd | ||
![]() |
96cd5618dd | ||
![]() |
c2a5a55e67 | ||
![]() |
6c5de8b414 | ||
![]() |
ea773cfa56 | ||
![]() |
a306561b55 | ||
![]() |
b6dcd88495 | ||
![]() |
a925650044 | ||
![]() |
77bbf6be1f | ||
![]() |
bd053b7e99 |
24
.github/workflows/pull_request.yml
vendored
Normal file
24
.github/workflows/pull_request.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: PR to main
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
MESSAGE: merge branch \`${{ github.head_ref || github.ref_name }}\` to \`main\`
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pull-request:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Open pull request
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
with:
|
||||||
|
destination_branch: 'main'
|
||||||
|
pr_title: 'chore: ${{ env.MESSAGE }}'
|
||||||
|
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
|
||||||
|
pr_draft: true
|
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -22,18 +22,15 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'zulu'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: "lts/*"
|
node-version: "latest"
|
||||||
- name: Build with Gradle
|
cache: 'npm'
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew build
|
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
run: npm install semantic-release @saithodev/semantic-release-backmerge @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -115,3 +115,6 @@ gradle-app.setting
|
|||||||
|
|
||||||
# Avoid ignoring test resources
|
# Avoid ignoring test resources
|
||||||
!src/test/resources/*
|
!src/test/resources/*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
13
.releaserc
13
.releaserc
@@ -7,11 +7,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
["@semantic-release/commit-analyzer", {
|
"@semantic-release/commit-analyzer",
|
||||||
"releaseRules": [
|
|
||||||
{"type": "build", "release": "patch"}
|
|
||||||
]
|
|
||||||
}],
|
|
||||||
"@semantic-release/release-notes-generator",
|
"@semantic-release/release-notes-generator",
|
||||||
"@semantic-release/changelog",
|
"@semantic-release/changelog",
|
||||||
"gradle-semantic-release-plugin",
|
"gradle-semantic-release-plugin",
|
||||||
@@ -24,6 +20,13 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"@saithodev/semantic-release-backmerge",
|
||||||
|
{
|
||||||
|
branches: [{from: "main", to: "dev"}],
|
||||||
|
clearWorkspace: true
|
||||||
|
}
|
||||||
|
],
|
||||||
"@semantic-release/github"
|
"@semantic-release/github"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,3 +1,61 @@
|
|||||||
|
## [6.3.2](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2) (2022-12-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check if fingerprint string is substring of any string references ([c5de9e2](https://github.com/revanced/revanced-patcher/commit/c5de9e29889dffd18b31e62a892881cc48e8b607))
|
||||||
|
* print full exception when patch fails ([7cf79e6](https://github.com/revanced/revanced-patcher/commit/7cf79e68e0e9dfd9faddee33139b127b71882d3e))
|
||||||
|
|
||||||
|
## [6.3.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2-dev.1) (2022-12-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check if fingerprint string is substring of any string references ([c5de9e2](https://github.com/revanced/revanced-patcher/commit/c5de9e29889dffd18b31e62a892881cc48e8b607))
|
||||||
|
* print full exception when patch fails ([7cf79e6](https://github.com/revanced/revanced-patcher/commit/7cf79e68e0e9dfd9faddee33139b127b71882d3e))
|
||||||
|
|
||||||
|
## [6.3.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2-dev.1) (2022-12-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* print full exception when patch fails ([27a8401](https://github.com/revanced/revanced-patcher/commit/27a8401d81e078e0303f7ddcb0ac6f342f8e4def))
|
||||||
|
|
||||||
|
## [6.3.1](https://github.com/revanced/revanced-patcher/compare/v6.3.0...v6.3.1) (2022-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* publicize types when merging files if necessary ([#137](https://github.com/revanced/revanced-patcher/issues/137)) ([9ec720e](https://github.com/revanced/revanced-patcher/commit/9ec720e983785d8b1dde330cc0e0e0f914c1803c))
|
||||||
|
|
||||||
|
## [6.3.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.0...v6.3.1-dev.1) (2022-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* publicize types when merging files if necessary ([#137](https://github.com/revanced/revanced-patcher/issues/137)) ([9ec720e](https://github.com/revanced/revanced-patcher/commit/9ec720e983785d8b1dde330cc0e0e0f914c1803c))
|
||||||
|
|
||||||
|
# [6.3.0](https://github.com/revanced/revanced-patcher/compare/v6.2.0...v6.3.0) (2022-12-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* sort patches in lexicographical order ([a306561](https://github.com/revanced/revanced-patcher/commit/a306561b55ac848792046378f582a036f7ffab03)), closes [#125](https://github.com/revanced/revanced-patcher/issues/125)
|
||||||
|
|
||||||
|
# [6.2.0](https://github.com/revanced/revanced-patcher/compare/v6.1.1...v6.2.0) (2022-12-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* merge classes on addition ([#127](https://github.com/revanced/revanced-patcher/issues/127)) ([a925650](https://github.com/revanced/revanced-patcher/commit/a9256500440f9b4117f1b8813ba0097dafee4ebb))
|
||||||
|
|
||||||
|
## [6.1.1](https://github.com/revanced/revanced-patcher/compare/v6.1.0...v6.1.1) (2022-11-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use `MethodUtil.methodSignaturesMatch` instead of `Method.softCompareTo` ([bd053b7](https://github.com/revanced/revanced-patcher/commit/bd053b7e9974c0282d56e6762459db7070452e4a))
|
||||||
|
|
||||||
# [6.1.0](https://github.com/revanced/revanced-patcher/compare/v6.0.2...v6.1.0) (2022-11-22)
|
# [6.1.0](https://github.com/revanced/revanced-patcher/compare/v6.0.2...v6.1.0) (2022-11-22)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 6.1.0
|
version = 6.3.2
|
||||||
|
6107
package-lock.json
generated
Normal file
6107
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@semantic-release/changelog": "^6.0.2",
|
||||||
|
"@semantic-release/git": "^10.0.1",
|
||||||
|
"gradle-semantic-release-plugin": "^1.7.4",
|
||||||
|
"semantic-release": "^19.0.5"
|
||||||
|
}
|
||||||
|
}
|
@@ -7,7 +7,12 @@ import app.revanced.patcher.extensions.PatchExtensions.patchName
|
|||||||
import app.revanced.patcher.extensions.nullOutputStream
|
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.TypeUtil.traverseClassHierarchy
|
||||||
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
|
||||||
@@ -21,8 +26,11 @@ import brut.directory.ExtFile
|
|||||||
import lanchon.multidexlib2.BasicDexFileNamer
|
import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
import lanchon.multidexlib2.DexIO
|
import lanchon.multidexlib2.DexIO
|
||||||
import lanchon.multidexlib2.MultiDexIO
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
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 +73,119 @@ 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
|
|
||||||
) {
|
) {
|
||||||
for (file in files) {
|
with(context.bytecodeContext.classes) {
|
||||||
var modified = false
|
for (file in files) {
|
||||||
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
|
process(file)
|
||||||
val type = classDef.type
|
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the [AccessFlags.PUBLIC] flag is set.
|
||||||
|
*
|
||||||
|
* @return True, if the flag is set.
|
||||||
|
*/
|
||||||
|
fun Int.isPublic() = AccessFlags.PUBLIC.isSet(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a class and its super class public recursively.
|
||||||
|
*/
|
||||||
|
fun MutableClass.publicize() {
|
||||||
|
context.bytecodeContext.traverseClassHierarchy(this) {
|
||||||
|
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||||
|
|
||||||
|
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add missing methods to the class, considering to publicise the [ClassDef] if necessary.
|
||||||
|
*/
|
||||||
|
fun ClassDef.addMissingMethods(): ClassDef {
|
||||||
|
fun getMissingMethods() = from.methods.filterNot {
|
||||||
|
this@addMissingMethods.methods.any { original ->
|
||||||
|
MethodUtil.methodSignaturesMatch(original, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMissingMethods()
|
||||||
|
.apply {
|
||||||
|
if (isEmpty()) return@addMissingMethods this@addMissingMethods else changed =
|
||||||
|
true
|
||||||
|
}
|
||||||
|
.map { it.toMutable() }
|
||||||
|
.let { missingMethods ->
|
||||||
|
this@addMissingMethods.transformClass { classDef ->
|
||||||
|
classDef.apply {
|
||||||
|
// make sure the class is public, if the class contains public methods
|
||||||
|
if (missingMethods.any { it.accessFlags.isPublic() })
|
||||||
|
classDef.publicize()
|
||||||
|
|
||||||
|
methods.addAll(missingMethods)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add missing fields to the class, considering to publicise the [ClassDef] if necessary.
|
||||||
|
*/
|
||||||
|
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 ->
|
||||||
|
// make sure the class is public, if the class contains public fields
|
||||||
|
if (missingFields.any { it.accessFlags.isPublic() })
|
||||||
|
classDef.publicize()
|
||||||
|
|
||||||
|
classDef.apply { fields.addAll(missingFields) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes[existingClassIndex] = addMissingMethods().addMissingFields()
|
||||||
|
.apply { if (!changed) return }
|
||||||
|
}
|
||||||
|
|
||||||
|
existingClass.addMissingFrom(classDef)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowedOverwrites.contains(type)) continue
|
|
||||||
|
|
||||||
logger.trace("Overwriting $type")
|
|
||||||
|
|
||||||
val index = existingClass.second
|
|
||||||
context.bytecodeContext.classes.classes[index] = classDef
|
|
||||||
modified = true
|
|
||||||
}
|
}
|
||||||
if (modified) callback(file)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +241,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 +328,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")
|
||||||
|
|
||||||
@@ -310,9 +399,10 @@ class Patcher(private val options: PatcherOptions) {
|
|||||||
val result = executePatch(dependency, executedPatches)
|
val result = executePatch(dependency, executedPatches)
|
||||||
if (result.isSuccess()) return@forEach
|
if (result.isSuccess()) return@forEach
|
||||||
|
|
||||||
val error = result.error()!!
|
return PatchResultError(
|
||||||
val errorMessage = error.cause ?: error.message
|
"'$patchName' depends on '${dependency.patchName}' but the following error was raised: " +
|
||||||
return PatchResultError("'$patchName' depends on '${dependency.patchName}' but the following error was raised: $errorMessage")
|
result.error()!!.let { it.cause?.stackTraceToString() ?: it.message }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
|
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
|
||||||
|
@@ -13,7 +13,6 @@ import org.jf.dexlib2.builder.MutableMethodImplementation
|
|||||||
import org.jf.dexlib2.builder.instruction.*
|
import org.jf.dexlib2.builder.instruction.*
|
||||||
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
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@@ -45,17 +44,6 @@ fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare a method to another, considering name and parameters.
|
|
||||||
* @param otherMethod The method to compare against.
|
|
||||||
* @return True if the methods match given the conditions.
|
|
||||||
*/
|
|
||||||
fun Method.softCompareTo(otherMethod: MethodReference): Boolean {
|
|
||||||
return this.name == otherMethod.name && parametersEqual(
|
|
||||||
this.parameterTypes, otherMethod.parameterTypes
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones the method.
|
* Clones the method.
|
||||||
* @param registerCount This parameter allows you to change the register count of the method.
|
* @param registerCount This parameter allows you to change the register count of the method.
|
||||||
|
@@ -4,7 +4,6 @@ import app.revanced.patcher.data.BytecodeContext
|
|||||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold
|
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold
|
||||||
import app.revanced.patcher.extensions.parametersEqual
|
import app.revanced.patcher.extensions.parametersEqual
|
||||||
import app.revanced.patcher.extensions.softCompareTo
|
|
||||||
import app.revanced.patcher.fingerprint.Fingerprint
|
import app.revanced.patcher.fingerprint.Fingerprint
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
@@ -14,6 +13,7 @@ import org.jf.dexlib2.iface.Method
|
|||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.StringReference
|
import org.jf.dexlib2.iface.reference.StringReference
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the [MethodFingerprint] for a method.
|
* Represents the [MethodFingerprint] for a method.
|
||||||
@@ -109,7 +109,7 @@ abstract class MethodFingerprint(
|
|||||||
if (instruction.opcode.ordinal != Opcode.CONST_STRING.ordinal) return@forEachIndexed
|
if (instruction.opcode.ordinal != Opcode.CONST_STRING.ordinal) return@forEachIndexed
|
||||||
|
|
||||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
val index = stringsList.indexOfFirst { it == string }
|
val index = stringsList.indexOfFirst(string::contains)
|
||||||
if (index == -1) return@forEachIndexed
|
if (index == -1) return@forEachIndexed
|
||||||
|
|
||||||
add(
|
add(
|
||||||
@@ -297,7 +297,7 @@ data class MethodFingerprintResult(
|
|||||||
*/
|
*/
|
||||||
val mutableMethod by lazy {
|
val mutableMethod by lazy {
|
||||||
mutableClass.methods.first {
|
mutableClass.methods.first {
|
||||||
it.softCompareTo(this.method)
|
MethodUtil.methodSignaturesMatch(it, this.method)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,7 +14,7 @@ import kotlin.reflect.KClass
|
|||||||
annotation class Patch(val include: Boolean = true)
|
annotation class Patch(val include: Boolean = true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation for dependencies of [Patch]es .
|
* Annotation for dependencies of [Patch]es.
|
||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
19
src/main/kotlin/app/revanced/patcher/util/TypeUtil.kt
Normal file
19
src/main/kotlin/app/revanced/patcher/util/TypeUtil.kt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
|
||||||
|
object TypeUtil {
|
||||||
|
/**
|
||||||
|
* traverse the class hierarchy starting from the given root class
|
||||||
|
*
|
||||||
|
* @param targetClass the class to start traversing the class hierarchy from
|
||||||
|
* @param callback function that is called for every class in the hierarchy
|
||||||
|
*/
|
||||||
|
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||||
|
callback(targetClass)
|
||||||
|
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||||
|
traverseClassHierarchy(it, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
package app.revanced.patcher.util.method
|
package app.revanced.patcher.util.method
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.softCompareTo
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import org.jf.dexlib2.iface.Method
|
import org.jf.dexlib2.iface.Method
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a method from another method via instruction offsets.
|
* Find a method from another method via instruction offsets.
|
||||||
@@ -44,8 +44,8 @@ class MethodWalker internal constructor(
|
|||||||
val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
|
val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
|
||||||
|
|
||||||
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
||||||
currentMethod = methods.first { it ->
|
currentMethod = methods.first {
|
||||||
return@first it.softCompareTo(newMethod)
|
return@first MethodUtil.methodSignaturesMatch(it, newMethod)
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
package app.revanced.patcher.util.patch
|
package app.revanced.patcher.util.patch
|
||||||
|
|
||||||
import app.revanced.patcher.data.Context
|
import app.revanced.patcher.data.Context
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import org.jf.dexlib2.DexFileFactory
|
import org.jf.dexlib2.DexFileFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -16,12 +17,12 @@ import java.util.jar.JarFile
|
|||||||
*/
|
*/
|
||||||
sealed class PatchBundle(path: String) : File(path) {
|
sealed class PatchBundle(path: String) : File(path) {
|
||||||
internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList {
|
internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList {
|
||||||
for (className in classNames) {
|
classNames.forEach { className ->
|
||||||
val clazz = classLoader.loadClass(className)
|
val clazz = classLoader.loadClass(className)
|
||||||
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) continue
|
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach
|
||||||
@Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>)
|
@Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>)
|
||||||
}
|
}
|
||||||
}
|
}.sortedBy { it.patchName }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A patch bundle of type [Jar].
|
* A patch bundle of type [Jar].
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions.*
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
internal class VersionReaderTest {
|
|
||||||
@Test
|
|
||||||
fun read() {
|
|
||||||
val version = VersionReader.read()
|
|
||||||
assertNotNull(version)
|
|
||||||
assertTrue(version.isNotEmpty())
|
|
||||||
val parts = version.split(".")
|
|
||||||
assertEquals(3, parts.size)
|
|
||||||
parts.forEach {
|
|
||||||
assertTrue(it.toInt() >= 0)
|
|
||||||
}
|
|
||||||
println(version)
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user