You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-10 05:30:49 +02:00
Compare commits
23 Commits
v5.0.1
...
v1.0.0-dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3cb1e01587 | ||
![]() |
cb4ee207e1 | ||
![]() |
ca6b94d943 | ||
![]() |
6cdb6887d4 | ||
![]() |
ab6453ca8a | ||
![]() |
e8182c17ad | ||
![]() |
49beec9fc6 | ||
![]() |
3ab42a932c | ||
![]() |
4d98cbc9e8 | ||
![]() |
87bbde5e06 | ||
![]() |
8db8893ab1 | ||
![]() |
00c6ab7faf | ||
![]() |
460d62a24c | ||
![]() |
89e4b9f762 | ||
![]() |
a8fd7c00c3 | ||
![]() |
1769132a9e | ||
![]() |
6c0f0823c9 | ||
![]() |
23e897a7a9 | ||
![]() |
7e67daf878 | ||
![]() |
593c83f29f | ||
![]() |
72e123dd01 | ||
![]() |
599a401ed9 | ||
![]() |
3f8500b059 |
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
name: 🐞 Bug report
|
|
||||||
description: Report a very clearly broken issue.
|
|
||||||
title: 'bug: <title>'
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced bug report
|
|
||||||
|
|
||||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Crash
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Bug description
|
|
||||||
description: How did you find the bug? Any additional details that might help?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: Add the steps to reproduce this bug including your environment.
|
|
||||||
placeholder: Step 1. Download some files. Step 2. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Relevant log output
|
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Screenshots or videos
|
|
||||||
description: Add screenshots or videos that show the bug here.
|
|
||||||
placeholder: Drag and drop the screenshots/videos into this box.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Solution
|
|
||||||
description: If applicable, add a possible solution.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: 📃 Documentation
|
|
||||||
url: https://github.com/revanced/revanced-documentation/
|
|
||||||
about: Don't know how or where to start? Check out our documentation!
|
|
||||||
- name: 🗨 Discussions
|
|
||||||
url: https://github.com/revanced/revanced-suggestions/discussions
|
|
||||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
|
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@@ -1,58 +0,0 @@
|
|||||||
name: ⭐ Feature request
|
|
||||||
description: Create a detailed feature request.
|
|
||||||
title: 'feat: <title>'
|
|
||||||
labels: [feature-request]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced feature request
|
|
||||||
|
|
||||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
|
||||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Functionality
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Issue
|
|
||||||
description: What is the current problem. Why does it require a feature request?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Feature
|
|
||||||
description: Describe your feature in detail. How does it solve the issue?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Motivation
|
|
||||||
description: Why should your feature should be considered?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: Release
|
name: Release
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
@@ -21,16 +20,16 @@ jobs:
|
|||||||
- name: Setup JDK
|
- name: Setup JDK
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '8'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: "lts/*"
|
node-version: "lts/*"
|
||||||
|
- name: Make gradlew executable
|
||||||
|
run: chmod +x gradlew
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew build
|
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 -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||||
|
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
@@ -6,4 +6,3 @@
|
|||||||
# Datasource local storage ignored files
|
# Datasource local storage ignored files
|
||||||
/dataSources/
|
/dataSources/
|
||||||
/dataSources.local.xml
|
/dataSources.local.xml
|
||||||
/kotlinc.xml
|
|
||||||
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -4,7 +4,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="azul-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@@ -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",
|
||||||
|
964
CHANGELOG.md
964
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1 @@
|
|||||||
# Patcher
|
# Patcher
|
||||||
Patcher framework used in the ReVanced project.
|
|
@@ -1,64 +1,48 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.7.0"
|
kotlin("jvm") version "1.6.10"
|
||||||
java
|
java
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
|
|
||||||
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
|
|
||||||
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
|
|
||||||
credentials {
|
|
||||||
username = githubUsername
|
|
||||||
password = githubPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("xpp3:xpp3:1.1.4c")
|
implementation(kotlin("stdlib"))
|
||||||
implementation("org.smali:smali:2.5.2")
|
implementation("org.ow2.asm:asm:9.2")
|
||||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
implementation("org.ow2.asm:asm-util:9.2")
|
||||||
implementation("org.apktool:apktool-lib:2.8.1-SNAPSHOT")
|
implementation("org.ow2.asm:asm-tree:9.2")
|
||||||
|
implementation("org.ow2.asm:asm-commons:9.2")
|
||||||
implementation(kotlin("reflect"))
|
implementation("io.github.microutils:kotlin-logging:2.1.21")
|
||||||
|
testImplementation("ch.qos.logback:logback-classic:1.2.11") // use your own logger!
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks.test {
|
||||||
test {
|
useJUnitPlatform()
|
||||||
useJUnitPlatform()
|
testLogging {
|
||||||
testLogging {
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processResources {
|
|
||||||
expand("projectVersion" to project.version)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
if (System.getenv("GITHUB_ACTOR") != null)
|
maven {
|
||||||
maven {
|
name = "GitHubPackages"
|
||||||
name = "GitHubPackages"
|
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patcher")
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
credentials {
|
||||||
credentials {
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
mavenLocal()
|
|
||||||
}
|
}
|
||||||
publications {
|
publications {
|
||||||
register<MavenPublication>("gpr") {
|
register<MavenPublication>("gpr") {
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.0.1
|
version = 1.0.0-dev.3
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
257
gradlew
vendored
Executable file → Normal file
257
gradlew
vendored
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.Data
|
|
||||||
import app.revanced.patcher.data.PackageMetadata
|
|
||||||
import app.revanced.patcher.data.impl.BytecodeData
|
|
||||||
import app.revanced.patcher.data.impl.ResourceData
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
data class PatcherData(
|
|
||||||
internal val internalClasses: MutableList<ClassDef>,
|
|
||||||
internal val resourceCacheDirectory: String,
|
|
||||||
val packageMetadata: PackageMetadata
|
|
||||||
) {
|
|
||||||
internal val patches = mutableListOf<Class<out Patch<Data>>>()
|
|
||||||
internal val bytecodeData = BytecodeData(internalClasses)
|
|
||||||
internal val resourceData = ResourceData(File(resourceCacheDirectory))
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.logging.Logger
|
|
||||||
import app.revanced.patcher.logging.impl.NopLogger
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for the [Patcher].
|
|
||||||
* @param inputFile The input file (usually an apk file).
|
|
||||||
* @param resourceCacheDirectory Directory to cache resources.
|
|
||||||
* @param patchResources Weather to use the resource patcher. Resources will still need to be decoded.
|
|
||||||
* @param aaptPath Optional path to a custom aapt binary.
|
|
||||||
* @param frameworkFolderLocation Optional path to a custom framework folder.
|
|
||||||
* @param logger Custom logger implementation for the [Patcher].
|
|
||||||
*/
|
|
||||||
data class PatcherOptions(
|
|
||||||
internal val inputFile: File,
|
|
||||||
internal val resourceCacheDirectory: String,
|
|
||||||
internal val patchResources: Boolean = false,
|
|
||||||
internal val aaptPath: String = "",
|
|
||||||
internal val frameworkFolderLocation: String? = null,
|
|
||||||
internal val logger: Logger = NopLogger
|
|
||||||
)
|
|
@@ -1,16 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.util.dex.DexFile
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patcher.
|
|
||||||
* @param dexFiles The patched dex files.
|
|
||||||
* @param doNotCompress List of relative paths to files to exclude from compressing.
|
|
||||||
* @param resourceFile File containing resources that need to be extracted into the APK.
|
|
||||||
*/
|
|
||||||
data class PatcherResult(
|
|
||||||
val dexFiles: List<DexFile>,
|
|
||||||
val doNotCompress: List<String>? = null,
|
|
||||||
val resourceFile: File?
|
|
||||||
)
|
|
@@ -1,28 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to constrain a [Patch] or [MethodFingerprint] to compatible packages.
|
|
||||||
* @param compatiblePackages A list of packages a [Patch] or [MethodFingerprint] is compatible with.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class Compatibility(
|
|
||||||
val compatiblePackages: Array<Package>,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to represent packages a patch can be compatible with.
|
|
||||||
* @param name The package identifier name.
|
|
||||||
* @param versions The versions of the package the [Patch] or [MethodFingerprint]is compatible with.
|
|
||||||
*/
|
|
||||||
@Target()
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class Package(
|
|
||||||
val name: String,
|
|
||||||
val versions: Array<String> = [],
|
|
||||||
)
|
|
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.Data
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Declares a [Patch] deprecated for removal.
|
|
||||||
* @param reason The reason why the patch is deprecated.
|
|
||||||
* @param replacement The replacement for the deprecated patch, if any.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class PatchDeprecated(
|
|
||||||
val reason: String,
|
|
||||||
val replacement: KClass<out Patch<Data>> = Patch::class
|
|
||||||
// Values cannot be nullable in annotations, so this will have to do.
|
|
||||||
)
|
|
@@ -1,38 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to name a [Patch] or [MethodFingerprint].
|
|
||||||
* @param name A suggestive name for the [Patch] or [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class Name(
|
|
||||||
val name: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to describe a [Patch] or [MethodFingerprint].
|
|
||||||
* @param description A description for the [Patch] or [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class Description(
|
|
||||||
val description: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to version a [Patch] or [MethodFingerprint].
|
|
||||||
* @param version The version of a [Patch] or [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class Version(
|
|
||||||
val version: String,
|
|
||||||
)
|
|
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Declares a [Patch] deprecated for removal.
|
|
||||||
* @param version The minimum version of the [Patcher] this [Patch] supports.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
@MustBeDocumented
|
|
||||||
annotation class SincePatcher(val version: String)
|
|
16
src/main/kotlin/app/revanced/patcher/cache/Cache.kt
vendored
Normal file
16
src/main/kotlin/app/revanced/patcher/cache/Cache.kt
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patcher.cache
|
||||||
|
|
||||||
|
import org.objectweb.asm.tree.ClassNode
|
||||||
|
|
||||||
|
class Cache(
|
||||||
|
val classes: List<ClassNode>,
|
||||||
|
val methods: MethodMap
|
||||||
|
)
|
||||||
|
|
||||||
|
class MethodMap : LinkedHashMap<String, PatchData>() {
|
||||||
|
override fun get(key: String): PatchData {
|
||||||
|
return super.get(key) ?: throw MethodNotFoundException("Method $key was not found in the method cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MethodNotFoundException(s: String) : Exception(s)
|
22
src/main/kotlin/app/revanced/patcher/cache/PatchData.kt
vendored
Normal file
22
src/main/kotlin/app/revanced/patcher/cache/PatchData.kt
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.patcher.cache
|
||||||
|
|
||||||
|
import app.revanced.patcher.resolver.MethodResolver
|
||||||
|
import app.revanced.patcher.signature.Signature
|
||||||
|
import org.objectweb.asm.tree.ClassNode
|
||||||
|
import org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
|
data class PatchData(
|
||||||
|
val declaringClass: ClassNode,
|
||||||
|
val method: MethodNode,
|
||||||
|
val scanData: PatternScanData
|
||||||
|
) {
|
||||||
|
@Suppress("Unused") // TODO(Sculas): remove this when we have coverage for this method.
|
||||||
|
fun findParentMethod(signature: Signature): PatchData? {
|
||||||
|
return MethodResolver.resolveMethod(declaringClass, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PatternScanData(
|
||||||
|
val startIndex: Int,
|
||||||
|
val endIndex: Int
|
||||||
|
)
|
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.data
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.impl.BytecodeData
|
|
||||||
import app.revanced.patcher.data.impl.ResourceData
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constraint interface for [BytecodeData] and [ResourceData]
|
|
||||||
*/
|
|
||||||
interface Data
|
|
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.patcher.data
|
|
||||||
|
|
||||||
import brut.androlib.meta.MetaInfo
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata about a package.
|
|
||||||
*/
|
|
||||||
class PackageMetadata {
|
|
||||||
lateinit var packageName: String
|
|
||||||
lateinit var packageVersion: String
|
|
||||||
|
|
||||||
internal val metaInfo: MetaInfo = MetaInfo()
|
|
||||||
}
|
|
@@ -1,69 +0,0 @@
|
|||||||
package app.revanced.patcher.data.impl
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.Data
|
|
||||||
import app.revanced.patcher.util.ProxyBackedClassList
|
|
||||||
import app.revanced.patcher.util.method.MethodWalker
|
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
|
||||||
import org.jf.dexlib2.iface.Method
|
|
||||||
|
|
||||||
class BytecodeData(
|
|
||||||
internalClasses: MutableList<ClassDef>
|
|
||||||
) : Data {
|
|
||||||
val classes = ProxyBackedClassList(internalClasses)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given class name.
|
|
||||||
* @param className The name of the class.
|
|
||||||
* @return A proxy for the first class that matches the class name.
|
|
||||||
*/
|
|
||||||
fun findClass(className: String) = findClass { it.type.contains(className) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given predicate.
|
|
||||||
* @param predicate A predicate to match the class.
|
|
||||||
* @return A proxy for the first class that matches the predicate.
|
|
||||||
*/
|
|
||||||
fun findClass(predicate: (ClassDef) -> Boolean) =
|
|
||||||
// if we already proxied the class matching the predicate...
|
|
||||||
classes.proxies.firstOrNull { predicate(it.immutableClass) } ?:
|
|
||||||
// else resolve the class to a proxy and return it, if the predicate is matching a class
|
|
||||||
classes.find(predicate)?.let { proxy(it) }
|
|
||||||
|
|
||||||
fun proxy(classDef: ClassDef): app.revanced.patcher.util.proxy.ClassProxy {
|
|
||||||
var proxy = this.classes.proxies.find { it.immutableClass.type == classDef.type }
|
|
||||||
if (proxy == null) {
|
|
||||||
proxy = app.revanced.patcher.util.proxy.ClassProxy(classDef)
|
|
||||||
this.classes.add(proxy)
|
|
||||||
}
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MethodNotFoundException(s: String) : Exception(s)
|
|
||||||
|
|
||||||
internal inline fun <reified T> Iterable<T>.find(predicate: (T) -> Boolean): T? {
|
|
||||||
for (element in this) {
|
|
||||||
if (predicate(element)) {
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a [MethodWalker] instance for the current [BytecodeData].
|
|
||||||
* @param startMethod The method to start at.
|
|
||||||
* @return A [MethodWalker] instance.
|
|
||||||
*/
|
|
||||||
fun BytecodeData.toMethodWalker(startMethod: Method): MethodWalker {
|
|
||||||
return MethodWalker(this, startMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inline fun <T> Iterable<T>.findIndexed(predicate: (T) -> Boolean): Pair<T, Int>? {
|
|
||||||
for ((index, element) in this.withIndex()) {
|
|
||||||
if (predicate(element)) {
|
|
||||||
return element to index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
@@ -1,84 +0,0 @@
|
|||||||
package app.revanced.patcher.data.impl
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.Data
|
|
||||||
import org.w3c.dom.Document
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
import javax.xml.transform.TransformerFactory
|
|
||||||
import javax.xml.transform.dom.DOMSource
|
|
||||||
import javax.xml.transform.stream.StreamResult
|
|
||||||
|
|
||||||
class ResourceData(private val resourceCacheDirectory: File) : Data, Iterable<File> {
|
|
||||||
val xmlEditor = XmlFileHolder()
|
|
||||||
|
|
||||||
operator fun get(path: String) = resourceCacheDirectory.resolve(path)
|
|
||||||
|
|
||||||
override fun iterator() = resourceCacheDirectory.walkTopDown().iterator()
|
|
||||||
|
|
||||||
inner class XmlFileHolder {
|
|
||||||
operator fun get(inputStream: InputStream) =
|
|
||||||
DomFileEditor(inputStream)
|
|
||||||
|
|
||||||
operator fun get(path: String): DomFileEditor {
|
|
||||||
return DomFileEditor(this@ResourceData[path])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DomFileEditor is a wrapper for a file that can be edited as a dom document.
|
|
||||||
*
|
|
||||||
* @param inputStream the input stream to read the xml file from.
|
|
||||||
* @param outputStream the output stream to write the xml file to. If null, the file will not be written.
|
|
||||||
*/
|
|
||||||
class DomFileEditor internal constructor(
|
|
||||||
private val inputStream: InputStream,
|
|
||||||
private val outputStream: Lazy<OutputStream>? = null,
|
|
||||||
) : Closeable {
|
|
||||||
// path to the xml file to unlock the resource when closing the editor
|
|
||||||
private var filePath: String? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The document of the xml file
|
|
||||||
*/
|
|
||||||
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
|
||||||
.also(Document::normalize)
|
|
||||||
|
|
||||||
// lazily open an output stream
|
|
||||||
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
|
||||||
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
|
||||||
constructor(file: File) : this(file.inputStream(), lazy { file.outputStream() }) {
|
|
||||||
filePath = file.path
|
|
||||||
|
|
||||||
// prevent sharing mutability of the same file between multiple instances of DomFileEditor
|
|
||||||
if (locks.contains(filePath))
|
|
||||||
throw IllegalStateException("Can not create a DomFileEditor for that file because it is already locked by another instance of DomFileEditor.")
|
|
||||||
locks.add(filePath!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
inputStream.close()
|
|
||||||
|
|
||||||
// if the output stream is not null, do not close it
|
|
||||||
outputStream?.let {
|
|
||||||
val result = StreamResult(it.value)
|
|
||||||
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), result)
|
|
||||||
|
|
||||||
it.value.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the lock, if it exists
|
|
||||||
filePath?.let {
|
|
||||||
locks.remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
// list of locked file paths
|
|
||||||
val locks = mutableListOf<String>()
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,79 +0,0 @@
|
|||||||
package app.revanced.patcher.extensions
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.*
|
|
||||||
import app.revanced.patcher.data.Data
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.OptionsContainer
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.KVisibility
|
|
||||||
import kotlin.reflect.full.companionObject
|
|
||||||
import kotlin.reflect.full.companionObjectInstance
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively find a given annotation on a class.
|
|
||||||
* @param targetAnnotation The annotation to find.
|
|
||||||
* @return The annotation.
|
|
||||||
*/
|
|
||||||
private fun <T : Annotation> Class<*>.recursiveAnnotation(targetAnnotation: KClass<T>) =
|
|
||||||
this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
|
||||||
|
|
||||||
|
|
||||||
private fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
|
||||||
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
|
||||||
): T? {
|
|
||||||
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
|
|
||||||
|
|
||||||
for (annotation in this.annotations) {
|
|
||||||
if (traversed.contains(annotation)) continue
|
|
||||||
traversed.add(annotation)
|
|
||||||
|
|
||||||
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
object PatchExtensions {
|
|
||||||
val Class<*>.patchName: String
|
|
||||||
get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
|
|
||||||
val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version
|
|
||||||
val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
|
||||||
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
|
||||||
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
|
|
||||||
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
|
||||||
val Class<out Patch<Data>>.options: PatchOptions?
|
|
||||||
get() = kotlin.companionObject?.let { cl ->
|
|
||||||
if (cl.visibility != KVisibility.PUBLIC) return null
|
|
||||||
kotlin.companionObjectInstance?.let {
|
|
||||||
(it as? OptionsContainer)?.options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val Class<out Patch<Data>>.deprecated: Pair<String, KClass<out Patch<Data>>?>?
|
|
||||||
get() = recursiveAnnotation(PatchDeprecated::class)?.let {
|
|
||||||
it.reason to it.replacement.let { cl ->
|
|
||||||
if (cl == Patch::class) null else cl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val Class<out Patch<Data>>.sincePatcherVersion get() = recursiveAnnotation(SincePatcher::class)?.version
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {
|
|
||||||
if (this.patchName == patch.patchName) throw IllegalArgumentException("thisval and patch may not be the same")
|
|
||||||
return this.dependencies?.any { it.java.patchName == this@dependsOn.patchName } == true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object MethodFingerprintExtensions {
|
|
||||||
val MethodFingerprint.name: String
|
|
||||||
get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
|
|
||||||
val MethodFingerprint.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1"
|
|
||||||
val MethodFingerprint.description get() = javaClass.recursiveAnnotation(Description::class)?.description
|
|
||||||
val MethodFingerprint.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
|
||||||
val MethodFingerprint.matchingMethod get() = javaClass.recursiveAnnotation(app.revanced.patcher.fingerprint.method.annotation.MatchingMethod::class)
|
|
||||||
val MethodFingerprint.fuzzyPatternScanMethod get() = javaClass.recursiveAnnotation(app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod::class)
|
|
||||||
val MethodFingerprint.fuzzyScanThreshold get() = fuzzyPatternScanMethod?.threshold ?: 0
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced fingerprint.
|
|
||||||
* Can be a [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
interface Fingerprint
|
|
@@ -1,32 +0,0 @@
|
|||||||
package app.revanced.patcher.fingerprint.method.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations for a method which matches to a [MethodFingerprint].
|
|
||||||
* @param definingClass The defining class name of the method.
|
|
||||||
* @param name A suggestive name for the method which the [MethodFingerprint] was created for.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
annotation class MatchingMethod(
|
|
||||||
val definingClass: String = "L<unspecified-class>;",
|
|
||||||
val name: String = "<unspecified-method>"
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations to scan a pattern [MethodFingerprint] with fuzzy algorithm.
|
|
||||||
* @param threshold if [threshold] or more of the opcodes do not match, skip.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
annotation class FuzzyPatternScanMethod(
|
|
||||||
val threshold: Int = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations to scan a pattern [MethodFingerprint] directly.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
annotation class DirectPatternScanMethod
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user