mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-18 17:03:56 +00:00
Compare commits
9 Commits
feat/load-
...
arsclib-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6fdf97794 | ||
|
|
c52f0b80f2 | ||
|
|
4b5e25b29c | ||
|
|
c752a3c596 | ||
|
|
740911a2a3 | ||
|
|
242d805c6c | ||
|
|
c543fdc18b | ||
|
|
d48a8e697f | ||
|
|
8749a61d39 |
@@ -1,3 +0,0 @@
|
|||||||
[*.{kt,kts}]
|
|
||||||
ktlint_code_style = intellij_idea
|
|
||||||
ktlint_standard_no-wildcard-imports = disabled
|
|
||||||
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,109 +0,0 @@
|
|||||||
name: 🐞 Bug report
|
|
||||||
description: Report a bug or an issue.
|
|
||||||
title: "bug: "
|
|
||||||
labels: ["Bug report"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
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="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">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# ReVanced Patcher bug report
|
|
||||||
|
|
||||||
Before creating a new bug report, please keep the following in mind:
|
|
||||||
|
|
||||||
- **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:
|
|
||||||
label: Bug description
|
|
||||||
description: |
|
|
||||||
- Describe your bug in detail
|
|
||||||
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
|
||||||
- Add images and videos if possible
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Error logs
|
|
||||||
description: Exceptions can be captured by running `logcat | grep AndroidRuntime` in a shell.
|
|
||||||
render: shell
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Solution
|
|
||||||
description: If applicable, add a possible solution to the bug.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your bug report will be closed if you don't follow the checklist below.
|
|
||||||
options:
|
|
||||||
- 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
|
|
||||||
- label: All requested information has been provided properly.
|
|
||||||
required: true
|
|
||||||
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
Normal file
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
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
|
||||||
107
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
107
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,107 +0,0 @@
|
|||||||
name: ⭐ Feature request
|
|
||||||
description: Create a detailed request for a new feature.
|
|
||||||
title: "feat: "
|
|
||||||
labels: ["Feature request"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
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="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">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# ReVanced Patcher feature request
|
|
||||||
|
|
||||||
Before creating a new feature request, please keep the following in mind:
|
|
||||||
|
|
||||||
- **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
|
|
||||||
attributes:
|
|
||||||
label: Feature description
|
|
||||||
description: |
|
|
||||||
- Describe your feature in detail
|
|
||||||
- Add images, videos, links, examples, references, etc. if possible
|
|
||||||
- Add the target application name in case you request a new patch
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Motivation
|
|
||||||
description: |
|
|
||||||
A strong motivation is necessary for a feature request to be considered.
|
|
||||||
|
|
||||||
- Why should this feature be implemented?
|
|
||||||
- What is the explicit use case?
|
|
||||||
- What are the benefits?
|
|
||||||
- What makes this feature important?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your feature request will be closed if you don't follow the checklist below.
|
|
||||||
options:
|
|
||||||
- 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
|
|
||||||
- label: All requested information has been provided properly.
|
|
||||||
required: true
|
|
||||||
2
.github/config.yml
vendored
2
.github/config.yml
vendored
@@ -1,2 +1,2 @@
|
|||||||
firstPRMergeComment: >
|
firstPRMergeComment: >
|
||||||
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.
|
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
||||||
|
|||||||
22
.github/dependabot.yml
vendored
22
.github/dependabot.yml
vendored
@@ -1,22 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
labels: []
|
|
||||||
directory: /
|
|
||||||
target-branch: dev
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
|
|
||||||
- package-ecosystem: npm
|
|
||||||
labels: []
|
|
||||||
directory: /
|
|
||||||
target-branch: dev
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
|
|
||||||
- package-ecosystem: gradle
|
|
||||||
labels: []
|
|
||||||
directory: /
|
|
||||||
target-branch: dev
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
25
.github/workflows/build_pull_request.yml
vendored
25
.github/workflows/build_pull_request.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: Build pull request
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Cache Gradle
|
|
||||||
uses: burrunan/gradle-cache-action@v3
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew build --no-daemon
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Open a PR to main
|
name: PR to main
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MESSAGE: Merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pull-request:
|
pull-request:
|
||||||
@@ -15,8 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Open pull request
|
- name: Open pull request
|
||||||
uses: repo-sync/pull-request@v2
|
uses: repo-sync/pull-request@v2
|
||||||
with:
|
with:
|
||||||
46
.github/workflows/release.yml
vendored
46
.github/workflows/release.yml
vendored
@@ -6,48 +6,40 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# Make sure the release step uses its own credentials:
|
# Make sure the release step uses its own credentials:
|
||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Cache
|
||||||
- name: Cache Gradle
|
uses: actions/cache@v3
|
||||||
uses: burrunan/gradle-cache-action@v3
|
with:
|
||||||
|
path: |
|
||||||
- name: Build
|
${{ runner.home }}/.gradle/caches
|
||||||
|
${{ runner.home }}/.gradle/wrapper
|
||||||
|
.gradle
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||||
|
- name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew build clean
|
run: ./gradlew clean --no-daemon
|
||||||
|
- name: Setup semantic-release
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: "lts/*"
|
|
||||||
cache: 'npm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Import GPG key
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
|
||||||
fingerprint: ${{ vars.GPG_FINGERPRINT }}
|
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||||
run: npm exec semantic-release
|
run: npm exec semantic-release
|
||||||
|
|||||||
19
.github/workflows/update_documentation.yml
vendored
19
.github/workflows/update_documentation.yml
vendored
@@ -1,19 +0,0 @@
|
|||||||
name: Update documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- docs/**
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
trigger:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Dispatch event to documentation repository
|
|
||||||
if: github.ref == 'refs/heads/main'
|
|
||||||
steps:
|
|
||||||
- uses: peter-evans/repository-dispatch@v3
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN }}
|
|
||||||
repository: revanced/revanced-documentation
|
|
||||||
event-type: update-documentation
|
|
||||||
client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'
|
|
||||||
11
.releaserc
11
.releaserc
@@ -7,13 +7,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
[
|
"@semantic-release/commit-analyzer",
|
||||||
"@semantic-release/commit-analyzer", {
|
|
||||||
"releaseRules": [
|
|
||||||
{ "type": "build", "scope": "Needs bump", "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",
|
||||||
@@ -23,8 +17,7 @@
|
|||||||
"assets": [
|
"assets": [
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"gradle.properties"
|
"gradle.properties"
|
||||||
],
|
]
|
||||||
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
973
CHANGELOG.md
973
CHANGELOG.md
@@ -1,976 +1,3 @@
|
|||||||
# [21.1.0-dev.5](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.4...v21.1.0-dev.5) (2025-10-16)
|
|
||||||
|
|
||||||
# [21.1.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.3...v21.1.0-dev.4) (2025-07-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Correctly save XML files in UTF-8 by using a bufferedWriter ([#356](https://github.com/ReVanced/revanced-patcher/issues/356)) ([33fadcb](https://github.com/ReVanced/revanced-patcher/commit/33fadcbd0c7076b848bdca4d62a9c684d5781232))
|
|
||||||
|
|
||||||
# [21.1.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.2...v21.1.0-dev.3) (2025-06-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Encode XML files as UTF-8 to fix compilation of resources ([#339](https://github.com/ReVanced/revanced-patcher/issues/339)) ([4f2ef3c](https://github.com/ReVanced/revanced-patcher/commit/4f2ef3c47cea76a26c464cfb45d4bb57fe7198b5))
|
|
||||||
|
|
||||||
# [21.1.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v21.1.0-dev.1...v21.1.0-dev.2) (2025-06-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Add back missing log by naming logger correctly ([#332](https://github.com/ReVanced/revanced-patcher/issues/332)) ([e4e66b0](https://github.com/ReVanced/revanced-patcher/commit/e4e66b0d8bb0986b79fb150b9c15da35b8e11561))
|
|
||||||
* Support UTF-8 chars when compiling instructions in Smali in non UTF-8 environments ([#331](https://github.com/ReVanced/revanced-patcher/issues/331)) ([bb8771b](https://github.com/ReVanced/revanced-patcher/commit/bb8771bb8b8ab1724d957e56f4de88c02684d87b))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Use option name as key for simplicity and consistency ([754b02e](https://github.com/ReVanced/revanced-patcher/commit/754b02e4ca66ec10764d5205c6643f2d86d0c6a2))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use a buffered writer to reduce IO overhead ([#347](https://github.com/ReVanced/revanced-patcher/issues/347)) ([99f4318](https://github.com/ReVanced/revanced-patcher/commit/99f431897eb9e607987fd5d09b879d7eda442f3e))
|
|
||||||
|
|
||||||
# [21.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v21.0.0...v21.1.0-dev.1) (2024-12-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add identity hash code to unnamed patches ([88a3252](https://github.com/ReVanced/revanced-patcher/commit/88a325257494939a79fb30dd51d60c5c52546755))
|
|
||||||
|
|
||||||
# [21.0.0](https://github.com/ReVanced/revanced-patcher/compare/v20.0.2...v21.0.0) (2024-11-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Match fingerprint before delegating the match property ([5d996de](https://github.com/ReVanced/revanced-patcher/commit/5d996def4d3de4e2bfc34562e5a6c7d89a8cddf0))
|
|
||||||
* Merge extension only when patch executes ([#315](https://github.com/ReVanced/revanced-patcher/issues/315)) ([aa472eb](https://github.com/ReVanced/revanced-patcher/commit/aa472eb9857145b53b49f843406a9764fbb7e5ce))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Improve Fingerprint API ([#316](https://github.com/ReVanced/revanced-patcher/issues/316)) ([0abf1c6](https://github.com/ReVanced/revanced-patcher/commit/0abf1c6c0279708fdef5cb66b141d07d17682693))
|
|
||||||
* Improve various APIs ([#317](https://github.com/ReVanced/revanced-patcher/issues/317)) ([b824978](https://github.com/ReVanced/revanced-patcher/commit/b8249789df8b90129f7b7ad0e523a8d0ceaab848))
|
|
||||||
* Move fingerprint match members to fingerprint for ease of access by using context receivers ([0746c22](https://github.com/ReVanced/revanced-patcher/commit/0746c22743a9561bae2284d234b151f2f8511ca5))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use smallest lookup map for strings ([1358d3f](https://github.com/ReVanced/revanced-patcher/commit/1358d3fa10cb8ba011b6b89cfe3684ecf9849d2f))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* Various APIs have been changed.
|
|
||||||
* Many APIs have been changed.
|
|
||||||
|
|
||||||
# [21.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v21.0.0-dev.3...v21.0.0-dev.4) (2024-11-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use smallest lookup map for strings ([1358d3f](https://github.com/ReVanced/revanced-patcher/commit/1358d3fa10cb8ba011b6b89cfe3684ecf9849d2f))
|
|
||||||
|
|
||||||
# [21.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v21.0.0-dev.2...v21.0.0-dev.3) (2024-11-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Move fingerprint match members to fingerprint for ease of access by using context receivers ([0746c22](https://github.com/ReVanced/revanced-patcher/commit/0746c22743a9561bae2284d234b151f2f8511ca5))
|
|
||||||
|
|
||||||
# [21.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v21.0.0-dev.1...v21.0.0-dev.2) (2024-11-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Match fingerprint before delegating the match property ([5d996de](https://github.com/ReVanced/revanced-patcher/commit/5d996def4d3de4e2bfc34562e5a6c7d89a8cddf0))
|
|
||||||
|
|
||||||
# [21.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.2...v21.0.0-dev.1) (2024-10-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Merge extension only when patch executes ([#315](https://github.com/ReVanced/revanced-patcher/issues/315)) ([aa472eb](https://github.com/ReVanced/revanced-patcher/commit/aa472eb9857145b53b49f843406a9764fbb7e5ce))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Improve Fingerprint API ([#316](https://github.com/ReVanced/revanced-patcher/issues/316)) ([0abf1c6](https://github.com/ReVanced/revanced-patcher/commit/0abf1c6c0279708fdef5cb66b141d07d17682693))
|
|
||||||
* Improve various APIs ([#317](https://github.com/ReVanced/revanced-patcher/issues/317)) ([b824978](https://github.com/ReVanced/revanced-patcher/commit/b8249789df8b90129f7b7ad0e523a8d0ceaab848))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* Various APIs have been changed.
|
|
||||||
* Many APIs have been changed.
|
|
||||||
|
|
||||||
## [20.0.2](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1...v20.0.2) (2024-10-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Make it work on Android 12 and lower by using existing APIs ([#312](https://github.com/ReVanced/revanced-patcher/issues/312)) ([a44802e](https://github.com/ReVanced/revanced-patcher/commit/a44802ef4ebf59ae47213854ba761c81dadc51f3))
|
|
||||||
|
|
||||||
## [20.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1...v20.0.2-dev.1) (2024-10-15)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Make it work on Android 12 and lower by using existing APIs ([#312](https://github.com/ReVanced/revanced-patcher/issues/312)) ([a44802e](https://github.com/ReVanced/revanced-patcher/commit/a44802ef4ebf59ae47213854ba761c81dadc51f3))
|
|
||||||
|
|
||||||
## [20.0.1](https://github.com/ReVanced/revanced-patcher/compare/v20.0.0...v20.0.1) (2024-10-13)
|
|
||||||
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* Make it work on Android by not using APIs from JVM unavailable to Android. ([2be6e97](https://github.com/ReVanced/revanced-patcher/commit/2be6e97817437f40e17893dfff3bea2cd4c3ff9e))
|
|
||||||
* Use non-nullable type for options ([ea6fc70](https://github.com/ReVanced/revanced-patcher/commit/ea6fc70caab055251ad4d0d3f1b5cf53865abb85))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Free memory earlier and remove negligible lookup maps ([d53aacd](https://github.com/ReVanced/revanced-patcher/commit/d53aacdad4ed3750ddae526fb307577ea36e6171))
|
|
||||||
|
|
||||||
## [20.0.1-dev.5](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.4...v20.0.1-dev.5) (2024-10-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Use non-nullable type for options ([ea6fc70](https://github.com/ReVanced/revanced-patcher/commit/ea6fc70caab055251ad4d0d3f1b5cf53865abb85))
|
|
||||||
|
|
||||||
## [20.0.1-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.3...v20.0.1-dev.4) (2024-10-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Make it work on Android by not using APIs from JVM unavailable to Android. ([2be6e97](https://github.com/ReVanced/revanced-patcher/commit/2be6e97817437f40e17893dfff3bea2cd4c3ff9e))
|
|
||||||
|
|
||||||
## [20.0.1-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.2...v20.0.1-dev.3) (2024-10-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Free memory earlier and remove negligible lookup maps ([d53aacd](https://github.com/ReVanced/revanced-patcher/commit/d53aacdad4ed3750ddae526fb307577ea36e6171))
|
|
||||||
|
|
||||||
## [20.0.1-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v20.0.1-dev.1...v20.0.1-dev.2) (2024-10-01)
|
|
||||||
|
|
||||||
## [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)
|
|
||||||
|
|
||||||
# [19.3.0](https://github.com/ReVanced/revanced-patcher/compare/v19.2.0...v19.3.0) (2024-02-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Use `Patch#toString` to get patch class name, when no name available ([c9a8260](https://github.com/ReVanced/revanced-patcher/commit/c9a82608f7f2d6b3e64c0c949ea5d9f76fa46165))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Read and write arbitrary files in APK files ([f1d7217](https://github.com/ReVanced/revanced-patcher/commit/f1d72174956c42234664dce152a27e6854e347e2))
|
|
||||||
|
|
||||||
# [19.3.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v19.3.0-dev.1...v19.3.0-dev.2) (2024-02-13)
|
|
||||||
|
|
||||||
# [19.3.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.2.1-dev.1...v19.3.0-dev.1) (2024-02-13)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Read and write arbitrary files in APK files ([f1d7217](https://github.com/ReVanced/revanced-patcher/commit/f1d72174956c42234664dce152a27e6854e347e2))
|
|
||||||
|
|
||||||
## [19.2.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.2.0...v19.2.1-dev.1) (2024-01-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Use `Patch#toString` to get patch class name, when no name available ([c9a8260](https://github.com/ReVanced/revanced-patcher/commit/c9a82608f7f2d6b3e64c0c949ea5d9f76fa46165))
|
|
||||||
|
|
||||||
# [19.2.0](https://github.com/ReVanced/revanced-patcher/compare/v19.1.0...v19.2.0) (2023-12-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Accept `PatchSet` in `PatchesConsumer#acceptPatches` ([716825f](https://github.com/ReVanced/revanced-patcher/commit/716825f232bf1aab3a97723968562aa6dbdb20b1))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `PatchExtensions#registerNewPatchOption` function to simplify instantiation and registration of patch options ([4a91845](https://github.com/ReVanced/revanced-patcher/commit/4a9184597be99cd458496cce0ee68994e6b8735c))
|
|
||||||
|
|
||||||
# [19.2.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.1.1-dev.1...v19.2.0-dev.1) (2023-12-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `PatchExtensions#registerNewPatchOption` function to simplify instantiation and registration of patch options ([4a91845](https://github.com/ReVanced/revanced-patcher/commit/4a9184597be99cd458496cce0ee68994e6b8735c))
|
|
||||||
|
|
||||||
## [19.1.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.1.0...v19.1.1-dev.1) (2023-12-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Accept `PatchSet` in `PatchesConsumer#acceptPatches` ([716825f](https://github.com/ReVanced/revanced-patcher/commit/716825f232bf1aab3a97723968562aa6dbdb20b1))
|
|
||||||
|
|
||||||
# [19.1.0](https://github.com/ReVanced/revanced-patcher/compare/v19.0.0...v19.1.0) (2023-12-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add constructor to initialize patches without annotations ([462fbe2](https://github.com/ReVanced/revanced-patcher/commit/462fbe2cadf56d8b0dde33319256021093bd39d5))
|
|
||||||
* Retrieve annotations in super and interface classes ([7aeae93](https://github.com/ReVanced/revanced-patcher/commit/7aeae93f3d9a13e294fe1bdb2586f79908af60af))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use a hash set for fast lookup ([f1de9b3](https://github.com/ReVanced/revanced-patcher/commit/f1de9b39eff1db44c00acd3e41902b3ec6124776))
|
|
||||||
|
|
||||||
# [19.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v19.0.0...v19.1.0-dev.1) (2023-11-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add constructor to initialize patches without annotations ([462fbe2](https://github.com/ReVanced/revanced-patcher/commit/462fbe2cadf56d8b0dde33319256021093bd39d5))
|
|
||||||
* Retrieve annotations in super and interface classes ([7aeae93](https://github.com/ReVanced/revanced-patcher/commit/7aeae93f3d9a13e294fe1bdb2586f79908af60af))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use a hash set for fast lookup ([f1de9b3](https://github.com/ReVanced/revanced-patcher/commit/f1de9b39eff1db44c00acd3e41902b3ec6124776))
|
|
||||||
|
|
||||||
# [19.0.0](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0...v19.0.0) (2023-10-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `PatchOption#valueType` to handle type erasure ([a46e948](https://github.com/ReVanced/revanced-patcher/commit/a46e948b5a0cf9bc8d31f557e371cd7d7c2f5b1c))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This changes the signature of the `PatchOption` constructor.
|
|
||||||
|
|
||||||
# [19.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0...v19.0.0-dev.1) (2023-10-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `PatchOption#valueType` to handle type erasure ([a46e948](https://github.com/ReVanced/revanced-patcher/commit/a46e948b5a0cf9bc8d31f557e371cd7d7c2f5b1c))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This changes the signature of the `PatchOption` constructor.
|
|
||||||
|
|
||||||
# [18.0.0](https://github.com/ReVanced/revanced-patcher/compare/v17.0.0...v18.0.0) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Do not set patch fields if they are empty ([a76ac04](https://github.com/ReVanced/revanced-patcher/commit/a76ac04214a2ab91e3b2f9dddb13ed52816fe723))
|
|
||||||
* Only allow setting `MethodFingerprint#result` privately ([aed1eac](https://github.com/ReVanced/revanced-patcher/commit/aed1eac3157317acf87f522750cf2f41509606c3))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Change `PatchOption` from abstract to open class ([09cd6aa](https://github.com/ReVanced/revanced-patcher/commit/09cd6aa568988dd5241bfa6a2e12b7926a7b0683))
|
|
||||||
* Change data classes to actual classes ([6192089](https://github.com/ReVanced/revanced-patcher/commit/6192089b71bdca15765369f3e607ddd1f8266205))
|
|
||||||
* Convert extension functions to member functions ([e2ca507](https://github.com/ReVanced/revanced-patcher/commit/e2ca50729da7085799c0ff6fc4f7afaf82579738))
|
|
||||||
* Move files to simplify package structure ([124a2e9](https://github.com/ReVanced/revanced-patcher/commit/124a2e9d3efb88f0f038ae306d941e918ad3ad3c))
|
|
||||||
* Remove deprecated classes and members ([a4212f6](https://github.com/ReVanced/revanced-patcher/commit/a4212f6bf952971541c4550e20f6bf57a382e19a))
|
|
||||||
|
|
||||||
|
|
||||||
* refactor!: Remove `Fingerprint` interface ([54a2f8f](https://github.com/ReVanced/revanced-patcher/commit/54a2f8f16fddf2b2ed47eb23717ba3734c4a6c5d))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add function to reset options to their default value ([ebbaafb](https://github.com/ReVanced/revanced-patcher/commit/ebbaafb78e88f34faeafe9ff8532afe29231bd79))
|
|
||||||
* Add function to reset options to their default value ([e6de90d](https://github.com/ReVanced/revanced-patcher/commit/e6de90d300bc9c82ca1696cb898db04c65a1cd5b))
|
|
||||||
* Add getter for default option value ([c7922e9](https://github.com/ReVanced/revanced-patcher/commit/c7922e90d0c6ae83f513611c706ebea33c1a2b63))
|
|
||||||
* Make `PatchOption#values` nullable ([56ce9ec](https://github.com/ReVanced/revanced-patcher/commit/56ce9ec2f98ff351c3d42df71b49e5c88f07e665))
|
|
||||||
* Name patch option value validator property correctly ([caa634f](https://github.com/ReVanced/revanced-patcher/commit/caa634fac6d7a717f54e3b015827c8858fd637b9))
|
|
||||||
* Remove patch annotation processor ([4456031](https://github.com/ReVanced/revanced-patcher/commit/445603145979a6f67823a79f9d6cd140299cff37))
|
|
||||||
* Use a map for `PatchOption#values` ([54ac139](https://github.com/ReVanced/revanced-patcher/commit/54ac1394a914d3eed7865ec697e8016834134911))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Run the garbage collector after writing dex files ([d9fb241](https://github.com/ReVanced/revanced-patcher/commit/d9fb241d57b0c4340130c0e5900250e66730ea56))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* The `MethodFingerprint#result` member can now only be set inside `MethodFingerprint`.
|
|
||||||
* The `Fingerprint` interface is no longer present.
|
|
||||||
* Some extension functions are now member functions.
|
|
||||||
* This gets rid of data class members.
|
|
||||||
* Some deprecated classes and members are not present anymore.
|
|
||||||
* Classes and members have changed packages.
|
|
||||||
* This gets rid of the existing basic implementations of the `PatchOptions` type and moves extension functions.
|
|
||||||
* This changes the getter name of the property.
|
|
||||||
* Various patch constructor signatures have changed.
|
|
||||||
|
|
||||||
# [18.0.0-dev.6](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.5...v18.0.0-dev.6) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Only allow setting `MethodFingerprint#result` privately ([aed1eac](https://github.com/ReVanced/revanced-patcher/commit/aed1eac3157317acf87f522750cf2f41509606c3))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Change data classes to actual classes ([6192089](https://github.com/ReVanced/revanced-patcher/commit/6192089b71bdca15765369f3e607ddd1f8266205))
|
|
||||||
* Convert extension functions to member functions ([e2ca507](https://github.com/ReVanced/revanced-patcher/commit/e2ca50729da7085799c0ff6fc4f7afaf82579738))
|
|
||||||
* Move files to simplify package structure ([124a2e9](https://github.com/ReVanced/revanced-patcher/commit/124a2e9d3efb88f0f038ae306d941e918ad3ad3c))
|
|
||||||
* Remove deprecated classes and members ([a4212f6](https://github.com/ReVanced/revanced-patcher/commit/a4212f6bf952971541c4550e20f6bf57a382e19a))
|
|
||||||
|
|
||||||
|
|
||||||
* refactor!: Remove `Fingerprint` interface ([54a2f8f](https://github.com/ReVanced/revanced-patcher/commit/54a2f8f16fddf2b2ed47eb23717ba3734c4a6c5d))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* The `MethodFingerprint#result` member can now only be set inside `MethodFingerprint`.
|
|
||||||
* The `Fingerprint` interface is no longer present.
|
|
||||||
* Some extension functions are now member functions.
|
|
||||||
* This gets rid of data class members.
|
|
||||||
* Some deprecated classes and members are not present anymore.
|
|
||||||
* Classes and members have changed packages.
|
|
||||||
|
|
||||||
# [18.0.0-dev.5](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.4...v18.0.0-dev.5) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Do not set patch fields if they are empty ([a76ac04](https://github.com/ReVanced/revanced-patcher/commit/a76ac04214a2ab91e3b2f9dddb13ed52816fe723))
|
|
||||||
|
|
||||||
# [18.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.3...v18.0.0-dev.4) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Use a map for `PatchOption#values` ([54ac139](https://github.com/ReVanced/revanced-patcher/commit/54ac1394a914d3eed7865ec697e8016834134911))
|
|
||||||
|
|
||||||
# [18.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.2...v18.0.0-dev.3) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Make `PatchOption#values` nullable ([56ce9ec](https://github.com/ReVanced/revanced-patcher/commit/56ce9ec2f98ff351c3d42df71b49e5c88f07e665))
|
|
||||||
|
|
||||||
# [18.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.1...v18.0.0-dev.2) (2023-10-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Change `PatchOption` from abstract to open class ([09cd6aa](https://github.com/ReVanced/revanced-patcher/commit/09cd6aa568988dd5241bfa6a2e12b7926a7b0683))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add function to reset options to their default value ([ebbaafb](https://github.com/ReVanced/revanced-patcher/commit/ebbaafb78e88f34faeafe9ff8532afe29231bd79))
|
|
||||||
* Add function to reset options to their default value ([e6de90d](https://github.com/ReVanced/revanced-patcher/commit/e6de90d300bc9c82ca1696cb898db04c65a1cd5b))
|
|
||||||
* Add getter for default option value ([c7922e9](https://github.com/ReVanced/revanced-patcher/commit/c7922e90d0c6ae83f513611c706ebea33c1a2b63))
|
|
||||||
* Name patch option value validator property correctly ([caa634f](https://github.com/ReVanced/revanced-patcher/commit/caa634fac6d7a717f54e3b015827c8858fd637b9))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This gets rid of the existing basic implementations of the `PatchOptions` type and moves extension functions.
|
|
||||||
* This changes the getter name of the property.
|
|
||||||
|
|
||||||
# [18.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v17.0.1-dev.1...v18.0.0-dev.1) (2023-10-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Remove patch annotation processor ([4456031](https://github.com/ReVanced/revanced-patcher/commit/445603145979a6f67823a79f9d6cd140299cff37))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* Various patch constructor signatures have changed.
|
|
||||||
|
|
||||||
## [17.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v17.0.0...v17.0.1-dev.1) (2023-10-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Run the garbage collector after writing dex files ([d9fb241](https://github.com/ReVanced/revanced-patcher/commit/d9fb241d57b0c4340130c0e5900250e66730ea56))
|
|
||||||
|
|
||||||
# [17.0.0](https://github.com/ReVanced/revanced-patcher/compare/v16.0.2...v17.0.0) (2023-10-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add option to use single threaded writer for dex files ([77dbee3](https://github.com/ReVanced/revanced-patcher/commit/77dbee3d6ae7b8dc77543e036624daa68ae63504))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This commit gets rid of deprecated constructors.
|
|
||||||
|
|
||||||
# [17.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.2...v17.0.0-dev.1) (2023-10-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add option to use single threaded writer for dex files ([77dbee3](https://github.com/ReVanced/revanced-patcher/commit/77dbee3d6ae7b8dc77543e036624daa68ae63504))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This commit gets rid of deprecated constructors.
|
|
||||||
|
|
||||||
## [16.0.2](https://github.com/ReVanced/revanced-patcher/compare/v16.0.1...v16.0.2) (2023-10-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use a map to merge integrations classes ([6059d3c](https://github.com/ReVanced/revanced-patcher/commit/6059d3ca2685cb659023b171b95d4b9d279c6e53))
|
|
||||||
|
|
||||||
## [16.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.1...v16.0.2-dev.1) (2023-10-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Use a map to merge integrations classes ([6059d3c](https://github.com/ReVanced/revanced-patcher/commit/6059d3ca2685cb659023b171b95d4b9d279c6e53))
|
|
||||||
|
|
||||||
## [16.0.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.0...v16.0.1) (2023-10-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Merge integrations when required ([06c2b76](https://github.com/ReVanced/revanced-patcher/commit/06c2b76f11ac1bfe43d51d54d425e7577ecefdf6))
|
|
||||||
|
|
||||||
## [16.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.0...v16.0.1-dev.1) (2023-10-05)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Merge integrations when required ([06c2b76](https://github.com/ReVanced/revanced-patcher/commit/06c2b76f11ac1bfe43d51d54d425e7577ecefdf6))
|
|
||||||
|
|
||||||
# [16.0.0](https://github.com/ReVanced/revanced-patcher/compare/v15.0.3...v16.0.0) (2023-10-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Use correct super class type ([f590436](https://github.com/ReVanced/revanced-patcher/commit/f590436399f6385c51cea54618251b5d823c31f9))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This changes the super classes of some `PatchOptionException` classes
|
|
||||||
|
|
||||||
# [16.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.3...v16.0.0-dev.1) (2023-10-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Use correct super class type ([f590436](https://github.com/ReVanced/revanced-patcher/commit/f590436399f6385c51cea54618251b5d823c31f9))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This changes the super classes of some `PatchOptionException` classes
|
|
||||||
|
|
||||||
## [15.0.3](https://github.com/ReVanced/revanced-patcher/compare/v15.0.2...v15.0.3) (2023-10-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix SMALI compilation on devices with RTL language ([#242](https://github.com/ReVanced/revanced-patcher/issues/242)) ([356f1f1](https://github.com/ReVanced/revanced-patcher/commit/356f1f155348347a8f318a2e024716ebf4fec99b))
|
|
||||||
|
|
||||||
## [15.0.3-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.2...v15.0.3-dev.1) (2023-09-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix SMALI compilation on devices with RTL language ([#242](https://github.com/ReVanced/revanced-patcher/issues/242)) ([356f1f1](https://github.com/ReVanced/revanced-patcher/commit/356f1f155348347a8f318a2e024716ebf4fec99b))
|
|
||||||
|
|
||||||
## [15.0.2](https://github.com/ReVanced/revanced-patcher/compare/v15.0.1...v15.0.2) (2023-09-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Do not unnecessary resolve fingeprints twice ([#241](https://github.com/ReVanced/revanced-patcher/issues/241)) ([4d6e08a](https://github.com/ReVanced/revanced-patcher/commit/4d6e08a650dde6ec2e18611c5db1ab92b9a61dd1))
|
|
||||||
|
|
||||||
## [15.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.1...v15.0.2-dev.1) (2023-09-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* Do not unnecessary resolve fingeprints twice ([#241](https://github.com/ReVanced/revanced-patcher/issues/241)) ([4d6e08a](https://github.com/ReVanced/revanced-patcher/commit/4d6e08a650dde6ec2e18611c5db1ab92b9a61dd1))
|
|
||||||
|
|
||||||
## [15.0.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0...v15.0.1) (2023-09-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Remove log management ([d51bc32](https://github.com/ReVanced/revanced-patcher/commit/d51bc32e37a865194c3825471085b3ccf8c78421))
|
|
||||||
|
|
||||||
## [15.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0...v15.0.1-dev.1) (2023-09-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Remove log management ([d51bc32](https://github.com/ReVanced/revanced-patcher/commit/d51bc32e37a865194c3825471085b3ccf8c78421))
|
|
||||||
|
|
||||||
# [15.0.0](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0) (2023-09-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
|
||||||
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
|
||||||
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
|
||||||
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
|
||||||
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
|
||||||
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
|
||||||
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
|
||||||
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
|
||||||
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
|
||||||
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
|
||||||
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
|
||||||
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
|
||||||
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
|
||||||
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
|
||||||
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
|
||||||
|
|
||||||
|
|
||||||
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
|
||||||
* feat!: Add patch annotation processor ([3fc6a13](https://github.com/ReVanced/revanced-patcher/commit/3fc6a139eef67237c116fb4e3e29bf9542d3a981))
|
|
||||||
* feat!: Remove patch annotations ([3b4db3d](https://github.com/ReVanced/revanced-patcher/commit/3b4db3ddb72cdcee8af2f787eadf58eeb37543de))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add patch annotation processor ([#231](https://github.com/ReVanced/revanced-patcher/issues/231)) ([a29931f](https://github.com/ReVanced/revanced-patcher/commit/a29931f2ec0a666dba209b54855425d9dc2f4462))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This gets rid of the public constructor.
|
|
||||||
* `PatchBundleLoader` is not a map anymore
|
|
||||||
* This renames packages and the Maven package.
|
|
||||||
* The manifest for patches has been removed, and the properties have been added to patches. Patches are now `OptionsContainer`. The `@Patch` annotation has been removed in favour of the `@Patch` annotation from the annotation processor.
|
|
||||||
* Patch annotations have been removed. PatcherException is now thrown in various places. PatchBundleLoader is now a map of patches associated by their name. Patches are now instances.
|
|
||||||
|
|
||||||
# [15.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.3...v15.0.0-dev.4) (2023-09-13)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
|
||||||
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
|
||||||
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
|
||||||
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
|
||||||
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
|
||||||
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
|
||||||
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
|
||||||
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
|
||||||
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
|
||||||
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
|
||||||
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
|
||||||
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
|
||||||
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This gets rid of the public constructor.
|
|
||||||
|
|
||||||
# [15.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.2...v15.0.0-dev.3) (2023-09-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
|
||||||
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
|
||||||
|
|
||||||
|
|
||||||
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* `PatchBundleLoader` is not a map anymore
|
|
||||||
* This renames packages and the Maven package.
|
|
||||||
|
|
||||||
# [15.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.1...v15.0.0-dev.2) (2023-09-06)
|
|
||||||
|
|
||||||
# [15.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0-dev.1) (2023-09-04)
|
|
||||||
|
|
||||||
|
|
||||||
* feat!: Add patch annotation processor ([3fc6a13](https://github.com/ReVanced/revanced-patcher/commit/3fc6a139eef67237c116fb4e3e29bf9542d3a981))
|
|
||||||
* feat!: Remove patch annotations ([3b4db3d](https://github.com/ReVanced/revanced-patcher/commit/3b4db3ddb72cdcee8af2f787eadf58eeb37543de))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add patch annotation processor ([#231](https://github.com/ReVanced/revanced-patcher/issues/231)) ([a29931f](https://github.com/ReVanced/revanced-patcher/commit/a29931f2ec0a666dba209b54855425d9dc2f4462))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* The manifest for patches has been removed, and the properties have been added to patches. Patches are now `OptionsContainer`. The `@Patch` annotation has been removed in favour of the `@Patch` annotation from the annotation processor.
|
|
||||||
* Patch annotations have been removed. PatcherException is now thrown in various places. PatchBundleLoader is now a map of patches associated by their name. Patches are now instances.
|
|
||||||
|
|
||||||
## [14.2.2](https://github.com/ReVanced/revanced-patcher/compare/v14.2.1...v14.2.2) (2023-08-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* allow setting `DexClassLoader.optimizedDirectory` ([11a3378](https://github.com/ReVanced/revanced-patcher/commit/11a337865947a6ac74a63ebb3f3f9bc2610f7771))
|
|
||||||
|
|
||||||
## [14.2.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.1...v14.2.2-dev.1) (2023-08-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* allow setting `DexClassLoader.optimizedDirectory` ([11a3378](https://github.com/ReVanced/revanced-patcher/commit/11a337865947a6ac74a63ebb3f3f9bc2610f7771))
|
|
||||||
|
|
||||||
## [14.2.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0...v14.2.1) (2023-08-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* do not flag resource table as sparse when main package is not loaded ([b832812](https://github.com/ReVanced/revanced-patcher/commit/b832812767a06ec6ec232291e6d14c8c2f14118c))
|
|
||||||
|
|
||||||
## [14.2.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0...v14.2.1-dev.1) (2023-08-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* do not flag resource table as sparse when main package is not loaded ([b832812](https://github.com/ReVanced/revanced-patcher/commit/b832812767a06ec6ec232291e6d14c8c2f14118c))
|
|
||||||
|
|
||||||
# [14.2.0](https://github.com/ReVanced/revanced-patcher/compare/v14.1.0...v14.2.0) (2023-08-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* load patches in lexicographical order ([e8f2087](https://github.com/ReVanced/revanced-patcher/commit/e8f2087a6ffa6077fb3a6a69e29f3aec72e2fc1b))
|
|
||||||
* log when merging integrations ([983563e](https://github.com/ReVanced/revanced-patcher/commit/983563efb6d7c8d289464b8bf71a016b8a735630))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* compare types of classes ([55d6945](https://github.com/ReVanced/revanced-patcher/commit/55d694579ac2718b9e2c61ca5f38419c3775ef87))
|
|
||||||
|
|
||||||
# [14.2.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0-dev.2...v14.2.0-dev.3) (2023-08-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* compare types of classes ([55d6945](https://github.com/ReVanced/revanced-patcher/commit/55d694579ac2718b9e2c61ca5f38419c3775ef87))
|
|
||||||
|
|
||||||
# [14.2.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0-dev.1...v14.2.0-dev.2) (2023-08-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* log when merging integrations ([983563e](https://github.com/ReVanced/revanced-patcher/commit/983563efb6d7c8d289464b8bf71a016b8a735630))
|
|
||||||
|
|
||||||
# [14.2.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.1.0...v14.2.0-dev.1) (2023-08-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* load patches in lexicographical order ([e8f2087](https://github.com/ReVanced/revanced-patcher/commit/e8f2087a6ffa6077fb3a6a69e29f3aec72e2fc1b))
|
|
||||||
|
|
||||||
# [14.1.0](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0...v14.1.0) (2023-08-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* move version properties file to correct package ([e985676](https://github.com/ReVanced/revanced-patcher/commit/e985676c2d8e5d6cb907d371de30428caaa6da43))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* properly make use of logging facade ([ba56a6a](https://github.com/ReVanced/revanced-patcher/commit/ba56a6a2eef503c0d6cdd846ddce2e1474d8ed1a))
|
|
||||||
|
|
||||||
# [14.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.0.1-dev.1...v14.1.0-dev.1) (2023-08-24)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* properly make use of logging facade ([ba56a6a](https://github.com/ReVanced/revanced-patcher/commit/ba56a6a2eef503c0d6cdd846ddce2e1474d8ed1a))
|
|
||||||
|
|
||||||
## [14.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0...v14.0.1-dev.1) (2023-08-23)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* move version properties file to correct package ([e985676](https://github.com/ReVanced/revanced-patcher/commit/e985676c2d8e5d6cb907d371de30428caaa6da43))
|
|
||||||
|
|
||||||
# [14.0.0](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0...v14.0.0) (2023-08-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* log decoding resources after logging deleting resource cache directory ([db62a16](https://github.com/ReVanced/revanced-patcher/commit/db62a1607b4a9d6256b5f5153decb088d9680553))
|
|
||||||
* only emit closed patches that did not throw an exception with the `@Patch` annotation ([5938f6b](https://github.com/ReVanced/revanced-patcher/commit/5938f6b7ea25103a0a1b56ceebe49139bc80c6f5))
|
|
||||||
* supply the parent classloader to `DexClassLoader` ([0f15077](https://github.com/ReVanced/revanced-patcher/commit/0f15077225600b65200022c1a318e504deb472b9))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* improve structure and public API ([6b8977f](https://github.com/ReVanced/revanced-patcher/commit/6b8977f17854ef0344d868e6391cb18134eceadc))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* do not log instantiation of ReVanced Patcher ([273dd8d](https://github.com/ReVanced/revanced-patcher/commit/273dd8d388f8e9b7436c6d6145a94c12c1fabe55))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* Various public APIs have been changed. The `Version` annotation has been removed. Patches do not return anything anymore and instead throw `PatchException`. Multiple patch bundles can now be loaded in a single ClassLoader to bypass class loader isolation.
|
|
||||||
|
|
||||||
# [14.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.3...v14.0.0-dev.4) (2023-08-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* only emit closed patches that did not throw an exception with the `@Patch` annotation ([5938f6b](https://github.com/ReVanced/revanced-patcher/commit/5938f6b7ea25103a0a1b56ceebe49139bc80c6f5))
|
|
||||||
|
|
||||||
# [14.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.2...v14.0.0-dev.3) (2023-08-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* supply the parent classloader to `DexClassLoader` ([0f15077](https://github.com/ReVanced/revanced-patcher/commit/0f15077225600b65200022c1a318e504deb472b9))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* do not log instantiation of ReVanced Patcher ([273dd8d](https://github.com/ReVanced/revanced-patcher/commit/273dd8d388f8e9b7436c6d6145a94c12c1fabe55))
|
|
||||||
|
|
||||||
# [14.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.1...v14.0.0-dev.2) (2023-08-19)
|
|
||||||
|
|
||||||
# [14.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0...v14.0.0-dev.1) (2023-08-18)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* log decoding resources after logging deleting resource cache directory ([db62a16](https://github.com/ReVanced/revanced-patcher/commit/db62a1607b4a9d6256b5f5153decb088d9680553))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* improve structure and public API ([6b8977f](https://github.com/ReVanced/revanced-patcher/commit/6b8977f17854ef0344d868e6391cb18134eceadc))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* Various public APIs have been changed. The `Version` annotation has been removed. Patches do not return anything anymore and instead throw `PatchException`. Multiple patch bundles can now be loaded in a single ClassLoader to bypass class loader isolation.
|
|
||||||
|
|
||||||
# [13.0.0](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1...v13.0.0) (2023-08-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* decode in correct order ([8fb2f2d](https://github.com/ReVanced/revanced-patcher/commit/8fb2f2dc1d3b9b1e9fd13b39485985d2886d52ae))
|
|
||||||
* disable correct loggers ([c2d89c6](https://github.com/ReVanced/revanced-patcher/commit/c2d89c622e06e58e5042e1a00ef67cee8a246e53))
|
|
||||||
* get framework ids to compile resources ([f2cb7ee](https://github.com/ReVanced/revanced-patcher/commit/f2cb7ee7dffa573c31df497cf235a3f5d120f91f))
|
|
||||||
* only enable logging for ReVanced ([783ccf8](https://github.com/ReVanced/revanced-patcher/commit/783ccf8529f5d16aa463982da6977328306232bb))
|
|
||||||
* set package metadata correctly ([02d6ff1](https://github.com/ReVanced/revanced-patcher/commit/02d6ff15fe87c2352de29749610e9d72db8ba418))
|
|
||||||
|
|
||||||
|
|
||||||
* build(Needs bump)!: Bump dependencies ([d5f89a9](https://github.com/ReVanced/revanced-patcher/commit/d5f89a903f019c199bdb27a50287124fc4b4978e))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This bump updates smali, a crucial dependency
|
|
||||||
|
|
||||||
# [13.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.2...v13.0.0-dev.3) (2023-08-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* decode in correct order ([8fb2f2d](https://github.com/ReVanced/revanced-patcher/commit/8fb2f2dc1d3b9b1e9fd13b39485985d2886d52ae))
|
|
||||||
* only enable logging for ReVanced ([783ccf8](https://github.com/ReVanced/revanced-patcher/commit/783ccf8529f5d16aa463982da6977328306232bb))
|
|
||||||
|
|
||||||
# [13.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.1...v13.0.0-dev.2) (2023-08-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* disable correct loggers ([c2d89c6](https://github.com/ReVanced/revanced-patcher/commit/c2d89c622e06e58e5042e1a00ef67cee8a246e53))
|
|
||||||
* get framework ids to compile resources ([f2cb7ee](https://github.com/ReVanced/revanced-patcher/commit/f2cb7ee7dffa573c31df497cf235a3f5d120f91f))
|
|
||||||
* set package metadata correctly ([02d6ff1](https://github.com/ReVanced/revanced-patcher/commit/02d6ff15fe87c2352de29749610e9d72db8ba418))
|
|
||||||
|
|
||||||
# [13.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1...v13.0.0-dev.1) (2023-08-11)
|
|
||||||
|
|
||||||
|
|
||||||
* build(Needs bump)!: Bump dependencies ([d5f89a9](https://github.com/ReVanced/revanced-patcher/commit/d5f89a903f019c199bdb27a50287124fc4b4978e))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This bump updates smali, a crucial dependency
|
|
||||||
|
|
||||||
## [12.1.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0...v12.1.1) (2023-08-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* clear method lookup maps before initializing them ([#210](https://github.com/ReVanced/revanced-patcher/issues/210)) ([746544f](https://github.com/ReVanced/revanced-patcher/commit/746544f9d51d1013bb160075709cd26bffd425b3))
|
|
||||||
|
|
||||||
## [12.1.1-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1-dev.1...v12.1.1-dev.2) (2023-08-03)
|
|
||||||
|
|
||||||
## [12.1.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0...v12.1.1-dev.1) (2023-08-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* clear method lookup maps before initializing them ([#210](https://github.com/ReVanced/revanced-patcher/issues/210)) ([746544f](https://github.com/ReVanced/revanced-patcher/commit/746544f9d51d1013bb160075709cd26bffd425b3))
|
|
||||||
|
|
||||||
# [12.1.0](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0...v12.1.0) (2023-08-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* add `MutableMethod.getInstructions` extension function ([fae4029](https://github.com/ReVanced/revanced-patcher/commit/fae4029cfccfad7aa3dd8f7fbef1c63ee26b85b3))
|
|
||||||
|
|
||||||
# [12.1.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0-dev.1...v12.1.0-dev.2) (2023-08-03)
|
|
||||||
|
|
||||||
# [12.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0...v12.1.0-dev.1) (2023-08-01)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* add `MutableMethod.getInstructions` extension function ([fae4029](https://github.com/ReVanced/revanced-patcher/commit/fae4029cfccfad7aa3dd8f7fbef1c63ee26b85b3))
|
|
||||||
|
|
||||||
# [12.0.0](https://github.com/ReVanced/revanced-patcher/compare/v11.0.4...v12.0.0) (2023-07-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* correct access flags of `PackageMetadata` ([416d691](https://github.com/ReVanced/revanced-patcher/commit/416d69142f50dab49c9ea3f027e9d53e4777f257))
|
|
||||||
* set resource table via resource decoder ([e0f8e1b](https://github.com/ReVanced/revanced-patcher/commit/e0f8e1b71a295948b610029c89a48f52762396b6))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Deprecate `Version` annotation ([c9bbcf2](https://github.com/ReVanced/revanced-patcher/commit/c9bbcf2bf2b0f50ab9100380a3a66c6346ad42ac))
|
|
||||||
* remove `Path` option ([#202](https://github.com/ReVanced/revanced-patcher/issues/202)) ([69e4a49](https://github.com/ReVanced/revanced-patcher/commit/69e4a490659ebc4fb4bf46148634f4b064ef1713))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This removes the previously available `Path` option
|
|
||||||
|
|
||||||
# [12.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0-dev.1...v12.0.0-dev.2) (2023-07-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Deprecate `Version` annotation ([400442f](https://github.com/ReVanced/revanced-patcher/commit/400442f70ee56cafd4493b2ce64a294db9836509))
|
|
||||||
|
|
||||||
# [12.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v11.0.4...v12.0.0-dev.1) (2023-07-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* correct access flags of `PackageMetadata` ([416d691](https://github.com/ReVanced/revanced-patcher/commit/416d69142f50dab49c9ea3f027e9d53e4777f257))
|
|
||||||
* set resource table via resource decoder ([e0f8e1b](https://github.com/ReVanced/revanced-patcher/commit/e0f8e1b71a295948b610029c89a48f52762396b6))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* remove `Path` option ([#202](https://github.com/ReVanced/revanced-patcher/issues/202)) ([69e4a49](https://github.com/ReVanced/revanced-patcher/commit/69e4a490659ebc4fb4bf46148634f4b064ef1713))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* This removes the previously available `Path` option
|
|
||||||
|
|
||||||
## [11.0.4](https://github.com/revanced/revanced-patcher/compare/v11.0.3...v11.0.4) (2023-07-01)
|
## [11.0.4](https://github.com/revanced/revanced-patcher/compare/v11.0.3...v11.0.4) (2023-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 👋 Contribution guidelines
|
|
||||||
|
|
||||||
This document describes how to contribute to ReVanced Patcher.
|
|
||||||
|
|
||||||
## 📖 Resources to help you get started
|
|
||||||
|
|
||||||
- The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs/docs) contains the fundamentals
|
|
||||||
of ReVanced Patcher and how to use ReVanced Patcher to create patches
|
|
||||||
- [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
|
||||||
- [Issues](https://github.com/ReVanced/revanced-patcher/issues) are where we keep track of bugs and feature requests
|
|
||||||
|
|
||||||
## 🙏 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+).
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patcher.
|
|
||||||
> Good motivation has to be provided for a request to be accepted.
|
|
||||||
|
|
||||||
## 🐞 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+).
|
|
||||||
|
|
||||||
## 📝 How to contribute
|
|
||||||
|
|
||||||
1. Before contributing, it is recommended to open an issue to discuss your change
|
|
||||||
with the maintainers of ReVanced Patcher. This will help you determine whether your change is acceptable
|
|
||||||
and whether it is worth your time to implement it
|
|
||||||
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
|
||||||
3. Commit your changes
|
|
||||||
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
|
||||||
that your pull request closes in the description of your pull request
|
|
||||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
|
||||||
it will be merged into the `dev` branch and will be included in the next release of ReVanced Patcher
|
|
||||||
|
|
||||||
❤️ Thank you for considering contributing to ReVanced Patcher,
|
|
||||||
ReVanced
|
|
||||||
122
README.md
122
README.md
@@ -1,125 +1,3 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 💉 ReVanced Patcher
|
# 💉 ReVanced Patcher
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
ReVanced Patcher used to patch Android applications.
|
ReVanced Patcher used to patch Android applications.
|
||||||
|
|
||||||
## ❓ About
|
|
||||||
|
|
||||||
ReVanced Patcher is a library that is used to patch Android applications.
|
|
||||||
It powers [ReVanced Manager](https://github.com/ReVanced/revanced-manager),
|
|
||||||
[ReVanced CLI](https://github.com/ReVanced/revanced-cli)
|
|
||||||
and [ReVanced Library](https://github.com/ReVanced/revanced-library) and a rich set of patches have been developed
|
|
||||||
using ReVanced Patcher in the [ReVanced Patches](https://github.com/ReVanced/revanced-patches) repository.
|
|
||||||
|
|
||||||
## 💪 Features
|
|
||||||
|
|
||||||
Some of the features the ReVanced Patcher provides are:
|
|
||||||
|
|
||||||
- 🔧 **Patch Dalvik VM bytecode**: Disassemble and assemble Dalvik bytecode
|
|
||||||
- 📦 **Patch APK resources**: Decode and build Android APK resources
|
|
||||||
- 📂 **Patch arbitrary APK files**: Read and write arbitrary files directly from and to APK files
|
|
||||||
- 🧩 **Write modular patches**: Extensive API to write modular patches that can patch Dalvik VM bytecode,
|
|
||||||
APK resources and arbitrary APK files
|
|
||||||
|
|
||||||
## 🚀 How to get started
|
|
||||||
|
|
||||||
To use ReVanced Patcher in your project, follow these steps:
|
|
||||||
|
|
||||||
1. [Add the repository](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#using-a-published-package)
|
|
||||||
to your project
|
|
||||||
2. Add the dependency to your project:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
dependencies {
|
|
||||||
implementation("app.revanced:revanced-patcher:{$version}")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For a minimal project configuration,
|
|
||||||
see [ReVanced Patches template](https://github.com/ReVanced/revanced-patches-template).
|
|
||||||
|
|
||||||
## 📚 Everything else
|
|
||||||
|
|
||||||
### 📙 Contributing
|
|
||||||
|
|
||||||
Thank you for considering contributing to ReVanced Patcher.
|
|
||||||
You can find the contribution guidelines [here](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
### 🛠️ Building
|
|
||||||
|
|
||||||
To build ReVanced Patcher,
|
|
||||||
you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
|
|
||||||
|
|
||||||
### 📃 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/main/docs).
|
|
||||||
|
|
||||||
## 📜 Licence
|
|
||||||
|
|
||||||
ReVanced Patcher is licensed under the GPLv3 license. Please see the [licence file](LICENSE) for more information.
|
|
||||||
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Patcher as long as you track changes/dates in source files.
|
|
||||||
Any modifications to ReVanced Patcher must also be made available under the GPL,
|
|
||||||
along with build & install instructions.
|
|
||||||
|
|||||||
@@ -1,890 +0,0 @@
|
|||||||
public final class app/revanced/patcher/Fingerprint {
|
|
||||||
public final fun getClassDef (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
public final fun getClassDefOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
public final fun getMethod (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun getMethodOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun getOriginalClassDef (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun getOriginalClassDefOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun getOriginalMethod (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun getOriginalMethodOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun getPatternMatch (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/Match$PatternMatch;
|
|
||||||
public final fun getPatternMatchOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Lapp/revanced/patcher/Match$PatternMatch;
|
|
||||||
public final fun getStringMatches (Lapp/revanced/patcher/patch/BytecodePatchContext;)Ljava/util/List;
|
|
||||||
public final fun getStringMatchesOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;)Ljava/util/List;
|
|
||||||
public final fun match (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
|
|
||||||
public final fun match (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
|
|
||||||
public final fun match (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
|
|
||||||
public final fun matchOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
|
|
||||||
public final fun matchOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
|
|
||||||
public final fun matchOrNull (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/FingerprintBuilder {
|
|
||||||
public fun <init> ()V
|
|
||||||
public final fun accessFlags (I)V
|
|
||||||
public final fun accessFlags ([Lcom/android/tools/smali/dexlib2/AccessFlags;)V
|
|
||||||
public final fun custom (Lkotlin/jvm/functions/Function2;)V
|
|
||||||
public final fun opcodes (Ljava/lang/String;)V
|
|
||||||
public final fun opcodes ([Lcom/android/tools/smali/dexlib2/Opcode;)V
|
|
||||||
public final fun parameters ([Ljava/lang/String;)V
|
|
||||||
public final fun returns (Ljava/lang/String;)V
|
|
||||||
public final fun strings ([Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/FingerprintKt {
|
|
||||||
public static final fun fingerprint (ILkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/Fingerprint;
|
|
||||||
public static synthetic fun fingerprint$default (ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/Fingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface annotation class app/revanced/patcher/InternalApi : java/lang/annotation/Annotation {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/Match {
|
|
||||||
public final fun getClassDef ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
public final fun getMethod ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun getOriginalClassDef ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun getOriginalMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun getPatternMatch ()Lapp/revanced/patcher/Match$PatternMatch;
|
|
||||||
public final fun getStringMatches ()Ljava/util/List;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/Match$PatternMatch {
|
|
||||||
public final fun getEndIndex ()I
|
|
||||||
public final fun getStartIndex ()I
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/Match$StringMatch {
|
|
||||||
public final fun getIndex ()I
|
|
||||||
public final fun getString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PackageMetadata {
|
|
||||||
public final fun getPackageName ()Ljava/lang/String;
|
|
||||||
public final fun getPackageVersion ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/Patcher : java/io/Closeable {
|
|
||||||
public fun <init> (Lapp/revanced/patcher/PatcherConfig;)V
|
|
||||||
public fun close ()V
|
|
||||||
public final fun get ()Lapp/revanced/patcher/PatcherResult;
|
|
||||||
public final fun getContext ()Lapp/revanced/patcher/PatcherContext;
|
|
||||||
public final fun invoke ()Lkotlinx/coroutines/flow/Flow;
|
|
||||||
public final fun plusAssign (Ljava/util/Set;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherConfig {
|
|
||||||
public fun <init> (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/lang/String;)V
|
|
||||||
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;)V
|
|
||||||
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherContext : java/io/Closeable {
|
|
||||||
public fun close ()V
|
|
||||||
public final fun getPackageMetadata ()Lapp/revanced/patcher/PackageMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherResult {
|
|
||||||
public final fun getDexFiles ()Ljava/util/Set;
|
|
||||||
public final fun getResources ()Lapp/revanced/patcher/PatcherResult$PatchedResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherResult$PatchedDexFile {
|
|
||||||
public final fun getName ()Ljava/lang/String;
|
|
||||||
public final fun getStream ()Ljava/io/InputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherResult$PatchedResources {
|
|
||||||
public final fun getDeleteResources ()Ljava/util/Set;
|
|
||||||
public final fun getDoNotCompress ()Ljava/util/Set;
|
|
||||||
public final fun getOtherResources ()Ljava/io/File;
|
|
||||||
public final fun getResourcesApk ()Ljava/io/File;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/extensions/ExtensionsKt {
|
|
||||||
public static final fun newLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/Label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/extensions/InstructionExtensions {
|
|
||||||
public static final field INSTANCE Lapp/revanced/patcher/extensions/InstructionExtensions;
|
|
||||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
|
||||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
|
||||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
|
||||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
|
||||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
|
||||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
|
||||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
|
||||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/util/List;)V
|
|
||||||
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
|
||||||
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;Ljava/util/List;)V
|
|
||||||
public final fun addInstructionsWithLabels (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
|
|
||||||
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
|
||||||
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;I)Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/iface/MethodImplementation;I)Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;
|
|
||||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/iface/MethodImplementation;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstructionOrNull (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
|
||||||
public final fun getInstructionOrNull (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstructionOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;I)Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;
|
|
||||||
public final fun getInstructionOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;I)Ljava/lang/Object;
|
|
||||||
public final fun getInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
|
||||||
public final fun getInstructions (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Iterable;
|
|
||||||
public final fun getInstructionsOrNull (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
|
||||||
public final fun getInstructionsOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Iterable;
|
|
||||||
public final fun removeInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
|
||||||
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
|
||||||
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;II)V
|
|
||||||
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)V
|
|
||||||
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;II)V
|
|
||||||
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
|
||||||
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
|
||||||
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
|
||||||
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
|
||||||
public final fun replaceInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch {
|
|
||||||
public final fun getExtensionInputStream ()Ljava/util/function/Supplier;
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/BytecodePatchBuilder : app/revanced/patcher/patch/PatchBuilder {
|
|
||||||
public synthetic fun build$revanced_patcher ()Lapp/revanced/patcher/patch/Patch;
|
|
||||||
public final fun extendWith (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatchBuilder;
|
|
||||||
public final fun getExtensionInputStream ()Ljava/util/function/Supplier;
|
|
||||||
public final fun setExtensionInputStream (Ljava/util/function/Supplier;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/BytecodePatchContext : app/revanced/patcher/patch/PatchContext, java/io/Closeable {
|
|
||||||
public final fun classBy (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
|
||||||
public fun close ()V
|
|
||||||
public synthetic fun get ()Ljava/lang/Object;
|
|
||||||
public fun get ()Ljava/util/Set;
|
|
||||||
public final fun getClasses ()Lapp/revanced/patcher/util/ProxyClassList;
|
|
||||||
public final fun navigate (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)Lapp/revanced/patcher/util/MethodNavigator;
|
|
||||||
public final fun proxy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/Option {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun getDefault ()Ljava/lang/Object;
|
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
|
||||||
public final fun getKey ()Ljava/lang/String;
|
|
||||||
public final fun getName ()Ljava/lang/String;
|
|
||||||
public final fun getRequired ()Z
|
|
||||||
public final fun getTitle ()Ljava/lang/String;
|
|
||||||
public final fun getType ()Lkotlin/reflect/KType;
|
|
||||||
public final fun getValidator ()Lkotlin/jvm/functions/Function2;
|
|
||||||
public final fun getValue ()Ljava/lang/Object;
|
|
||||||
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
|
||||||
public final fun getValues ()Ljava/util/Map;
|
|
||||||
public final fun reset ()V
|
|
||||||
public final fun setValue (Ljava/lang/Object;)V
|
|
||||||
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/OptionException : java/lang/Exception {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/OptionException$InvalidValueTypeException : app/revanced/patcher/patch/OptionException {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/OptionException$OptionNotFoundException : app/revanced/patcher/patch/OptionException {
|
|
||||||
public fun <init> (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/OptionException$ValueRequiredException : app/revanced/patcher/patch/OptionException {
|
|
||||||
public fun <init> (Lapp/revanced/patcher/patch/Option;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/OptionException$ValueValidationException : app/revanced/patcher/patch/OptionException {
|
|
||||||
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/Option;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/OptionKt {
|
|
||||||
public static final fun booleanOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun booleanOption (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun booleanOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun booleanOption$default (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun booleansOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun booleansOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun booleansOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun booleansOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun floatOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun floatOption (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun floatOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun floatOption$default (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun floatsOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun floatsOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun intOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun intOption (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun intOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun intOption$default (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun intsOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun intsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun intsOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun intsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun longOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun longOption (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun longOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun longOption$default (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun longsOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun longsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun longsOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun longsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun stringOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun stringOption (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun stringOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun stringOption$default (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun stringsOption (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static final fun stringsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun stringsOption$default (Lapp/revanced/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public static synthetic fun stringsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/Options : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker {
|
|
||||||
public fun clear ()V
|
|
||||||
public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
|
|
||||||
public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;
|
|
||||||
public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
|
|
||||||
public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public final fun containsKey (Ljava/lang/Object;)Z
|
|
||||||
public fun containsKey (Ljava/lang/String;)Z
|
|
||||||
public fun containsValue (Lapp/revanced/patcher/patch/Option;)Z
|
|
||||||
public final fun containsValue (Ljava/lang/Object;)Z
|
|
||||||
public final fun entrySet ()Ljava/util/Set;
|
|
||||||
public final fun get (Ljava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun get (Ljava/lang/String;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public fun getEntries ()Ljava/util/Set;
|
|
||||||
public fun getKeys ()Ljava/util/Set;
|
|
||||||
public fun getSize ()I
|
|
||||||
public fun getValues ()Ljava/util/Collection;
|
|
||||||
public fun isEmpty ()Z
|
|
||||||
public final fun keySet ()Ljava/util/Set;
|
|
||||||
public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
|
|
||||||
public fun merge (Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Ljava/util/function/BiFunction;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun put (Ljava/lang/String;Lapp/revanced/patcher/patch/Option;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public fun putAll (Ljava/util/Map;)V
|
|
||||||
public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun putIfAbsent (Ljava/lang/String;Lapp/revanced/patcher/patch/Option;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public fun remove (Ljava/lang/Object;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z
|
|
||||||
public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
|
|
||||||
public fun replace (Ljava/lang/String;Lapp/revanced/patcher/patch/Option;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public fun replace (Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lapp/revanced/patcher/patch/Option;)Z
|
|
||||||
public fun replaceAll (Ljava/util/function/BiFunction;)V
|
|
||||||
public final fun set (Ljava/lang/String;Ljava/lang/Object;)V
|
|
||||||
public final fun size ()I
|
|
||||||
public final fun values ()Ljava/util/Collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/Patch {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZLjava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun execute (Lapp/revanced/patcher/patch/PatchContext;)V
|
|
||||||
public final fun finalize (Lapp/revanced/patcher/patch/PatchContext;)V
|
|
||||||
public final fun getCompatiblePackages ()Ljava/util/Set;
|
|
||||||
public final fun getDependencies ()Ljava/util/Set;
|
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
|
||||||
public final fun getName ()Ljava/lang/String;
|
|
||||||
public final fun getOptions ()Lapp/revanced/patcher/patch/Options;
|
|
||||||
public final fun getUse ()Z
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/PatchBuilder {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun compatibleWith ([Ljava/lang/String;)V
|
|
||||||
public final fun compatibleWith ([Lkotlin/Pair;)V
|
|
||||||
public final fun dependsOn ([Lapp/revanced/patcher/patch/Patch;)V
|
|
||||||
public final fun execute (Lkotlin/jvm/functions/Function1;)V
|
|
||||||
public final fun finalize (Lkotlin/jvm/functions/Function1;)V
|
|
||||||
protected final fun getCompatiblePackages ()Ljava/util/Set;
|
|
||||||
protected final fun getDependencies ()Ljava/util/Set;
|
|
||||||
protected final fun getDescription ()Ljava/lang/String;
|
|
||||||
protected final fun getExecutionBlock ()Lkotlin/jvm/functions/Function1;
|
|
||||||
protected final fun getFinalizeBlock ()Lkotlin/jvm/functions/Function1;
|
|
||||||
protected final fun getName ()Ljava/lang/String;
|
|
||||||
protected final fun getOptions ()Ljava/util/Set;
|
|
||||||
protected final fun getUse ()Z
|
|
||||||
public final fun invoke (Lapp/revanced/patcher/patch/Option;)Lapp/revanced/patcher/patch/Option;
|
|
||||||
public final fun invoke (Ljava/lang/String;[Ljava/lang/String;)Lkotlin/Pair;
|
|
||||||
protected final fun setCompatiblePackages (Ljava/util/Set;)V
|
|
||||||
protected final fun setDependencies (Ljava/util/Set;)V
|
|
||||||
protected final fun setExecutionBlock (Lkotlin/jvm/functions/Function1;)V
|
|
||||||
protected final fun setFinalizeBlock (Lkotlin/jvm/functions/Function1;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/patch/PatchContext : java/util/function/Supplier {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchException : java/lang/Exception {
|
|
||||||
public fun <init> (Ljava/lang/String;)V
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
|
|
||||||
public fun <init> (Ljava/lang/Throwable;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchKt {
|
|
||||||
public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
public static synthetic fun bytecodePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
|
|
||||||
public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
|
|
||||||
public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
|
|
||||||
public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Dex;
|
|
||||||
public static final fun loadPatchesFromJar (Ljava/util/Set;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
|
|
||||||
public static final fun loadPatchesFromJar (Ljava/util/Set;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
|
|
||||||
public static synthetic fun loadPatchesFromJar$default (Ljava/util/Set;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/PatchLoader$Jar;
|
|
||||||
public static final fun rawResourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/RawResourcePatch;
|
|
||||||
public static synthetic fun rawResourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/RawResourcePatch;
|
|
||||||
public static final fun resourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch;
|
|
||||||
public static synthetic fun resourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/PatchLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
|
|
||||||
public synthetic fun <init> (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public synthetic fun <init> (Ljava/util/Set;Lkotlin/jvm/functions/Function1;Ljava/lang/ClassLoader;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public fun add (Lapp/revanced/patcher/patch/Patch;)Z
|
|
||||||
public synthetic fun add (Ljava/lang/Object;)Z
|
|
||||||
public fun addAll (Ljava/util/Collection;)Z
|
|
||||||
public fun clear ()V
|
|
||||||
public fun contains (Lapp/revanced/patcher/patch/Patch;)Z
|
|
||||||
public final fun contains (Ljava/lang/Object;)Z
|
|
||||||
public fun containsAll (Ljava/util/Collection;)Z
|
|
||||||
public final fun getByPatchesFile ()Ljava/util/Map;
|
|
||||||
public fun getSize ()I
|
|
||||||
public fun isEmpty ()Z
|
|
||||||
public fun iterator ()Ljava/util/Iterator;
|
|
||||||
public fun remove (Ljava/lang/Object;)Z
|
|
||||||
public fun removeAll (Ljava/util/Collection;)Z
|
|
||||||
public fun retainAll (Ljava/util/Collection;)Z
|
|
||||||
public final fun size ()I
|
|
||||||
public fun toArray ()[Ljava/lang/Object;
|
|
||||||
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchLoader$Dex : app/revanced/patcher/patch/PatchLoader {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchLoader$Jar : app/revanced/patcher/patch/PatchLoader {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchResult {
|
|
||||||
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
|
||||||
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/RawResourcePatch : app/revanced/patcher/patch/Patch {
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/RawResourcePatchBuilder : app/revanced/patcher/patch/PatchBuilder {
|
|
||||||
public synthetic fun build$revanced_patcher ()Lapp/revanced/patcher/patch/Patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/ResourcePatchBuilder : app/revanced/patcher/patch/PatchBuilder {
|
|
||||||
public synthetic fun build$revanced_patcher ()Lapp/revanced/patcher/patch/Patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/ResourcePatchContext : app/revanced/patcher/patch/PatchContext {
|
|
||||||
public final fun delete (Ljava/lang/String;)Z
|
|
||||||
public final fun document (Ljava/io/InputStream;)Lapp/revanced/patcher/util/Document;
|
|
||||||
public final fun document (Ljava/lang/String;)Lapp/revanced/patcher/util/Document;
|
|
||||||
public fun get ()Lapp/revanced/patcher/PatcherResult$PatchedResources;
|
|
||||||
public synthetic fun get ()Ljava/lang/Object;
|
|
||||||
public final fun get (Ljava/lang/String;Z)Ljava/io/File;
|
|
||||||
public static synthetic fun get$default (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;ZILjava/lang/Object;)Ljava/io/File;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/Document : java/io/Closeable, org/w3c/dom/Document {
|
|
||||||
public fun adoptNode (Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
|
||||||
public fun appendChild (Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
|
||||||
public fun cloneNode (Z)Lorg/w3c/dom/Node;
|
|
||||||
public fun close ()V
|
|
||||||
public fun compareDocumentPosition (Lorg/w3c/dom/Node;)S
|
|
||||||
public fun createAttribute (Ljava/lang/String;)Lorg/w3c/dom/Attr;
|
|
||||||
public fun createAttributeNS (Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/Attr;
|
|
||||||
public fun createCDATASection (Ljava/lang/String;)Lorg/w3c/dom/CDATASection;
|
|
||||||
public fun createComment (Ljava/lang/String;)Lorg/w3c/dom/Comment;
|
|
||||||
public fun createDocumentFragment ()Lorg/w3c/dom/DocumentFragment;
|
|
||||||
public fun createElement (Ljava/lang/String;)Lorg/w3c/dom/Element;
|
|
||||||
public fun createElementNS (Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/Element;
|
|
||||||
public fun createEntityReference (Ljava/lang/String;)Lorg/w3c/dom/EntityReference;
|
|
||||||
public fun createProcessingInstruction (Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/ProcessingInstruction;
|
|
||||||
public fun createTextNode (Ljava/lang/String;)Lorg/w3c/dom/Text;
|
|
||||||
public fun getAttributes ()Lorg/w3c/dom/NamedNodeMap;
|
|
||||||
public fun getBaseURI ()Ljava/lang/String;
|
|
||||||
public fun getChildNodes ()Lorg/w3c/dom/NodeList;
|
|
||||||
public fun getDoctype ()Lorg/w3c/dom/DocumentType;
|
|
||||||
public fun getDocumentElement ()Lorg/w3c/dom/Element;
|
|
||||||
public fun getDocumentURI ()Ljava/lang/String;
|
|
||||||
public fun getDomConfig ()Lorg/w3c/dom/DOMConfiguration;
|
|
||||||
public fun getElementById (Ljava/lang/String;)Lorg/w3c/dom/Element;
|
|
||||||
public fun getElementsByTagName (Ljava/lang/String;)Lorg/w3c/dom/NodeList;
|
|
||||||
public fun getElementsByTagNameNS (Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/NodeList;
|
|
||||||
public fun getFeature (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
|
||||||
public fun getFirstChild ()Lorg/w3c/dom/Node;
|
|
||||||
public fun getImplementation ()Lorg/w3c/dom/DOMImplementation;
|
|
||||||
public fun getInputEncoding ()Ljava/lang/String;
|
|
||||||
public fun getLastChild ()Lorg/w3c/dom/Node;
|
|
||||||
public fun getLocalName ()Ljava/lang/String;
|
|
||||||
public fun getNamespaceURI ()Ljava/lang/String;
|
|
||||||
public fun getNextSibling ()Lorg/w3c/dom/Node;
|
|
||||||
public fun getNodeName ()Ljava/lang/String;
|
|
||||||
public fun getNodeType ()S
|
|
||||||
public fun getNodeValue ()Ljava/lang/String;
|
|
||||||
public fun getOwnerDocument ()Lorg/w3c/dom/Document;
|
|
||||||
public fun getParentNode ()Lorg/w3c/dom/Node;
|
|
||||||
public fun getPrefix ()Ljava/lang/String;
|
|
||||||
public fun getPreviousSibling ()Lorg/w3c/dom/Node;
|
|
||||||
public fun getStrictErrorChecking ()Z
|
|
||||||
public fun getTextContent ()Ljava/lang/String;
|
|
||||||
public fun getUserData (Ljava/lang/String;)Ljava/lang/Object;
|
|
||||||
public fun getXmlEncoding ()Ljava/lang/String;
|
|
||||||
public fun getXmlStandalone ()Z
|
|
||||||
public fun getXmlVersion ()Ljava/lang/String;
|
|
||||||
public fun hasAttributes ()Z
|
|
||||||
public fun hasChildNodes ()Z
|
|
||||||
public fun importNode (Lorg/w3c/dom/Node;Z)Lorg/w3c/dom/Node;
|
|
||||||
public fun insertBefore (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
|
||||||
public fun isDefaultNamespace (Ljava/lang/String;)Z
|
|
||||||
public fun isEqualNode (Lorg/w3c/dom/Node;)Z
|
|
||||||
public fun isSameNode (Lorg/w3c/dom/Node;)Z
|
|
||||||
public fun isSupported (Ljava/lang/String;Ljava/lang/String;)Z
|
|
||||||
public fun lookupNamespaceURI (Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
public fun lookupPrefix (Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
public fun normalize ()V
|
|
||||||
public fun normalizeDocument ()V
|
|
||||||
public fun removeChild (Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
|
||||||
public fun renameNode (Lorg/w3c/dom/Node;Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/Node;
|
|
||||||
public fun replaceChild (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
|
||||||
public fun setDocumentURI (Ljava/lang/String;)V
|
|
||||||
public fun setNodeValue (Ljava/lang/String;)V
|
|
||||||
public fun setPrefix (Ljava/lang/String;)V
|
|
||||||
public fun setStrictErrorChecking (Z)V
|
|
||||||
public fun setTextContent (Ljava/lang/String;)V
|
|
||||||
public fun setUserData (Ljava/lang/String;Ljava/lang/Object;Lorg/w3c/dom/UserDataHandler;)Ljava/lang/Object;
|
|
||||||
public fun setXmlStandalone (Z)V
|
|
||||||
public fun setXmlVersion (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/MethodNavigator {
|
|
||||||
public final fun getValue (Ljava/lang/Void;Lkotlin/reflect/KProperty;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun original ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun stop ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun to (ILkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/MethodNavigator;
|
|
||||||
public final fun to ([I)Lapp/revanced/patcher/util/MethodNavigator;
|
|
||||||
public static synthetic fun to$default (Lapp/revanced/patcher/util/MethodNavigator;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/util/MethodNavigator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/ProxyClassList : java/util/List, kotlin/jvm/internal/markers/KMutableList {
|
|
||||||
public fun add (ILcom/android/tools/smali/dexlib2/iface/ClassDef;)V
|
|
||||||
public synthetic fun add (ILjava/lang/Object;)V
|
|
||||||
public fun add (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public synthetic fun add (Ljava/lang/Object;)Z
|
|
||||||
public fun addAll (ILjava/util/Collection;)Z
|
|
||||||
public fun addAll (Ljava/util/Collection;)Z
|
|
||||||
public fun clear ()V
|
|
||||||
public fun contains (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun contains (Ljava/lang/Object;)Z
|
|
||||||
public fun containsAll (Ljava/util/Collection;)Z
|
|
||||||
public fun get (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public synthetic fun get (I)Ljava/lang/Object;
|
|
||||||
public fun getSize ()I
|
|
||||||
public fun indexOf (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)I
|
|
||||||
public final fun indexOf (Ljava/lang/Object;)I
|
|
||||||
public fun isEmpty ()Z
|
|
||||||
public fun iterator ()Ljava/util/Iterator;
|
|
||||||
public fun lastIndexOf (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)I
|
|
||||||
public final fun lastIndexOf (Ljava/lang/Object;)I
|
|
||||||
public fun listIterator ()Ljava/util/ListIterator;
|
|
||||||
public fun listIterator (I)Ljava/util/ListIterator;
|
|
||||||
public final fun remove (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public synthetic fun remove (I)Ljava/lang/Object;
|
|
||||||
public fun remove (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun remove (Ljava/lang/Object;)Z
|
|
||||||
public fun removeAll (Ljava/util/Collection;)Z
|
|
||||||
public fun removeAt (I)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public fun retainAll (Ljava/util/Collection;)Z
|
|
||||||
public fun set (ILcom/android/tools/smali/dexlib2/iface/ClassDef;)Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public synthetic fun set (ILjava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public final fun size ()I
|
|
||||||
public fun subList (II)Ljava/util/List;
|
|
||||||
public fun toArray ()[Ljava/lang/Object;
|
|
||||||
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/ClassProxy {
|
|
||||||
public final fun getImmutableClass ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun getMutableClass ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation : com/android/tools/smali/dexlib2/base/BaseAnnotation {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Annotation;)V
|
|
||||||
public fun getElements ()Ljava/util/Set;
|
|
||||||
public fun getType ()Ljava/lang/String;
|
|
||||||
public fun getVisibility ()I
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Annotation;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement : com/android/tools/smali/dexlib2/base/BaseAnnotationElement {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)V
|
|
||||||
public fun getName ()Ljava/lang/String;
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
|
||||||
public final fun setName (Ljava/lang/String;)V
|
|
||||||
public final fun setValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass : com/android/tools/smali/dexlib2/base/reference/BaseTypeReference, com/android/tools/smali/dexlib2/iface/ClassDef {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)V
|
|
||||||
public final fun charAt (I)C
|
|
||||||
public fun get (I)C
|
|
||||||
public fun getAccessFlags ()I
|
|
||||||
public fun getAnnotations ()Ljava/util/Set;
|
|
||||||
public synthetic fun getDirectMethods ()Ljava/lang/Iterable;
|
|
||||||
public fun getDirectMethods ()Ljava/util/Set;
|
|
||||||
public synthetic fun getFields ()Ljava/lang/Iterable;
|
|
||||||
public fun getFields ()Ljava/util/Set;
|
|
||||||
public synthetic fun getInstanceFields ()Ljava/lang/Iterable;
|
|
||||||
public fun getInstanceFields ()Ljava/util/Set;
|
|
||||||
public fun getInterfaces ()Ljava/util/List;
|
|
||||||
public fun getLength ()I
|
|
||||||
public synthetic fun getMethods ()Ljava/lang/Iterable;
|
|
||||||
public fun getMethods ()Ljava/util/Set;
|
|
||||||
public fun getSourceFile ()Ljava/lang/String;
|
|
||||||
public synthetic fun getStaticFields ()Ljava/lang/Iterable;
|
|
||||||
public fun getStaticFields ()Ljava/util/Set;
|
|
||||||
public fun getSuperclass ()Ljava/lang/String;
|
|
||||||
public fun getType ()Ljava/lang/String;
|
|
||||||
public synthetic fun getVirtualMethods ()Ljava/lang/Iterable;
|
|
||||||
public fun getVirtualMethods ()Ljava/util/Set;
|
|
||||||
public final fun length ()I
|
|
||||||
public final fun setAccessFlags (I)V
|
|
||||||
public final fun setSourceFile (Ljava/lang/String;)V
|
|
||||||
public final fun setSuperClass (Ljava/lang/String;)V
|
|
||||||
public final fun setType (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField : com/android/tools/smali/dexlib2/base/reference/BaseFieldReference, com/android/tools/smali/dexlib2/iface/Field {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Field;)V
|
|
||||||
public fun getAccessFlags ()I
|
|
||||||
public fun getAnnotations ()Ljava/util/Set;
|
|
||||||
public fun getDefiningClass ()Ljava/lang/String;
|
|
||||||
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
|
||||||
public fun getInitialValue ()Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
|
||||||
public synthetic fun getInitialValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
|
||||||
public fun getName ()Ljava/lang/String;
|
|
||||||
public fun getType ()Ljava/lang/String;
|
|
||||||
public final fun setAccessFlags (I)V
|
|
||||||
public final fun setDefiningClass (Ljava/lang/String;)V
|
|
||||||
public final fun setInitialValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
|
||||||
public final fun setName (Ljava/lang/String;)V
|
|
||||||
public final fun setType (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Field;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod : com/android/tools/smali/dexlib2/base/reference/BaseMethodReference, com/android/tools/smali/dexlib2/iface/Method {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Method;)V
|
|
||||||
public fun getAccessFlags ()I
|
|
||||||
public fun getAnnotations ()Ljava/util/Set;
|
|
||||||
public fun getDefiningClass ()Ljava/lang/String;
|
|
||||||
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
|
||||||
public fun getImplementation ()Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;
|
|
||||||
public synthetic fun getImplementation ()Lcom/android/tools/smali/dexlib2/iface/MethodImplementation;
|
|
||||||
public fun getName ()Ljava/lang/String;
|
|
||||||
public fun getParameterTypes ()Ljava/util/List;
|
|
||||||
public fun getParameters ()Ljava/util/List;
|
|
||||||
public fun getReturnType ()Ljava/lang/String;
|
|
||||||
public final fun setAccessFlags (I)V
|
|
||||||
public final fun setDefiningClass (Ljava/lang/String;)V
|
|
||||||
public final fun setName (Ljava/lang/String;)V
|
|
||||||
public final fun setReturnType (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter : com/android/tools/smali/dexlib2/base/BaseMethodParameter, com/android/tools/smali/dexlib2/iface/MethodParameter {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)V
|
|
||||||
public final fun charAt (I)C
|
|
||||||
public fun get (I)C
|
|
||||||
public fun getAnnotations ()Ljava/util/Set;
|
|
||||||
public fun getLength ()I
|
|
||||||
public fun getName ()Ljava/lang/String;
|
|
||||||
public fun getSignature ()Ljava/lang/String;
|
|
||||||
public fun getType ()Ljava/lang/String;
|
|
||||||
public final fun length ()I
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseAnnotationEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)V
|
|
||||||
public fun getElements ()Ljava/util/Set;
|
|
||||||
public fun getType ()Ljava/lang/String;
|
|
||||||
public final fun setType (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseArrayEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)V
|
|
||||||
public fun getValue ()Ljava/util/List;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseBooleanEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)V
|
|
||||||
public fun getValue ()Z
|
|
||||||
public final fun setValue (Z)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseByteEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)V
|
|
||||||
public fun getValue ()B
|
|
||||||
public final fun setValue (B)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseCharEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)V
|
|
||||||
public fun getValue ()C
|
|
||||||
public final fun setValue (C)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseDoubleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)V
|
|
||||||
public fun getValue ()D
|
|
||||||
public final fun setValue (D)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue : com/android/tools/smali/dexlib2/iface/value/EncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseEnumEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)V
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
|
||||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFieldEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)V
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
|
||||||
public fun getValueType ()I
|
|
||||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFloatEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)V
|
|
||||||
public fun getValue ()F
|
|
||||||
public final fun setValue (F)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseIntEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)V
|
|
||||||
public fun getValue ()I
|
|
||||||
public final fun setValue (I)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseLongEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)V
|
|
||||||
public fun getValue ()J
|
|
||||||
public final fun setValue (J)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)V
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;
|
|
||||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodHandleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)V
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;
|
|
||||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)V
|
|
||||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;
|
|
||||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseNullEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion;
|
|
||||||
public fun <init> ()V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseShortEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)V
|
|
||||||
public fun getValue ()S
|
|
||||||
public final fun setValue (S)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseStringEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/StringEncodedValue;)V
|
|
||||||
public fun getValue ()Ljava/lang/String;
|
|
||||||
public final fun setValue (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion;
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)V
|
|
||||||
public fun getValue ()Ljava/lang/String;
|
|
||||||
public final fun setValue (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion {
|
|
||||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/smali/ExternalLabel {
|
|
||||||
public fun <init> (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)V
|
|
||||||
public final fun copy (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/util/smali/ExternalLabel;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;ILjava/lang/Object;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/util/smali/InlineSmaliCompiler$Companion;
|
|
||||||
public fun <init> ()V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler$Companion {
|
|
||||||
public final fun compile (Ljava/lang/String;Ljava/lang/String;IZ)Ljava/util/List;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompilerKt {
|
|
||||||
public static final fun toInstruction (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
|
||||||
public static synthetic fun toInstruction$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
|
||||||
public static final fun toInstructions (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
|
||||||
public static synthetic fun toInstructions$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Ljava/util/List;
|
|
||||||
}
|
|
||||||
|
|
||||||
42
arsclib-utils/.gitignore
vendored
Normal file
42
arsclib-utils/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
18
arsclib-utils/build.gradle.kts
Normal file
18
arsclib-utils/build.gradle.kts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "app.revanced"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("io.github.reandroid:ARSCLib:1.1.7")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package app.revanced.arsc
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when there is an error with APK resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
sealed class ApkResourceException(message: String, throwable: Throwable? = null) : Exception(message, throwable) {
|
||||||
|
/**
|
||||||
|
* An exception when locking resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class Locked(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception when writing resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class Write(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception when reading resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class Read(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
||||||
|
/**
|
||||||
|
* An exception when decoding resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class Decode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception when encoding resources.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class Encode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a reference could not be resolved.
|
||||||
|
*
|
||||||
|
* @param reference The invalid reference.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class InvalidReference(reference: String, throwable: Throwable? = null) :
|
||||||
|
ApkResourceException("Failed to resolve: $reference", throwable) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a reference could not be resolved.
|
||||||
|
*
|
||||||
|
* @param type The type of the reference.
|
||||||
|
* @param name The name of the reference.
|
||||||
|
* @param throwable The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
constructor(type: String, name: String, throwable: Throwable? = null) : this("@$type/$name", throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when the Apk file not have a resource table, but was expected to have one.
|
||||||
|
*/
|
||||||
|
class MissingResourceTable : ApkResourceException("Apk does not have a resource table.")
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package app.revanced.arsc.archive
|
||||||
|
|
||||||
|
import app.revanced.arsc.resource.ResourceContainer
|
||||||
|
import com.reandroid.apk.ApkModule
|
||||||
|
import com.reandroid.apk.DexFileInputSource
|
||||||
|
import com.reandroid.archive.InputSource
|
||||||
|
import java.io.File
|
||||||
|
import java.io.Flushable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for reading/writing files in an [ApkModule].
|
||||||
|
*
|
||||||
|
* @param module The [ApkModule] to operate on.
|
||||||
|
*/
|
||||||
|
class Archive(internal val module: ApkModule) : Flushable {
|
||||||
|
val mainPackageResources = ResourceContainer(this, module.tableBlock)
|
||||||
|
|
||||||
|
fun save(output: File) {
|
||||||
|
flush()
|
||||||
|
module.writeApk(output)
|
||||||
|
}
|
||||||
|
fun readDexFiles(): MutableList<DexFileInputSource> = module.listDexFiles()
|
||||||
|
fun write(inputSource: InputSource) = module.apkArchive.add(inputSource) // Overwrites existing files.
|
||||||
|
fun read(name: String): InputSource? = module.apkArchive.getInputSource(name)
|
||||||
|
override fun flush() = mainPackageResources.flush()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.arsc.logging
|
||||||
|
interface Logger {
|
||||||
|
fun error(msg: String)
|
||||||
|
fun warn(msg: String)
|
||||||
|
fun info(msg: String)
|
||||||
|
fun trace(msg: String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package app.revanced.arsc.resource
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import com.reandroid.arsc.coder.EncodeResult
|
||||||
|
import com.reandroid.arsc.coder.ValueDecoder
|
||||||
|
import com.reandroid.arsc.value.Entry
|
||||||
|
import com.reandroid.arsc.value.ValueType
|
||||||
|
import com.reandroid.arsc.value.array.ArrayBag
|
||||||
|
import com.reandroid.arsc.value.array.ArrayBagItem
|
||||||
|
import com.reandroid.arsc.value.plurals.PluralsBag
|
||||||
|
import com.reandroid.arsc.value.plurals.PluralsBagItem
|
||||||
|
import com.reandroid.arsc.value.plurals.PluralsQuantity
|
||||||
|
import com.reandroid.arsc.value.style.StyleBag
|
||||||
|
import com.reandroid.arsc.value.style.StyleBagItem
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource value.
|
||||||
|
*/
|
||||||
|
sealed class Resource {
|
||||||
|
internal abstract fun write(entry: Entry, resources: ResourceContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val Resource.isComplex get() = when (this) {
|
||||||
|
is Scalar -> false
|
||||||
|
is Complex -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple resource.
|
||||||
|
*/
|
||||||
|
open class Scalar internal constructor(private val valueType: ValueType, private val value: Int) : Resource() {
|
||||||
|
protected open fun data(resources: ResourceContainer) = value
|
||||||
|
|
||||||
|
override fun write(entry: Entry, resources: ResourceContainer) {
|
||||||
|
entry.setValueAsRaw(valueType, data(resources))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.create(valueType, data(resources))
|
||||||
|
internal open fun toStyleItem(resources: ResourceContainer) = StyleBagItem.create(valueType, data(resources))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker class for complex resources.
|
||||||
|
*/
|
||||||
|
sealed class Complex : Resource()
|
||||||
|
|
||||||
|
private fun encoded(encodeResult: EncodeResult?) = encodeResult?.let { Scalar(it.valueType, it.value) }
|
||||||
|
?: throw ApkResourceException.Encode("Failed to encode value")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a color.
|
||||||
|
*
|
||||||
|
* @param hex The hex value of the color.
|
||||||
|
* @return The encoded [Resource].
|
||||||
|
*/
|
||||||
|
fun color(hex: String) = encoded(ValueDecoder.encodeColor(hex))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a dimension or fraction.
|
||||||
|
*
|
||||||
|
* @param value The dimension value such as 24dp.
|
||||||
|
* @return The encoded [Resource].
|
||||||
|
*/
|
||||||
|
fun dimension(value: String) = encoded(ValueDecoder.encodeDimensionOrFraction(value))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a boolean resource.
|
||||||
|
*
|
||||||
|
* @param value The boolean.
|
||||||
|
* @return The encoded [Resource].
|
||||||
|
*/
|
||||||
|
fun boolean(value: Boolean) = Scalar(ValueType.INT_BOOLEAN, if (value) -Int.MAX_VALUE else 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a float.
|
||||||
|
*
|
||||||
|
* @param n The number to encode.
|
||||||
|
* @return The encoded [Resource].
|
||||||
|
*/
|
||||||
|
fun float(n: Float) = Scalar(ValueType.FLOAT, n.toBits())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an integer [Resource].
|
||||||
|
*
|
||||||
|
* @param n The number to encode.
|
||||||
|
* @return The integer [Resource].
|
||||||
|
*/
|
||||||
|
fun integer(n: Int) = Scalar(ValueType.INT_DEC, n)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference [Resource].
|
||||||
|
*
|
||||||
|
* @param resourceId The target resource.
|
||||||
|
* @return The reference resource.
|
||||||
|
*/
|
||||||
|
fun reference(resourceId: Int) = Scalar(ValueType.REFERENCE, resourceId)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve and create a reference [Resource].
|
||||||
|
*
|
||||||
|
* @see reference
|
||||||
|
* @param ref The reference string to resolve.
|
||||||
|
* @param resourceTable The resource table to resolve the reference with.
|
||||||
|
* @return The reference resource.
|
||||||
|
*/
|
||||||
|
fun reference(resourceTable: ResourceTable, ref: String) = reference(resourceTable.resolve(ref))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array [Resource].
|
||||||
|
*
|
||||||
|
* @param elements The elements of the array.
|
||||||
|
*/
|
||||||
|
class Array(private val elements: Collection<Scalar>) : Complex() {
|
||||||
|
override fun write(entry: Entry, resources: ResourceContainer) {
|
||||||
|
ArrayBag.create(entry).addAll(elements.map { it.toArrayItem(resources) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A style resource.
|
||||||
|
*
|
||||||
|
* @param elements The attributes to override.
|
||||||
|
* @param parent A reference to the parent style.
|
||||||
|
*/
|
||||||
|
class Style(private val elements: Map<String, Scalar>, private val parent: String? = null) : Complex() {
|
||||||
|
override fun write(entry: Entry, resources: ResourceContainer) {
|
||||||
|
val resTable = resources.resourceTable
|
||||||
|
val style = StyleBag.create(entry)
|
||||||
|
parent?.let {
|
||||||
|
style.parentId = resTable.resolve(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
style.putAll(
|
||||||
|
elements.asIterable().associate {
|
||||||
|
StyleBag.resolve(resTable.encodeMaterials, it.key) to it.value.toStyleItem(resources)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A quantity string [Resource].
|
||||||
|
*
|
||||||
|
* @param elements A map of the quantity to the corresponding string.
|
||||||
|
*/
|
||||||
|
class Plurals(private val elements: Map<String, String>) : Complex() {
|
||||||
|
override fun write(entry: Entry, resources: ResourceContainer) {
|
||||||
|
val plurals = PluralsBag.create(entry)
|
||||||
|
|
||||||
|
plurals.putAll(elements.asIterable().associate { (k, v) ->
|
||||||
|
PluralsQuantity.value(k) to PluralsBagItem.string(resources.getOrCreateString(v))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string [Resource].
|
||||||
|
*
|
||||||
|
* @param value The string value.
|
||||||
|
*/
|
||||||
|
class StringResource(val value: String) : Scalar(ValueType.STRING, 0) {
|
||||||
|
private fun tableString(resources: ResourceContainer) = resources.getOrCreateString(value)
|
||||||
|
|
||||||
|
override fun data(resources: ResourceContainer) = tableString(resources).index
|
||||||
|
override fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.string(tableString(resources))
|
||||||
|
override fun toStyleItem(resources: ResourceContainer) = StyleBagItem.string(tableString(resources))
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package app.revanced.arsc.resource
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import app.revanced.arsc.archive.Archive
|
||||||
|
import com.reandroid.apk.xmlencoder.EncodeUtil
|
||||||
|
import com.reandroid.arsc.chunk.TableBlock
|
||||||
|
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
||||||
|
import com.reandroid.arsc.value.Entry
|
||||||
|
import com.reandroid.arsc.value.ResConfig
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.io.Flushable
|
||||||
|
|
||||||
|
class ResourceContainer(private val archive: Archive, internal val tableBlock: TableBlock) : Flushable {
|
||||||
|
private val packageBlock = tableBlock.pickOne() // Pick the main package block.
|
||||||
|
internal lateinit var resourceTable: ResourceTable // TODO: Set this.
|
||||||
|
|
||||||
|
private val lockedResourceFileNames = mutableSetOf<String>()
|
||||||
|
|
||||||
|
private fun lock(resourceFile: ResourceFile) {
|
||||||
|
if (resourceFile.name in lockedResourceFileNames) {
|
||||||
|
throw ApkResourceException.Locked("Resource file ${resourceFile.name} is already locked.")
|
||||||
|
}
|
||||||
|
|
||||||
|
lockedResourceFileNames.add(resourceFile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unlock(resourceFile: ResourceFile) {
|
||||||
|
lockedResourceFileNames.remove(resourceFile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T : ResourceFile> openResource(name: String): ResourceFileEditor<T> {
|
||||||
|
val inputSource = archive.read(name)
|
||||||
|
?: throw ApkResourceException.Read("Resource file $name not found.")
|
||||||
|
|
||||||
|
val resourceFile = when {
|
||||||
|
ResXmlDocument.isResXmlBlock(inputSource.openStream()) -> {
|
||||||
|
val xmlDocument = archive.module
|
||||||
|
.loadResXmlDocument(inputSource)
|
||||||
|
.decodeToXml(resourceTable.entryStore, packageBlock.id)
|
||||||
|
|
||||||
|
ResourceFile.XmlResourceFile(name, xmlDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val bytes = inputSource.openStream().use { it.readAllBytes() }
|
||||||
|
|
||||||
|
ResourceFile.BinaryResourceFile(name, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return ResourceFileEditor(resourceFile as T).also {
|
||||||
|
lockedResourceFileNames.add(name)
|
||||||
|
}
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
throw ApkResourceException.Decode("Resource file $name is not ${resourceFile::class}.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ResourceFileEditor<T : ResourceFile> internal constructor(
|
||||||
|
private val resourceFile: T,
|
||||||
|
) : Closeable {
|
||||||
|
fun use(block: (T) -> Unit) = block(resourceFile)
|
||||||
|
override fun close() {
|
||||||
|
lockedResourceFileNames.remove(resourceFile.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun flush() {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a resource file, creating it if the file does not exist.
|
||||||
|
*
|
||||||
|
* @param path The resource file path.
|
||||||
|
* @return The corresponding [ResourceFiles],
|
||||||
|
*/
|
||||||
|
fun openFile(path: String) = ResourceFiles(createHandle(path), archive)
|
||||||
|
|
||||||
|
private fun getPackageBlock() = packageBlock ?: throw ApkResourceException.MissingResourceTable
|
||||||
|
|
||||||
|
internal fun getOrCreateString(value: String) =
|
||||||
|
tableBlock?.stringPool?.getOrCreate(value) ?: throw ApkResourceException.MissingResourceTable
|
||||||
|
|
||||||
|
private fun Entry.set(resource: Resource) {
|
||||||
|
val existingEntryNameReference = specReference
|
||||||
|
|
||||||
|
// Sets this.specReference if the entry is not yet initialized.
|
||||||
|
// Sets this.specReference to 0 if the resource type of the existing entry changes.
|
||||||
|
ensureComplex(resource.isComplex)
|
||||||
|
|
||||||
|
if (existingEntryNameReference != 0) {
|
||||||
|
// Preserve the entry name by restoring the previous spec block reference (if present).
|
||||||
|
specReference = existingEntryNameReference
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.write(this, this@ResourceContainer)
|
||||||
|
resourceTable.registerChanged(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an [Entry] from the resource table.
|
||||||
|
*
|
||||||
|
* @param type The resource type.
|
||||||
|
* @param name The resource name.
|
||||||
|
* @param qualifiers The variant to use.
|
||||||
|
*/
|
||||||
|
private fun getEntry(type: String, name: String, qualifiers: String?): Entry? {
|
||||||
|
val resourceId = try {
|
||||||
|
resourceTable.resolve("@$type/$name")
|
||||||
|
} catch (_: ApkResourceException.InvalidReference) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = ResConfig.parse(qualifiers)
|
||||||
|
return tableBlock?.resolveReference(resourceId)?.singleOrNull { it.resConfig == config }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a [ResourceFiles.Handle] that can be used to open a [ResourceFiles].
|
||||||
|
* This may involve looking it up in the resource table to find the actual location in the archive.
|
||||||
|
*
|
||||||
|
* @param path The path of the resource.
|
||||||
|
*/
|
||||||
|
private fun createHandle(path: String): ResourceFiles.Handle {
|
||||||
|
if (path.startsWith("res/values")) throw ApkResourceException.Decode("Decoding the resource table as a file is not supported")
|
||||||
|
|
||||||
|
var onClose = {}
|
||||||
|
var archivePath = path
|
||||||
|
|
||||||
|
if (tableBlock != null && path.startsWith("res/") && path.count { it == '/' } == 2) {
|
||||||
|
val file = File(path)
|
||||||
|
|
||||||
|
val qualifiers = EncodeUtil.getQualifiersFromResFile(file)
|
||||||
|
val type = EncodeUtil.getTypeNameFromResFile(file)
|
||||||
|
val name = file.nameWithoutExtension
|
||||||
|
|
||||||
|
// The resource file names that the app developers used may have been minified, so we have to resolve it with the resource table.
|
||||||
|
// Example: res/drawable-hdpi/icon.png -> res/4a.png
|
||||||
|
getEntry(type, name, qualifiers)?.resValue?.valueAsString?.let {
|
||||||
|
archivePath = it
|
||||||
|
} ?: run {
|
||||||
|
// An entry for this specific resource file was not found in the resource table, so we have to register it after we save.
|
||||||
|
onClose = { setResource(type, name, StringResource(archivePath), qualifiers) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResourceFiles.Handle(path, archivePath, onClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setResource(type: String, entryName: String, resource: Resource, qualifiers: String? = null) =
|
||||||
|
getPackageBlock().getOrCreate(qualifiers, type, entryName).also { it.set(resource) }.resourceId
|
||||||
|
|
||||||
|
fun setResources(type: String, resources: Map<String, Resource>, configuration: String? = null) {
|
||||||
|
getPackageBlock().getOrCreateSpecTypePair(type).getOrCreateTypeBlock(configuration).apply {
|
||||||
|
resources.forEach { (entryName, resource) -> getOrCreateEntry(entryName).set(resource) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun flush() {
|
||||||
|
packageBlock?.name = archive
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package app.revanced.arsc.resource
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import app.revanced.arsc.archive.Archive
|
||||||
|
import com.reandroid.archive.InputSource
|
||||||
|
import com.reandroid.xml.XMLDocument
|
||||||
|
import com.reandroid.xml.XMLException
|
||||||
|
import java.io.*
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ResourceFile(val name: String) {
|
||||||
|
internal var realName: String? = null
|
||||||
|
|
||||||
|
class XmlResourceFile(name: String, val document: XMLDocument) : ResourceFile(name)
|
||||||
|
class BinaryResourceFile(name: String, var bytes: ByteArray) : ResourceFile(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceFiles private constructor(
|
||||||
|
) : Closeable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a [ResourceFiles].
|
||||||
|
*
|
||||||
|
* @param handle The [Handle] associated with this file.
|
||||||
|
* @param archive The [Archive] that the file resides in.
|
||||||
|
*/
|
||||||
|
internal constructor(handle: Handle, archive: Archive) : this(
|
||||||
|
handle,
|
||||||
|
archive,
|
||||||
|
try {
|
||||||
|
archive.read(handle.archivePath)
|
||||||
|
} catch (e: XMLException) {
|
||||||
|
throw ApkResourceException.Decode("Failed to decode XML while reading ${handle.virtualPath}", e)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw ApkResourceException.Decode("Could not read ${handle.virtualPath}", e)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DEFAULT_BUFFER_SIZE = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
var contents = readResult?.data ?: ByteArray(0)
|
||||||
|
set(value) {
|
||||||
|
pendingWrite = true
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
val exists = readResult != null
|
||||||
|
|
||||||
|
override fun toString() = handle.virtualPath
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (pendingWrite) {
|
||||||
|
val path = handle.archivePath
|
||||||
|
|
||||||
|
if (isXmlResource) archive.writeXml(
|
||||||
|
path,
|
||||||
|
try {
|
||||||
|
XMLDocument.load(inputStream())
|
||||||
|
} catch (e: XMLException) {
|
||||||
|
throw ApkResourceException.Encode("Failed to parse XML while writing ${handle.virtualPath}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
) else archive.writeRaw(path, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.onClose()
|
||||||
|
|
||||||
|
|
||||||
|
archive.unlock(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inputStream(): InputStream = ByteArrayInputStream(contents)
|
||||||
|
|
||||||
|
fun outputStream(bufferSize: Int = DEFAULT_BUFFER_SIZE): OutputStream =
|
||||||
|
object : ByteArrayOutputStream(bufferSize) {
|
||||||
|
override fun close() {
|
||||||
|
this@ResourceFiles.contents = if (buf.size > count) buf.copyOf(count) else buf
|
||||||
|
super.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param virtualPath The resource file path. Example: /res/drawable-hdpi/icon.png.
|
||||||
|
* @param archivePath The actual file path in the archive. Example: res/4a.png.
|
||||||
|
* @param onClose An action to perform when the file associated with this handle is closed
|
||||||
|
*/
|
||||||
|
internal data class Handle(val virtualPath: String, val archivePath: String, val onClose: () -> Unit)
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package app.revanced.arsc.resource
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import com.reandroid.apk.xmlencoder.EncodeException
|
||||||
|
import com.reandroid.apk.xmlencoder.EncodeMaterials
|
||||||
|
import com.reandroid.arsc.util.FrameworkTable
|
||||||
|
import com.reandroid.arsc.value.Entry
|
||||||
|
import com.reandroid.common.TableEntryStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A high-level API for resolving resources in the resource table, which spans the entire ApkBundle.
|
||||||
|
*/
|
||||||
|
class ResourceTable(base: ResourceContainer, all: Sequence<ResourceContainer>) {
|
||||||
|
private val packageName = base.tableBlock!!.name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [TableEntryStore] used to decode XML.
|
||||||
|
*/
|
||||||
|
internal val entryStore = TableEntryStore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [EncodeMaterials] to use for resolving resources and encoding XML.
|
||||||
|
*/
|
||||||
|
internal val encodeMaterials: EncodeMaterials = object : EncodeMaterials() {
|
||||||
|
/*
|
||||||
|
Our implementation is more efficient because it does not have to loop through every single entry group
|
||||||
|
when the resource id cannot be found in the TableIdentifier, which does not update when you create a new resource.
|
||||||
|
It also looks at the entire table instead of just the current package.
|
||||||
|
*/
|
||||||
|
override fun resolveLocalResourceId(type: String, name: String) = resolveLocal(type, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource mappings which are generated when the ApkBundle is created.
|
||||||
|
*/
|
||||||
|
private val tableIdentifier = encodeMaterials.tableIdentifier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A table of all the resources that have been changed or added.
|
||||||
|
*/
|
||||||
|
private val modifiedResources = HashMap<String, HashMap<String, Int>>()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a resource id for the specified resource.
|
||||||
|
* Cannot resolve resources from the android framework.
|
||||||
|
*
|
||||||
|
* @param type The type of the resource.
|
||||||
|
* @param name The name of the resource.
|
||||||
|
* @return The id of the resource.
|
||||||
|
*/
|
||||||
|
fun resolveLocal(type: String, name: String) =
|
||||||
|
modifiedResources[type]?.get(name)
|
||||||
|
?: tableIdentifier.get(packageName, type, name)?.resourceId
|
||||||
|
?: throw ApkResourceException.InvalidReference(
|
||||||
|
type,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a resource id for the specified resource.
|
||||||
|
*
|
||||||
|
* @param reference The resource reference string.
|
||||||
|
* @return The id of the resource.
|
||||||
|
*/
|
||||||
|
fun resolve(reference: String) = try {
|
||||||
|
encodeMaterials.resolveReference(reference)
|
||||||
|
} catch (e: EncodeException) {
|
||||||
|
throw ApkResourceException.InvalidReference(reference, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the [ResourceTable] that an [Entry] has been created or modified.
|
||||||
|
*/
|
||||||
|
internal fun registerChanged(entry: Entry) {
|
||||||
|
modifiedResources.getOrPut(entry.typeName, ::HashMap)[entry.name] = entry.resourceId
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
all.forEach {
|
||||||
|
it.tableBlock?.let { table ->
|
||||||
|
entryStore.add(table)
|
||||||
|
tableIdentifier.load(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.resourceTable = this
|
||||||
|
}
|
||||||
|
|
||||||
|
base.also {
|
||||||
|
encodeMaterials.currentPackage = it.tableBlock
|
||||||
|
|
||||||
|
it.tableBlock!!.frameWorks.forEach { fw ->
|
||||||
|
if (fw is FrameworkTable) {
|
||||||
|
entryStore.add(fw)
|
||||||
|
encodeMaterials.addFramework(fw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.arsc.xml
|
||||||
|
|
||||||
|
import app.revanced.arsc.resource.ResourceContainer
|
||||||
|
import app.revanced.arsc.resource.boolean
|
||||||
|
import com.reandroid.apk.xmlencoder.EncodeException
|
||||||
|
import com.reandroid.apk.xmlencoder.XMLEncodeSource
|
||||||
|
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
||||||
|
import com.reandroid.xml.XMLDocument
|
||||||
|
import com.reandroid.xml.XMLElement
|
||||||
|
import com.reandroid.xml.source.XMLDocumentSource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Archive input source to lazily encode an [XMLDocument] after it has been modified.
|
||||||
|
*
|
||||||
|
* @param name The file name of this input source.
|
||||||
|
* @param document The [XMLDocument] to encode.
|
||||||
|
* @param resources The [ResourceContainer] to use for encoding.
|
||||||
|
*/
|
||||||
|
internal class LazyXMLEncodeSource(
|
||||||
|
name: String,
|
||||||
|
val document: XMLDocument,
|
||||||
|
private val resources: ResourceContainer
|
||||||
|
) : XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document)) {
|
||||||
|
private var encoded = false
|
||||||
|
|
||||||
|
override fun getResXmlBlock(): ResXmlDocument {
|
||||||
|
if (encoded) return super.getResXmlBlock()
|
||||||
|
|
||||||
|
XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document))
|
||||||
|
|
||||||
|
fun XMLElement.registerIds() {
|
||||||
|
listAttributes().forEach { attr ->
|
||||||
|
if (!attr.value.startsWith("@+id/")) return@forEach
|
||||||
|
|
||||||
|
val name = attr.value.split('/').last()
|
||||||
|
resources.setResource("id", name, boolean(false))
|
||||||
|
attr.value = "@id/$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
listChildElements().forEach { it.registerIds() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle all @+id/id_name references in the document.
|
||||||
|
document.documentElement.registerIds()
|
||||||
|
|
||||||
|
encoded = true
|
||||||
|
|
||||||
|
// This will call XMLEncodeSource.getResXmlBlock(),
|
||||||
|
// which will encode the document if it has not already been encoded.
|
||||||
|
try {
|
||||||
|
return super.getResXmlBlock()
|
||||||
|
} catch (e: EncodeException) {
|
||||||
|
throw EncodeException("Failed to encode $name", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 11 KiB |
@@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 800 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g id="Logo"><g id="Ring"><circle id="Ring-Background" serif:id="Ring Background" cx="400" cy="400" r="400" style="fill:#1b1b1b;"/><path id="Ring1" serif:id="Ring" d="M400,0c220.766,0 400,179.234 400,400c-0,220.766 -179.234,400 -400,400c-220.766,-0 -400,-179.234 -400,-400c0,-220.766 179.234,-400 400,-400Zm-0,36c200.897,-0 364,163.103 364,364c0,200.897 -163.103,364 -364,364c-200.897,0 -364,-163.103 -364,-364c-0,-200.897 163.103,-364 364,-364Z" style="fill:url(#_Linear1);"/></g><g id="Shape"><path id="V-Shape" serif:id="V Shape" d="M538.74,269.872c1.481,-3.382 1.157,-7.283 -0.863,-10.373c-2.021,-3.091 -5.464,-4.954 -9.156,-4.954c-5.148,0 -10.435,0 -14.165,0c-3.1,0 -5.907,1.834 -7.153,4.672c-12.468,28.396 -78.273,178.273 -100.25,228.328c-1.246,2.838 -4.053,4.671 -7.154,4.671c-3.1,0 -5.907,-1.833 -7.153,-4.671c-21.977,-50.055 -87.782,-199.932 -100.25,-228.328c-1.246,-2.838 -4.053,-4.672 -7.153,-4.672c-3.73,0 -9.017,0 -14.164,0c-3.693,0 -7.135,1.863 -9.156,4.954c-2.02,3.09 -2.344,6.991 -0.863,10.373c23.557,53.766 101.872,232.519 117.871,269.034c1.743,3.979 5.674,6.549 10.018,6.549c6.293,-0 15.408,-0 21.701,-0c4.344,-0 8.275,-2.57 10.018,-6.549c15.999,-36.515 94.315,-215.268 117.872,-269.034Z" style="fill:#fff;"/><path id="Diamond" d="M408.119,395.312c-1.675,2.901 -4.77,4.688 -8.119,4.688c-3.349,-0 -6.444,-1.787 -8.119,-4.688c-16.997,-29.44 -56.156,-97.264 -73.153,-126.704c-1.675,-2.901 -1.675,-6.474 0,-9.375c1.675,-2.901 4.77,-4.688 8.119,-4.688c33.995,0 112.311,0 146.306,0c3.349,0 6.444,1.787 8.119,4.688c1.675,2.901 1.675,6.474 -0,9.375c-16.997,29.44 -56.156,97.264 -73.153,126.704Z" style="fill:url(#_Linear2);"/></g></g><defs><linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(4.89859e-14,800,-800,4.89859e-14,400.001,3.31681e-10)"><stop offset="0" style="stop-color:#f04e98;stop-opacity:1"/><stop offset="0.5" style="stop-color:#5f65d4;stop-opacity:1"/><stop offset="1" style="stop-color:#4e98f0;stop-opacity:1"/></linearGradient><linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.77155e-14,289.317,-282.535,1.73003e-14,400,254.545)"><stop offset="0" style="stop-color:#f04e98;stop-opacity:1"/><stop offset="0.5" style="stop-color:#5f65d4;stop-opacity:1"/><stop offset="1" style="stop-color:#4e98f0;stop-opacity:1"/></linearGradient></defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.8 KiB |
118
build.gradle.kts
118
build.gradle.kts
@@ -1,119 +1,3 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin)
|
kotlin("jvm") version "1.8.20" apply false
|
||||||
alias(libs.plugins.binary.compatibility.validator)
|
|
||||||
`maven-publish`
|
|
||||||
signing
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
processResources {
|
|
||||||
expand("projectVersion" to project.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
google()
|
|
||||||
maven {
|
|
||||||
// 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")
|
|
||||||
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// 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 {
|
|
||||||
compilerOptions {
|
|
||||||
jvmTarget.set(JvmTarget.JVM_11)
|
|
||||||
|
|
||||||
freeCompilerArgs = listOf("-Xcontext-receivers")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
name = "GitHubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
|
||||||
credentials {
|
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publications {
|
|
||||||
create<MavenPublication>("revanced-patcher-publication") {
|
|
||||||
from(components["java"])
|
|
||||||
|
|
||||||
version = project.version.toString()
|
|
||||||
|
|
||||||
pom {
|
|
||||||
name = "ReVanced Patcher"
|
|
||||||
description = "Patcher used by ReVanced."
|
|
||||||
url = "https://revanced.app"
|
|
||||||
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name = "GNU General Public License v3.0"
|
|
||||||
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
developers {
|
|
||||||
developer {
|
|
||||||
id = "ReVanced"
|
|
||||||
name = "ReVanced"
|
|
||||||
email = "contact@revanced.app"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scm {
|
|
||||||
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
|
||||||
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
|
||||||
url = "https://github.com/revanced/revanced-patcher"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signing {
|
|
||||||
useGpgCmd()
|
|
||||||
sign(publishing.publications["revanced-patcher-publication"])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 💉 Introduction to 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 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 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
|
|
||||||
val patches = loadPatchesFromJar(setOf(File("revanced-patches.rvp")))
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
// 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" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
val dexFiles = patcherResult.dexFiles
|
|
||||||
val resources = patcherResult.resources
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page teaches the fundamentals of ReVanced Patches.
|
|
||||||
|
|
||||||
Continue: [🧩 Introduction to ReVanced Patches](2_patches_intro.md)
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 👶 Setting up a development environment
|
|
||||||
|
|
||||||
To start developing patches with ReVanced Patcher, you must prepare a development environment.
|
|
||||||
|
|
||||||
## 📝 Prerequisites
|
|
||||||
|
|
||||||
- A Java IDE with Kotlin support, such as [IntelliJ IDEA](https://www.jetbrains.com/idea/)
|
|
||||||
- Knowledge of Java, [Kotlin](https://kotlinlang.org), and [Dalvik bytecode](https://source.android.com/docs/core/runtime/dalvik-bytecode)
|
|
||||||
- Android reverse engineering skills and tools such as [jadx](https://github.com/skylot/jadx)
|
|
||||||
|
|
||||||
## 🏃 Prepare the environment
|
|
||||||
|
|
||||||
Throughout the documentation, [ReVanced Patches](https://github.com/revanced/revanced-patches) will be used as an example project.
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> To start a fresh project,
|
|
||||||
> you can use the [ReVanced Patches template](https://github.com/revanced/revanced-patches-template).
|
|
||||||
|
|
||||||
1. Clone the repository
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/revanced/revanced-patches && cd revanced-patches
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Build the project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./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
|
|
||||||
> ```
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will go into details about a ReVanced patch.
|
|
||||||
|
|
||||||
Continue: [🧩 Anatomy of a patch](2_2_patch_anatomy.md)
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 🔎 Fingerprinting
|
|
||||||
|
|
||||||
In the context of ReVanced, a fingerprint is a partial description of a method.
|
|
||||||
It is used to uniquely match a method by its characteristics.
|
|
||||||
Fingerprinting is used to match methods with a limited amount of known information.
|
|
||||||
Methods with obfuscated names that change with each update are primary candidates for fingerprinting.
|
|
||||||
The goal of fingerprinting is to uniquely identify a method by capturing various attributes, such as the return type,
|
|
||||||
access flags, an opcode pattern, strings, and more.
|
|
||||||
|
|
||||||
## ⛳️ Example fingerprint
|
|
||||||
|
|
||||||
An example fingerprint is shown below:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
|
|
||||||
package app.revanced.patches.ads.fingerprints
|
|
||||||
|
|
||||||
fingerprint {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
returns("Z")
|
|
||||||
parameters("Z")
|
|
||||||
opcodes(Opcode.RETURN)
|
|
||||||
strings("pro")
|
|
||||||
custom { (method, classDef) -> classDef == "Lcom/some/app/ads/AdsLoader;" }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔎 Reconstructing the original code from the example fingerprint from above
|
|
||||||
|
|
||||||
The following code is reconstructed from the fingerprint to understand how a fingerprint is created.
|
|
||||||
|
|
||||||
The fingerprint contains the following information:
|
|
||||||
|
|
||||||
- Method signature:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
returns("Z")
|
|
||||||
parameters("Z")
|
|
||||||
```
|
|
||||||
|
|
||||||
- Method implementation:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
opcodes(Opcode.RETURN)
|
|
||||||
strings("pro")
|
|
||||||
```
|
|
||||||
|
|
||||||
- Package and class name:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
custom { (method, classDef) -> classDef == "Lcom/some/app/ads/AdsLoader;" }
|
|
||||||
```
|
|
||||||
|
|
||||||
With this information, the original code can be reconstructed:
|
|
||||||
|
|
||||||
```java
|
|
||||||
package com.some.app.ads;
|
|
||||||
|
|
||||||
<accessFlags>
|
|
||||||
|
|
||||||
class AdsLoader {
|
|
||||||
public final boolean <methodName>(boolean <parameter>)
|
|
||||||
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
|
|
||||||
var userStatus = "pro";
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
return <returnValue >;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Using that fingerprint, this method can be matched uniquely from all other methods.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> A fingerprint should contain information about a method likely to remain the same across updates.
|
|
||||||
> A method's name is not included in the fingerprint because it will likely change with each update in an obfuscated
|
|
||||||
> app.
|
|
||||||
> In contrast, the return type, access flags, parameters, patterns of opcodes, and strings are likely to remain the
|
|
||||||
> same.
|
|
||||||
|
|
||||||
## 🔨 How to use fingerprints
|
|
||||||
|
|
||||||
After declaring a fingerprint, it can be used in a patch to find the method it matches to:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
val fingerprint = fingerprint {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
val patch = bytecodePatch {
|
|
||||||
execute {
|
|
||||||
fingerprint.method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The fingerprint won't be matched again, if it has already been matched once, for performance reasons.
|
|
||||||
This makes it useful, to share fingerprints between multiple patches,
|
|
||||||
and let the first executing patch match the fingerprint:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
// Either of these two patches will match the fingerprint first and the other patch can reuse the match:
|
|
||||||
val mainActivityPatch1 = bytecodePatch {
|
|
||||||
execute {
|
|
||||||
mainActivityOnCreateFingerprint.method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val mainActivityPatch2 = bytecodePatch {
|
|
||||||
execute {
|
|
||||||
mainActivityOnCreateFingerprint.method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> If the fingerprint can not be matched to any method,
|
|
||||||
> accessing certain properties of the fingerprint will raise an exception.
|
|
||||||
> Instead, the `orNull` properties can be used to return `null` if no match is found.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> If a fingerprint has an opcode pattern, you can use the `fuzzyPatternScanThreshhold` parameter of the `opcode`
|
|
||||||
> function to fuzzy match the pattern.
|
|
||||||
> `null` can be used as a wildcard to match any opcode:
|
|
||||||
>
|
|
||||||
> ```kt
|
|
||||||
> fingerprint(fuzzyPatternScanThreshhold = 2) {
|
|
||||||
> opcodes(
|
|
||||||
> Opcode.ICONST_0,
|
|
||||||
> null,
|
|
||||||
> Opcode.ICONST_1,
|
|
||||||
> Opcode.IRETURN,
|
|
||||||
> )
|
|
||||||
>}
|
|
||||||
> ```
|
|
||||||
|
|
||||||
The following properties can be accessed in a fingerprint:
|
|
||||||
|
|
||||||
- `originalClassDef`: The original class definition the fingerprint matches to.
|
|
||||||
- `originalClassDefOrNull`: The original class definition the fingerprint matches to.
|
|
||||||
- `originalMethod`: The original method the fingerprint matches to.
|
|
||||||
- `originalMethodOrNull`: The original method the fingerprint matches to.
|
|
||||||
- `classDef`: The class the fingerprint matches to.
|
|
||||||
- `classDefOrNull`: The class the fingerprint matches to.
|
|
||||||
- `method`: The method the fingerprint matches to. If no match is found, an exception is raised.
|
|
||||||
- `methodOrNull`: The method the fingerprint matches to.
|
|
||||||
|
|
||||||
The difference between the `original` and non-`original` properties is that the `original` properties return the
|
|
||||||
original class or method definition, while the non-`original` properties return a mutable copy of the class or method.
|
|
||||||
The mutable copies can be modified. They are lazy properties, so they are only computed
|
|
||||||
and only then will effectively replace the `original` method or class definition when accessed.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> If only read-only access to the class or method is needed,
|
|
||||||
> the `originalClassDef` and `originalMethod` properties should be used,
|
|
||||||
> to avoid making a mutable copy of the class or method.
|
|
||||||
|
|
||||||
## 🏹 Manually matching fingerprints
|
|
||||||
|
|
||||||
By default, a fingerprint is matched automatically against all classes
|
|
||||||
when one of the fingerprint's properties is accessed.
|
|
||||||
|
|
||||||
Instead, the fingerprint can be matched manually using various overloads of a fingerprint's `match` function:
|
|
||||||
|
|
||||||
- In a **list of classes**, if the fingerprint can match in a known subset of classes
|
|
||||||
|
|
||||||
If you have a known list of classes you know the fingerprint can match in,
|
|
||||||
you can match the fingerprint on the list of classes:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val match = showAdsFingerprint(classes)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- In a **single class**, if the fingerprint can match in a single known class
|
|
||||||
|
|
||||||
If you know the fingerprint can match a method in a specific class, you can match the fingerprint in the class:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val adsLoaderClass = classes.single { it.name == "Lcom/some/app/ads/Loader;" }
|
|
||||||
|
|
||||||
val match = showAdsFingerprint.match(adsLoaderClass)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Another common usecase is to use a fingerprint to reduce the search space of a method to a single class.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
// Match showAdsFingerprint in the class of the ads loader found by adsLoaderClassFingerprint.
|
|
||||||
val match = showAdsFingerprint.match(adsLoaderClassFingerprint.classDef)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Match a **single method**, to extract certain information about it
|
|
||||||
|
|
||||||
The match of a fingerprint contains useful information about the method,
|
|
||||||
such as the start and end index of an opcode pattern or the indices of the instructions with certain string
|
|
||||||
references.
|
|
||||||
A fingerprint can be leveraged to extract such information from a method instead of manually figuring it out:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val currentPlanFingerprint = fingerprint {
|
|
||||||
strings("free", "trial")
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPlanFingerprint.match(adsFingerprint.method).let { match ->
|
|
||||||
match.stringMatches.forEach { match ->
|
|
||||||
println("The index of the string '${match.string}' is ${match.index}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> If the fingerprint can not be matched to any method, calling `match` will raise an
|
|
||||||
> exception.
|
|
||||||
> Instead, the `orNull` overloads can be used to return `null` if no match is found.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> To see real-world examples of fingerprints,
|
|
||||||
> check out the repository for [ReVanced Patches](https://github.com/revanced/revanced-patches).
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page discusses the structure and conventions of patches.
|
|
||||||
|
|
||||||
Continue: [📜 Project structure and conventions](3_structure_and_conventions.md)
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 🧩 Anatomy of a ReVanced patch
|
|
||||||
|
|
||||||
Learn the API to create patches using ReVanced Patcher.
|
|
||||||
|
|
||||||
## ⛳️ Example patch
|
|
||||||
|
|
||||||
The following example patch disables ads in an app.
|
|
||||||
In the following sections, each part of the patch will be explained in detail.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
package app.revanced.patches.ads
|
|
||||||
|
|
||||||
val disableAdsPatch = bytecodePatch(
|
|
||||||
name = "Disable ads",
|
|
||||||
description = "Disable ads in the app.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.some.app"("1.0.0"))
|
|
||||||
|
|
||||||
// Patches can depend on other patches, executing them first.
|
|
||||||
dependsOn(disableAdsResourcePatch)
|
|
||||||
|
|
||||||
// Merge precompiled DEX files into the patched app, before the patch is executed.
|
|
||||||
extendWith("disable-ads.rve")
|
|
||||||
|
|
||||||
// Business logic of the patch to disable ads in the app.
|
|
||||||
execute {
|
|
||||||
// Fingerprint to find the method to patch.
|
|
||||||
val showAdsFingerprint = fingerprint {
|
|
||||||
// More about fingerprints on the next page of the documentation.
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the method that shows ads,
|
|
||||||
// call DisableAdsPatch.shouldDisableAds() from the extension (precompiled DEX file)
|
|
||||||
// to enable or disable ads.
|
|
||||||
showAdsFingerprint.method.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {}, LDisableAdsPatch;->shouldDisableAds()Z
|
|
||||||
move-result v0
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> To see real-world examples of patches,
|
|
||||||
> check out the repository for [ReVanced Patches](https://github.com/revanced/revanced-patches).
|
|
||||||
|
|
||||||
## 🧩 Patch API
|
|
||||||
|
|
||||||
### ⚙️ Patch options
|
|
||||||
|
|
||||||
Patches can have options to get and set before a patch is executed.
|
|
||||||
Options are useful for making patches configurable.
|
|
||||||
After loading the patches using `PatchLoader`, options can be set for a patch.
|
|
||||||
Multiple types are already built into ReVanced Patcher and are supported by any application that uses ReVanced Patcher.
|
|
||||||
|
|
||||||
To define an option, use the available `option` functions:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
val patch = bytecodePatch(name = "Patch") {
|
|
||||||
// Add an inbuilt option and delegate it to a property.
|
|
||||||
val value by stringOption(name = "Inbuilt option")
|
|
||||||
|
|
||||||
// Add an option with a custom type and delegate it to a property.
|
|
||||||
val string by option<String>(name = "String option")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
println(value)
|
|
||||||
println(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Options of a patch can be set after loading the patches with `PatchLoader` by obtaining the instance for the patch:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
loadPatchesJar(patches).apply {
|
|
||||||
// Type is checked at runtime.
|
|
||||||
first { it.name == "Patch" }.options["Option"] = "Value"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The type of an option can be obtained from the `type` property of the option:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
option.type // The KType of the option. Captures the full type information of the option.
|
|
||||||
```
|
|
||||||
|
|
||||||
Options can be declared outside a patch and added to a patch manually:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
val option = stringOption(name = "Option")
|
|
||||||
|
|
||||||
bytecodePatch(name = "Patch") {
|
|
||||||
val value by option()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful when the same option is referenced in multiple patches.
|
|
||||||
|
|
||||||
### 🧩 Extensions
|
|
||||||
|
|
||||||
An extension is a precompiled DEX file merged into the patched app before a patch is executed.
|
|
||||||
While patches are compile-time constructs, extensions are runtime constructs
|
|
||||||
that extend the patched app with additional classes.
|
|
||||||
|
|
||||||
Assume you want to add a complex feature to an app that would need multiple classes and methods:
|
|
||||||
|
|
||||||
```java
|
|
||||||
public class ComplexPatch {
|
|
||||||
public static void doSomething() {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After compiling the above code as a DEX file, you can add the DEX file as a resource in the patches file
|
|
||||||
and use it in a patch:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
val patch = bytecodePatch(name = "Complex patch") {
|
|
||||||
extendWith("complex-patch.rve")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
fingerprint.method.addInstructions(0, "invoke-static { }, LComplexPatch;->doSomething()V")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
ReVanced Patcher merges the classes from the extension into `context.classes` before executing the patch.
|
|
||||||
When the patch is executed, it can reference the classes and methods from the extension.
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> The [ReVanced Patches template](https://github.com/ReVanced/revanced-patches-template) repository
|
|
||||||
> is a template project to create patches and extensions.
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> To see real-world examples of extensions,
|
|
||||||
> check out the repository for [ReVanced Patches](https://github.com/revanced/revanced-patches).
|
|
||||||
|
|
||||||
### ♻️ Finalization
|
|
||||||
|
|
||||||
Patches can have a finalization block called after all patches have been executed, in reverse order of patch execution.
|
|
||||||
The finalization block is called after all patches that depend on the patch have been executed.
|
|
||||||
This is useful for doing post-processing tasks.
|
|
||||||
A simple real-world example would be a patch that opens a resource file of the app for writing.
|
|
||||||
Other patches that depend on this patch can write to the file, and the finalization block can close the file.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
val patch = bytecodePatch(name = "Patch") {
|
|
||||||
dependsOn(
|
|
||||||
bytecodePatch(name = "Dependency") {
|
|
||||||
execute {
|
|
||||||
print("1")
|
|
||||||
}
|
|
||||||
|
|
||||||
finalize {
|
|
||||||
print("4")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
print("2")
|
|
||||||
}
|
|
||||||
|
|
||||||
finalize {
|
|
||||||
print("3")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Because `Patch` depends on `Dependency`, first `Dependency` is executed, then `Patch`.
|
|
||||||
Finalization blocks are called in reverse order of patch execution, which means,
|
|
||||||
first, the finalization block of `Patch`, then the finalization block of `Dependency` is called.
|
|
||||||
The output after executing the patch above would be `1234`.
|
|
||||||
The same order is followed for multiple patches depending on the patch.
|
|
||||||
|
|
||||||
## 💡 Additional tips
|
|
||||||
|
|
||||||
- When using `PatchLoader` to load patches, only patches with a name are loaded.
|
|
||||||
Refer to the inline documentation of `PatchLoader` for detailed information.
|
|
||||||
- Patches can depend on others. Dependencies are executed first.
|
|
||||||
The dependent patch will not be executed if a dependency raises an exception while executing.
|
|
||||||
- A patch can declare compatibility with specific packages and versions,
|
|
||||||
but patches can still be executed on any package or version.
|
|
||||||
It is recommended that compatibility is specified to present known compatible packages and versions.
|
|
||||||
- If `compatibleWith` is not used, the patch is treated as compatible with any package
|
|
||||||
- If a package is specified with no versions, the patch is compatible with any version of the package
|
|
||||||
- If an empty array of versions is specified, the patch is not compatible with any version of the package.
|
|
||||||
This is useful for declaring incompatibility with a specific package.
|
|
||||||
- A patch can raise a `PatchException` at any time of execution to indicate that the patch failed to execute.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page explains the concept of fingerprinting in ReVanced Patcher.
|
|
||||||
|
|
||||||
Continue: [🔎 Fingerprinting](2_2_1_fingerprinting.md)
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 🧩 Introduction to ReVanced Patches
|
|
||||||
|
|
||||||
Learn the basic concepts of ReVanced Patcher and how to create patches.
|
|
||||||
|
|
||||||
## 📙 Fundamentals
|
|
||||||
|
|
||||||
A patch is a piece of code that modifies an Android application.
|
|
||||||
There are multiple types of patches. Each type can modify a different part of the APK, such as the Dalvik VM bytecode,
|
|
||||||
the APK resources, or arbitrary files in the APK:
|
|
||||||
|
|
||||||
- A `BytecodePatch` modifies the Dalvik VM bytecode
|
|
||||||
- A `ResourcePatch` modifies (decoded) resources
|
|
||||||
- A `RawResourcePatch` modifies arbitrary files
|
|
||||||
|
|
||||||
Each patch can declare a set of dependencies on other patches. ReVanced Patcher will first execute dependencies
|
|
||||||
before executing the patch itself. This way, multiple patches can work together for abstract purposes in a modular way.
|
|
||||||
|
|
||||||
The `execute` function is the entry point for a patch. It is called by ReVanced Patcher when the patch is executed.
|
|
||||||
The `execute` function receives an instance of a context object that provides access to the APK.
|
|
||||||
The patch can use this context to modify the APK.
|
|
||||||
|
|
||||||
Each type of context provides different APIs to modify the APK. For example, the `BytecodePatchContext` provides APIs
|
|
||||||
to modify the Dalvik VM bytecode, while the `ResourcePatchContext` provides APIs to modify resources.
|
|
||||||
|
|
||||||
The difference between `ResourcePatch` and `RawResourcePatch` is that ReVanced Patcher will decode the resources
|
|
||||||
if it is supplied a `ResourcePatch` for execution or if any patch depends on a `ResourcePatch`
|
|
||||||
and will not decode the resources before executing `RawResourcePatch`.
|
|
||||||
Both, `ResourcePatch` and `RawResourcePatch` can modify arbitrary files in the APK,
|
|
||||||
whereas only `ResourcePatch` can modify decoded resources. The choice of which type to use depends on the use case.
|
|
||||||
Decoding and building resources is a time- and resource-consuming,
|
|
||||||
so if the patch does not need to modify decoded resources, it is better to use `RawResourcePatch` or `BytecodePatch`.
|
|
||||||
|
|
||||||
Example of patches:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
@Surpress("unused")
|
|
||||||
val bytecodePatch = bytecodePatch {
|
|
||||||
execute {
|
|
||||||
// More about this on the next page of the documentation.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Surpress("unused")
|
|
||||||
val rawResourcePatch = rawResourcePatch {
|
|
||||||
execute {
|
|
||||||
// More about this on the next page of the documentation.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Surpress("unused")
|
|
||||||
val resourcePatch = resourcePatch {
|
|
||||||
execute {
|
|
||||||
// More about this on the next page of the documentation.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> To see real-world examples of patches,
|
|
||||||
> check out the repository for [ReVanced Patches](https://github.com/revanced/revanced-patches).
|
|
||||||
|
|
||||||
## ⏭️ Whats next
|
|
||||||
|
|
||||||
The next page will guide you through creating a development environment for creating patches.
|
|
||||||
|
|
||||||
Continue: [👶 Setting up a development environment](2_1_setup.md)
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 📜 Project structure and conventions
|
|
||||||
|
|
||||||
Over time, a specific project structure and conventions have been established.
|
|
||||||
|
|
||||||
## 📁 File structure
|
|
||||||
|
|
||||||
Patches are organized in a specific way. The file structure looks as follows:
|
|
||||||
|
|
||||||
```text
|
|
||||||
📦your.patches.app.category
|
|
||||||
├ 🔍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 `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.
|
|
||||||
- 🔥🔥 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 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, 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
|
|
||||||
|
|
||||||
The next page discusses useful APIs for patch development.
|
|
||||||
|
|
||||||
Continue: [💪 Advanced APIs](4_apis.md)
|
|
||||||
117
docs/4_apis.md
117
docs/4_apis.md
@@ -1,117 +0,0 @@
|
|||||||
# 💪 Advanced APIs
|
|
||||||
|
|
||||||
A handful of APIs are available to make patch development easier and more efficient.
|
|
||||||
|
|
||||||
## 📙 Overview
|
|
||||||
|
|
||||||
1. 👹 Create mutable replacements of classes with `proxy(ClassDef)`
|
|
||||||
2. 🔍 Find and create mutable replaces with `classBy(Predicate)`
|
|
||||||
3. 🏃 Navigate method calls recursively by index with `navigate(Method)`
|
|
||||||
4. 💾 Read and write resource files with `get(String, Boolean)` and `delete(String)`
|
|
||||||
5. 📃 Read and write DOM files using `document(String)` and `document(InputStream)`
|
|
||||||
|
|
||||||
### 🧰 APIs
|
|
||||||
|
|
||||||
#### 👹 `proxy(ClassDef)`
|
|
||||||
|
|
||||||
By default, the classes are immutable, meaning they cannot be modified.
|
|
||||||
To make a class mutable, use the `proxy(ClassDef)` function.
|
|
||||||
This function creates a lazy mutable copy of the class definition.
|
|
||||||
Accessing the property will replace the original class definition with the mutable copy,
|
|
||||||
thus allowing you to make changes to the class. Subsequent accesses will return the same mutable copy.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val mutableClass = proxy(classDef)
|
|
||||||
mutableClass.methods.add(Method())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 🔍 `classBy(Predicate)`
|
|
||||||
|
|
||||||
The `classBy(Predicate)` function is an alternative to finding and creating mutable classes by a predicate.
|
|
||||||
It automatically proxies the class definition, making it mutable.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
// Alternative to proxy(classes.find { it.name == "Lcom/example/MyClass;" })?.classDef
|
|
||||||
val classDef = classBy { it.name == "Lcom/example/MyClass;" }?.classDef
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 🏃 `navigate(Method).at(index)`
|
|
||||||
|
|
||||||
The `navigate(Method)` function allows you to navigate method calls recursively by index.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
// Sequentially navigate to the instructions at index 1 within 'someMethod'.
|
|
||||||
val method = navigate(someMethod).to(1).original() // original() returns the original immutable method.
|
|
||||||
|
|
||||||
// Further navigate to the second occurrence where the instruction's opcode is 'INVOKEVIRTUAL'.
|
|
||||||
// stop() returns the mutable copy of the method.
|
|
||||||
val method = navigate(someMethod).to(2) { instruction -> instruction.opcode == Opcode.INVOKEVIRTUAL }.stop()
|
|
||||||
|
|
||||||
// Alternatively, to stop(), you can delegate the method to a variable.
|
|
||||||
val method by navigate(someMethod).to(1)
|
|
||||||
|
|
||||||
// You can chain multiple calls to at() to navigate deeper into the method.
|
|
||||||
val method by navigate(someMethod).to(1).to(2, 3, 4).to(5)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 💾 `get(String, Boolean)` and `delete(String)`
|
|
||||||
|
|
||||||
The `get(String, Boolean)` function returns a `File` object that can be used to read and write resource files.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val file = get("res/values/strings.xml")
|
|
||||||
val content = file.readText()
|
|
||||||
file.writeText(content)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `delete` function can mark files for deletion when the APK is rebuilt.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
delete("res/values/strings.xml")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 📃 `document(String)` and `document(InputStream)`
|
|
||||||
|
|
||||||
The `document` function is used to read and write DOM files.
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
document("res/values/strings.xml").use { document ->
|
|
||||||
val element = doc.createElement("string").apply {
|
|
||||||
textContent = "Hello, World!"
|
|
||||||
}
|
|
||||||
document.documentElement.appendChild(element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also read documents from an `InputStream`:
|
|
||||||
|
|
||||||
```kt
|
|
||||||
execute {
|
|
||||||
val inputStream = classLoader.getResourceAsStream("some.xml")
|
|
||||||
document(inputStream).use { document ->
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎉 Afterword
|
|
||||||
|
|
||||||
ReVanced Patcher is a powerful library to patch Android applications, offering a rich set of APIs to develop patches
|
|
||||||
that outlive app updates. Patches make up ReVanced; without you, the community of patch developers,
|
|
||||||
ReVanced would not be what it is today. We hope that this documentation has been helpful to you
|
|
||||||
and are excited to see what you will create with ReVanced Patcher. If you have any questions or need help,
|
|
||||||
talk to us on one of our platforms linked on [revanced.app](https://revanced.app) or open an issue in case of a bug or
|
|
||||||
feature request,
|
|
||||||
ReVanced
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<picture>
|
|
||||||
<source
|
|
||||||
width="256px"
|
|
||||||
media="(prefers-color-scheme: dark)"
|
|
||||||
srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
width="256px"
|
|
||||||
src="../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" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
|
||||||
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="http://revanced.app/discord">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://reddit.com/r/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://t.me/app_revanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://x.com/revancedapp">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@ReVanced">
|
|
||||||
<picture>
|
|
||||||
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Continuing the legacy of Vanced
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# 💉 Documentation of ReVanced Patcher
|
|
||||||
|
|
||||||
This documentation contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches
|
|
||||||
|
|
||||||
## 📖 Table of content
|
|
||||||
|
|
||||||
1. [💉 Introduction to ReVanced Patcher](1_patcher_intro.md)
|
|
||||||
2. [🧩 Introduction to ReVanced Patches](2_patches_intro.md)
|
|
||||||
1. [👶 Setting up a development environment](2_1_setup.md)
|
|
||||||
2. [🧩 Anatomy of a ReVanced patch](2_2_patch_anatomy.md)
|
|
||||||
1. [🔎 Fingerprinting](2_2_1_fingerprinting.md)
|
|
||||||
3. [📜 Project structure and conventions](3_structure_and_conventions.md)
|
|
||||||
4. [💪 Advanced APIs](4_apis.md)
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel=true
|
||||||
org.gradle.caching = true
|
org.gradle.caching=true
|
||||||
version = 21.1.0-dev.5
|
kotlin.code.style = official
|
||||||
|
version = 11.0.4
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
[versions]
|
|
||||||
android = "4.1.1.4"
|
|
||||||
apktool-lib = "2.10.1.1"
|
|
||||||
binary-compatibility-validator = "0.18.1"
|
|
||||||
kotlin = "2.0.20"
|
|
||||||
kotlinx-coroutines-core = "1.10.2"
|
|
||||||
mockk = "1.14.5"
|
|
||||||
multidexlib2 = "3.0.3.r3"
|
|
||||||
# Tracking https://github.com/google/smali/issues/64.
|
|
||||||
#noinspection GradleDependency
|
|
||||||
smali = "3.0.9"
|
|
||||||
xpp3 = "1.1.4c"
|
|
||||||
|
|
||||||
[libraries]
|
|
||||||
android = { module = "com.google.android:android", version.ref = "android" }
|
|
||||||
apktool-lib = { module = "app.revanced:apktool-lib", version.ref = "apktool-lib" }
|
|
||||||
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" }
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
22
gradlew
vendored
22
gradlew
vendored
@@ -15,8 +15,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -57,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -85,9 +83,7 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -148,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC2039,SC3045
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -156,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC2039,SC3045
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -205,11 +201,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# 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"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
# Collect all arguments for the java command;
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# and any embedded shellness will be escaped.
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
# double quotes to make sure that they get re-expanded; and
|
||||||
# treated as '${Hostname}' itself on the command line.
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|||||||
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,8 +13,6 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|||||||
7034
package-lock.json
generated
7034
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
"@saithodev/semantic-release-backmerge": "^3.1.0",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.2",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.10.1",
|
"gradle-semantic-release-plugin": "^1.7.6",
|
||||||
"semantic-release": "^24.2.9"
|
"semantic-release": "^20.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
revanced-patcher/build.gradle.kts
Normal file
61
revanced-patcher/build.gradle.kts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "app.revanced"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("xpp3:xpp3:1.1.4c")
|
||||||
|
implementation("app.revanced:smali:2.5.3-a3836654")
|
||||||
|
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
||||||
|
implementation("io.github.reandroid:ARSCLib:1.1.7")
|
||||||
|
implementation(project(":arsclib-utils"))
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
|
||||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
|
||||||
|
|
||||||
|
compileOnly("com.google.android:android:4.1.1.4")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processResources {
|
||||||
|
expand("projectVersion" to project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
if (System.getenv("GITHUB_ACTOR") != null)
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
register<MavenPublication>("gpr") {
|
||||||
|
from(components["java"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
revanced-patcher/settings.gradle.kts
Normal file
1
revanced-patcher/settings.gradle.kts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "revanced-patcher"
|
||||||
113
revanced-patcher/src/main/kotlin/app/revanced/patcher/Context.kt
Normal file
113
revanced-patcher/src/main/kotlin/app/revanced/patcher/Context.kt
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.arsc.resource.ResourceContainer
|
||||||
|
import app.revanced.patcher.apk.Apk
|
||||||
|
import app.revanced.patcher.apk.ApkBundle
|
||||||
|
import app.revanced.arsc.resource.ResourceFiles
|
||||||
|
import app.revanced.patcher.util.method.MethodWalker
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.w3c.dom.Document
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.StringWriter
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.transform.TransformerFactory
|
||||||
|
import javax.xml.transform.dom.DOMSource
|
||||||
|
import javax.xml.transform.stream.StreamResult
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common class to constrain [Context] to [BytecodeContext] and [ResourceContext].
|
||||||
|
* @param apkBundle The [ApkBundle] for this context.
|
||||||
|
*/
|
||||||
|
sealed class Context(val apkBundle: ApkBundle)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for the bytecode of an [Apk.Base] file.
|
||||||
|
*
|
||||||
|
* @param apkBundle The [ApkBundle] for this context.
|
||||||
|
*/
|
||||||
|
class BytecodeContext internal constructor(apkBundle: ApkBundle) : Context(apkBundle) {
|
||||||
|
/**
|
||||||
|
* The list of classes.
|
||||||
|
*/
|
||||||
|
val classes = apkBundle.base.bytecodeData.classes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a [MethodWalker] instance for the current [BytecodeContext].
|
||||||
|
*
|
||||||
|
* @param startMethod The method to start at.
|
||||||
|
* @return A [MethodWalker] instance.
|
||||||
|
*/
|
||||||
|
fun traceMethodCalls(startMethod: Method) = MethodWalker(this, startMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for [Apk] file resources.
|
||||||
|
*
|
||||||
|
* @param apkBundle the [ApkBundle] for this context.
|
||||||
|
*/
|
||||||
|
class ResourceContext internal constructor(apkBundle: ApkBundle) : Context(apkBundle) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an [DomFileEditor] for a given DOM file.
|
||||||
|
*
|
||||||
|
* @param inputStream The input stream to read the DOM file from.
|
||||||
|
* @return A [DomFileEditor] instance.
|
||||||
|
*/
|
||||||
|
fun openXmlFile(inputStream: InputStream) = DomFileEditor(inputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a [DomFileEditor] for a resource file in the archive.
|
||||||
|
*
|
||||||
|
* @see [ResourceContainer.openFile]
|
||||||
|
* @param path The resource file path.
|
||||||
|
* @return A [DomFileEditor].
|
||||||
|
*/
|
||||||
|
fun ResourceContainer.openXmlFile(path: String) = DomFileEditor(openFile(path))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a file that can be edited as a dom document.
|
||||||
|
*
|
||||||
|
* @param inputStream the input stream to read the xml file from.
|
||||||
|
* @param onSave A callback that will be called when the editor is closed to save the file.
|
||||||
|
*/
|
||||||
|
class DomFileEditor internal constructor(
|
||||||
|
private val inputStream: InputStream,
|
||||||
|
private val onSave: ((String) -> Unit)? = null
|
||||||
|
) : Closeable {
|
||||||
|
private var closed: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The document of the xml file.
|
||||||
|
*/
|
||||||
|
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
||||||
|
.also(Document::normalize)
|
||||||
|
|
||||||
|
internal constructor(file: ResourceFiles) : this(
|
||||||
|
file.inputStream(),
|
||||||
|
{
|
||||||
|
file.contents = it.toByteArray()
|
||||||
|
file.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the editor and writes back to the file.
|
||||||
|
*/
|
||||||
|
override fun close() {
|
||||||
|
if (closed) return
|
||||||
|
|
||||||
|
inputStream.close()
|
||||||
|
|
||||||
|
onSave?.let { callback ->
|
||||||
|
// Save the updated file.
|
||||||
|
val writer = StringWriter()
|
||||||
|
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), StreamResult(writer))
|
||||||
|
callback(writer.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
closed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
222
revanced-patcher/src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
222
revanced-patcher/src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.apk.Apk
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
|
||||||
|
import app.revanced.patcher.patch.*
|
||||||
|
import app.revanced.patcher.util.VersionReader
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
typealias ExecutedPatchResults = Flow<Pair<String, PatchException?>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ReVanced Patcher.
|
||||||
|
* @param options The options for the patcher.
|
||||||
|
* @param patches The patches to use.
|
||||||
|
* @param integrations The integrations to merge if necessary. Must be dex files or dex file container such as ZIP, APK or DEX files.
|
||||||
|
*/
|
||||||
|
class Patcher(private val options: PatcherOptions, patches: Iterable<PatchClass>, integrations: Iterable<File>) :
|
||||||
|
Function<Boolean, ExecutedPatchResults> {
|
||||||
|
private val context = PatcherContext(options, patches.toList(), integrations)
|
||||||
|
private val logger = options.logger
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* The version of the ReVanced Patcher.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val version = VersionReader.read()
|
||||||
|
|
||||||
|
@Suppress("SpellCheckingInspection")
|
||||||
|
internal val dexFileNamer = BasicDexFileNamer()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
/**
|
||||||
|
* Returns true if at least one patches or its dependencies matches the given predicate.
|
||||||
|
*/
|
||||||
|
fun PatchClass.anyRecursively(predicate: (PatchClass) -> Boolean): Boolean =
|
||||||
|
predicate(this) || dependencies?.any { it.java.anyRecursively(predicate) } == true
|
||||||
|
|
||||||
|
// Determine if merging integrations is required.
|
||||||
|
for (patch in context.patches) {
|
||||||
|
if (patch.anyRecursively { it.requiresIntegrations }) {
|
||||||
|
context.integrations.merge = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the patcher.
|
||||||
|
*
|
||||||
|
* @param stopOnError If true, the patches will stop on the first error.
|
||||||
|
* @return A pair of the name of the [Patch] and a [PatchException] if it failed.
|
||||||
|
*/
|
||||||
|
override fun apply(stopOnError: Boolean) = flow {
|
||||||
|
/**
|
||||||
|
* Execute a [Patch] and its dependencies recursively.
|
||||||
|
*
|
||||||
|
* @param patchClass The [Patch] to execute.
|
||||||
|
* @param executedPatches A map of [Patch]es paired to a boolean indicating their success, to prevent infinite recursion.
|
||||||
|
*/
|
||||||
|
suspend fun executePatch(
|
||||||
|
patchClass: PatchClass,
|
||||||
|
executedPatches: HashMap<String, ExecutedPatch>
|
||||||
|
) {
|
||||||
|
val patchName = patchClass.patchName
|
||||||
|
|
||||||
|
// If the patch has already executed silently skip it.
|
||||||
|
if (executedPatches.contains(patchName)) {
|
||||||
|
if (!executedPatches[patchName]!!.success)
|
||||||
|
throw PatchException("'$patchName' did not succeed previously")
|
||||||
|
|
||||||
|
logger.trace("Skipping '$patchName' because it has already been executed")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively execute all dependency patches.
|
||||||
|
patchClass.dependencies?.forEach { dependencyClass ->
|
||||||
|
val dependency = dependencyClass.java
|
||||||
|
|
||||||
|
try {
|
||||||
|
executePatch(dependency, executedPatches)
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
throw PatchException(
|
||||||
|
"'$patchName' depends on '${dependency.patchName}' " +
|
||||||
|
"but the following exception was raised: ${throwable.cause?.stackTraceToString() ?: throwable.message}",
|
||||||
|
throwable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
|
||||||
|
val patchInstance = patchClass.getDeclaredConstructor().newInstance()
|
||||||
|
|
||||||
|
// TODO: implement this in a more polymorphic way.
|
||||||
|
val patchContext = if (isResourcePatch) {
|
||||||
|
context.resourceContext
|
||||||
|
} else {
|
||||||
|
context.bytecodeContext.apply {
|
||||||
|
val bytecodePatch = patchInstance as BytecodePatch
|
||||||
|
bytecodePatch.fingerprints?.resolveUsingLookupMap(context.bytecodeContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Executing '$patchName' of type: ${if (isResourcePatch) "resource" else "bytecode"}")
|
||||||
|
|
||||||
|
var success = false
|
||||||
|
try {
|
||||||
|
patchInstance.execute(patchContext)
|
||||||
|
|
||||||
|
success = true
|
||||||
|
} catch (patchException: PatchException) {
|
||||||
|
throw patchException
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
throw PatchException("Unhandled patch exception: ${throwable.message}", throwable)
|
||||||
|
} finally {
|
||||||
|
executedPatches[patchName] = ExecutedPatch(patchInstance, success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.integrations.merge) context.integrations.merge(logger)
|
||||||
|
|
||||||
|
logger.trace("Initialize lookup maps for method MethodFingerprint resolution")
|
||||||
|
|
||||||
|
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
|
||||||
|
|
||||||
|
logger.info("Executing patches")
|
||||||
|
|
||||||
|
// Key is patch name.
|
||||||
|
LinkedHashMap<String, ExecutedPatch>().apply {
|
||||||
|
context.patches.forEach { patch ->
|
||||||
|
var exception: PatchException? = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
executePatch(patch, this)
|
||||||
|
} catch (patchException: PatchException) {
|
||||||
|
exception = patchException
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: only emit if the patch is not a closeable.
|
||||||
|
// If it is a closeable, this should be done when closing the patch.
|
||||||
|
emit(patch.patchName to exception)
|
||||||
|
|
||||||
|
if (stopOnError && exception != null) return@flow
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
it.values
|
||||||
|
.filter(ExecutedPatch::success)
|
||||||
|
.map(ExecutedPatch::patchInstance)
|
||||||
|
.filterIsInstance(Closeable::class.java)
|
||||||
|
.asReversed().forEach { patch ->
|
||||||
|
try {
|
||||||
|
patch.close()
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
val patchException =
|
||||||
|
if (throwable is PatchException) throwable
|
||||||
|
else PatchException(throwable)
|
||||||
|
|
||||||
|
val patchName = (patch as Patch<Context>).javaClass.patchName
|
||||||
|
|
||||||
|
logger.error("Failed to close '$patchName': ${patchException.stackTraceToString()}")
|
||||||
|
|
||||||
|
emit(patchName to patchException)
|
||||||
|
|
||||||
|
// This is not failsafe. If a patch throws an exception while closing,
|
||||||
|
// the other patches that depend on it may fail.
|
||||||
|
if (stopOnError) return@flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodFingerprint.clearFingerprintResolutionLookupMaps()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish patching all [Apk]s.
|
||||||
|
*
|
||||||
|
* @return The [PatcherResult] of the [Patcher].
|
||||||
|
*/
|
||||||
|
fun finish(): PatcherResult {
|
||||||
|
val patchResults = buildList {
|
||||||
|
logger.info("Processing patched apks")
|
||||||
|
options.apkBundle.cleanup(options).forEach { result ->
|
||||||
|
if (result.exception != null) {
|
||||||
|
logger.error("Got exception while processing ${result.apk}: ${result.exception.stackTraceToString()}")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val patch = result.let {
|
||||||
|
when (it.apk) {
|
||||||
|
is Apk.Base -> PatcherResult.Patch.Base(it.apk)
|
||||||
|
is Apk.Split -> PatcherResult.Patch.Split(it.apk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(patch)
|
||||||
|
|
||||||
|
logger.info("Patched ${result.apk}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatcherResult(patchResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A result of executing a [Patch].
|
||||||
|
*
|
||||||
|
* @param patchInstance The instance of the [Patch] that was executed.
|
||||||
|
* @param success The result of the [Patch].
|
||||||
|
*/
|
||||||
|
internal data class ExecutedPatch(val patchInstance: Patch<Context>, val success: Boolean)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.logging.Logger
|
||||||
|
import app.revanced.patcher.patch.PatchClass
|
||||||
|
import app.revanced.patcher.util.ClassMerger.merge
|
||||||
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class PatcherContext(
|
||||||
|
options: PatcherOptions,
|
||||||
|
internal val patches: List<PatchClass>,
|
||||||
|
integrations: Iterable<File>
|
||||||
|
) {
|
||||||
|
internal val integrations = Integrations(this, integrations)
|
||||||
|
internal val bytecodeContext = BytecodeContext(options.apkBundle)
|
||||||
|
internal val resourceContext = ResourceContext(options.apkBundle)
|
||||||
|
|
||||||
|
internal class Integrations(val context: PatcherContext, private val dexContainers: Iterable<File>) {
|
||||||
|
var merge = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge integrations.
|
||||||
|
* @param logger A logger.
|
||||||
|
*/
|
||||||
|
fun merge(logger: Logger) {
|
||||||
|
context.bytecodeContext.classes.apply {
|
||||||
|
for (integrations in dexContainers) {
|
||||||
|
logger.info("Merging $integrations")
|
||||||
|
|
||||||
|
for (classDef in MultiDexIO.readDexFile(true, integrations, Patcher.dexFileNamer, null, null).classes) {
|
||||||
|
val type = classDef.type
|
||||||
|
|
||||||
|
val existingClassIndex = this.indexOfFirst { it.type == type }
|
||||||
|
if (existingClassIndex == -1) {
|
||||||
|
logger.trace("Merging type $type")
|
||||||
|
add(classDef)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
logger.trace("Type $type exists. Adding missing methods and fields.")
|
||||||
|
|
||||||
|
get(existingClassIndex).apply {
|
||||||
|
merge(classDef, context.bytecodeContext, logger).let { mergedClass ->
|
||||||
|
if (mergedClass !== this) // referential equality check
|
||||||
|
set(existingClassIndex, mergedClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.apk.ApkBundle
|
||||||
|
import app.revanced.patcher.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the [Patcher].
|
||||||
|
* @param apkBundle The [ApkBundle].
|
||||||
|
* @param logger Custom logger implementation for the [Patcher].
|
||||||
|
*/
|
||||||
|
class PatcherOptions(
|
||||||
|
internal val apkBundle: ApkBundle,
|
||||||
|
internal val logger: Logger = Logger.Nop
|
||||||
|
)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.apk.Apk
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a patcher.
|
||||||
|
* @param apkFiles The patched [Apk] files.
|
||||||
|
*/
|
||||||
|
data class PatcherResult(val apkFiles: List<Patch>) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a patch.
|
||||||
|
*
|
||||||
|
* @param apk The patched [Apk] file.
|
||||||
|
*/
|
||||||
|
sealed class Patch(val apk: Apk) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a patch of an [Apk.Split] file.
|
||||||
|
*
|
||||||
|
* @param apk The patched [Apk.Split] file.
|
||||||
|
*/
|
||||||
|
class Split(apk: Apk.Split) : Patch(apk)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a patch of an [Apk.Split] file.
|
||||||
|
*
|
||||||
|
* @param apk The patched [Apk.Base] file.
|
||||||
|
*/
|
||||||
|
class Base(apk: Apk.Base) : Patch(apk)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patcher.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to constrain a [Patch] to compatible packages.
|
||||||
|
* @param compatiblePackages A list of packages a [Patch] is compatible with.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
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] is compatible with.
|
||||||
|
*/
|
||||||
|
@Target()
|
||||||
|
annotation class Package(
|
||||||
|
val name: String,
|
||||||
|
val versions: Array<String> = [],
|
||||||
|
)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package app.revanced.patcher.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to name a [Patch].
|
||||||
|
* @param name A suggestive name for the [Patch].
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class Name(
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to describe a [Patch].
|
||||||
|
* @param description A description for the [Patch].
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class Description(
|
||||||
|
val description: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to version a [Patch].
|
||||||
|
* @param version The version of a [Patch].
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Deprecated("This annotation is deprecated and will be removed in the future.")
|
||||||
|
annotation class Version(
|
||||||
|
val version: String,
|
||||||
|
)
|
||||||
285
revanced-patcher/src/main/kotlin/app/revanced/patcher/apk/Apk.kt
Normal file
285
revanced-patcher/src/main/kotlin/app/revanced/patcher/apk/Apk.kt
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package app.revanced.patcher.apk
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import app.revanced.arsc.archive.Archive
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
import app.revanced.patcher.PatcherOptions
|
||||||
|
import app.revanced.patcher.logging.asArscLogger
|
||||||
|
import app.revanced.patcher.util.ProxyBackedClassList
|
||||||
|
import com.reandroid.apk.ApkModule
|
||||||
|
import com.reandroid.apk.xmlencoder.EncodeException
|
||||||
|
import com.reandroid.archive.InputSource
|
||||||
|
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock
|
||||||
|
import com.reandroid.arsc.value.ResConfig
|
||||||
|
import lanchon.multidexlib2.*
|
||||||
|
import org.jf.dexlib2.Opcodes
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile
|
||||||
|
import org.jf.dexlib2.iface.DexFile
|
||||||
|
import org.jf.dexlib2.iface.MultiDexContainer
|
||||||
|
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [Apk] file.
|
||||||
|
*/
|
||||||
|
sealed class Apk private constructor(module: ApkModule) {
|
||||||
|
/**
|
||||||
|
* A wrapper around the zip archive of this [Apk].
|
||||||
|
*
|
||||||
|
* @see Archive
|
||||||
|
*/
|
||||||
|
private val archive = Archive(module)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metadata of the [Apk].
|
||||||
|
*/
|
||||||
|
val packageMetadata = PackageMetadata(module.androidManifestBlock)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh updated resources and close any open files.
|
||||||
|
*
|
||||||
|
* @param options The [PatcherOptions] of the [Patcher].
|
||||||
|
*/
|
||||||
|
internal open fun cleanup(options: PatcherOptions) {
|
||||||
|
try {
|
||||||
|
archive.cleanup(options.logger.asArscLogger())
|
||||||
|
} catch (e: EncodeException) {
|
||||||
|
throw ApkResourceException.Encode(e.message!!, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.mainPackageResources.refreshPackageName()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the [Apk] to a file.
|
||||||
|
*
|
||||||
|
* @param output The target file.
|
||||||
|
*/
|
||||||
|
fun write(output: File) = archive.save(output)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MANIFEST_FILE_NAME = "AndroidManifest.xml"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the [Module] and [Type] of an [ApkModule].
|
||||||
|
*
|
||||||
|
* @return A [Pair] containing the [Module] and [Type] of the [ApkModule].
|
||||||
|
*/
|
||||||
|
fun ApkModule.identify(): Pair<Module, Type> {
|
||||||
|
val manifestElement = androidManifestBlock.manifestElement
|
||||||
|
return when {
|
||||||
|
isBaseModule -> Module.Main to Type.Base
|
||||||
|
// The module is a base apk for a dynamic feature module if the "isFeatureModule" attribute is set to true.
|
||||||
|
manifestElement.searchAttributeByName("isFeatureModule")?.valueAsBoolean == true -> Module.DynamicFeature(
|
||||||
|
split
|
||||||
|
) to Type.Base
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val module = manifestElement.searchAttributeByName("configForSplit")
|
||||||
|
?.let { Module.DynamicFeature(it.valueAsString) } ?: Module.Main
|
||||||
|
|
||||||
|
// Examples:
|
||||||
|
// config.xhdpi
|
||||||
|
// df_my_feature.config.en
|
||||||
|
val config = this.split.split(".").last()
|
||||||
|
|
||||||
|
val type = when {
|
||||||
|
// Language splits have a two-letter country code.
|
||||||
|
config.length == 2 -> Type.Language(config)
|
||||||
|
// Library splits use the target CPU architecture.
|
||||||
|
Split.Library.architectures.contains(config) -> Type.Library(config)
|
||||||
|
// Asset splits use the density.
|
||||||
|
ResConfig.Density.valueOf(config) != null -> Type.Asset(config)
|
||||||
|
else -> throw IllegalArgumentException("Invalid split config: $config")
|
||||||
|
}
|
||||||
|
|
||||||
|
module to type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inner class BytecodeData {
|
||||||
|
private val opcodes: Opcodes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The classes and proxied classes of the [Base] apk file.
|
||||||
|
*/
|
||||||
|
val classes: ProxyBackedClassList
|
||||||
|
|
||||||
|
init {
|
||||||
|
MultiDexContainerBackedDexFile(object : MultiDexContainer<DexBackedDexFile> {
|
||||||
|
// Load all dex files from the apk module and create a dex entry for each of them.
|
||||||
|
private val entries = archive.readDexFiles().associateBy { it.name }
|
||||||
|
.mapValues { (name, inputSource) ->
|
||||||
|
BasicDexEntry(
|
||||||
|
this,
|
||||||
|
name,
|
||||||
|
RawDexIO.readRawDexFile(inputSource.openStream(), inputSource.length, null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDexEntryNames() = entries.keys.toList()
|
||||||
|
override fun getEntry(entryName: String) = entries[entryName]
|
||||||
|
}).let {
|
||||||
|
opcodes = it.opcodes
|
||||||
|
classes = ProxyBackedClassList(it.classes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write [classes] to the archive.
|
||||||
|
*/
|
||||||
|
internal fun writeDexFiles() {
|
||||||
|
// Create patched dex files.
|
||||||
|
mutableMapOf<String, MemoryDataStore>().also {
|
||||||
|
val newDexFile = object : DexFile {
|
||||||
|
override fun getClasses() =
|
||||||
|
this@BytecodeData.classes.also(ProxyBackedClassList::applyProxies).toSet()
|
||||||
|
override fun getOpcodes() = this@BytecodeData.opcodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write modified dex files.
|
||||||
|
MultiDexIO.writeDexFile(
|
||||||
|
true, -1, // Core count.
|
||||||
|
it, Patcher.dexFileNamer, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
|
||||||
|
)
|
||||||
|
}.forEach { (name, store) ->
|
||||||
|
val dexFileInputSource = object : InputSource(name) {
|
||||||
|
override fun openStream() = store.readAt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.write(dexFileInputSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about an [Apk] file.
|
||||||
|
*
|
||||||
|
* @param packageName The package name of the [Apk] file.
|
||||||
|
* @param packageVersion The package version of the [Apk] file.
|
||||||
|
*/
|
||||||
|
data class PackageMetadata(val packageName: String?, val packageVersion: String?) {
|
||||||
|
internal constructor(manifestBlock: AndroidManifestBlock) : this(
|
||||||
|
manifestBlock.packageName,
|
||||||
|
manifestBlock.versionName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [Apk] of type [Split].
|
||||||
|
*
|
||||||
|
* @param config The device configuration associated with this [Split], such as arm64_v8a, en or xhdpi.
|
||||||
|
* @see Apk
|
||||||
|
*/
|
||||||
|
sealed class Split(val config: String, module: ApkModule) : Apk(module) {
|
||||||
|
override fun toString() = "split_config.$config.apk"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The split apk file which contains libraries.
|
||||||
|
*
|
||||||
|
* @see Split
|
||||||
|
*/
|
||||||
|
class Library internal constructor(config: String, module: ApkModule) : Split(config, module) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* A set of all architectures supported by android.
|
||||||
|
*/
|
||||||
|
val architectures = setOf("armeabi_v7a", "arm64_v8a", "x86", "x86_64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The split apk file which contains language strings.
|
||||||
|
*
|
||||||
|
* @see Split
|
||||||
|
*/
|
||||||
|
class Language internal constructor(config: String, module: ApkModule) : Split(config, module)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The split apk file which contains assets.
|
||||||
|
*
|
||||||
|
* @see Split
|
||||||
|
*/
|
||||||
|
class Asset internal constructor(config: String, module: ApkModule) : Split(config, module)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base [Apk] file..
|
||||||
|
*
|
||||||
|
* @see Apk
|
||||||
|
*/
|
||||||
|
class Base internal constructor(module: ApkModule) : Apk(module) {
|
||||||
|
/**
|
||||||
|
* Data of the [Base] apk file.
|
||||||
|
*/
|
||||||
|
internal val bytecodeData = BytecodeData()
|
||||||
|
|
||||||
|
override fun toString() = "base.apk"
|
||||||
|
|
||||||
|
override fun cleanup(options: PatcherOptions) {
|
||||||
|
super.cleanup(options)
|
||||||
|
|
||||||
|
options.logger.info("Writing patched dex files")
|
||||||
|
bytecodeData.writeDexFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module that the [ApkModule] belongs to.
|
||||||
|
*/
|
||||||
|
sealed class Module {
|
||||||
|
/**
|
||||||
|
* The default [Module] that is always installed by software repositories.
|
||||||
|
*/
|
||||||
|
object Main : Module()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Module] that can be installed later by software repositories when requested by the application.
|
||||||
|
*
|
||||||
|
* @param name The name of the feature.
|
||||||
|
*/
|
||||||
|
data class DynamicFeature(val name: String) : Module()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the [ApkModule].
|
||||||
|
*/
|
||||||
|
sealed class Type {
|
||||||
|
/**
|
||||||
|
* The main Apk of a [Module].
|
||||||
|
*/
|
||||||
|
object Base : Type()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A superclass for all split configuration types.
|
||||||
|
*
|
||||||
|
* @param target The target device configuration.
|
||||||
|
*/
|
||||||
|
sealed class SplitConfig(val target: String) : Type()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Type] of an apk containing native libraries.
|
||||||
|
*
|
||||||
|
* @param architecture The target CPU architecture.
|
||||||
|
*/
|
||||||
|
data class Library(val architecture: String) : SplitConfig(architecture)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Type] for an Apk containing language resources.
|
||||||
|
*
|
||||||
|
* @param language The target language code.
|
||||||
|
*/
|
||||||
|
data class Language(val language: String) : SplitConfig(language)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Type] for an Apk containing assets.
|
||||||
|
*
|
||||||
|
* @param pixelDensity The target screen density.
|
||||||
|
*/
|
||||||
|
data class Asset(val pixelDensity: String) : SplitConfig(pixelDensity)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package app.revanced.patcher.apk
|
||||||
|
|
||||||
|
import app.revanced.arsc.ApkResourceException
|
||||||
|
import app.revanced.arsc.resource.ResourceTable
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
import app.revanced.patcher.PatcherOptions
|
||||||
|
import app.revanced.patcher.apk.Apk.Companion.identify
|
||||||
|
import com.reandroid.apk.ApkModule
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [Apk] file of type [Apk.Split].
|
||||||
|
*
|
||||||
|
* @param files A list of apk files to load.
|
||||||
|
*/
|
||||||
|
class ApkBundle(files: List<File>) : Sequence<Apk> {
|
||||||
|
/**
|
||||||
|
* The [Apk.Base] of this [ApkBundle].
|
||||||
|
*/
|
||||||
|
val base: Apk.Base
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map containing all the [Apk.Split]s in this bundle associated by their configuration.
|
||||||
|
*/
|
||||||
|
val splits: Map<String, Apk.Split>?
|
||||||
|
|
||||||
|
init {
|
||||||
|
var baseApk: Apk.Base? = null
|
||||||
|
|
||||||
|
splits = buildMap {
|
||||||
|
files.forEach {
|
||||||
|
val apk = ApkModule.loadApkFile(it)
|
||||||
|
val (module, type) = apk.identify()
|
||||||
|
if (module is Apk.Module.DynamicFeature) {
|
||||||
|
return@forEach // Dynamic feature modules are not supported yet.
|
||||||
|
}
|
||||||
|
|
||||||
|
when (type) {
|
||||||
|
Apk.Type.Base -> {
|
||||||
|
if (baseApk != null) {
|
||||||
|
throw IllegalArgumentException("Cannot have more than one base apk")
|
||||||
|
}
|
||||||
|
baseApk = Apk.Base(apk)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Apk.Type.SplitConfig -> {
|
||||||
|
val target = type.target
|
||||||
|
if (this.contains(target)) {
|
||||||
|
throw IllegalArgumentException("Duplicate split: $target")
|
||||||
|
}
|
||||||
|
|
||||||
|
val constructor = when (type) {
|
||||||
|
is Apk.Type.Asset -> Apk.Split::Asset
|
||||||
|
is Apk.Type.Library -> Apk.Split::Library
|
||||||
|
is Apk.Type.Language -> Apk.Split::Language
|
||||||
|
}
|
||||||
|
|
||||||
|
this[target] = constructor(target, apk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
|
base = baseApk ?: throw IllegalArgumentException("Base apk not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [ResourceTable] of this [ApkBundle].
|
||||||
|
*/
|
||||||
|
val resources = ResourceTable(base.resources, map { it.resources })
|
||||||
|
|
||||||
|
override fun iterator() = sequence {
|
||||||
|
yield(base)
|
||||||
|
splits?.values?.let {
|
||||||
|
yieldAll(it)
|
||||||
|
}
|
||||||
|
}.iterator()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh all updated resources in an [ApkBundle].
|
||||||
|
*
|
||||||
|
* @param options The [PatcherOptions] of the [Patcher].
|
||||||
|
* @return A sequence of the [Apk] files which are being refreshed.
|
||||||
|
*/
|
||||||
|
internal fun cleanup(options: PatcherOptions) = map {
|
||||||
|
var exception: ApkResourceException? = null
|
||||||
|
try {
|
||||||
|
it.cleanup(options)
|
||||||
|
} catch (e: ApkResourceException) {
|
||||||
|
exception = e
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitApkResult(it, exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of writing an [Apk] file.
|
||||||
|
*
|
||||||
|
* @param apk The corresponding [Apk] file.
|
||||||
|
* @param exception The optional [ApkResourceException] when an exception occurred.
|
||||||
|
*/
|
||||||
|
data class SplitApkResult(val apk: Apk, val exception: ApkResourceException? = null)
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
internal object AnnotationExtensions {
|
||||||
|
/**
|
||||||
|
* Recursively find a given annotation on a class.
|
||||||
|
*
|
||||||
|
* @param targetAnnotation The annotation to find.
|
||||||
|
* @return The annotation.
|
||||||
|
*/
|
||||||
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a label for the instruction at given index.
|
||||||
|
*
|
||||||
|
* @param index The index to create the label for the instruction at.
|
||||||
|
* @return The label.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a bitwise OR operation between two [AccessFlags].
|
||||||
|
*
|
||||||
|
* @param other The other [AccessFlags] to perform the operation with.
|
||||||
|
*/
|
||||||
|
infix fun AccessFlags.or(other: AccessFlags) = value or other.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a bitwise OR operation between an [AccessFlags] and an [Int].
|
||||||
|
*
|
||||||
|
* @param other The [Int] to perform the operation with.
|
||||||
|
*/
|
||||||
|
infix fun Int.or(other: AccessFlags) = this or other.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a bitwise OR operation between an [Int] and an [AccessFlags].
|
||||||
|
*
|
||||||
|
* @param other The [AccessFlags] to perform the operation with.
|
||||||
|
*/
|
||||||
|
infix fun AccessFlags.or(other: Int) = value or other
|
||||||
@@ -4,16 +4,15 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
import app.revanced.patcher.util.smali.toInstructions
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
import org.jf.dexlib2.builder.BuilderInstruction
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
||||||
import com.android.tools.smali.dexlib2.builder.Label
|
import org.jf.dexlib2.builder.Label
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.*
|
import org.jf.dexlib2.builder.instruction.*
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
import com.android.tools.smali.dexlib2.iface.MethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|
||||||
|
|
||||||
object InstructionExtensions {
|
object InstructionExtensions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* Add instructions to a method at the given index.
|
||||||
*
|
*
|
||||||
@@ -22,8 +21,9 @@ object InstructionExtensions {
|
|||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.addInstructions(
|
fun MutableMethodImplementation.addInstructions(
|
||||||
index: Int,
|
index: Int,
|
||||||
instructions: List<BuilderInstruction>,
|
instructions: List<BuilderInstruction>
|
||||||
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
) =
|
||||||
|
instructions.asReversed().forEach { addInstruction(index, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
@@ -32,7 +32,7 @@ object InstructionExtensions {
|
|||||||
* @param instructions The instructions to add.
|
* @param instructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
|
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
|
||||||
instructions.forEach { addInstruction(it) }
|
instructions.forEach { this.addInstruction(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instructions from a method at the given index.
|
* Remove instructions from a method at the given index.
|
||||||
@@ -40,10 +40,7 @@ object InstructionExtensions {
|
|||||||
* @param index The index to remove the instructions at.
|
* @param index The index to remove the instructions at.
|
||||||
* @param count The amount of instructions to remove.
|
* @param count The amount of instructions to remove.
|
||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.removeInstructions(
|
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) = repeat(count) {
|
||||||
index: Int,
|
|
||||||
count: Int,
|
|
||||||
) = repeat(count) {
|
|
||||||
removeInstruction(index)
|
removeInstruction(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,10 +58,7 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param instructions The instructions to replace the instructions with.
|
* @param instructions The instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethodImplementation.replaceInstructions(
|
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
||||||
index: Int,
|
|
||||||
instructions: List<BuilderInstruction>,
|
|
||||||
) {
|
|
||||||
// Remove the instructions at the given index.
|
// Remove the instructions at the given index.
|
||||||
removeInstructions(index, instructions.size)
|
removeInstructions(index, instructions.size)
|
||||||
|
|
||||||
@@ -78,17 +72,16 @@ object InstructionExtensions {
|
|||||||
* @param index The index to add the instruction at.
|
* @param index The index to add the instruction at.
|
||||||
* @param instruction The instruction to add.
|
* @param instruction The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(
|
fun MutableMethod.addInstruction(index: Int, instruction: BuilderInstruction) =
|
||||||
index: Int,
|
implementation!!.addInstruction(index, instruction)
|
||||||
instruction: BuilderInstruction,
|
|
||||||
) = implementation!!.addInstruction(index, instruction)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method.
|
* Add an instruction to a method.
|
||||||
*
|
*
|
||||||
* @param instruction The instructions to add.
|
* @param instruction The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction)
|
fun MutableMethod.addInstruction(instruction: BuilderInstruction) =
|
||||||
|
implementation!!.addInstruction(instruction)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method at the given index.
|
* Add an instruction to a method at the given index.
|
||||||
@@ -96,17 +89,17 @@ object InstructionExtensions {
|
|||||||
* @param index The index to add the instruction at.
|
* @param index The index to add the instruction at.
|
||||||
* @param smaliInstructions The instruction to add.
|
* @param smaliInstructions The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(
|
fun MutableMethod.addInstruction(index: Int, smaliInstructions: String) =
|
||||||
index: Int,
|
implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
||||||
smaliInstructions: String,
|
|
||||||
) = implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an instruction to a method.
|
* Add an instruction to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instruction to add.
|
* @param smaliInstructions The instruction to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
fun MutableMethod.addInstruction(smaliInstructions: String) =
|
||||||
|
implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* Add instructions to a method at the given index.
|
||||||
@@ -114,34 +107,32 @@ object InstructionExtensions {
|
|||||||
* @param index The index to add the instructions at.
|
* @param index The index to add the instructions at.
|
||||||
* @param instructions The instructions to add.
|
* @param instructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(
|
fun MutableMethod.addInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||||
index: Int,
|
implementation!!.addInstructions(index, instructions)
|
||||||
instructions: List<BuilderInstruction>,
|
|
||||||
) = implementation!!.addInstructions(index, instructions)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param instructions The instructions to add.
|
* @param instructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) = implementation!!.addInstructions(instructions)
|
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) =
|
||||||
|
implementation!!.addInstructions(instructions)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instructions to add.
|
* @param smaliInstructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(
|
fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) =
|
||||||
index: Int,
|
implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
smaliInstructions: String,
|
|
||||||
) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
*
|
*
|
||||||
* @param smaliInstructions The instructions to add.
|
* @param smaliInstructions The instructions to add.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
fun MutableMethod.addInstructions(smaliInstructions: String) =
|
||||||
|
implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method at the given index.
|
* Add instructions to a method at the given index.
|
||||||
@@ -154,15 +145,14 @@ object InstructionExtensions {
|
|||||||
fun MutableMethod.addInstructionsWithLabels(
|
fun MutableMethod.addInstructionsWithLabels(
|
||||||
index: Int,
|
index: Int,
|
||||||
smaliInstructions: String,
|
smaliInstructions: String,
|
||||||
vararg externalLabels: ExternalLabel,
|
vararg externalLabels: ExternalLabel
|
||||||
) {
|
) {
|
||||||
// Create reference dummy instructions for the instructions.
|
// Create reference dummy instructions for the instructions.
|
||||||
val nopSmali =
|
val nopSmali = StringBuilder(smaliInstructions).also { builder ->
|
||||||
StringBuilder(smaliInstructions).also { builder ->
|
externalLabels.forEach { (name, _) ->
|
||||||
externalLabels.forEach { (name, _) ->
|
builder.append("\n:$name\nnop")
|
||||||
builder.append("\n:$name\nnop")
|
}
|
||||||
}
|
}.toString()
|
||||||
}.toString()
|
|
||||||
|
|
||||||
// Compile the instructions with the dummy labels
|
// Compile the instructions with the dummy labels
|
||||||
val compiledInstructions = nopSmali.toInstructions(this)
|
val compiledInstructions = nopSmali.toInstructions(this)
|
||||||
@@ -170,7 +160,7 @@ object InstructionExtensions {
|
|||||||
// Add the compiled list of instructions to the method.
|
// Add the compiled list of instructions to the method.
|
||||||
addInstructions(
|
addInstructions(
|
||||||
index,
|
index,
|
||||||
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size),
|
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size)
|
||||||
)
|
)
|
||||||
|
|
||||||
implementation!!.apply {
|
implementation!!.apply {
|
||||||
@@ -180,29 +170,27 @@ object InstructionExtensions {
|
|||||||
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new label for the instruction
|
* Creates a new label for the instruction
|
||||||
* and replace it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
* and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
||||||
*/
|
*/
|
||||||
fun Instruction.makeNewLabel() {
|
fun Instruction.makeNewLabel() {
|
||||||
fun replaceOffset(
|
fun replaceOffset(
|
||||||
i: BuilderOffsetInstruction,
|
i: BuilderOffsetInstruction, label: Label
|
||||||
label: Label,
|
|
||||||
): BuilderOffsetInstruction {
|
): BuilderOffsetInstruction {
|
||||||
return when (i) {
|
return when (i) {
|
||||||
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
||||||
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
||||||
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
||||||
is BuilderInstruction22t ->
|
is BuilderInstruction22t -> BuilderInstruction22t(
|
||||||
BuilderInstruction22t(
|
i.opcode,
|
||||||
i.opcode,
|
i.registerA,
|
||||||
i.registerA,
|
i.registerB,
|
||||||
i.registerB,
|
label
|
||||||
label,
|
)
|
||||||
)
|
|
||||||
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
||||||
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
||||||
else -> throw IllegalStateException(
|
else -> throw IllegalStateException(
|
||||||
"A non-offset instruction was given, this should never happen!",
|
"A non-offset instruction was given, this should never happen!"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,11 +199,9 @@ object InstructionExtensions {
|
|||||||
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
||||||
|
|
||||||
// Create the final instruction with the new label.
|
// Create the final instruction with the new label.
|
||||||
val newInstruction =
|
val newInstruction = replaceOffset(
|
||||||
replaceOffset(
|
compiledInstruction, label
|
||||||
compiledInstruction,
|
)
|
||||||
label,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Replace the instruction pointing to the dummy label
|
// Replace the instruction pointing to the dummy label
|
||||||
// with the new instruction pointing to the real instruction.
|
// with the new instruction pointing to the real instruction.
|
||||||
@@ -248,7 +234,8 @@ object InstructionExtensions {
|
|||||||
*
|
*
|
||||||
* @param index The index to remove the instruction at.
|
* @param index The index to remove the instruction at.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index)
|
fun MutableMethod.removeInstruction(index: Int) =
|
||||||
|
implementation!!.removeInstruction(index)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instructions at the given index.
|
* Remove instructions at the given index.
|
||||||
@@ -256,17 +243,16 @@ object InstructionExtensions {
|
|||||||
* @param index The index to remove the instructions at.
|
* @param index The index to remove the instructions at.
|
||||||
* @param count The amount of instructions to remove.
|
* @param count The amount of instructions to remove.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstructions(
|
fun MutableMethod.removeInstructions(index: Int, count: Int) =
|
||||||
index: Int,
|
implementation!!.removeInstructions(index, count)
|
||||||
count: Int,
|
|
||||||
) = implementation!!.removeInstructions(index, count)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instructions at the given index.
|
* Remove instructions at the given index.
|
||||||
*
|
*
|
||||||
* @param count The amount of instructions to remove.
|
* @param count The amount of instructions to remove.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count)
|
fun MutableMethod.removeInstructions(count: Int) =
|
||||||
|
implementation!!.removeInstructions(count)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace an instruction at the given index.
|
* Replace an instruction at the given index.
|
||||||
@@ -274,10 +260,8 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instruction at.
|
* @param index The index to replace the instruction at.
|
||||||
* @param instruction The instruction to replace the instruction with.
|
* @param instruction The instruction to replace the instruction with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstruction(
|
fun MutableMethod.replaceInstruction(index: Int, instruction: BuilderInstruction) =
|
||||||
index: Int,
|
implementation!!.replaceInstruction(index, instruction)
|
||||||
instruction: BuilderInstruction,
|
|
||||||
) = implementation!!.replaceInstruction(index, instruction)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace an instruction at the given index.
|
* Replace an instruction at the given index.
|
||||||
@@ -285,10 +269,8 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instruction at.
|
* @param index The index to replace the instruction at.
|
||||||
* @param smaliInstruction The smali instruction to replace the instruction with.
|
* @param smaliInstruction The smali instruction to replace the instruction with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstruction(
|
fun MutableMethod.replaceInstruction(index: Int, smaliInstruction: String) =
|
||||||
index: Int,
|
implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
||||||
smaliInstruction: String,
|
|
||||||
) = implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace instructions at the given index.
|
* Replace instructions at the given index.
|
||||||
@@ -296,10 +278,8 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param instructions The instructions to replace the instructions with.
|
* @param instructions The instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstructions(
|
fun MutableMethod.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||||
index: Int,
|
implementation!!.replaceInstructions(index, instructions)
|
||||||
instructions: List<BuilderInstruction>,
|
|
||||||
) = implementation!!.replaceInstructions(index, instructions)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace instructions at the given index.
|
* Replace instructions at the given index.
|
||||||
@@ -307,28 +287,8 @@ object InstructionExtensions {
|
|||||||
* @param index The index to replace the instructions at.
|
* @param index The index to replace the instructions at.
|
||||||
* @param smaliInstructions The smali instructions to replace the instructions with.
|
* @param smaliInstructions The smali instructions to replace the instructions with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.replaceInstructions(
|
fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) =
|
||||||
index: Int,
|
implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
smaliInstructions: String,
|
|
||||||
) = implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
*
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @return The instruction.
|
|
||||||
*/
|
|
||||||
fun MethodImplementation.getInstruction(index: Int) = instructions.elementAt(index)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
*
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @param T The type of instruction to return.
|
|
||||||
* @return The instruction.
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T> MethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index.
|
* Get an instruction at the given index.
|
||||||
@@ -348,27 +308,12 @@ object InstructionExtensions {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @return The instruction or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
fun Method.getInstructionOrNull(index: Int): Instruction? = implementation?.getInstruction(index)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index.
|
* Get an instruction at the given index.
|
||||||
* @param index The index to get the instruction at.
|
* @param index The index to get the instruction at.
|
||||||
* @return The instruction.
|
* @return The instruction.
|
||||||
*/
|
*/
|
||||||
fun Method.getInstruction(index: Int): Instruction = getInstructionOrNull(index)!!
|
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = implementation!!.getInstruction(index)
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @param T The type of instruction to return.
|
|
||||||
* @return The instruction or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
fun <T> Method.getInstructionOrNull(index: Int): T? = implementation?.getInstruction<T>(index)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index.
|
* Get an instruction at the given index.
|
||||||
@@ -376,59 +321,6 @@ object InstructionExtensions {
|
|||||||
* @param T The type of instruction to return.
|
* @param T The type of instruction to return.
|
||||||
* @return The instruction.
|
* @return The instruction.
|
||||||
*/
|
*/
|
||||||
fun <T> Method.getInstruction(index: Int): T = getInstructionOrNull<T>(index)!!
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
|
||||||
/**
|
}
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @return The instruction or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.getInstructionOrNull(index: Int): BuilderInstruction? = implementation?.getInstruction(index)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @return The instruction.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = getInstructionOrNull(index)!!
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @param T The type of instruction to return.
|
|
||||||
* @return The instruction or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
fun <T> MutableMethod.getInstructionOrNull(index: Int): T? = implementation?.getInstruction<T>(index)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction at the given index.
|
|
||||||
* @param index The index to get the instruction at.
|
|
||||||
* @param T The type of instruction to return.
|
|
||||||
* @return The instruction.
|
|
||||||
*/
|
|
||||||
fun <T> MutableMethod.getInstruction(index: Int): T = getInstructionOrNull<T>(index)!!
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The instructions of a method.
|
|
||||||
* @return The instructions or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
val Method.instructionsOrNull: Iterable<Instruction>? get() = implementation?.instructions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The instructions of a method.
|
|
||||||
* @return The instructions.
|
|
||||||
*/
|
|
||||||
val Method.instructions: Iterable<Instruction> get() = instructionsOrNull!!
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The instructions of a method.
|
|
||||||
* @return The instructions or null if the method has no implementation.
|
|
||||||
*/
|
|
||||||
val MutableMethod.instructionsOrNull: MutableList<BuilderInstruction>? get() = implementation?.instructions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The instructions of a method.
|
|
||||||
* @return The instructions.
|
|
||||||
*/
|
|
||||||
val MutableMethod.instructions: MutableList<BuilderInstruction> get() = instructionsOrNull!!
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object MethodFingerprintExtensions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.name: String
|
||||||
|
get() = this.javaClass.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.fuzzyPatternScanMethod
|
||||||
|
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.PatchClass
|
||||||
|
import app.revanced.patcher.patch.PatchOptions
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
|
import kotlin.reflect.KVisibility
|
||||||
|
import kotlin.reflect.full.companionObject
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
|
||||||
|
object PatchExtensions {
|
||||||
|
/**
|
||||||
|
* The name of a [Patch].
|
||||||
|
*/
|
||||||
|
val PatchClass.patchName: String
|
||||||
|
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of a [Patch].
|
||||||
|
*/
|
||||||
|
@Deprecated("This property is deprecated and will be removed in the future.")
|
||||||
|
val PatchClass.version
|
||||||
|
get() = findAnnotationRecursively(Version::class)?.version
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] should be included.
|
||||||
|
*/
|
||||||
|
val PatchClass.include
|
||||||
|
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of a [Patch].
|
||||||
|
*/
|
||||||
|
val PatchClass.description
|
||||||
|
get() = findAnnotationRecursively(Description::class)?.description
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dependencies of a [Patch].
|
||||||
|
*/
|
||||||
|
val PatchClass.dependencies
|
||||||
|
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packages a [Patch] is compatible with.
|
||||||
|
*/
|
||||||
|
val PatchClass.compatiblePackages
|
||||||
|
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] requires integrations.
|
||||||
|
*/
|
||||||
|
internal val PatchClass.requiresIntegrations
|
||||||
|
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options of a [Patch].
|
||||||
|
*/
|
||||||
|
val PatchClass.options: PatchOptions?
|
||||||
|
get() = kotlin.companionObject?.let { cl ->
|
||||||
|
if (cl.visibility != KVisibility.PUBLIC) return null
|
||||||
|
kotlin.companionObjectInstance?.let {
|
||||||
|
(it as? OptionsContainer)?.options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced fingerprint.
|
||||||
|
* Can be a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
interface Fingerprint
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patcher.fingerprint.method.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
annotation class FuzzyPatternScanMethod(
|
||||||
|
val threshold: Int = 1
|
||||||
|
)
|
||||||
@@ -0,0 +1,513 @@
|
|||||||
|
package app.revanced.patcher.fingerprint.method.impl
|
||||||
|
|
||||||
|
import app.revanced.patcher.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.Fingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.StringReference
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch
|
||||||
|
private typealias StringsScanResult = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult
|
||||||
|
private typealias MethodClassPair = Pair<Method, ClassDef>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fingerprint to resolve methods.
|
||||||
|
*
|
||||||
|
* @param returnType The method's return type compared using [String.startsWith].
|
||||||
|
* @param accessFlags The method's exact access flags using values of [AccessFlags].
|
||||||
|
* @param parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType].
|
||||||
|
* @param opcodes An opcode pattern of the method's instructions. Wildcard or unknown opcodes can be specified by `null`.
|
||||||
|
* @param strings A list of the method's strings compared each using [String.contains].
|
||||||
|
* @param customFingerprint A custom condition for this fingerprint.
|
||||||
|
*/
|
||||||
|
abstract class MethodFingerprint(
|
||||||
|
internal val returnType: String? = null,
|
||||||
|
internal val accessFlags: Int? = null,
|
||||||
|
internal val parameters: Iterable<String>? = null,
|
||||||
|
internal val opcodes: Iterable<Opcode?>? = null,
|
||||||
|
internal val strings: Iterable<String>? = null,
|
||||||
|
internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null
|
||||||
|
) : Fingerprint {
|
||||||
|
/**
|
||||||
|
* The result of the [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
var result: MethodFingerprintResult? = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* A list of methods and the class they were found in.
|
||||||
|
*/
|
||||||
|
private val methods = mutableListOf<MethodClassPair>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup map for methods keyed to the methods access flags, return type and parameter.
|
||||||
|
*/
|
||||||
|
private val methodSignatureLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup map for methods keyed to the strings contained in the method.
|
||||||
|
*/
|
||||||
|
private val methodStringsLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a string based on the parameter reference types of this method.
|
||||||
|
*/
|
||||||
|
private fun StringBuilder.appendParameters(parameters: Iterable<CharSequence>) {
|
||||||
|
// Maximum parameters to use in the signature key.
|
||||||
|
// Some apps have methods with an incredible number of parameters (over 100 parameters have been seen).
|
||||||
|
// To keep the signature map from becoming needlessly bloated,
|
||||||
|
// group together in the same map entry all methods with the same access/return and 5 or more parameters.
|
||||||
|
// The value of 5 was chosen based on local performance testing and is not set in stone.
|
||||||
|
val maxSignatureParameters = 5
|
||||||
|
// Must append a unique value before the parameters to distinguish this key includes the parameters.
|
||||||
|
// If this is not appended, then methods with no parameters
|
||||||
|
// will collide with different keys that specify access/return but omit the parameters.
|
||||||
|
append("p:")
|
||||||
|
parameters.forEachIndexed { index, parameter ->
|
||||||
|
if (index >= maxSignatureParameters) return
|
||||||
|
append(parameter.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes lookup maps for [MethodFingerprint] resolution
|
||||||
|
* using attributes of methods such as the method signature or strings.
|
||||||
|
*
|
||||||
|
* @param context The [BytecodeContext] containing the classes to initialize the lookup maps with.
|
||||||
|
*/
|
||||||
|
internal fun initializeFingerprintResolutionLookupMaps(context: BytecodeContext) {
|
||||||
|
fun MutableMap<String, MutableList<MethodClassPair>>.add(
|
||||||
|
key: String,
|
||||||
|
methodClassPair: MethodClassPair
|
||||||
|
) {
|
||||||
|
var methodClassPairs = this[key]
|
||||||
|
|
||||||
|
methodClassPairs ?: run {
|
||||||
|
methodClassPairs = LinkedList<MethodClassPair>().also { this[key] = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
methodClassPairs!!.add(methodClassPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.isNotEmpty()) throw PatchException("Map already initialized")
|
||||||
|
|
||||||
|
context.classes.forEach { classDef ->
|
||||||
|
classDef.methods.forEach { method ->
|
||||||
|
val methodClassPair = method to classDef
|
||||||
|
|
||||||
|
// For fingerprints with no access or return type specified.
|
||||||
|
methods += methodClassPair
|
||||||
|
|
||||||
|
val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first()
|
||||||
|
|
||||||
|
// Add <access><returnType> as the key.
|
||||||
|
methodSignatureLookupMap.add(accessFlagsReturnKey, methodClassPair)
|
||||||
|
|
||||||
|
// Add <access><returnType>[parameters] as the key.
|
||||||
|
methodSignatureLookupMap.add(
|
||||||
|
buildString {
|
||||||
|
append(accessFlagsReturnKey)
|
||||||
|
appendParameters(method.parameterTypes)
|
||||||
|
},
|
||||||
|
methodClassPair
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add strings contained in the method as the key.
|
||||||
|
method.implementation?.instructions?.forEach instructions@{ instruction ->
|
||||||
|
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO)
|
||||||
|
return@instructions
|
||||||
|
|
||||||
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
|
|
||||||
|
methodStringsLookupMap.add(string, methodClassPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the future, the class type could be added to the lookup map.
|
||||||
|
// This would require MethodFingerprint to be changed to include the class type.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the internal lookup maps created in [initializeFingerprintResolutionLookupMaps]
|
||||||
|
*/
|
||||||
|
internal fun clearFingerprintResolutionLookupMaps() {
|
||||||
|
methods.clear()
|
||||||
|
methodSignatureLookupMap.clear()
|
||||||
|
methodStringsLookupMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a list of [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
||||||
|
*
|
||||||
|
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
|
||||||
|
* amount of time because they are resolved in sequence.
|
||||||
|
*
|
||||||
|
* For apps with many fingerprints, resolving performance can be improved by:
|
||||||
|
* - Slowest: Specify [opcodes] and nothing else.
|
||||||
|
* - Fast: Specify [accessFlags], [returnType].
|
||||||
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
|
*/
|
||||||
|
internal fun Iterable<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
||||||
|
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
||||||
|
|
||||||
|
for (fingerprint in this) {
|
||||||
|
fingerprint.resolveUsingLookupMap(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
||||||
|
*
|
||||||
|
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
|
||||||
|
* amount of time because they are resolved in sequence.
|
||||||
|
*
|
||||||
|
* For apps with many fingerprints, resolving performance can be improved by:
|
||||||
|
* - Slowest: Specify [opcodes] and nothing else.
|
||||||
|
* - Fast: Specify [accessFlags], [returnType].
|
||||||
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
|
*/
|
||||||
|
internal fun MethodFingerprint.resolveUsingLookupMap(context: BytecodeContext): Boolean {
|
||||||
|
/**
|
||||||
|
* Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
||||||
|
*
|
||||||
|
* @return A list of [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
fun MethodFingerprint.methodStringsLookup(): List<MethodClassPair>? {
|
||||||
|
strings?.forEach {
|
||||||
|
val methods = methodStringsLookupMap[it]
|
||||||
|
if (methods != null) return methods
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
|
||||||
|
*
|
||||||
|
* @return A list of [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
fun MethodFingerprint.methodSignatureLookup(): List<MethodClassPair> {
|
||||||
|
if (accessFlags == null) return methods
|
||||||
|
|
||||||
|
var returnTypeValue = returnType
|
||||||
|
if (returnTypeValue == null) {
|
||||||
|
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||||
|
// Constructors always have void return type
|
||||||
|
returnTypeValue = "V"
|
||||||
|
} else {
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = buildString {
|
||||||
|
append(accessFlags)
|
||||||
|
append(returnTypeValue.first())
|
||||||
|
if (parameters != null) appendParameters(parameters)
|
||||||
|
}
|
||||||
|
return methodSignatureLookupMap[key] ?: return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a [MethodFingerprint] using a list of [MethodClassPair].
|
||||||
|
*
|
||||||
|
* @return True if the resolution was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
fun MethodFingerprint.resolveUsingMethodClassPair(classMethods: Iterable<MethodClassPair>): Boolean {
|
||||||
|
classMethods.forEach { classAndMethod ->
|
||||||
|
if (resolve(context, classAndMethod.first, classAndMethod.second)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val methodsWithSameStrings = methodStringsLookup()
|
||||||
|
if (methodsWithSameStrings != null) if (resolveUsingMethodClassPair(methodsWithSameStrings)) return true
|
||||||
|
|
||||||
|
// No strings declared or none matched (partial matches are allowed).
|
||||||
|
// Use signature matching.
|
||||||
|
return resolveUsingMethodClassPair(methodSignatureLookup())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
|
||||||
|
*
|
||||||
|
* @param classes The classes on which to resolve the [MethodFingerprint] in.
|
||||||
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
|
* @return True if the resolution was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) {
|
||||||
|
for (fingerprint in this) // For each fingerprint...
|
||||||
|
classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint
|
||||||
|
if (fingerprint.resolve(context, classDef))
|
||||||
|
break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a [MethodFingerprint] against a [ClassDef].
|
||||||
|
*
|
||||||
|
* @param forClass The class on which to resolve the [MethodFingerprint] in.
|
||||||
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
|
* @return True if the resolution was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
fun MethodFingerprint.resolve(context: BytecodeContext, forClass: ClassDef): Boolean {
|
||||||
|
for (method in forClass.methods)
|
||||||
|
if (this.resolve(context, method, forClass))
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a [MethodFingerprint] against a [Method].
|
||||||
|
*
|
||||||
|
* @param method The class on which to resolve the [MethodFingerprint] in.
|
||||||
|
* @param forClass The class on which to resolve the [MethodFingerprint].
|
||||||
|
* @param context The [BytecodeContext] to host proxies.
|
||||||
|
* @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise.
|
||||||
|
*/
|
||||||
|
fun MethodFingerprint.resolve(context: BytecodeContext, method: Method, forClass: ClassDef): Boolean {
|
||||||
|
val methodFingerprint = this
|
||||||
|
|
||||||
|
if (methodFingerprint.result != null) return true
|
||||||
|
|
||||||
|
if (methodFingerprint.returnType != null && !method.returnType.startsWith(methodFingerprint.returnType))
|
||||||
|
return false
|
||||||
|
|
||||||
|
if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags)
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
fun parametersEqual(
|
||||||
|
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
||||||
|
): Boolean {
|
||||||
|
if (parameters1.count() != parameters2.count()) return false
|
||||||
|
val iterator1 = parameters1.iterator()
|
||||||
|
parameters2.forEach {
|
||||||
|
if (!it.startsWith(iterator1.next())) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodFingerprint.parameters != null && !parametersEqual(
|
||||||
|
methodFingerprint.parameters, // TODO: parseParameters()
|
||||||
|
method.parameterTypes
|
||||||
|
)
|
||||||
|
) return false
|
||||||
|
|
||||||
|
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||||
|
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass))
|
||||||
|
return false
|
||||||
|
|
||||||
|
val stringsScanResult: StringsScanResult? =
|
||||||
|
if (methodFingerprint.strings != null) {
|
||||||
|
StringsScanResult(
|
||||||
|
buildList {
|
||||||
|
val implementation = method.implementation ?: return false
|
||||||
|
|
||||||
|
val stringsList = methodFingerprint.strings.toMutableList()
|
||||||
|
|
||||||
|
implementation.instructions.forEachIndexed { instructionIndex, instruction ->
|
||||||
|
if (
|
||||||
|
instruction.opcode != Opcode.CONST_STRING &&
|
||||||
|
instruction.opcode != Opcode.CONST_STRING_JUMBO
|
||||||
|
) return@forEachIndexed
|
||||||
|
|
||||||
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
|
val index = stringsList.indexOfFirst(string::contains)
|
||||||
|
if (index == -1) return@forEachIndexed
|
||||||
|
|
||||||
|
add(
|
||||||
|
StringMatch(
|
||||||
|
string,
|
||||||
|
instructionIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
stringsList.removeAt(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stringsList.isNotEmpty()) return false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
|
||||||
|
val patternScanResult = if (methodFingerprint.opcodes != null) {
|
||||||
|
method.implementation?.instructions ?: return false
|
||||||
|
|
||||||
|
fun Method.patternScan(
|
||||||
|
fingerprint: MethodFingerprint
|
||||||
|
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
||||||
|
val instructions = this.implementation!!.instructions
|
||||||
|
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
|
||||||
|
|
||||||
|
val pattern = fingerprint.opcodes!!
|
||||||
|
val instructionLength = instructions.count()
|
||||||
|
val patternLength = pattern.count()
|
||||||
|
|
||||||
|
for (index in 0 until instructionLength) {
|
||||||
|
var patternIndex = 0
|
||||||
|
var threshold = fingerprintFuzzyPatternScanThreshold
|
||||||
|
|
||||||
|
while (index + patternIndex < instructionLength) {
|
||||||
|
val originalOpcode = instructions.elementAt(index + patternIndex).opcode
|
||||||
|
val patternOpcode = pattern.elementAt(patternIndex)
|
||||||
|
|
||||||
|
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
|
||||||
|
// Reaching maximum threshold (0) means,
|
||||||
|
// the pattern does not match to the current instructions.
|
||||||
|
if (threshold-- == 0) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patternIndex < patternLength - 1) {
|
||||||
|
// If the entire pattern has not been scanned yet
|
||||||
|
// continue the scan.
|
||||||
|
patternIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
|
||||||
|
val result =
|
||||||
|
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
||||||
|
index,
|
||||||
|
index + patternIndex
|
||||||
|
)
|
||||||
|
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
|
||||||
|
result.warnings = result.createWarnings(pattern, instructions)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
method.patternScan(methodFingerprint) ?: return false
|
||||||
|
} else null
|
||||||
|
|
||||||
|
methodFingerprint.result = MethodFingerprintResult(
|
||||||
|
method,
|
||||||
|
forClass,
|
||||||
|
MethodFingerprintResult.MethodFingerprintScanResult(
|
||||||
|
patternScanResult,
|
||||||
|
stringsScanResult
|
||||||
|
),
|
||||||
|
context
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings(
|
||||||
|
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
||||||
|
) = buildList {
|
||||||
|
for ((patternIndex, instructionIndex) in (this@createWarnings.startIndex until this@createWarnings.endIndex).withIndex()) {
|
||||||
|
val originalOpcode = instructions.elementAt(instructionIndex).opcode
|
||||||
|
val patternOpcode = pattern.elementAt(patternIndex)
|
||||||
|
|
||||||
|
if (patternOpcode == null || patternOpcode.ordinal == originalOpcode.ordinal) continue
|
||||||
|
|
||||||
|
this.add(
|
||||||
|
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.Warning(
|
||||||
|
originalOpcode,
|
||||||
|
patternOpcode,
|
||||||
|
instructionIndex,
|
||||||
|
patternIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of a [MethodFingerprintResult].
|
||||||
|
*
|
||||||
|
* @param method The matching method.
|
||||||
|
* @param classDef The [ClassDef] that contains the matching [method].
|
||||||
|
* @param scanResult The result of scanning for the [MethodFingerprint].
|
||||||
|
* @param context The [BytecodeContext] this [MethodFingerprintResult] is attached to, to create proxies.
|
||||||
|
*/
|
||||||
|
data class MethodFingerprintResult(
|
||||||
|
val method: Method,
|
||||||
|
val classDef: ClassDef,
|
||||||
|
val scanResult: MethodFingerprintScanResult,
|
||||||
|
internal val context: BytecodeContext
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Returns a mutable clone of [classDef]
|
||||||
|
*
|
||||||
|
* Please note, this method allocates a [ClassProxy].
|
||||||
|
* Use [classDef] where possible.
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
val mutableClass by lazy { context.classes.proxy(classDef).mutableClass }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mutable clone of [method]
|
||||||
|
*
|
||||||
|
* Please note, this method allocates a [ClassProxy].
|
||||||
|
* Use [method] where possible.
|
||||||
|
*/
|
||||||
|
val mutableMethod by lazy {
|
||||||
|
mutableClass.methods.first {
|
||||||
|
MethodUtil.methodSignaturesMatch(it, this.method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of scanning on the [MethodFingerprint].
|
||||||
|
* @param patternScanResult The result of the pattern scan.
|
||||||
|
* @param stringsScanResult The result of the string scan.
|
||||||
|
*/
|
||||||
|
data class MethodFingerprintScanResult(
|
||||||
|
val patternScanResult: PatternScanResult?,
|
||||||
|
val stringsScanResult: StringsScanResult?
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The result of scanning strings on the [MethodFingerprint].
|
||||||
|
* @param matches The list of strings that were matched.
|
||||||
|
*/
|
||||||
|
data class StringsScanResult(val matches: List<StringMatch>) {
|
||||||
|
/**
|
||||||
|
* Represents a match for a string at an index.
|
||||||
|
* @param string The string that was matched.
|
||||||
|
* @param index The index of the string.
|
||||||
|
*/
|
||||||
|
data class StringMatch(val string: String, val index: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a pattern scan.
|
||||||
|
* @param startIndex The start index of the instructions where to which this pattern matches.
|
||||||
|
* @param endIndex The end index of the instructions where to which this pattern matches.
|
||||||
|
* @param warnings A list of warnings considering this [PatternScanResult].
|
||||||
|
*/
|
||||||
|
data class PatternScanResult(
|
||||||
|
val startIndex: Int,
|
||||||
|
val endIndex: Int,
|
||||||
|
var warnings: List<Warning>? = null
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Represents warnings of the pattern scan.
|
||||||
|
* @param correctOpcode The opcode the instruction list has.
|
||||||
|
* @param wrongOpcode The opcode the pattern list of the signature currently has.
|
||||||
|
* @param instructionIndex The index of the opcode relative to the instruction list.
|
||||||
|
* @param patternIndex The index of the opcode relative to the pattern list from the signature.
|
||||||
|
*/
|
||||||
|
data class Warning(
|
||||||
|
val correctOpcode: Opcode,
|
||||||
|
val wrongOpcode: Opcode,
|
||||||
|
val instructionIndex: Int,
|
||||||
|
val patternIndex: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patcher.logging
|
||||||
|
|
||||||
|
interface Logger {
|
||||||
|
fun error(msg: String) {}
|
||||||
|
fun warn(msg: String) {}
|
||||||
|
fun info(msg: String) {}
|
||||||
|
fun trace(msg: String) {}
|
||||||
|
|
||||||
|
object Nop : Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a Patcher [Logger] into an [app.revanced.arsc.logging.Logger].
|
||||||
|
*/
|
||||||
|
internal fun Logger.asArscLogger() = object : app.revanced.arsc.logging.Logger {
|
||||||
|
override fun error(msg: String) = this@asArscLogger.error(msg)
|
||||||
|
override fun warn(msg: String) = this@asArscLogger.warn(msg)
|
||||||
|
override fun info(msg: String) = this@asArscLogger.info(msg)
|
||||||
|
override fun trace(msg: String) = this@asArscLogger.error(msg)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for patch options.
|
||||||
|
*/
|
||||||
|
abstract class OptionsContainer {
|
||||||
|
/**
|
||||||
|
* A list of [PatchOption]s.
|
||||||
|
* @see PatchOptions
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
|
protected fun <T> option(opt: PatchOption<T>): PatchOption<T> {
|
||||||
|
options.register(opt)
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.BytecodeContext
|
||||||
|
import app.revanced.patcher.Context
|
||||||
|
import app.revanced.patcher.ResourceContext
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced patch.
|
||||||
|
*
|
||||||
|
* If it implements [Closeable], it will be closed after all patches have been executed.
|
||||||
|
* Closing will be done in reverse execution order.
|
||||||
|
*/
|
||||||
|
sealed interface Patch<out T : Context> {
|
||||||
|
/**
|
||||||
|
* The main function of the [Patch] which the patcher will call.
|
||||||
|
*
|
||||||
|
* @param context The [Context] the patch will work on.
|
||||||
|
*/
|
||||||
|
suspend fun execute(context: @UnsafeVariance T)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource patch for the Patcher.
|
||||||
|
*/
|
||||||
|
interface ResourcePatch : Patch<ResourceContext>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bytecode patch for the Patcher.
|
||||||
|
*
|
||||||
|
* @param fingerprints A list of [MethodFingerprint] this patch relies on.
|
||||||
|
*/
|
||||||
|
abstract class BytecodePatch(
|
||||||
|
internal val fingerprints: Iterable<MethodFingerprint>? = null
|
||||||
|
) : Patch<BytecodeContext>
|
||||||
|
|
||||||
|
// TODO: populate this everywhere where the alias is not used yet
|
||||||
|
/**
|
||||||
|
* The class type of [Patch].
|
||||||
|
*/
|
||||||
|
typealias PatchClass = Class<out Patch<Context>>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when patching.
|
||||||
|
*
|
||||||
|
* @param errorMessage The exception message.
|
||||||
|
* @param cause The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
class PatchException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
||||||
|
constructor(errorMessage: String) : this(errorMessage, null)
|
||||||
|
constructor(cause: Throwable) : this(cause.message, cause)
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
@file:Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST")
|
||||||
|
|
||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
||||||
|
class IllegalValueException(val value: Any?) : Exception("Illegal value: $value")
|
||||||
|
class InvalidTypeException(val got: String, val expected: String) :
|
||||||
|
Exception("Invalid option value type: $got, expected $expected")
|
||||||
|
|
||||||
|
object RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registry for an array of [PatchOption]s.
|
||||||
|
* @param options An array of [PatchOption]s.
|
||||||
|
*/
|
||||||
|
class PatchOptions(vararg options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
||||||
|
private val register = mutableMapOf<String, PatchOption<*>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
options.forEach { register(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun register(option: PatchOption<*>) {
|
||||||
|
if (register.containsKey(option.key)) {
|
||||||
|
throw IllegalStateException("Multiple options found with the same key")
|
||||||
|
}
|
||||||
|
register[option.key] = option
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a [PatchOption] by its key.
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
*/
|
||||||
|
@JvmName("getUntyped")
|
||||||
|
operator fun get(key: String) = register[key] ?: throw NoSuchOptionException(key)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a [PatchOption] by its key.
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
*/
|
||||||
|
inline operator fun <reified T> get(key: String): PatchOption<T> {
|
||||||
|
val opt = get(key)
|
||||||
|
if (opt.value !is T) throw InvalidTypeException(
|
||||||
|
opt.value?.let { it::class.java.canonicalName } ?: "null",
|
||||||
|
T::class.java.canonicalName
|
||||||
|
)
|
||||||
|
return opt as PatchOption<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of a [PatchOption].
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
* @param value The value you want it to be.
|
||||||
|
* Please note that using the wrong value type results in a runtime error.
|
||||||
|
*/
|
||||||
|
inline operator fun <reified T> set(key: String, value: T) {
|
||||||
|
val opt = get<T>(key)
|
||||||
|
if (opt.value !is T) throw InvalidTypeException(
|
||||||
|
T::class.java.canonicalName,
|
||||||
|
opt.value?.let { it::class.java.canonicalName } ?: "null"
|
||||||
|
)
|
||||||
|
opt.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of a [PatchOption] to `null`.
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
*/
|
||||||
|
fun nullify(key: String) {
|
||||||
|
get(key).value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator() = register.values.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Patch] option.
|
||||||
|
* @param key Unique identifier of the option. Example: _`settings.microg.enabled`_
|
||||||
|
* @param default The default value of the option.
|
||||||
|
* @param title A human-readable title of the option. Example: _MicroG Settings_
|
||||||
|
* @param description A human-readable description of the option. Example: _Settings integration for MicroG._
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
sealed class PatchOption<T>(
|
||||||
|
val key: String,
|
||||||
|
default: T?,
|
||||||
|
val title: String,
|
||||||
|
val description: String,
|
||||||
|
val required: Boolean,
|
||||||
|
val validator: (T?) -> Boolean
|
||||||
|
) {
|
||||||
|
var value: T? = default
|
||||||
|
get() {
|
||||||
|
if (field == null && required) {
|
||||||
|
throw RequirementNotMetException
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
if (value == null && required) {
|
||||||
|
throw RequirementNotMetException
|
||||||
|
}
|
||||||
|
if (!validator(value)) {
|
||||||
|
throw IllegalValueException(value)
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the option.
|
||||||
|
* Please note that using the wrong value type results in a runtime error.
|
||||||
|
*/
|
||||||
|
@JvmName("getValueTyped")
|
||||||
|
inline operator fun <reified V> getValue(thisRef: Nothing?, property: KProperty<*>): V? {
|
||||||
|
if (value !is V?) throw InvalidTypeException(
|
||||||
|
V::class.java.canonicalName,
|
||||||
|
value?.let { it::class.java.canonicalName } ?: "null"
|
||||||
|
)
|
||||||
|
return value as? V?
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the option.
|
||||||
|
* Please note that using the wrong value type results in a runtime error.
|
||||||
|
*/
|
||||||
|
@JvmName("setValueTyped")
|
||||||
|
inline operator fun <reified V> setValue(thisRef: Nothing?, property: KProperty<*>, new: V) {
|
||||||
|
if (value !is V) throw InvalidTypeException(
|
||||||
|
V::class.java.canonicalName,
|
||||||
|
value?.let { it::class.java.canonicalName } ?: "null"
|
||||||
|
)
|
||||||
|
value = new as T
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun setValue(thisRef: Any?, property: KProperty<*>, new: T?) {
|
||||||
|
value = new
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [String].
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class StringOption(
|
||||||
|
key: String,
|
||||||
|
default: String?,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (String?) -> Boolean = { true }
|
||||||
|
) : PatchOption<String>(
|
||||||
|
key, default, title, description, required, validator
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] representing a [Boolean].
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
class BooleanOption(
|
||||||
|
key: String,
|
||||||
|
default: Boolean?,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Boolean?) -> Boolean = { true }
|
||||||
|
) : PatchOption<Boolean>(
|
||||||
|
key, default, title, description, required, validator
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchOption] with a list of allowed options.
|
||||||
|
* @param options A list of allowed options for the [ListOption].
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
sealed class ListOption<E>(
|
||||||
|
key: String,
|
||||||
|
default: E?,
|
||||||
|
val options: Iterable<E>,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (E?) -> Boolean = { true }
|
||||||
|
) : PatchOption<E>(
|
||||||
|
key, default, title, description, required, {
|
||||||
|
(it?.let { it in options } ?: true) && validator(it)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
if (default != null && default !in options) {
|
||||||
|
throw IllegalStateException("Default option must be an allowed option")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ListOption] of type [String].
|
||||||
|
* @see ListOption
|
||||||
|
*/
|
||||||
|
class StringListOption(
|
||||||
|
key: String,
|
||||||
|
default: String?,
|
||||||
|
options: Iterable<String>,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (String?) -> Boolean = { true }
|
||||||
|
) : ListOption<String>(
|
||||||
|
key, default, options, title, description, required, validator
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ListOption] of type [Int].
|
||||||
|
* @see ListOption
|
||||||
|
*/
|
||||||
|
class IntListOption(
|
||||||
|
key: String,
|
||||||
|
default: Int?,
|
||||||
|
options: Iterable<Int>,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: (Int?) -> Boolean = { true }
|
||||||
|
) : ListOption<Int>(
|
||||||
|
key, default, options, title, description, required, validator
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package app.revanced.patcher.patch.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.Context
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to mark a class as a patch.
|
||||||
|
* @param include If false, the patch should be treated as optional by default.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class Patch(val include: Boolean = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for dependencies of [Patch]es.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class DependsOn(
|
||||||
|
val dependencies: Array<KClass<out Patch<Context>>> = [] // TODO: This should be a list of PatchClass instead
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to mark [Patch]es which depend on integrations.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class RequiresIntegrations // TODO: Remove this annotation and replace it with a proper system
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
package app.revanced.patcher.util
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
import app.revanced.patcher.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.logging.Logger
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.traverseClassHierarchy
|
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
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.MutableClass.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
import java.util.logging.Logger
|
|
||||||
import kotlin.reflect.KFunction2
|
import kotlin.reflect.KFunction2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,43 +25,38 @@ import kotlin.reflect.KFunction2
|
|||||||
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
||||||
*/
|
*/
|
||||||
internal object ClassMerger {
|
internal object ClassMerger {
|
||||||
private val logger = Logger.getLogger(ClassMerger::class.java.name)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge a class with [otherClass].
|
* Merge a class with [otherClass].
|
||||||
*
|
*
|
||||||
* @param otherClass The class to merge with
|
* @param otherClass The class to merge with
|
||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
* @return The merged class or the original class if no merge was needed.
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
fun ClassDef.merge(
|
fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext, logger: Logger? = null) = this
|
||||||
otherClass: ClassDef,
|
//.fixFieldAccess(otherClass, logger)
|
||||||
context: BytecodePatchContext,
|
//.fixMethodAccess(otherClass, logger)
|
||||||
) = this
|
.addMissingFields(otherClass, logger)
|
||||||
// .fixFieldAccess(otherClass)
|
.addMissingMethods(otherClass, logger)
|
||||||
// .fixMethodAccess(otherClass)
|
.publicize(otherClass, context, logger)
|
||||||
.addMissingFields(otherClass)
|
|
||||||
.addMissingMethods(otherClass)
|
|
||||||
.publicize(otherClass, context)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add methods which are missing but existing in [fromClass].
|
* Add methods which are missing but existing in [fromClass].
|
||||||
*
|
*
|
||||||
* @param fromClass The class to add missing methods from.
|
* @param fromClass The class to add missing methods from.
|
||||||
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef {
|
private fun ClassDef.addMissingMethods(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
val missingMethods =
|
val missingMethods = fromClass.methods.let { fromMethods ->
|
||||||
fromClass.methods.let { fromMethods ->
|
methods.filterNot { method ->
|
||||||
methods.filterNot { method ->
|
fromMethods.any { fromMethod ->
|
||||||
fromMethods.any { fromMethod ->
|
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
||||||
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (missingMethods.isEmpty()) return this
|
if (missingMethods.isEmpty()) return this
|
||||||
|
|
||||||
logger.fine { "Found ${missingMethods.size} missing methods" }
|
logger?.trace("Found ${missingMethods.size} missing methods")
|
||||||
|
|
||||||
return asMutableClass().apply {
|
return asMutableClass().apply {
|
||||||
methods.addAll(missingMethods.map { it.toMutable() })
|
methods.addAll(missingMethods.map { it.toMutable() })
|
||||||
@@ -71,16 +67,16 @@ internal object ClassMerger {
|
|||||||
* Add fields which are missing but existing in [fromClass].
|
* Add fields which are missing but existing in [fromClass].
|
||||||
*
|
*
|
||||||
* @param fromClass The class to add missing fields from.
|
* @param fromClass The class to add missing fields from.
|
||||||
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef {
|
private fun ClassDef.addMissingFields(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
val missingFields =
|
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
||||||
fields.filterNotAny(fromClass.fields) { field, fromField ->
|
fromField.name == field.name
|
||||||
fromField.name == field.name
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (missingFields.isEmpty()) return this
|
if (missingFields.isEmpty()) return this
|
||||||
|
|
||||||
logger.fine { "Found ${missingFields.size} missing fields" }
|
logger?.trace("Found ${missingFields.size} missing fields")
|
||||||
|
|
||||||
return asMutableClass().apply {
|
return asMutableClass().apply {
|
||||||
fields.addAll(missingFields.map { it.toMutable() })
|
fields.addAll(missingFields.map { it.toMutable() })
|
||||||
@@ -91,40 +87,37 @@ internal object ClassMerger {
|
|||||||
* Make a class and its super class public recursively.
|
* Make a class and its super class public recursively.
|
||||||
* @param reference The class to check the [AccessFlags] of.
|
* @param reference The class to check the [AccessFlags] of.
|
||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.publicize(
|
private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext, logger: Logger? = null) =
|
||||||
reference: ClassDef,
|
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
||||||
context: BytecodePatchContext,
|
this.asMutableClass().apply {
|
||||||
) = if (reference.accessFlags.isPublic() && !accessFlags.isPublic()) {
|
context.traverseClassHierarchy(this) {
|
||||||
this.asMutableClass().apply {
|
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||||
context.traverseClassHierarchy(this) {
|
|
||||||
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
|
||||||
|
|
||||||
logger.fine { "Publicizing ${this.type}" }
|
logger?.trace("Publicizing ${this.type}")
|
||||||
|
|
||||||
accessFlags = accessFlags.toPublic()
|
accessFlags = accessFlags.toPublic()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else this
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publicize fields if they are public in [reference].
|
* Publicize fields if they are public in [reference].
|
||||||
*
|
*
|
||||||
* @param reference The class to check the [AccessFlags] of the fields in.
|
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||||
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef {
|
private fun ClassDef.fixFieldAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
val brokenFields =
|
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
||||||
fields.filterAny(reference.fields) { field, referenceField ->
|
if (field.name != referenceField.name) return@filterAny false
|
||||||
if (field.name != referenceField.name) return@filterAny false
|
|
||||||
|
|
||||||
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brokenFields.isEmpty()) return this
|
if (brokenFields.isEmpty()) return this
|
||||||
|
|
||||||
logger.fine { "Found ${brokenFields.size} broken fields" }
|
logger?.trace("Found ${brokenFields.size} broken fields")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a field public.
|
* Make a field public.
|
||||||
@@ -142,18 +135,18 @@ internal object ClassMerger {
|
|||||||
* Publicize methods if they are public in [reference].
|
* Publicize methods if they are public in [reference].
|
||||||
*
|
*
|
||||||
* @param reference The class to check the [AccessFlags] of the methods in.
|
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||||
|
* @param logger A logger.
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef {
|
private fun ClassDef.fixMethodAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||||
val brokenMethods =
|
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||||
methods.filterAny(reference.methods) { method, referenceMethod ->
|
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||||
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
|
||||||
|
|
||||||
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (brokenMethods.isEmpty()) return this
|
if (brokenMethods.isEmpty()) return this
|
||||||
|
|
||||||
logger.fine { "Found ${brokenMethods.size} methods" }
|
logger?.trace("Found ${brokenMethods.size} methods")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a method public.
|
* Make a method public.
|
||||||
@@ -168,24 +161,6 @@ internal object ClassMerger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object Utils {
|
private object Utils {
|
||||||
/**
|
|
||||||
* 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 BytecodePatchContext.traverseClassHierarchy(
|
|
||||||
targetClass: MutableClass,
|
|
||||||
callback: MutableClass.() -> Unit,
|
|
||||||
) {
|
|
||||||
callback(targetClass)
|
|
||||||
|
|
||||||
targetClass.superclass ?: return
|
|
||||||
this.classBy { targetClass.superclass == it.type }?.mutableClass?.let {
|
|
||||||
traverseClassHierarchy(it, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,40 +175,40 @@ internal object ClassMerger {
|
|||||||
*
|
*
|
||||||
* @return The new [AccessFlags].
|
* @return The new [AccessFlags].
|
||||||
*/
|
*/
|
||||||
fun Int.toPublic() = or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
fun Int.toPublic() = this.or(AccessFlags.PUBLIC).and(AccessFlags.PRIVATE.value.inv())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter [this] on [needles] matching the given [predicate].
|
* Filter [this] on [needles] matching the given [predicate].
|
||||||
*
|
*
|
||||||
|
* @param this The hay to filter for [needles].
|
||||||
* @param needles The needles to filter [this] with.
|
* @param needles The needles to filter [this] with.
|
||||||
* @param predicate The filter.
|
* @param predicate The filter.
|
||||||
* @return The [this] filtered on [needles] matching the given [predicate].
|
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||||
*/
|
*/
|
||||||
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
||||||
needles: Iterable<NeedleType>,
|
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||||
predicate: (HayType, NeedleType) -> Boolean,
|
|
||||||
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter [this] on [needles] not matching the given [predicate].
|
* Filter [this] on [needles] not matching the given [predicate].
|
||||||
*
|
*
|
||||||
|
* @param this The hay to filter for [needles].
|
||||||
* @param needles The needles to filter [this] with.
|
* @param needles The needles to filter [this] with.
|
||||||
* @param predicate The filter.
|
* @param predicate The filter.
|
||||||
* @return The [this] filtered on [needles] not matching the given [predicate].
|
* @return The [this] filtered on [needles] not matching the given [predicate].
|
||||||
*/
|
*/
|
||||||
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
||||||
needles: Iterable<NeedleType>,
|
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||||
predicate: (HayType, NeedleType) -> Boolean,
|
|
||||||
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
||||||
|
|
||||||
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
||||||
haystack: Iterable<HayType>,
|
haystack: Iterable<HayType>,
|
||||||
needles: Iterable<NeedleType>,
|
needles: Iterable<NeedleType>,
|
||||||
predicate: (HayType, NeedleType) -> Boolean,
|
predicate: (HayType, NeedleType) -> Boolean
|
||||||
) = this(haystack) { hay ->
|
) = this(haystack) { hay ->
|
||||||
needles.any { needle ->
|
needles.any { needle ->
|
||||||
predicate(hay, needle)
|
predicate(hay, needle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
internal class ListBackedSet<E>(private val list: MutableList<E>) : MutableSet<E> {
|
||||||
|
override val size get() = list.size
|
||||||
|
override fun add(element: E) = list.add(element)
|
||||||
|
override fun addAll(elements: Collection<E>) = list.addAll(elements)
|
||||||
|
override fun clear() = list.clear()
|
||||||
|
override fun iterator() = list.listIterator()
|
||||||
|
override fun remove(element: E) = list.remove(element)
|
||||||
|
override fun removeAll(elements: Collection<E>) = list.removeAll(elements)
|
||||||
|
override fun retainAll(elements: Collection<E>) = list.retainAll(elements)
|
||||||
|
override fun contains(element: E) = list.contains(element)
|
||||||
|
override fun containsAll(elements: Collection<E>) = list.containsAll(elements)
|
||||||
|
override fun isEmpty() = list.isEmpty()
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents a list of classes and proxies.
|
||||||
|
*
|
||||||
|
* @param classes The classes to be backed by proxies.
|
||||||
|
*/
|
||||||
|
class ProxyBackedClassList(classes: Set<ClassDef>) : Iterable<ClassDef> {
|
||||||
|
// A list for pending proxied classes to be added to the current ProxyBackedClassList instance.
|
||||||
|
private val proxiedClasses = mutableListOf<ClassProxy>()
|
||||||
|
private val mutableClasses = classes.toMutableList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the [mutableClasses]es with their proxies.
|
||||||
|
*/
|
||||||
|
internal fun applyProxies() {
|
||||||
|
proxiedClasses.removeIf { proxy ->
|
||||||
|
// If the proxy is unused, keep it in the proxiedClasses list.
|
||||||
|
if (!proxy.resolved) return@removeIf false
|
||||||
|
|
||||||
|
with(mutableClasses) {
|
||||||
|
remove(proxy.immutableClass)
|
||||||
|
add(proxy.mutableClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@removeIf true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a [ClassDef] at a given [index].
|
||||||
|
*
|
||||||
|
* @param index The index of the class to be replaced.
|
||||||
|
* @param classDef The new class to replace the old one.
|
||||||
|
*/
|
||||||
|
operator fun set(index: Int, classDef: ClassDef) {
|
||||||
|
mutableClasses[index] = classDef
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a [ClassDef] at a given [index].
|
||||||
|
*
|
||||||
|
* @param index The index of the class.
|
||||||
|
*/
|
||||||
|
operator fun get(index: Int) = mutableClasses[index]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator for the classes in [ProxyBackedClassList].
|
||||||
|
*
|
||||||
|
* @return The iterator for the classes.
|
||||||
|
*/
|
||||||
|
override fun iterator() = mutableClasses.iterator()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy a [ClassDef].
|
||||||
|
*
|
||||||
|
* Note: This creates a [ClassProxy] of the [ClassDef], if not already present.
|
||||||
|
*
|
||||||
|
* @return A proxy for the given class.
|
||||||
|
*/
|
||||||
|
fun proxy(classDef: ClassDef) = proxiedClasses
|
||||||
|
.find { it.immutableClass.type == classDef.type } ?: ClassProxy(classDef).also(proxiedClasses::add)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a [ClassDef].
|
||||||
|
*/
|
||||||
|
fun add(classDef: ClassDef) = mutableClasses.add(classDef)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 findClassProxied(className: String) = findClassProxied { 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 findClassProxied(predicate: (ClassDef) -> Boolean) = this.find(predicate)?.let(::proxy)
|
||||||
|
|
||||||
|
val size get() = mutableClasses.size
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import app.revanced.patcher.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 The function that is called for every class in the hierarchy.
|
||||||
|
*/
|
||||||
|
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||||
|
callback(targetClass)
|
||||||
|
this.classes.findClassProxied(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||||
|
traverseClassHierarchy(it, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Deprecated("This class serves no purpose anymore")
|
||||||
|
internal object VersionReader {
|
||||||
|
@JvmStatic
|
||||||
|
private val properties = Properties().apply {
|
||||||
|
load(
|
||||||
|
VersionReader::class.java.getResourceAsStream("/app/revanced/patcher/version.properties")
|
||||||
|
?: throw IllegalStateException("Could not load version.properties")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun read(): String {
|
||||||
|
return properties.getProperty("version") ?: throw IllegalStateException("Version not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.patcher.util.method
|
||||||
|
|
||||||
|
import app.revanced.patcher.BytecodeContext
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a method from another method via instruction offsets.
|
||||||
|
* @param bytecodeContext The context to use when resolving the next method reference.
|
||||||
|
* @param currentMethod The method to start from.
|
||||||
|
*/
|
||||||
|
class MethodWalker internal constructor(
|
||||||
|
private val bytecodeContext: BytecodeContext,
|
||||||
|
private var currentMethod: Method
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Get the method which was walked last.
|
||||||
|
*
|
||||||
|
* It is possible to cast this method to a [MutableMethod], if the method has been walked mutably.
|
||||||
|
*
|
||||||
|
* @return The method which was walked last.
|
||||||
|
*/
|
||||||
|
fun getMethod(): Method {
|
||||||
|
return currentMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk to a method defined at the offset in the instruction list of the current method.
|
||||||
|
*
|
||||||
|
* The current method will be mutable.
|
||||||
|
*
|
||||||
|
* @param offset The offset of the instruction. This instruction must be of format 35c.
|
||||||
|
* @param walkMutable If this is true, the class of the method will be resolved mutably.
|
||||||
|
* @return The same [MethodWalker] instance with the method at [offset].
|
||||||
|
*/
|
||||||
|
fun nextMethod(offset: Int, walkMutable: Boolean = false): MethodWalker {
|
||||||
|
currentMethod.implementation?.instructions?.let { instructions ->
|
||||||
|
val instruction = instructions.elementAt(offset)
|
||||||
|
|
||||||
|
val newMethod = (instruction as ReferenceInstruction).reference as MethodReference
|
||||||
|
val proxy = bytecodeContext.classes.findClassProxied(newMethod.definingClass)!!
|
||||||
|
|
||||||
|
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
||||||
|
currentMethod = methods.first {
|
||||||
|
return@first MethodUtil.methodSignaturesMatch(it, newMethod)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
throw MethodNotFoundException("This method can not be walked at offset $offset inside the method ${currentMethod.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MethodNotFoundException(exception: String) : Exception(exception)
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package app.revanced.patcher.util.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.PatchClass
|
||||||
|
import dalvik.system.PathClassLoader
|
||||||
|
import org.jf.dexlib2.DexFileFactory
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import kotlin.streams.toList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A patch bundle.
|
||||||
|
*
|
||||||
|
* @param fromClasses The classes to get [Patch]es from.
|
||||||
|
*/
|
||||||
|
sealed class PatchBundle private constructor(fromClasses: Iterable<Class<*>>) : Iterable<PatchClass> {
|
||||||
|
private val patches = fromClasses.filter {
|
||||||
|
if (it.isAnnotation) return@filter false
|
||||||
|
|
||||||
|
it.findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class) != null
|
||||||
|
}.map {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
it as PatchClass
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator() = patches.iterator()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A patch bundle of type [Jar].
|
||||||
|
*
|
||||||
|
* @param patchBundlePath The path to a patch bundle.
|
||||||
|
*/
|
||||||
|
class Jar(private val patchBundlePath: File) : PatchBundle(
|
||||||
|
with(URLClassLoader(arrayOf(patchBundlePath.toURI().toURL()), PatchBundle::class.java.classLoader)) {
|
||||||
|
JarFile(patchBundlePath).stream().filter { it.name.endsWith(".class") }.map {
|
||||||
|
loadClass(
|
||||||
|
it.realName.replace('/', '.').replace(".class", "")
|
||||||
|
)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A patch bundle of type [Dex] format.
|
||||||
|
*
|
||||||
|
* @param patchBundlePath The path to a patch bundle of dex format.
|
||||||
|
*/
|
||||||
|
class Dex(private val patchBundlePath: File) : PatchBundle(
|
||||||
|
with(PathClassLoader(patchBundlePath.absolutePath, null, PatchBundle::class.java.classLoader)) {
|
||||||
|
DexFileFactory.loadDexFile(patchBundlePath, null).classes.map { classDef ->
|
||||||
|
classDef.type.substring(1, classDef.length - 1).replace('/', '.')
|
||||||
|
}.map { loadClass(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
package app.revanced.patcher.util.proxy
|
package app.revanced.patcher.util.proxy
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A proxy class for a [ClassDef].
|
* A proxy class for a [ClassDef].
|
||||||
*
|
*
|
||||||
* A class proxy simply holds a reference to the original class
|
* A class proxy simply holds a reference to the original class
|
||||||
* and allocates a mutable clone for the original class if needed.
|
* and allocates a mutable clone for the original class if needed.
|
||||||
*
|
|
||||||
* @param immutableClass The class to proxy.
|
* @param immutableClass The class to proxy.
|
||||||
*/
|
*/
|
||||||
class ClassProxy internal constructor(
|
class ClassProxy internal constructor(
|
||||||
@@ -28,8 +27,7 @@ class ClassProxy internal constructor(
|
|||||||
resolved = true
|
resolved = true
|
||||||
if (immutableClass is MutableClass) {
|
if (immutableClass is MutableClass) {
|
||||||
immutableClass
|
immutableClass
|
||||||
} else {
|
} else
|
||||||
MutableClass(immutableClass)
|
MutableClass(immutableClass)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,29 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes
|
package app.revanced.patcher.util.proxy.mutableTypes
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.base.BaseAnnotation
|
import org.jf.dexlib2.base.BaseAnnotation
|
||||||
import com.android.tools.smali.dexlib2.iface.Annotation
|
import org.jf.dexlib2.iface.Annotation
|
||||||
|
|
||||||
class MutableAnnotation(annotation: Annotation) : BaseAnnotation() {
|
class MutableAnnotation(annotation: Annotation) : BaseAnnotation() {
|
||||||
private val visibility = annotation.visibility
|
private val visibility = annotation.visibility
|
||||||
private val type = annotation.type
|
private val type = annotation.type
|
||||||
private val _elements by lazy { annotation.elements.map { element -> element.toMutable() }.toMutableSet() }
|
private val _elements by lazy { annotation.elements.map { element -> element.toMutable() }.toMutableSet() }
|
||||||
|
|
||||||
override fun getType(): String = type
|
override fun getType(): String {
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
|
||||||
override fun getElements(): MutableSet<MutableAnnotationElement> = _elements
|
override fun getElements(): MutableSet<MutableAnnotationElement> {
|
||||||
|
return _elements
|
||||||
|
}
|
||||||
|
|
||||||
override fun getVisibility(): Int = visibility
|
override fun getVisibility(): Int {
|
||||||
|
return visibility
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun Annotation.toMutable(): MutableAnnotation = MutableAnnotation(this)
|
fun Annotation.toMutable(): MutableAnnotation {
|
||||||
|
return MutableAnnotation(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.base.BaseAnnotationElement
|
import org.jf.dexlib2.base.BaseAnnotationElement
|
||||||
import com.android.tools.smali.dexlib2.iface.AnnotationElement
|
import org.jf.dexlib2.iface.AnnotationElement
|
||||||
import com.android.tools.smali.dexlib2.iface.value.EncodedValue
|
import org.jf.dexlib2.iface.value.EncodedValue
|
||||||
|
|
||||||
class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnotationElement() {
|
class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnotationElement() {
|
||||||
private var name = annotationElement.name
|
private var name = annotationElement.name
|
||||||
@@ -18,11 +18,17 @@ class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnot
|
|||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getName(): String = name
|
override fun getName(): String {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
override fun getValue(): EncodedValue = value
|
override fun getValue(): EncodedValue {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun AnnotationElement.toMutable(): MutableAnnotationElement = MutableAnnotationElement(this)
|
fun AnnotationElement.toMutable(): MutableAnnotationElement {
|
||||||
|
return MutableAnnotationElement(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package app.revanced.patcher.util.proxy.mutableTypes
|
||||||
|
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import com.google.common.collect.Iterables
|
||||||
|
import org.jf.dexlib2.base.reference.BaseTypeReference
|
||||||
|
import org.jf.dexlib2.iface.ClassDef
|
||||||
|
import org.jf.dexlib2.util.FieldUtil
|
||||||
|
import org.jf.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
|
class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() {
|
||||||
|
// Class
|
||||||
|
private var type = classDef.type
|
||||||
|
private var sourceFile = classDef.sourceFile
|
||||||
|
private var accessFlags = classDef.accessFlags
|
||||||
|
private var superclass = classDef.superclass
|
||||||
|
|
||||||
|
private val _interfaces by lazy { classDef.interfaces.toMutableList() }
|
||||||
|
private val _annotations by lazy {
|
||||||
|
classDef.annotations.map { annotation -> annotation.toMutable() }.toMutableSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
private val _methods by lazy { classDef.methods.map { method -> method.toMutable() }.toMutableSet() }
|
||||||
|
private val _directMethods by lazy { Iterables.filter(_methods, MethodUtil.METHOD_IS_DIRECT).toMutableSet() }
|
||||||
|
private val _virtualMethods by lazy { Iterables.filter(_methods, MethodUtil.METHOD_IS_VIRTUAL).toMutableSet() }
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
private val _fields by lazy { classDef.fields.map { field -> field.toMutable() }.toMutableSet() }
|
||||||
|
private val _staticFields by lazy { Iterables.filter(_fields, FieldUtil.FIELD_IS_STATIC).toMutableSet() }
|
||||||
|
private val _instanceFields by lazy { Iterables.filter(_fields, FieldUtil.FIELD_IS_INSTANCE).toMutableSet() }
|
||||||
|
|
||||||
|
fun setType(type: String) {
|
||||||
|
this.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSourceFile(sourceFile: String?) {
|
||||||
|
this.sourceFile = sourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAccessFlags(accessFlags: Int) {
|
||||||
|
this.accessFlags = accessFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSuperClass(superclass: String?) {
|
||||||
|
this.superclass = superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getType(): String {
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAccessFlags(): Int {
|
||||||
|
return accessFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSourceFile(): String? {
|
||||||
|
return sourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSuperclass(): String? {
|
||||||
|
return superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInterfaces(): MutableList<String> {
|
||||||
|
return _interfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAnnotations(): MutableSet<MutableAnnotation> {
|
||||||
|
return _annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStaticFields(): MutableSet<MutableField> {
|
||||||
|
return _staticFields
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInstanceFields(): MutableSet<MutableField> {
|
||||||
|
return _instanceFields
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFields(): MutableSet<MutableField> {
|
||||||
|
return _fields
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDirectMethods(): MutableSet<MutableMethod> {
|
||||||
|
return _directMethods
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getVirtualMethods(): MutableSet<MutableMethod> {
|
||||||
|
return _virtualMethods
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMethods(): MutableSet<MutableMethod> {
|
||||||
|
return _methods
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun ClassDef.toMutable(): MutableClass {
|
||||||
|
return MutableClass(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,11 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.HiddenApiRestriction
|
import org.jf.dexlib2.HiddenApiRestriction
|
||||||
import com.android.tools.smali.dexlib2.base.reference.BaseFieldReference
|
import org.jf.dexlib2.base.reference.BaseFieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.Field
|
import org.jf.dexlib2.iface.Field
|
||||||
|
|
||||||
class MutableField(field: Field) :
|
class MutableField(field: Field) : Field, BaseFieldReference() {
|
||||||
BaseFieldReference(),
|
|
||||||
Field {
|
|
||||||
private var definingClass = field.definingClass
|
private var definingClass = field.definingClass
|
||||||
private var name = field.name
|
private var name = field.name
|
||||||
private var type = field.type
|
private var type = field.type
|
||||||
@@ -39,21 +37,37 @@ class MutableField(field: Field) :
|
|||||||
this.initialValue = initialValue
|
this.initialValue = initialValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDefiningClass(): String = this.definingClass
|
override fun getDefiningClass(): String {
|
||||||
|
return this.definingClass
|
||||||
|
}
|
||||||
|
|
||||||
override fun getName(): String = this.name
|
override fun getName(): String {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
override fun getType(): String = this.type
|
override fun getType(): String {
|
||||||
|
return this.type
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAnnotations(): MutableSet<MutableAnnotation> = this._annotations
|
override fun getAnnotations(): MutableSet<MutableAnnotation> {
|
||||||
|
return this._annotations
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAccessFlags(): Int = this.accessFlags
|
override fun getAccessFlags(): Int {
|
||||||
|
return this.accessFlags
|
||||||
|
}
|
||||||
|
|
||||||
override fun getHiddenApiRestrictions(): MutableSet<HiddenApiRestriction> = this._hiddenApiRestrictions
|
override fun getHiddenApiRestrictions(): MutableSet<HiddenApiRestriction> {
|
||||||
|
return this._hiddenApiRestrictions
|
||||||
|
}
|
||||||
|
|
||||||
override fun getInitialValue(): MutableEncodedValue? = this.initialValue
|
override fun getInitialValue(): MutableEncodedValue? {
|
||||||
|
return this.initialValue
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun Field.toMutable(): MutableField = MutableField(this)
|
fun Field.toMutable(): MutableField {
|
||||||
|
return MutableField(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,18 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethodParameter.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethodParameter.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.HiddenApiRestriction
|
import org.jf.dexlib2.HiddenApiRestriction
|
||||||
import com.android.tools.smali.dexlib2.base.reference.BaseMethodReference
|
import org.jf.dexlib2.base.reference.BaseMethodReference
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import org.jf.dexlib2.iface.Method
|
||||||
|
|
||||||
class MutableMethod(method: Method) :
|
class MutableMethod(method: Method) : Method, BaseMethodReference() {
|
||||||
BaseMethodReference(),
|
|
||||||
Method {
|
|
||||||
private var definingClass = method.definingClass
|
private var definingClass = method.definingClass
|
||||||
private var name = method.name
|
private var name = method.name
|
||||||
private var accessFlags = method.accessFlags
|
private var accessFlags = method.accessFlags
|
||||||
private var returnType = method.returnType
|
private var returnType = method.returnType
|
||||||
|
|
||||||
// Create own mutable MethodImplementation (due to not being able to change members like register count)
|
// TODO: Create own mutable MethodImplementation (due to not being able to change members like register count).
|
||||||
private val _implementation by lazy { method.implementation?.let { MutableMethodImplementation(it) } }
|
private val _implementation by lazy { method.implementation?.let { MutableMethodImplementation(it) } }
|
||||||
private val _annotations by lazy { method.annotations.map { annotation -> annotation.toMutable() }.toMutableSet() }
|
private val _annotations by lazy { method.annotations.map { annotation -> annotation.toMutable() }.toMutableSet() }
|
||||||
private val _parameters by lazy { method.parameters.map { parameter -> parameter.toMutable() }.toMutableList() }
|
private val _parameters by lazy { method.parameters.map { parameter -> parameter.toMutable() }.toMutableList() }
|
||||||
@@ -38,25 +36,45 @@ class MutableMethod(method: Method) :
|
|||||||
this.returnType = returnType
|
this.returnType = returnType
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDefiningClass(): String = definingClass
|
override fun getDefiningClass(): String {
|
||||||
|
return definingClass
|
||||||
|
}
|
||||||
|
|
||||||
override fun getName(): String = name
|
override fun getName(): String {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
override fun getParameterTypes(): MutableList<CharSequence> = _parameterTypes
|
override fun getParameterTypes(): MutableList<CharSequence> {
|
||||||
|
return _parameterTypes
|
||||||
|
}
|
||||||
|
|
||||||
override fun getReturnType(): String = returnType
|
override fun getReturnType(): String {
|
||||||
|
return returnType
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAnnotations(): MutableSet<MutableAnnotation> = _annotations
|
override fun getAnnotations(): MutableSet<MutableAnnotation> {
|
||||||
|
return _annotations
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAccessFlags(): Int = accessFlags
|
override fun getAccessFlags(): Int {
|
||||||
|
return accessFlags
|
||||||
|
}
|
||||||
|
|
||||||
override fun getHiddenApiRestrictions(): MutableSet<HiddenApiRestriction> = _hiddenApiRestrictions
|
override fun getHiddenApiRestrictions(): MutableSet<HiddenApiRestriction> {
|
||||||
|
return _hiddenApiRestrictions
|
||||||
|
}
|
||||||
|
|
||||||
override fun getParameters(): MutableList<MutableMethodParameter> = _parameters
|
override fun getParameters(): MutableList<MutableMethodParameter> {
|
||||||
|
return _parameters
|
||||||
|
}
|
||||||
|
|
||||||
override fun getImplementation(): MutableMethodImplementation? = _implementation
|
override fun getImplementation(): MutableMethodImplementation? {
|
||||||
|
return _implementation
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun Method.toMutable(): MutableMethod = MutableMethod(this)
|
fun Method.toMutable(): MutableMethod {
|
||||||
|
return MutableMethod(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes
|
package app.revanced.patcher.util.proxy.mutableTypes
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.base.BaseMethodParameter
|
import org.jf.dexlib2.base.BaseMethodParameter
|
||||||
import com.android.tools.smali.dexlib2.iface.MethodParameter
|
import org.jf.dexlib2.iface.MethodParameter
|
||||||
|
|
||||||
// TODO: finish overriding all members if necessary
|
// TODO: Finish overriding all members if necessary.
|
||||||
class MutableMethodParameter(parameter: MethodParameter) :
|
class MutableMethodParameter(parameter: MethodParameter) : MethodParameter, BaseMethodParameter() {
|
||||||
BaseMethodParameter(),
|
|
||||||
MethodParameter {
|
|
||||||
private var type = parameter.type
|
private var type = parameter.type
|
||||||
private var name = parameter.name
|
private var name = parameter.name
|
||||||
private var signature = parameter.signature
|
private var signature = parameter.signature
|
||||||
@@ -15,15 +13,25 @@ class MutableMethodParameter(parameter: MethodParameter) :
|
|||||||
parameter.annotations.map { annotation -> annotation.toMutable() }.toMutableSet()
|
parameter.annotations.map { annotation -> annotation.toMutable() }.toMutableSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(): String = type
|
override fun getType(): String {
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
|
||||||
override fun getName(): String? = name
|
override fun getName(): String? {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
override fun getSignature(): String? = signature
|
override fun getSignature(): String? {
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAnnotations(): MutableSet<MutableAnnotation> = _annotations
|
override fun getAnnotations(): MutableSet<MutableAnnotation> {
|
||||||
|
return _annotations
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun MethodParameter.toMutable(): MutableMethodParameter = MutableMethodParameter(this)
|
fun MethodParameter.toMutable(): MutableMethodParameter {
|
||||||
|
return MutableMethodParameter(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseAnnotationEncodedValue
|
import org.jf.dexlib2.base.value.BaseAnnotationEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.AnnotationElement
|
import org.jf.dexlib2.iface.AnnotationElement
|
||||||
import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue
|
import org.jf.dexlib2.iface.value.AnnotationEncodedValue
|
||||||
|
|
||||||
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) :
|
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : BaseAnnotationEncodedValue(),
|
||||||
BaseAnnotationEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var type = annotationEncodedValue.type
|
private var type = annotationEncodedValue.type
|
||||||
|
|
||||||
@@ -14,15 +13,21 @@ class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedVal
|
|||||||
annotationEncodedValue.elements.map { annotationElement -> annotationElement.toMutable() }.toMutableSet()
|
annotationEncodedValue.elements.map { annotationElement -> annotationElement.toMutable() }.toMutableSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(): String = this.type
|
override fun getType(): String {
|
||||||
|
return this.type
|
||||||
|
}
|
||||||
|
|
||||||
fun setType(type: String) {
|
fun setType(type: String) {
|
||||||
this.type = type
|
this.type = type
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElements(): MutableSet<out AnnotationElement> = _elements
|
override fun getElements(): MutableSet<out AnnotationElement> {
|
||||||
|
return _elements
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun AnnotationEncodedValue.toMutable(): MutableAnnotationEncodedValue = MutableAnnotationEncodedValue(this)
|
fun AnnotationEncodedValue.toMutable(): MutableAnnotationEncodedValue {
|
||||||
|
return MutableAnnotationEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseArrayEncodedValue
|
import org.jf.dexlib2.base.value.BaseArrayEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.ArrayEncodedValue
|
import org.jf.dexlib2.iface.value.ArrayEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.EncodedValue
|
import org.jf.dexlib2.iface.value.EncodedValue
|
||||||
|
|
||||||
class MutableArrayEncodedValue(arrayEncodedValue: ArrayEncodedValue) :
|
class MutableArrayEncodedValue(arrayEncodedValue: ArrayEncodedValue) : BaseArrayEncodedValue(), MutableEncodedValue {
|
||||||
BaseArrayEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private val _value by lazy {
|
private val _value by lazy {
|
||||||
arrayEncodedValue.value.map { encodedValue -> encodedValue.toMutable() }.toMutableList()
|
arrayEncodedValue.value.map { encodedValue -> encodedValue.toMutable() }.toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getValue(): MutableList<out EncodedValue> = _value
|
override fun getValue(): MutableList<out EncodedValue> {
|
||||||
|
return _value
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun ArrayEncodedValue.toMutable(): MutableArrayEncodedValue = MutableArrayEncodedValue(this)
|
fun ArrayEncodedValue.toMutable(): MutableArrayEncodedValue {
|
||||||
|
return MutableArrayEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseBooleanEncodedValue
|
import org.jf.dexlib2.base.value.BaseBooleanEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue
|
import org.jf.dexlib2.iface.value.BooleanEncodedValue
|
||||||
|
|
||||||
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) :
|
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : BaseBooleanEncodedValue(),
|
||||||
BaseBooleanEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = booleanEncodedValue.value
|
private var value = booleanEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Boolean = this.value
|
override fun getValue(): Boolean {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Boolean) {
|
fun setValue(value: Boolean) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun BooleanEncodedValue.toMutable(): MutableBooleanEncodedValue = MutableBooleanEncodedValue(this)
|
fun BooleanEncodedValue.toMutable(): MutableBooleanEncodedValue {
|
||||||
|
return MutableBooleanEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseByteEncodedValue
|
import org.jf.dexlib2.base.value.BaseByteEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue
|
import org.jf.dexlib2.iface.value.ByteEncodedValue
|
||||||
|
|
||||||
class MutableByteEncodedValue(byteEncodedValue: ByteEncodedValue) :
|
class MutableByteEncodedValue(byteEncodedValue: ByteEncodedValue) : BaseByteEncodedValue(), MutableEncodedValue {
|
||||||
BaseByteEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private var value = byteEncodedValue.value
|
private var value = byteEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Byte = this.value
|
override fun getValue(): Byte {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Byte) {
|
fun setValue(value: Byte) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun ByteEncodedValue.toMutable(): MutableByteEncodedValue = MutableByteEncodedValue(this)
|
fun ByteEncodedValue.toMutable(): MutableByteEncodedValue {
|
||||||
|
return MutableByteEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseCharEncodedValue
|
import org.jf.dexlib2.base.value.BaseCharEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.CharEncodedValue
|
import org.jf.dexlib2.iface.value.CharEncodedValue
|
||||||
|
|
||||||
class MutableCharEncodedValue(charEncodedValue: CharEncodedValue) :
|
class MutableCharEncodedValue(charEncodedValue: CharEncodedValue) : BaseCharEncodedValue(), MutableEncodedValue {
|
||||||
BaseCharEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private var value = charEncodedValue.value
|
private var value = charEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Char = this.value
|
override fun getValue(): Char {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Char) {
|
fun setValue(value: Char) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun CharEncodedValue.toMutable(): MutableCharEncodedValue = MutableCharEncodedValue(this)
|
fun CharEncodedValue.toMutable(): MutableCharEncodedValue {
|
||||||
|
return MutableCharEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseDoubleEncodedValue
|
import org.jf.dexlib2.base.value.BaseDoubleEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue
|
import org.jf.dexlib2.iface.value.DoubleEncodedValue
|
||||||
|
|
||||||
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) :
|
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDoubleEncodedValue(),
|
||||||
BaseDoubleEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = doubleEncodedValue.value
|
private var value = doubleEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Double = this.value
|
override fun getValue(): Double {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Double) {
|
fun setValue(value: Double) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun DoubleEncodedValue.toMutable(): MutableDoubleEncodedValue = MutableDoubleEncodedValue(this)
|
fun DoubleEncodedValue.toMutable(): MutableDoubleEncodedValue {
|
||||||
|
return MutableDoubleEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.ValueType
|
import org.jf.dexlib2.ValueType
|
||||||
import com.android.tools.smali.dexlib2.iface.value.*
|
import org.jf.dexlib2.iface.value.*
|
||||||
|
|
||||||
interface MutableEncodedValue : EncodedValue {
|
interface MutableEncodedValue : EncodedValue {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -29,4 +29,4 @@ interface MutableEncodedValue : EncodedValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
|
import org.jf.dexlib2.base.value.BaseEnumEncodedValue
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.value.EnumEncodedValue
|
||||||
|
|
||||||
|
class MutableEnumEncodedValue(enumEncodedValue: EnumEncodedValue) : BaseEnumEncodedValue(), MutableEncodedValue {
|
||||||
|
private var value = enumEncodedValue.value
|
||||||
|
|
||||||
|
override fun getValue(): FieldReference {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: FieldReference) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun EnumEncodedValue.toMutable(): MutableEnumEncodedValue {
|
||||||
|
return MutableEnumEncodedValue(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
|
import org.jf.dexlib2.ValueType
|
||||||
|
import org.jf.dexlib2.base.value.BaseFieldEncodedValue
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.value.FieldEncodedValue
|
||||||
|
|
||||||
|
class MutableFieldEncodedValue(fieldEncodedValue: FieldEncodedValue) : BaseFieldEncodedValue(), MutableEncodedValue {
|
||||||
|
private var value = fieldEncodedValue.value
|
||||||
|
|
||||||
|
override fun getValueType(): Int {
|
||||||
|
return ValueType.FIELD
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(): FieldReference {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: FieldReference) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun FieldEncodedValue.toMutable(): MutableFieldEncodedValue {
|
||||||
|
return MutableFieldEncodedValue(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseFloatEncodedValue
|
import org.jf.dexlib2.base.value.BaseFloatEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.FloatEncodedValue
|
import org.jf.dexlib2.iface.value.FloatEncodedValue
|
||||||
|
|
||||||
class MutableFloatEncodedValue(floatEncodedValue: FloatEncodedValue) :
|
class MutableFloatEncodedValue(floatEncodedValue: FloatEncodedValue) : BaseFloatEncodedValue(), MutableEncodedValue {
|
||||||
BaseFloatEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private var value = floatEncodedValue.value
|
private var value = floatEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Float = this.value
|
override fun getValue(): Float {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Float) {
|
fun setValue(value: Float) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun FloatEncodedValue.toMutable(): MutableFloatEncodedValue = MutableFloatEncodedValue(this)
|
fun FloatEncodedValue.toMutable(): MutableFloatEncodedValue {
|
||||||
|
return MutableFloatEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseIntEncodedValue
|
import org.jf.dexlib2.base.value.BaseIntEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.IntEncodedValue
|
import org.jf.dexlib2.iface.value.IntEncodedValue
|
||||||
|
|
||||||
class MutableIntEncodedValue(intEncodedValue: IntEncodedValue) :
|
class MutableIntEncodedValue(intEncodedValue: IntEncodedValue) : BaseIntEncodedValue(), MutableEncodedValue {
|
||||||
BaseIntEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private var value = intEncodedValue.value
|
private var value = intEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Int = this.value
|
override fun getValue(): Int {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Int) {
|
fun setValue(value: Int) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun IntEncodedValue.toMutable(): MutableIntEncodedValue = MutableIntEncodedValue(this)
|
fun IntEncodedValue.toMutable(): MutableIntEncodedValue {
|
||||||
|
return MutableIntEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseLongEncodedValue
|
import org.jf.dexlib2.base.value.BaseLongEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.value.LongEncodedValue
|
import org.jf.dexlib2.iface.value.LongEncodedValue
|
||||||
|
|
||||||
class MutableLongEncodedValue(longEncodedValue: LongEncodedValue) :
|
class MutableLongEncodedValue(longEncodedValue: LongEncodedValue) : BaseLongEncodedValue(), MutableEncodedValue {
|
||||||
BaseLongEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
|
||||||
private var value = longEncodedValue.value
|
private var value = longEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): Long = this.value
|
override fun getValue(): Long {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: Long) {
|
fun setValue(value: Long) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun LongEncodedValue.toMutable(): MutableLongEncodedValue = MutableLongEncodedValue(this)
|
fun LongEncodedValue.toMutable(): MutableLongEncodedValue {
|
||||||
|
return MutableLongEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,24 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseMethodEncodedValue
|
import org.jf.dexlib2.base.value.BaseMethodEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.value.MethodEncodedValue
|
import org.jf.dexlib2.iface.value.MethodEncodedValue
|
||||||
|
|
||||||
class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) :
|
class MutableMethodEncodedValue(methodEncodedValue: MethodEncodedValue) : BaseMethodEncodedValue(),
|
||||||
BaseMethodEncodedValue(),
|
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = methodEncodedValue.value
|
private var value = methodEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): MethodReference = this.value
|
override fun getValue(): MethodReference {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: MethodReference) {
|
fun setValue(value: MethodReference) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun MethodEncodedValue.toMutable(): MutableMethodEncodedValue = MutableMethodEncodedValue(this)
|
fun MethodEncodedValue.toMutable(): MutableMethodEncodedValue {
|
||||||
|
return MutableMethodEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import com.android.tools.smali.dexlib2.base.value.BaseMethodHandleEncodedValue
|
import org.jf.dexlib2.base.value.BaseMethodHandleEncodedValue
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodHandleReference
|
import org.jf.dexlib2.iface.reference.MethodHandleReference
|
||||||
import com.android.tools.smali.dexlib2.iface.value.MethodHandleEncodedValue
|
import org.jf.dexlib2.iface.value.MethodHandleEncodedValue
|
||||||
|
|
||||||
class MutableMethodHandleEncodedValue(methodHandleEncodedValue: MethodHandleEncodedValue) :
|
class MutableMethodHandleEncodedValue(methodHandleEncodedValue: MethodHandleEncodedValue) :
|
||||||
BaseMethodHandleEncodedValue(),
|
BaseMethodHandleEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
private var value = methodHandleEncodedValue.value
|
private var value = methodHandleEncodedValue.value
|
||||||
|
|
||||||
override fun getValue(): MethodHandleReference = this.value
|
override fun getValue(): MethodHandleReference {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
fun setValue(value: MethodHandleReference) {
|
fun setValue(value: MethodHandleReference) {
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun MethodHandleEncodedValue.toMutable(): MutableMethodHandleEncodedValue = MutableMethodHandleEncodedValue(this)
|
fun MethodHandleEncodedValue.toMutable(): MutableMethodHandleEncodedValue {
|
||||||
|
return MutableMethodHandleEncodedValue(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
|
import org.jf.dexlib2.base.value.BaseMethodTypeEncodedValue
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodProtoReference
|
||||||
|
import org.jf.dexlib2.iface.value.MethodTypeEncodedValue
|
||||||
|
|
||||||
|
class MutableMethodTypeEncodedValue(methodTypeEncodedValue: MethodTypeEncodedValue) : BaseMethodTypeEncodedValue(),
|
||||||
|
MutableEncodedValue {
|
||||||
|
private var value = methodTypeEncodedValue.value
|
||||||
|
|
||||||
|
override fun getValue(): MethodProtoReference {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setValue(value: MethodProtoReference) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun MethodTypeEncodedValue.toMutable(): MutableMethodTypeEncodedValue {
|
||||||
|
return MutableMethodTypeEncodedValue(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user