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
37 Commits
v20.0.0-de
...
v20.0.1-de
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c2dc29e061 | ||
![]() |
69f2f20fd9 | ||
![]() |
525beda18e | ||
![]() |
73d3cbf4ff | ||
![]() |
70278dd79d | ||
![]() |
5e98e9e30a | ||
![]() |
ac1aff5a1a | ||
![]() |
5481d0c54c | ||
![]() |
4604742d0f | ||
![]() |
4beb907a61 | ||
![]() |
7f44174d91 | ||
![]() |
d310246852 | ||
![]() |
dcc989243c | ||
![]() |
5227e98abf | ||
![]() |
8c4dd5b3a3 | ||
![]() |
736b3eebbf | ||
![]() |
b41a542952 | ||
![]() |
d21128fe2e | ||
![]() |
cf4374b8cf | ||
![]() |
8a30b0fa10 | ||
![]() |
11a911dc67 | ||
![]() |
6e3ba7419b | ||
![]() |
50a66ccfed | ||
![]() |
0be79840b1 | ||
![]() |
d8b4c60321 | ||
![]() |
f77e99e817 | ||
![]() |
ea26c486c0 | ||
![]() |
bebb734608 | ||
![]() |
d842f82d07 | ||
![]() |
82bab58ac2 | ||
![]() |
90b7631d9e | ||
![]() |
26d449e6d9 | ||
![]() |
49466060e3 | ||
![]() |
620ea5b852 | ||
![]() |
3e2168a2b2 | ||
![]() |
13c77967b1 | ||
![]() |
f57e571a14 |
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,18 +11,18 @@ body:
|
||||
<source
|
||||
width="256px"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="../../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
>
|
||||
<img
|
||||
width="256px"
|
||||
src="../../assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
>
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://revanced.app/">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../../assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="../../assets/revanced-logo/revanced-logo.svg" />
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://github.com/ReVanced">
|
||||
@@ -70,7 +70,8 @@ body:
|
||||
|
||||
Before creating a new bug report, please keep the following in mind:
|
||||
|
||||
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-patcher/labels/Bug%20report).
|
||||
- **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-patcher/issues?q=label%3A%22Bug+report%22).
|
||||
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patcher/blob/main/CONTRIBUTING.md).
|
||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -100,7 +101,7 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your bug report will be closed if you don't follow the checklist below.
|
||||
options:
|
||||
- label: This issue is not a duplicate of an existing bug report.
|
||||
- label: I have checked all open and closed bug reports and this is not a duplicate.
|
||||
required: true
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
|
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -11,18 +11,18 @@ body:
|
||||
<source
|
||||
width="256px"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcset="../../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||
>
|
||||
<img
|
||||
width="256px"
|
||||
src="../../assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||
>
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://revanced.app/">
|
||||
<picture>
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="../../assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="../../assets/revanced-logo/revanced-logo.svg" />
|
||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
<a href="https://github.com/ReVanced">
|
||||
@@ -70,7 +70,8 @@ body:
|
||||
|
||||
Before creating a new feature request, please keep the following in mind:
|
||||
|
||||
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-patcher/labels/Feature%20request).
|
||||
- **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-patcher/issues?q=label%3A%22Feature+request%22).
|
||||
- **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patcher/blob/main/CONTRIBUTING.md).
|
||||
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
|
||||
|
||||
- type: textarea
|
||||
@@ -98,7 +99,7 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your feature request will be closed if you don't follow the checklist below.
|
||||
options:
|
||||
- label: This issue is not a duplicate of an existing feature request.
|
||||
- label: I have checked all open and closed feature requests and this is not a duplicate.
|
||||
required: true
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
fingerprint: ${{ env.GPG_FINGERPRINT }}
|
||||
fingerprint: ${{ vars.GPG_FINGERPRINT }}
|
||||
|
||||
- name: Release
|
||||
env:
|
||||
|
2
.github/workflows/update_documentation.yml
vendored
2
.github/workflows/update_documentation.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Dispatch event to documentation repository
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: peter-evans/repository-dispatch@v2
|
||||
- uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
|
||||
repository: revanced/revanced-documentation
|
||||
|
69
CHANGELOG.md
69
CHANGELOG.md
@@ -1,3 +1,72 @@
|
||||
## [20.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0...v20.0.1-dev.1) (2024-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Check for class type exactly instead of with contains ([#310](https://github.com/ReVanced/revanced-patcher/issues/310)) ([69f2f20](https://github.com/ReVanced/revanced-patcher/commit/69f2f20fd99162f91cd9c531dfe47d00d3152ead))
|
||||
|
||||
# [20.0.0](https://github.com/ReVanced/revanced-patcher/compare/v19.3.1...v20.0.0) (2024-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Downgrade smali to fix dex compilation issue ([5227e98](https://github.com/ReVanced/revanced-patcher/commit/5227e98abfaa2ff1204eb20a0f2671f58c489930))
|
||||
* Improve exception message wording ([5481d0c](https://github.com/ReVanced/revanced-patcher/commit/5481d0c54ccecc91cd8d15af1ba2d3285a33e5ab))
|
||||
* Make constructor internal as supposed ([7f44174](https://github.com/ReVanced/revanced-patcher/commit/7f44174d91f0af0d50a83d80a7103c779241e094))
|
||||
* Merge all extensions before initializing lookup maps ([8c4dd5b](https://github.com/ReVanced/revanced-patcher/commit/8c4dd5b3a309077fa9a3827b4931fc28b0517809))
|
||||
* Use null for compatible package version when adding packages only ([736b3ee](https://github.com/ReVanced/revanced-patcher/commit/736b3eebbfdd7279b8d5fcfc5c46c9e3aadbee12))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add ability to create options outside of a patch ([d310246](https://github.com/ReVanced/revanced-patcher/commit/d310246852504b08a15f6376bbf25ac7c6fae76f))
|
||||
* Convert APIs to Kotlin DSL ([#298](https://github.com/ReVanced/revanced-patcher/issues/298)) ([11a911d](https://github.com/ReVanced/revanced-patcher/commit/11a911dc674eb0801649949dd3f28dfeb00efe97))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Various old APIs are removed, and DSL APIs are added instead.
|
||||
|
||||
# [20.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0-dev.3...v20.0.0-dev.4) (2024-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Improve exception message wording ([bd434ce](https://github.com/ReVanced/revanced-patcher/commit/bd434ceb3394d1d5292e8b94e5bfd6da0e4e9c72))
|
||||
|
||||
# [20.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0-dev.2...v20.0.0-dev.3) (2024-08-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Make constructor internal as supposed ([e95fcd1](https://github.com/ReVanced/revanced-patcher/commit/e95fcd1c0b641164bbf0840ec7e562aeb3bacc3e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add ability to create options outside of a patch ([b8d763a](https://github.com/ReVanced/revanced-patcher/commit/b8d763a66e0601627dd71c8c24247726aa300146))
|
||||
|
||||
# [20.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0-dev.1...v20.0.0-dev.2) (2024-07-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Downgrade smali to fix dex compilation issue ([714447d](https://github.com/ReVanced/revanced-patcher/commit/714447de70096bf736e8e1d31c14bb5f24195070))
|
||||
* Merge all extensions before initializing lookup maps ([328aa87](https://github.com/ReVanced/revanced-patcher/commit/328aa876d8ed7826be3713754b6404195e9fe84b))
|
||||
* Use null for compatible package version when adding packages only ([a8e8fa4](https://github.com/ReVanced/revanced-patcher/commit/a8e8fa4093deb8cffbd7a582409f41867f6b568b))
|
||||
|
||||
# [20.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.3.1...v20.0.0-dev.1) (2024-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Convert APIs to Kotlin DSL ([#298](https://github.com/ReVanced/revanced-patcher/issues/298)) ([3f9cbd2](https://github.com/ReVanced/revanced-patcher/commit/3f9cbd2408fa085690a062b357e11e42c51e7f8b))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Various old APIs are removed, and DSL APIs are added instead.
|
||||
|
||||
## [19.3.1](https://github.com/ReVanced/revanced-patcher/compare/v19.3.0...v19.3.1) (2024-02-14)
|
||||
|
||||
## [19.3.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.3.0...v19.3.1-dev.1) (2024-02-14)
|
||||
|
@@ -72,7 +72,7 @@ This document describes how to contribute to ReVanced Patcher.
|
||||
## 🙏 Submitting a feature request
|
||||
|
||||
Features can be requested by opening an issue using the
|
||||
[Feature request issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Feature+request&projects=&template=feature-request.yml&title=feat%3A+).
|
||||
[Feature request issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
|
||||
|
||||
> **Note**
|
||||
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patcher.
|
||||
@@ -81,7 +81,7 @@ Features can be requested by opening an issue using the
|
||||
## 🐞 Submitting a bug report
|
||||
|
||||
If you encounter a bug while using ReVanced Patcher, open an issue using the
|
||||
[Bug report issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Bug+report&projects=&template=bug-report.yml&title=bug%3A+).
|
||||
[Bug report issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
|
||||
|
||||
## 📝 How to contribute
|
||||
|
||||
|
@@ -115,7 +115,7 @@ you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced
|
||||
### 📃 Documentation
|
||||
|
||||
The documentation contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches.
|
||||
You can find it [here](https://github.com/ReVanced/revanced-patcher/tree/docs/docs).
|
||||
You can find it [here](https://github.com/ReVanced/revanced-patcher/tree/main/docs).
|
||||
|
||||
## 📜 Licence
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
@@ -22,10 +24,9 @@ tasks {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
google()
|
||||
maven {
|
||||
// A repository must be speficied for some reason. "registry" is a dummy.
|
||||
// A repository must be specified for some reason. "registry" is a dummy.
|
||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||
credentials {
|
||||
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
|
||||
@@ -35,24 +36,33 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.xpp3)
|
||||
implementation(libs.smali)
|
||||
implementation(libs.multidexlib2)
|
||||
implementation(libs.apktool.lib)
|
||||
implementation(libs.kotlin.reflect)
|
||||
|
||||
// TODO: Convert project to KMP.
|
||||
compileOnly(libs.android) {
|
||||
// Exclude, otherwise the org.w3c.dom API breaks.
|
||||
exclude(group = "xerces", module = "xmlParserAPIs")
|
||||
}
|
||||
|
||||
implementation(libs.apktool.lib)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.multidexlib2)
|
||||
implementation(libs.smali)
|
||||
implementation(libs.xpp3)
|
||||
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(libs.kotlin.test)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(11)
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@@ -60,40 +60,43 @@
|
||||
|
||||
# 💉 Introduction to ReVanced Patcher
|
||||
|
||||
In order to create patches for Android applications, you first need to understand the fundamentals of ReVanced Patcher.
|
||||
To create patches for Android apps, it is recommended to know the basic concept of ReVanced Patcher.
|
||||
|
||||
## 📙 How it works
|
||||
|
||||
ReVanced Patcher is a library that allows you to modify Android applications by applying patches to their APKs. It is built on top of [Smali](https://github.com/google/smali) for bytecode manipulation and [Androlib (Apktool)](https://github.com/iBotPeaches/Apktool) for resource decoding and encoding.
|
||||
ReVanced Patcher accepts a list of patches and integrations, and applies them to a given APK file. It then returns the modified components of the APK file, such as modified dex files and resources, that can be repackaged into a new APK file.
|
||||
ReVanced Patcher is a library that allows modifying Android apps by applying patches.
|
||||
It is built on top of [Smali](https://github.com/google/smali) for bytecode manipulation and [Androlib (Apktool)](https://github.com/iBotPeaches/Apktool)
|
||||
for resource decoding and encoding.
|
||||
|
||||
ReVanced Patcher has a simple API that allows you to load patches and integrations from JAR files and apply them to an APK file.
|
||||
Later on, you will learn how to create patches.
|
||||
ReVanced Patcher receives a list of patches and applies them to a given APK file.
|
||||
It then returns the modified components of the APK file, such as modified dex files and resources,
|
||||
that can be repackaged into a new APK file.
|
||||
|
||||
ReVanced Patcher has a simple API that allows you to load patches from RVP (JAR or DEX container) files
|
||||
and apply them to an APK file. Later on, you will learn how to create patches.
|
||||
|
||||
```kt
|
||||
// Executed patches do not necessarily reset their state.
|
||||
// For that reason it is important to create a new instance of the PatchBundleLoader
|
||||
// once the patches are executed instead of reusing the same instance of patches loaded by PatchBundleLoader.
|
||||
val patches: PatchSet /* = Set<Patch<*>> */ = PatchBundleLoader.Jar(File("revanced-patches.jar"))
|
||||
val integrations = setOf(File("integrations.apk"))
|
||||
val patches = loadPatchesFromJar(setOf(File("revanced-patches.rvp")))
|
||||
|
||||
// Instantiating the patcher will decode the manifest of the APK file to read the package and version name.
|
||||
val patcherConfig = PatcherConfig(apkFile = File("some.apk"))
|
||||
val patcherResult = Patcher(patcherConfig).use { patcher ->
|
||||
patcher.apply {
|
||||
acceptIntegrations(integrations)
|
||||
acceptPatches(patches)
|
||||
val patcherResult = Patcher(PatcherConfig(apkFile = File("some.apk"))).use { patcher ->
|
||||
// Here you can access metadata about the APK file through patcher.context.packageMetadata
|
||||
// such as package name, version code, version name, etc.
|
||||
|
||||
// Execute patches.
|
||||
runBlocking {
|
||||
patcher.apply(returnOnError = false).collect { patchResult ->
|
||||
if (patchResult.exception != null)
|
||||
println("${patchResult.patchName} failed:\n${patchResult.exception}")
|
||||
else
|
||||
println("${patchResult.patchName} succeeded")
|
||||
}
|
||||
// Add patches.
|
||||
patcher += patches
|
||||
|
||||
// Execute the patches.
|
||||
runBlocking {
|
||||
patcher().collect { patchResult ->
|
||||
if (patchResult.exception != null)
|
||||
logger.info("\"${patchResult.patch}\" failed:\n${patchResult.exception}")
|
||||
else
|
||||
logger.info("\"${patchResult.patch}\" succeeded")
|
||||
}
|
||||
}.get()
|
||||
}
|
||||
|
||||
// Compile and save the patched APK file components.
|
||||
patcher.get()
|
||||
}
|
||||
|
||||
// The result of the patcher contains the modified components of the APK file that can be repackaged into a new APK file.
|
||||
|
@@ -60,7 +60,7 @@
|
||||
|
||||
# 👶 Setting up a development environment
|
||||
|
||||
To get started developing patches with ReVanced Patcher, you need to prepare a development environment.
|
||||
To start developing patches with ReVanced Patcher, you must prepare a development environment.
|
||||
|
||||
## 📝 Prerequisites
|
||||
|
||||
@@ -84,21 +84,22 @@ Throughout the documentation, [ReVanced Patches](https://github.com/revanced/rev
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
|
||||
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
|
||||
>
|
||||
> Example `gradle.properties` file:
|
||||
>
|
||||
> ```properties
|
||||
> gpr.user = user
|
||||
> gpr.key = key
|
||||
> ```
|
||||
> [!NOTE]
|
||||
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
|
||||
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
|
||||
>
|
||||
> Example `gradle.properties` file:
|
||||
>
|
||||
> ```properties
|
||||
> gpr.user = user
|
||||
> gpr.key = key
|
||||
> ```
|
||||
|
||||
3. Open the project in your IDE
|
||||
|
||||
> [!TIP]
|
||||
> It is a good idea to set up a complete development environment for ReVanced, so that you can also test your patches by following the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
|
||||
> It is a good idea to set up a complete development environment for ReVanced, so that you can also test your patches
|
||||
> by following the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
|
||||
|
||||
## ⏭️ What's next
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -64,31 +64,39 @@ Over time, a specific project structure and conventions have been established.
|
||||
|
||||
## 📁 File structure
|
||||
|
||||
Patches are organized in a specific file structure. The file structure is as follows:
|
||||
Patches are organized in a specific way. The file structure looks as follows:
|
||||
|
||||
```text
|
||||
📦your.patches.app.category
|
||||
├ 📂fingerprints
|
||||
├ ├ 🔍SomeFingerprintA.kt
|
||||
├ └ 🔍SomeFingerprintB.kt
|
||||
├ 🔍Fingerprints.kt
|
||||
└ 🧩SomePatch.kt
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Moving fingerprints to a separate file isn't strictly necessary, but it helps the organization when a patch uses multiple fingerprints.
|
||||
|
||||
## 📙 Conventions
|
||||
|
||||
- 🔥 Name a patch after what it does. For example, if a patch removes ads, name it `RemoveAdsPatch`.
|
||||
If a patch changes the color of a button, name it `ChangeButtonColorPatch`
|
||||
- 🔥 Name a patch after what it does. For example, if a patch removes ads, name it `Remove ads`.
|
||||
If a patch changes the color of a button, name it `Change button color`
|
||||
- 🔥 Write the patch description in the third person, present tense, and end it with a period.
|
||||
If a patch removes ads, the description can be omitted because of redundancy, but if a patch changes the color of a button, the description can be _Changes the color of the resume button to red._
|
||||
- 🔥 Write patches with modularity and reusability in mind. Patches can depend on each other, so it is important to write patches in a way that can be used in different contexts.
|
||||
If a patch removes ads, the description can be omitted because of redundancy,
|
||||
but if a patch changes the color of a button, the description can be _Changes the color of the resume button to red._
|
||||
- 🔥 Write patches with modularity and reusability in mind. Patches can depend on each other,
|
||||
so it is important to write patches in a way that can be used in different contexts.
|
||||
- 🔥🔥 Keep patches as minimal as possible. This reduces the risk of failing patches.
|
||||
Instead of involving many abstract changes in one patch or writing entire methods or classes in a patch,
|
||||
you can write code in integrations. Integrations are compiled classes that are merged into the app before patches are executed as described in [💉 Introduction to ReVanced Patcher](1_patcher_intro).
|
||||
Patches can then reference methods and classes from integrations.
|
||||
A real-world example of integrations can be found in the [ReVanced Integrations](https://github.com/ReVanced/revanced-integrations) repository
|
||||
you can write code in extensions. An extension is a precompiled DEX file that is merged into the patched app
|
||||
before this patch is executed.
|
||||
Patches can then reference methods and classes from extensions.
|
||||
A real-world example of extensions can be found in the [ReVanced Patches](https://github.com/ReVanced/revanced-patches) repository
|
||||
- 🔥🔥🔥 Do not overload a fingerprint with information about a method that's likely to change.
|
||||
In the example of an obfuscated method, it's better to fingerprint the method by its return type and parameters rather than its name because the name is likely to change. An intelligent selection of an opcode pattern or strings in a method can result in a strong fingerprint dynamic to app updates.
|
||||
- 🔥🔥🔥 Document your patches. Patches are abstract by nature, so it is important to document parts of the code that are not self-explanatory. For example, explain why and how a certain method is patched or large blocks of instructions that are modified or added to a method
|
||||
In the example of an obfuscated method, it's better to fingerprint the method by its return type
|
||||
and parameters rather than its name because the name is likely to change. An intelligent selection
|
||||
of an opcode pattern or strings in a method can result in a strong fingerprint dynamic to app updates.
|
||||
- 🔥🔥🔥 Document your patches. Patches are abstract, so it is important to document parts of the code
|
||||
that are not self-explanatory. For example, explain why and how a certain method is patched or large blocks
|
||||
of instructions that are modified or added to a method
|
||||
|
||||
## ⏭️ What's next
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
version = 19.3.1
|
||||
version = 20.0.1-dev.1
|
||||
|
@@ -1,23 +1,27 @@
|
||||
[versions]
|
||||
android = "4.1.1.4"
|
||||
apktool-lib = "2.9.3"
|
||||
kotlin = "1.9.22"
|
||||
kotlinx-coroutines-core = "1.7.3"
|
||||
binary-compatibility-validator = "0.15.1"
|
||||
kotlin = "2.0.0"
|
||||
kotlinx-coroutines-core = "1.8.1"
|
||||
mockk = "1.13.10"
|
||||
multidexlib2 = "3.0.3.r3"
|
||||
smali = "3.0.4"
|
||||
binary-compatibility-validator = "0.14.0"
|
||||
# Tracking https://github.com/google/smali/issues/64.
|
||||
#noinspection GradleDependency
|
||||
smali = "3.0.5"
|
||||
xpp3 = "1.1.4c"
|
||||
|
||||
[libraries]
|
||||
android = { module = "com.google.android:android", version.ref = "android" }
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
apktool-lib = { module = "app.revanced:apktool-lib", version.ref = "apktool-lib" }
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" }
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||
multidexlib2 = { module = "app.revanced:multidexlib2", version.ref = "multidexlib2" }
|
||||
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||
xpp3 = { module = "xpp3:xpp3", version.ref = "xpp3" }
|
||||
|
||||
[plugins]
|
||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
8
gradle/wrapper/gradle-wrapper.properties
vendored
8
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,8 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
|
||||
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dist
|
||||
zipStorePath=wrapper/dists
|
||||
|
22
gradlew
vendored
22
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -83,7 +85,9 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -201,11 +205,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
90
package-lock.json
generated
90
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.9.1",
|
||||
"semantic-release": "^23.0.0"
|
||||
"semantic-release": "^23.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -272,18 +272,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz",
|
||||
"integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==",
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
|
||||
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "9.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz",
|
||||
"integrity": "sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==",
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.0.tgz",
|
||||
"integrity": "sha512-NKi0bJEZqOSbBLMv9kdAcuocpe05Q2xAXNLTGi0HN2GSMFJHNZuSoPNa0tcQFTOFCKe+ZaYBZ3lpXh1yxgUDCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.4.0"
|
||||
"@octokit/types": "^12.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
@@ -310,9 +310,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-throttling": {
|
||||
"version": "8.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.1.3.tgz",
|
||||
"integrity": "sha512-pfyqaqpc0EXh5Cn4HX9lWYsZ4gGbjnSmUILeu4u2gnuM50K/wIk9s1Pxt3lVeVwekmITgN/nJdoh43Ka+vye8A==",
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz",
|
||||
"integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.2.0",
|
||||
@@ -355,12 +355,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "12.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.4.0.tgz",
|
||||
"integrity": "sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==",
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
|
||||
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^19.1.0"
|
||||
"@octokit/openapi-types": "^20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pnpm/config.env-replace": {
|
||||
@@ -597,9 +597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@saithodev/semantic-release-backmerge/node_modules/npm-run-path": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
|
||||
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
@@ -1101,9 +1101,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@semantic-release/npm/node_modules/npm-run-path": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
|
||||
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
@@ -1215,9 +1215,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/merge-streams": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.2.0.tgz",
|
||||
"integrity": "sha512-UTce8mUwUW0RikMb/eseJ7ys0BRkZVFB86orHzrfW12ZmFtym5zua8joZ4L7okH2dDFHkcFjqnZ5GocWBXOFtA==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
|
||||
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -1835,9 +1835,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/env-ci/node_modules/npm-run-path": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
|
||||
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
@@ -2288,9 +2288,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz",
|
||||
"integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
@@ -2301,9 +2301,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==",
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
|
||||
"integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.0.2",
|
||||
@@ -6011,9 +6011,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg-up/node_modules/type-fest": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
|
||||
"integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.3.tgz",
|
||||
"integrity": "sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
@@ -6040,9 +6040,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg/node_modules/type-fest": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
|
||||
"integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.3.tgz",
|
||||
"integrity": "sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
@@ -6320,9 +6320,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semantic-release/node_modules/npm-run-path": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
|
||||
"integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
@@ -6616,9 +6616,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-exceptions": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz",
|
||||
"integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/spdx-expression-parse": {
|
||||
|
@@ -4,6 +4,6 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.9.1",
|
||||
"semantic-release": "^23.0.0"
|
||||
"semantic-release": "^23.0.2"
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1 @@
|
||||
rootProject.name = "revanced-patcher"
|
||||
|
||||
buildCache {
|
||||
local {
|
||||
isEnabled = "CI" !in System.getenv()
|
||||
}
|
||||
}
|
||||
|
466
src/main/kotlin/app/revanced/patcher/Fingerprint.kt
Normal file
466
src/main/kotlin/app/revanced/patcher/Fingerprint.kt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import java.io.File
|
||||
|
||||
@FunctionalInterface
|
||||
interface IntegrationsConsumer {
|
||||
fun acceptIntegrations(integrations: Set<File>)
|
||||
|
||||
@Deprecated("Use acceptIntegrations(Set<File>) instead.")
|
||||
fun acceptIntegrations(integrations: List<File>)
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import dalvik.system.DexClassLoader
|
||||
import lanchon.multidexlib2.BasicDexFileNamer
|
||||
import lanchon.multidexlib2.MultiDexIO
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarFile
|
||||
import java.util.logging.Logger
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
/**
|
||||
* A set of [Patch]es.
|
||||
*/
|
||||
typealias PatchSet = Set<Patch<*>>
|
||||
|
||||
/**
|
||||
* A [Patch] class.
|
||||
*/
|
||||
typealias PatchClass = KClass<out Patch<*>>
|
||||
|
||||
/**
|
||||
* A loader of [Patch]es from patch bundles.
|
||||
* This will load all [Patch]es from the given patch bundles that have a name.
|
||||
*
|
||||
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
||||
* @param classLoader The [ClassLoader] to use for loading the classes.
|
||||
* @param patchBundles A set of patches to initialize this instance with.
|
||||
*/
|
||||
sealed class PatchBundleLoader private constructor(
|
||||
classLoader: ClassLoader,
|
||||
patchBundles: Array<out File>,
|
||||
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
||||
// This constructor parameter is unfortunately necessary,
|
||||
// so that a reference to the mutable set is present in the constructor to be able to add patches to it.
|
||||
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
||||
private val patchSet: MutableSet<Patch<*>> = mutableSetOf(),
|
||||
) : PatchSet by patchSet {
|
||||
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||
|
||||
init {
|
||||
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
||||
classLoader.loadClass(it)
|
||||
}.filter {
|
||||
Patch::class.java.isAssignableFrom(it)
|
||||
}.mapNotNull { patchClass ->
|
||||
patchClass.getInstance(logger, silent = true)
|
||||
}.filter {
|
||||
it.name != null
|
||||
}.let { patches ->
|
||||
patchSet.addAll(patches)
|
||||
}
|
||||
}
|
||||
|
||||
internal companion object Utils {
|
||||
/**
|
||||
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
||||
*
|
||||
* @param logger The [Logger] to use for logging.
|
||||
* @param silent Whether to suppress logging.
|
||||
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
||||
*/
|
||||
internal fun Class<*>.getInstance(
|
||||
logger: Logger,
|
||||
silent: Boolean = false,
|
||||
): Patch<*>? {
|
||||
return try {
|
||||
getField("INSTANCE").get(null)
|
||||
} catch (exception: NoSuchFieldException) {
|
||||
if (!silent) {
|
||||
logger.fine(
|
||||
"Patch class '$name' has no INSTANCE field, therefor not a singleton. " +
|
||||
"Attempting to instantiate it.",
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
getDeclaredConstructor().newInstance()
|
||||
} catch (exception: Exception) {
|
||||
if (!silent) {
|
||||
logger.severe(
|
||||
"Patch class '$name' is not singleton and has no suitable constructor, " +
|
||||
"therefor cannot be instantiated and is ignored.",
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
} as Patch<*>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [PatchBundleLoader] for JAR files.
|
||||
*
|
||||
* @param patchBundles The path to patch bundles of JAR format.
|
||||
*/
|
||||
class Jar(vararg patchBundles: File) : PatchBundleLoader(
|
||||
URLClassLoader(patchBundles.map { it.toURI().toURL() }.toTypedArray()),
|
||||
patchBundles,
|
||||
{ patchBundle ->
|
||||
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
||||
.map { it.name.replace('/', '.').replace(".class", "") }
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* A [PatchBundleLoader] for [Dex] files.
|
||||
*
|
||||
* @param patchBundles The path to patch bundles of DEX format.
|
||||
* @param optimizedDexDirectory The directory to store optimized DEX files in.
|
||||
* This parameter is deprecated and has no effect since API level 26.
|
||||
*/
|
||||
class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader(
|
||||
DexClassLoader(
|
||||
patchBundles.joinToString(File.pathSeparator) { it.absolutePath },
|
||||
optimizedDexDirectory?.absolutePath,
|
||||
null,
|
||||
PatchBundleLoader::class.java.classLoader,
|
||||
),
|
||||
patchBundles,
|
||||
{ patchBundle ->
|
||||
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
||||
.map { classDef ->
|
||||
classDef.type.substring(1, classDef.length - 1)
|
||||
}
|
||||
},
|
||||
) {
|
||||
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
||||
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.function.Function
|
||||
|
||||
@FunctionalInterface
|
||||
interface PatchExecutorFunction : Function<Boolean, Flow<PatchResult>>
|
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import brut.androlib.Config
|
||||
import java.io.File
|
||||
import java.util.logging.Logger
|
||||
@@ -27,9 +27,9 @@ class PatcherConfig(
|
||||
/**
|
||||
* The mode to use for resource decoding and compiling.
|
||||
*
|
||||
* @see ResourceContext.ResourceMode
|
||||
* @see ResourcePatchContext.ResourceMode
|
||||
*/
|
||||
internal var resourceMode = ResourceContext.ResourceMode.NONE
|
||||
internal var resourceMode = ResourcePatchContext.ResourceMode.NONE
|
||||
|
||||
/**
|
||||
* The configuration for decoding and compiling resources.
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import brut.androlib.apk.ApkInfo
|
||||
import brut.directory.ExtFile
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* A context for the patcher containing the current state of the patcher.
|
||||
@@ -12,29 +13,31 @@ import brut.directory.ExtFile
|
||||
* @param config The configuration for the patcher.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class PatcherContext internal constructor(config: PatcherConfig) {
|
||||
class PatcherContext internal constructor(config: PatcherConfig): Closeable {
|
||||
/**
|
||||
* [PackageMetadata] of the supplied [PatcherConfig.apkFile].
|
||||
*/
|
||||
val packageMetadata = PackageMetadata(ApkInfo(ExtFile(config.apkFile)))
|
||||
|
||||
/**
|
||||
* The map of [Patch]es associated by their [PatchClass].
|
||||
* The set of [Patch]es.
|
||||
*/
|
||||
internal val executablePatches = mutableMapOf<PatchClass, Patch<*>>()
|
||||
internal val executablePatches = mutableSetOf<Patch<*>>()
|
||||
|
||||
/**
|
||||
* The map of all [Patch]es and their dependencies associated by their [PatchClass].
|
||||
* The set of all [Patch]es and their dependencies.
|
||||
*/
|
||||
internal val allPatches = mutableMapOf<PatchClass, Patch<*>>()
|
||||
internal val allPatches = mutableSetOf<Patch<*>>()
|
||||
|
||||
/**
|
||||
* A context for the patcher containing the current state of the resources.
|
||||
* The context for patches containing the current state of the resources.
|
||||
*/
|
||||
internal val resourceContext = ResourceContext(packageMetadata, config)
|
||||
internal val resourceContext = ResourcePatchContext(packageMetadata, config)
|
||||
|
||||
/**
|
||||
* A context for the patcher containing the current state of the bytecode.
|
||||
* The context for patches containing the current state of the bytecode.
|
||||
*/
|
||||
internal val bytecodeContext = BytecodeContext(config)
|
||||
internal val bytecodeContext = BytecodePatchContext(config)
|
||||
|
||||
override fun close() = bytecodeContext.close()
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user