mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-18 08:53:57 +00:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39aaf42b2b | ||
|
|
d2cf491ffc | ||
|
|
3a8b2ba935 | ||
|
|
39c5a66ce3 | ||
|
|
b160a2adc0 | ||
|
|
33fadcbd0c | ||
|
|
68db95b99b | ||
|
|
4f2ef3c47c | ||
|
|
062ae14936 | ||
|
|
99f431897e | ||
|
|
d80abbcd17 | ||
|
|
509ecc81e1 | ||
|
|
e4e66b0d8b | ||
|
|
bb8771bb8b | ||
|
|
754b02e4ca | ||
|
|
fe5fb736cb | ||
|
|
fc505a8726 | ||
|
|
88a3252574 | ||
|
|
ead701bdaf | ||
|
|
0581dcf931 | ||
|
|
62191e3c4a | ||
|
|
1358d3fa10 | ||
|
|
6712f0ea72 | ||
|
|
0746c22743 | ||
|
|
7f55868e6f | ||
|
|
5d996def4d | ||
|
|
49f4570164 | ||
|
|
b8249789df | ||
|
|
0abf1c6c02 | ||
|
|
aa472eb985 | ||
|
|
ab624f04f6 | ||
|
|
21b5c079fb | ||
|
|
5024204046 | ||
|
|
a44802ef4e | ||
|
|
4c1c34ad01 | ||
|
|
b2aecb726d | ||
|
|
851f9c7885 | ||
|
|
ea6fc70caa | ||
|
|
a2875d1d64 | ||
|
|
2be6e97817 | ||
|
|
348d0070e7 | ||
|
|
d53aacdad4 | ||
|
|
f1615b7ab5 | ||
|
|
ffb1d880d7 | ||
|
|
e95f13ae3e | ||
|
|
e1b984d601 | ||
|
|
c2dc29e061 | ||
|
|
69f2f20fd9 | ||
|
|
525beda18e | ||
|
|
73d3cbf4ff | ||
|
|
70278dd79d | ||
|
|
5e98e9e30a | ||
|
|
ac1aff5a1a | ||
|
|
5481d0c54c | ||
|
|
4604742d0f | ||
|
|
4beb907a61 | ||
|
|
7f44174d91 | ||
|
|
d310246852 | ||
|
|
dcc989243c | ||
|
|
5227e98abf | ||
|
|
8c4dd5b3a3 | ||
|
|
736b3eebbf | ||
|
|
b41a542952 | ||
|
|
d21128fe2e | ||
|
|
cf4374b8cf | ||
|
|
8a30b0fa10 | ||
|
|
11a911dc67 | ||
|
|
6e3ba7419b | ||
|
|
50a66ccfed | ||
|
|
0be79840b1 | ||
|
|
d8b4c60321 | ||
|
|
f77e99e817 | ||
|
|
ea26c486c0 | ||
|
|
bebb734608 | ||
|
|
d842f82d07 | ||
|
|
82bab58ac2 | ||
|
|
90b7631d9e | ||
|
|
26d449e6d9 | ||
|
|
49466060e3 | ||
|
|
620ea5b852 | ||
|
|
3e2168a2b2 | ||
|
|
13c77967b1 | ||
|
|
f57e571a14 | ||
|
|
fe616beb22 | ||
|
|
41257ee87e | ||
|
|
33ed5f0aa3 | ||
|
|
d0a57ac00d | ||
|
|
b0b2c10665 | ||
|
|
cc183062ab | ||
|
|
fe8ea9130d | ||
|
|
f1c60093cf | ||
|
|
687b884dc4 | ||
|
|
ceb6fd51c1 | ||
|
|
0b223bfe65 | ||
|
|
308e95cf62 | ||
|
|
db8866212a | ||
|
|
608a05d9aa | ||
|
|
55746ed705 | ||
|
|
e33026c538 | ||
|
|
ff215620bb | ||
|
|
fec31f45da | ||
|
|
7684b70324 | ||
|
|
55a5d3bd4e | ||
|
|
17a4675a8e | ||
|
|
98085d1d45 | ||
|
|
bc5c16f112 | ||
|
|
f1d7217495 | ||
|
|
64dd1526cd | ||
|
|
c9a82608f7 | ||
|
|
9fc42e132c | ||
|
|
efa98ece45 | ||
|
|
68e2acebba | ||
|
|
7a7a8fc353 | ||
|
|
f8306ac43d | ||
|
|
d03591b735 | ||
|
|
4a9184597b | ||
|
|
0a482f8c9a | ||
|
|
e7dacfba8c | ||
|
|
2d7fffd4ec | ||
|
|
f8baabbcec | ||
|
|
716825f232 | ||
|
|
58bd46750b | ||
|
|
288240f163 | ||
|
|
ff02452cb8 | ||
|
|
462fbe2cad | ||
|
|
7aeae93f3d | ||
|
|
f1de9b39ef | ||
|
|
db5b0ed7be | ||
|
|
9f28a01c03 | ||
|
|
80407b6102 | ||
|
|
287841d806 | ||
|
|
10c3be1195 | ||
|
|
0c0e22013b | ||
|
|
f35c8d4446 | ||
|
|
17418d4b9c | ||
|
|
ec1fbdf2ae | ||
|
|
56e5a46fd5 | ||
|
|
32e86d44a3 | ||
|
|
7100606dfc | ||
|
|
d7eb111460 | ||
|
|
27ea46653e | ||
|
|
12c43072cb | ||
|
|
671aa6d507 | ||
|
|
b697bbad2b | ||
|
|
f05a404e48 | ||
|
|
a46e948b5a | ||
|
|
dc09ea639f | ||
|
|
49ed096e85 | ||
|
|
167bd83f4e | ||
|
|
aed1eac315 | ||
|
|
54a2f8f16f | ||
|
|
2ca543ffb9 | ||
|
|
58e7f815a5 | ||
|
|
15b38fc841 | ||
|
|
e2ca50729d | ||
|
|
6192089b71 | ||
|
|
a4212f6bf9 | ||
|
|
124a2e9d3e | ||
|
|
f77624b3b9 | ||
|
|
a76ac04214 | ||
|
|
0447fa9c28 | ||
|
|
54ac1394a9 | ||
|
|
0b04c73ac5 | ||
|
|
079de45238 | ||
|
|
56ce9ec2f9 | ||
|
|
1b52e4b0f9 | ||
|
|
098c2c1efa | ||
|
|
64343e5a7c | ||
|
|
0caf6caeb9 | ||
|
|
55f6c2a9fc | ||
|
|
c6095bc38a | ||
|
|
09cd6aa568 | ||
|
|
ebbaafb78e | ||
|
|
e6de90d300 | ||
|
|
c7922e90d0 | ||
|
|
c1f4c0445a | ||
|
|
caa634fac6 | ||
|
|
f28bfe0dbd | ||
|
|
155e787ff4 | ||
|
|
c38f0ef42a | ||
|
|
4456031459 | ||
|
|
5fb59a227f | ||
|
|
d9fb241d57 | ||
|
|
642c4ea97e | ||
|
|
8c8a251626 | ||
|
|
5953d6cfb5 | ||
|
|
a1962fe600 | ||
|
|
77dbee3d6a | ||
|
|
cb5e39d73e | ||
|
|
38ef2f470a | ||
|
|
129d84e108 | ||
|
|
affeba76b8 | ||
|
|
6059d3ca26 | ||
|
|
444dee5a16 | ||
|
|
d314466ce2 | ||
|
|
fdaf9c21c8 | ||
|
|
06c2b76f11 | ||
|
|
3896b30738 | ||
|
|
2c4b88e1a0 | ||
|
|
dfc7e1596b | ||
|
|
f590436399 | ||
|
|
cbfb9ba02f | ||
|
|
b4cfe80ad5 | ||
|
|
b37906fa35 | ||
|
|
356f1f1553 | ||
|
|
e882af74ee | ||
|
|
46875fb28e | ||
|
|
417c3e4234 | ||
|
|
6d2c28807b | ||
|
|
4d6e08a650 | ||
|
|
5cebc1fd30 |
3
.editorconfig
Normal file
3
.editorconfig
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[*.{kt,kts}]
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_standard_no-wildcard-imports = disabled
|
||||||
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
name: 🐞 Bug report
|
|
||||||
description: Report a very clearly broken issue.
|
|
||||||
title: 'bug: <title>'
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced bug report
|
|
||||||
|
|
||||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Crash
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Bug description
|
|
||||||
description: How did you find the bug? Any additional details that might help?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: Add the steps to reproduce this bug including your environment.
|
|
||||||
placeholder: Step 1. Download some files. Step 2. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Relevant log output
|
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Screenshots or videos
|
|
||||||
description: Add screenshots or videos that show the bug here.
|
|
||||||
placeholder: Drag and drop the screenshots/videos into this box.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Solution
|
|
||||||
description: If applicable, add a possible solution.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
||||||
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
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
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@@ -1,58 +0,0 @@
|
|||||||
name: ⭐ Feature request
|
|
||||||
description: Create a detailed feature request.
|
|
||||||
title: 'feat: <title>'
|
|
||||||
labels: [feature-request]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced feature request
|
|
||||||
|
|
||||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
|
||||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Functionality
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Issue
|
|
||||||
description: What is the current problem. Why does it require a feature request?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Feature
|
|
||||||
description: Describe your feature in detail. How does it solve the issue?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Motivation
|
|
||||||
description: Why should your feature should be considered?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
||||||
107
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
107
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
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) if you want to receive a contributor role.
|
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.
|
||||||
|
|||||||
22
.github/dependabot.yml
vendored
Normal file
22
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
Normal file
25
.github/workflows/build_pull_request.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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: PR to main
|
name: Open a 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,7 +15,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- 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,40 +6,48 @@ 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@v3
|
uses: actions/checkout@v5
|
||||||
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
|
|
||||||
uses: actions/cache@v3
|
- name: Cache Gradle
|
||||||
with:
|
uses: burrunan/gradle-cache-action@v3
|
||||||
path: |
|
|
||||||
${{ runner.home }}/.gradle/caches
|
- name: Build
|
||||||
${{ 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 --no-daemon
|
run: ./gradlew build clean
|
||||||
- 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.REPOSITORY_PUSH_ACCESS }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: npm exec semantic-release
|
run: npm exec semantic-release
|
||||||
|
|||||||
19
.github/workflows/update_documentation.yml
vendored
Normal file
19
.github/workflows/update_documentation.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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 }}"}'
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
"assets": [
|
"assets": [
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"gradle.properties"
|
"gradle.properties"
|
||||||
]
|
],
|
||||||
|
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
577
CHANGELOG.md
577
CHANGELOG.md
@@ -1,3 +1,580 @@
|
|||||||
|
# [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)
|
## [15.0.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0...v15.0.1) (2023-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
99
CONTRIBUTING.md
Normal file
99
CONTRIBUTING.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<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,3 +1,125 @@
|
|||||||
|
<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).
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
ReVanced Patcher is licensed under the GPLv3 license. Please see the [license 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,5 +1,61 @@
|
|||||||
public abstract interface class app/revanced/patcher/IntegrationsConsumer {
|
public final class app/revanced/patcher/Fingerprint {
|
||||||
public abstract fun acceptIntegrations (Ljava/util/List;)V
|
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 class app/revanced/patcher/PackageMetadata {
|
||||||
@@ -7,134 +63,46 @@ public final class app/revanced/patcher/PackageMetadata {
|
|||||||
public final fun getPackageVersion ()Ljava/lang/String;
|
public final fun getPackageVersion ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/PatchBundleLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
|
public final class app/revanced/patcher/Patcher : java/io/Closeable {
|
||||||
public synthetic fun <init> (Ljava/lang/ClassLoader;[Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public fun <init> (Lapp/revanced/patcher/PatcherConfig;)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 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/PatchBundleLoader$Dex : app/revanced/patcher/PatchBundleLoader {
|
|
||||||
public fun <init> ([Ljava/io/File;)V
|
|
||||||
public fun <init> ([Ljava/io/File;Ljava/io/File;)V
|
|
||||||
public synthetic fun <init> ([Ljava/io/File;Ljava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatchBundleLoader$Jar : app/revanced/patcher/PatchBundleLoader {
|
|
||||||
public fun <init> ([Ljava/io/File;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/PatchExecutorFunction : java/util/function/Function {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/Patcher : app/revanced/patcher/IntegrationsConsumer, app/revanced/patcher/PatchExecutorFunction, app/revanced/patcher/PatchesConsumer, java/io/Closeable, java/util/function/Supplier {
|
|
||||||
public fun <init> (Lapp/revanced/patcher/PatcherOptions;)V
|
|
||||||
public fun acceptIntegrations (Ljava/util/List;)V
|
|
||||||
public fun acceptPatches (Ljava/util/List;)V
|
|
||||||
public synthetic fun apply (Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun apply (Z)Lkotlinx/coroutines/flow/Flow;
|
|
||||||
public fun close ()V
|
public fun close ()V
|
||||||
public fun get ()Lapp/revanced/patcher/PatcherResult;
|
public final fun get ()Lapp/revanced/patcher/PatcherResult;
|
||||||
public synthetic fun get ()Ljava/lang/Object;
|
|
||||||
public final fun getContext ()Lapp/revanced/patcher/PatcherContext;
|
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/PatcherContext {
|
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 fun getPackageMetadata ()Lapp/revanced/patcher/PackageMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/PatcherException : java/lang/Exception {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherException$CircularDependencyException : app/revanced/patcher/PatcherException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherOptions {
|
|
||||||
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 fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/logging/Logger;)V
|
|
||||||
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/logging/Logger;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun copy (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/logging/Logger;)Lapp/revanced/patcher/PatcherOptions;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/PatcherOptions;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/logging/Logger;ILjava/lang/Object;)Lapp/revanced/patcher/PatcherOptions;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public fun hashCode ()I
|
|
||||||
public final fun recreateResourceCacheDirectory ()Ljava/io/File;
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherResult {
|
public final class app/revanced/patcher/PatcherResult {
|
||||||
public fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;)V
|
public final fun getDexFiles ()Ljava/util/Set;
|
||||||
public synthetic fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public final fun getResources ()Lapp/revanced/patcher/PatcherResult$PatchedResources;
|
||||||
public final fun component1 ()Ljava/util/List;
|
|
||||||
public final fun component2 ()Ljava/io/File;
|
|
||||||
public final fun component3 ()Ljava/util/List;
|
|
||||||
public final fun copy (Ljava/util/List;Ljava/io/File;Ljava/util/List;)Lapp/revanced/patcher/PatcherResult;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/PatcherResult;Ljava/util/List;Ljava/io/File;Ljava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/PatcherResult;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getDexFiles ()Ljava/util/List;
|
|
||||||
public final fun getDoNotCompress ()Ljava/util/List;
|
|
||||||
public final fun getResourceFile ()Ljava/io/File;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/PatcherResult$PatchedDexFile {
|
public final class app/revanced/patcher/PatcherResult$PatchedDexFile {
|
||||||
public fun <init> (Ljava/lang/String;Ljava/io/InputStream;)V
|
|
||||||
public final fun getName ()Ljava/lang/String;
|
public final fun getName ()Ljava/lang/String;
|
||||||
public final fun getStream ()Ljava/io/InputStream;
|
public final fun getStream ()Ljava/io/InputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/PatchesConsumer {
|
public final class app/revanced/patcher/PatcherResult$PatchedResources {
|
||||||
public abstract fun acceptPatches (Ljava/util/List;)V
|
public final fun getDeleteResources ()Ljava/util/Set;
|
||||||
}
|
public final fun getDoNotCompress ()Ljava/util/Set;
|
||||||
|
public final fun getOtherResources ()Ljava/io/File;
|
||||||
public final class app/revanced/patcher/data/BytecodeContext : app/revanced/patcher/data/Context {
|
public final fun getResourcesApk ()Ljava/io/File;
|
||||||
public final fun findClass (Ljava/lang/String;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
|
||||||
public final fun findClass (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
|
||||||
public synthetic fun get ()Ljava/lang/Object;
|
|
||||||
public fun get ()Ljava/util/List;
|
|
||||||
public final fun getClasses ()Lapp/revanced/patcher/util/ProxyClassList;
|
|
||||||
public final fun proxy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
|
||||||
public final fun toMethodWalker (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/method/MethodWalker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/data/Context : java/util/function/Supplier {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/data/ResourceContext : app/revanced/patcher/data/Context, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
|
||||||
public fun get ()Ljava/io/File;
|
|
||||||
public synthetic fun get ()Ljava/lang/Object;
|
|
||||||
public final fun get (Ljava/lang/String;)Ljava/io/File;
|
|
||||||
public final fun getXmlEditor ()Lapp/revanced/patcher/data/ResourceContext$XmlFileHolder;
|
|
||||||
public fun iterator ()Ljava/util/Iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/data/ResourceContext$XmlFileHolder {
|
|
||||||
public fun <init> (Lapp/revanced/patcher/data/ResourceContext;)V
|
|
||||||
public final fun get (Ljava/io/InputStream;)Lapp/revanced/patcher/util/DomFileEditor;
|
|
||||||
public final fun get (Ljava/lang/String;)Lapp/revanced/patcher/util/DomFileEditor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/extensions/ExtensionsKt {
|
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 static final fun newLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/Label;
|
||||||
public static final fun or (ILcom/android/tools/smali/dexlib2/AccessFlags;)I
|
|
||||||
public static final fun or (Lcom/android/tools/smali/dexlib2/AccessFlags;I)I
|
|
||||||
public static final fun or (Lcom/android/tools/smali/dexlib2/AccessFlags;Lcom/android/tools/smali/dexlib2/AccessFlags;)I
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/extensions/InstructionExtensions {
|
public final class app/revanced/patcher/extensions/InstructionExtensions {
|
||||||
@@ -154,7 +122,18 @@ public final class app/revanced/patcher/extensions/InstructionExtensions {
|
|||||||
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Ljava/lang/Object;
|
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)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/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 (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 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;I)V
|
||||||
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;II)V
|
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;II)V
|
||||||
@@ -167,170 +146,190 @@ public final class app/revanced/patcher/extensions/InstructionExtensions {
|
|||||||
public final fun replaceInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;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/extensions/MethodFingerprintExtensions {
|
public final class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch {
|
||||||
public static final field INSTANCE Lapp/revanced/patcher/extensions/MethodFingerprintExtensions;
|
public final fun getExtensionInputStream ()Ljava/util/function/Supplier;
|
||||||
public final fun getFuzzyPatternScanMethod (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/method/annotation/FuzzyPatternScanMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/fingerprint/Fingerprint {
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface annotation class app/revanced/patcher/fingerprint/method/annotation/FuzzyPatternScanMethod : java/lang/annotation/Annotation {
|
|
||||||
public abstract fun threshold ()I
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/fingerprint/method/impl/MethodFingerprint : app/revanced/patcher/fingerprint/Fingerprint {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint$Companion;
|
|
||||||
public fun <init> ()V
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun getResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
|
||||||
public final fun setResult (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprint$Companion {
|
|
||||||
public final fun resolve (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun resolve (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public final fun resolve (Ljava/lang/Iterable;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/Iterable;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult {
|
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;)V
|
|
||||||
public final fun component1 ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun component2 ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun component3 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
|
||||||
public final fun copy (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getClassDef ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
|
||||||
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
|
||||||
public final fun getMutableClass ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
|
||||||
public final fun getMutableMethod ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
|
||||||
public final fun getScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
public fun toString ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult {
|
public final class app/revanced/patcher/patch/BytecodePatchBuilder : app/revanced/patcher/patch/PatchBuilder {
|
||||||
public fun <init> (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;)V
|
public synthetic fun build$revanced_patcher ()Lapp/revanced/patcher/patch/Patch;
|
||||||
public final fun component1 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
public final fun extendWith (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatchBuilder;
|
||||||
public final fun component2 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
public final fun getExtensionInputStream ()Ljava/util/function/Supplier;
|
||||||
public final fun copy (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
public final fun setExtensionInputStream (Ljava/util/function/Supplier;)V
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
}
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getPatternScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
public final class app/revanced/patcher/patch/BytecodePatchContext : app/revanced/patcher/patch/PatchContext, java/io/Closeable {
|
||||||
public final fun getStringsScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
public final fun classBy (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
||||||
public fun hashCode ()I
|
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 fun toString ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult {
|
public abstract class app/revanced/patcher/patch/OptionException : java/lang/Exception {
|
||||||
public fun <init> (IILjava/util/List;)V
|
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public synthetic fun <init> (IILjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun component1 ()I
|
|
||||||
public final fun component2 ()I
|
|
||||||
public final fun component3 ()Ljava/util/List;
|
|
||||||
public final fun copy (IILjava/util/List;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;IILjava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getEndIndex ()I
|
|
||||||
public final fun getStartIndex ()I
|
|
||||||
public final fun getWarnings ()Ljava/util/List;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public final fun setWarnings (Ljava/util/List;)V
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning {
|
public final class app/revanced/patcher/patch/OptionException$InvalidValueTypeException : app/revanced/patcher/patch/OptionException {
|
||||||
public fun <init> (Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;II)V
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
public final fun component1 ()Lcom/android/tools/smali/dexlib2/Opcode;
|
|
||||||
public final fun component2 ()Lcom/android/tools/smali/dexlib2/Opcode;
|
|
||||||
public final fun component3 ()I
|
|
||||||
public final fun component4 ()I
|
|
||||||
public final fun copy (Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;II)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;IIILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getCorrectOpcode ()Lcom/android/tools/smali/dexlib2/Opcode;
|
|
||||||
public final fun getInstructionIndex ()I
|
|
||||||
public final fun getPatternIndex ()I
|
|
||||||
public final fun getWrongOpcode ()Lcom/android/tools/smali/dexlib2/Opcode;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult {
|
public final class app/revanced/patcher/patch/OptionException$OptionNotFoundException : app/revanced/patcher/patch/OptionException {
|
||||||
public fun <init> (Ljava/util/List;)V
|
public fun <init> (Ljava/lang/String;)V
|
||||||
public final fun component1 ()Ljava/util/List;
|
|
||||||
public final fun copy (Ljava/util/List;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;Ljava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getMatches ()Ljava/util/List;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch {
|
public final class app/revanced/patcher/patch/OptionException$ValueRequiredException : app/revanced/patcher/patch/OptionException {
|
||||||
public fun <init> (Ljava/lang/String;I)V
|
public fun <init> (Lapp/revanced/patcher/patch/Option;)V
|
||||||
public final fun component1 ()Ljava/lang/String;
|
|
||||||
public final fun component2 ()I
|
|
||||||
public final fun copy (Ljava/lang/String;I)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;
|
|
||||||
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;Ljava/lang/String;IILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;
|
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
|
||||||
public final fun getIndex ()I
|
|
||||||
public final fun getString ()Ljava/lang/String;
|
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract interface class app/revanced/patcher/logging/Logger {
|
public final class app/revanced/patcher/patch/OptionException$ValueValidationException : app/revanced/patcher/patch/OptionException {
|
||||||
public abstract fun error (Ljava/lang/String;)V
|
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/Option;)V
|
||||||
public abstract fun info (Ljava/lang/String;)V
|
|
||||||
public abstract fun trace (Ljava/lang/String;)V
|
|
||||||
public abstract fun warn (Ljava/lang/String;)V
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/logging/Logger$DefaultImpls {
|
public final class app/revanced/patcher/patch/OptionKt {
|
||||||
public static fun error (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
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 fun info (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
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 fun trace (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
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 fun warn (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
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/logging/impl/NopLogger : app/revanced/patcher/logging/Logger {
|
public final class app/revanced/patcher/patch/Options : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker {
|
||||||
public static final field INSTANCE Lapp/revanced/patcher/logging/impl/NopLogger;
|
public fun clear ()V
|
||||||
public fun error (Ljava/lang/String;)V
|
public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
|
||||||
public fun info (Ljava/lang/String;)V
|
public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Lapp/revanced/patcher/patch/Option;
|
||||||
public fun trace (Ljava/lang/String;)V
|
public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;
|
||||||
public fun warn (Ljava/lang/String;)V
|
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 abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch {
|
public final fun containsKey (Ljava/lang/Object;)Z
|
||||||
public fun <init> ()V
|
public fun containsKey (Ljava/lang/String;)Z
|
||||||
public fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V
|
public fun containsValue (Lapp/revanced/patcher/patch/Option;)Z
|
||||||
public synthetic fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
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 abstract class app/revanced/patcher/patch/Patch {
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
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 synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public final fun execute (Lapp/revanced/patcher/patch/PatchContext;)V
|
||||||
public fun equals (Ljava/lang/Object;)Z
|
public final fun finalize (Lapp/revanced/patcher/patch/PatchContext;)V
|
||||||
public abstract fun execute (Lapp/revanced/patcher/data/Context;)V
|
|
||||||
public final fun getCompatiblePackages ()Ljava/util/Set;
|
public final fun getCompatiblePackages ()Ljava/util/Set;
|
||||||
public final fun getDependencies ()Ljava/util/Set;
|
public final fun getDependencies ()Ljava/util/Set;
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
public final fun getDescription ()Ljava/lang/String;
|
||||||
public final fun getName ()Ljava/lang/String;
|
public final fun getName ()Ljava/lang/String;
|
||||||
public final fun getOptions ()Lapp/revanced/patcher/patch/options/PatchOptions;
|
public final fun getOptions ()Lapp/revanced/patcher/patch/Options;
|
||||||
public final fun getRequiresIntegrations ()Z
|
|
||||||
public final fun getUse ()Z
|
public final fun getUse ()Z
|
||||||
public fun hashCode ()I
|
|
||||||
public fun toString ()Ljava/lang/String;
|
public fun toString ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/Patch$CompatiblePackage {
|
public abstract class app/revanced/patcher/patch/PatchBuilder {
|
||||||
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public final fun compatibleWith ([Ljava/lang/String;)V
|
||||||
public final fun getName ()Ljava/lang/String;
|
public final fun compatibleWith ([Lkotlin/Pair;)V
|
||||||
public final fun getVersions ()Ljava/util/Set;
|
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 final class app/revanced/patcher/patch/PatchException : java/lang/Exception {
|
||||||
@@ -339,199 +338,33 @@ public final class app/revanced/patcher/patch/PatchException : java/lang/Excepti
|
|||||||
public fun <init> (Ljava/lang/Throwable;)V
|
public fun <init> (Ljava/lang/Throwable;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/PatchResult {
|
public final class app/revanced/patcher/patch/PatchKt {
|
||||||
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
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 synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;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 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/ResourcePatch : app/revanced/patcher/patch/Patch {
|
public abstract class app/revanced/patcher/patch/PatchLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
|
||||||
public fun <init> ()V
|
public synthetic fun <init> (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V
|
public synthetic fun <init> (Ljava/util/Set;Lkotlin/jvm/functions/Function1;Ljava/lang/ClassLoader;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public fun add (Lapp/revanced/patcher/patch/Patch;)Z
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V
|
|
||||||
public final fun getDescription ()Ljava/lang/String;
|
|
||||||
public final fun getKey ()Ljava/lang/String;
|
|
||||||
public final fun getRequired ()Z
|
|
||||||
public final fun getTitle ()Ljava/lang/String;
|
|
||||||
public final fun getValidate ()Lkotlin/jvm/functions/Function1;
|
|
||||||
public final fun getValue ()Ljava/lang/Object;
|
|
||||||
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
|
||||||
public final fun setValue (Ljava/lang/Object;)V
|
|
||||||
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class app/revanced/patcher/patch/options/PatchOptionException : java/lang/Exception {
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/PatchOptionException$InvalidValueTypeException : app/revanced/patcher/patch/options/PatchOptionException {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/PatchOptionException$PatchOptionNotFoundException : java/lang/Exception {
|
|
||||||
public fun <init> (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueRequiredException : java/lang/Exception {
|
|
||||||
public fun <init> (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueValidationException : java/lang/Exception {
|
|
||||||
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/options/PatchOption;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/PatchOptions : java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
|
|
||||||
public fun <init> ()V
|
|
||||||
public fun clear ()V
|
|
||||||
public final fun containsKey (Ljava/lang/Object;)Z
|
|
||||||
public fun containsKey (Ljava/lang/String;)Z
|
|
||||||
public fun containsValue (Lapp/revanced/patcher/patch/options/PatchOption;)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/options/PatchOption;
|
|
||||||
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun get (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
|
||||||
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 put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun put (Ljava/lang/String;Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/patcher/patch/options/PatchOption;
|
|
||||||
public fun putAll (Ljava/util/Map;)V
|
|
||||||
public final fun register (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
|
||||||
public final fun remove (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
|
||||||
public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
|
|
||||||
public fun remove (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
|
||||||
public final fun set (Ljava/lang/String;Ljava/lang/Object;)V
|
|
||||||
public final fun size ()I
|
|
||||||
public final fun values ()Ljava/util/Collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/BooleanPatchOption$Companion {
|
|
||||||
public final fun booleanPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
|
||||||
public static synthetic fun booleanPatchOption$default (Lapp/revanced/patcher/patch/options/types/BooleanPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/BooleanPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/FloatPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/FloatPatchOption$Companion {
|
|
||||||
public final fun floatPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
|
||||||
public static synthetic fun floatPatchOption$default (Lapp/revanced/patcher/patch/options/types/FloatPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/FloatPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/IntPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/IntPatchOption$Companion {
|
|
||||||
public final fun intPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
|
||||||
public static synthetic fun intPatchOption$default (Lapp/revanced/patcher/patch/options/types/IntPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/IntPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/LongPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/LongPatchOption$Companion {
|
|
||||||
public final fun longPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
|
||||||
public static synthetic fun longPatchOption$default (Lapp/revanced/patcher/patch/options/types/LongPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/LongPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/StringPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/StringPatchOption$Companion {
|
|
||||||
public final fun stringPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
|
||||||
public static synthetic fun stringPatchOption$default (Lapp/revanced/patcher/patch/options/types/StringPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/StringPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion {
|
|
||||||
public final fun booleanArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
|
||||||
public static synthetic fun booleanArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/BooleanArrayPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion {
|
|
||||||
public final fun floatArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
|
||||||
public static synthetic fun floatArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/FloatArrayPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion {
|
|
||||||
public final fun intArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
|
||||||
public static synthetic fun intArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/IntArrayPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion {
|
|
||||||
public final fun longArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
|
||||||
public static synthetic fun longArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/LongArrayPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption : app/revanced/patcher/patch/options/PatchOption {
|
|
||||||
public static final field Companion Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion {
|
|
||||||
public final fun stringArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
|
||||||
public static synthetic fun stringArrayPatchOption$default (Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption$Companion;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/types/array/StringArrayPatchOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/DomFileEditor : java/io/Closeable {
|
|
||||||
public fun <init> (Ljava/io/File;)V
|
|
||||||
public fun close ()V
|
|
||||||
public final fun getFile ()Lorg/w3c/dom/Document;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/ProxyClassList : java/util/Set, kotlin/jvm/internal/markers/KMutableSet {
|
|
||||||
public final fun add (Lapp/revanced/patcher/util/proxy/ClassProxy;)Z
|
|
||||||
public fun add (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
|
||||||
public synthetic fun add (Ljava/lang/Object;)Z
|
public synthetic fun add (Ljava/lang/Object;)Z
|
||||||
public fun addAll (Ljava/util/Collection;)Z
|
public fun addAll (Ljava/util/Collection;)Z
|
||||||
public fun clear ()V
|
public fun clear ()V
|
||||||
public fun contains (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
public fun contains (Lapp/revanced/patcher/patch/Patch;)Z
|
||||||
public final fun contains (Ljava/lang/Object;)Z
|
public final fun contains (Ljava/lang/Object;)Z
|
||||||
public fun containsAll (Ljava/util/Collection;)Z
|
public fun containsAll (Ljava/util/Collection;)Z
|
||||||
|
public final fun getByPatchesFile ()Ljava/util/Map;
|
||||||
public fun getSize ()I
|
public fun getSize ()I
|
||||||
public fun isEmpty ()Z
|
public fun isEmpty ()Z
|
||||||
public fun iterator ()Ljava/util/Iterator;
|
public fun iterator ()Ljava/util/Iterator;
|
||||||
public fun remove (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
public fun remove (Ljava/lang/Object;)Z
|
||||||
public final fun remove (Ljava/lang/Object;)Z
|
|
||||||
public fun removeAll (Ljava/util/Collection;)Z
|
public fun removeAll (Ljava/util/Collection;)Z
|
||||||
public fun retainAll (Ljava/util/Collection;)Z
|
public fun retainAll (Ljava/util/Collection;)Z
|
||||||
public final fun size ()I
|
public final fun size ()I
|
||||||
@@ -539,10 +372,162 @@ public final class app/revanced/patcher/util/ProxyClassList : java/util/Set, kot
|
|||||||
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patcher/util/method/MethodWalker {
|
public final class app/revanced/patcher/patch/PatchLoader$Dex : app/revanced/patcher/patch/PatchLoader {
|
||||||
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
public fun <init> (Ljava/util/Set;Ljava/io/File;)V
|
||||||
public final fun nextMethod (IZ)Lapp/revanced/patcher/util/method/MethodWalker;
|
public synthetic fun <init> (Ljava/util/Set;Ljava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public static synthetic fun nextMethod$default (Lapp/revanced/patcher/util/method/MethodWalker;IZILjava/lang/Object;)Lapp/revanced/patcher/util/method/MethodWalker;
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/PatchLoader$Jar : app/revanced/patcher/patch/PatchLoader {
|
||||||
|
public fun <init> (Ljava/util/Set;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
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 class app/revanced/patcher/util/proxy/ClassProxy {
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
1
assets/revanced-logo/revanced-logo.svg
Normal file
1
assets/revanced-logo/revanced-logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
119
build.gradle.kts
119
build.gradle.kts
@@ -1,10 +1,119 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.0" apply false
|
alias(libs.plugins.kotlin)
|
||||||
alias(libs.plugins.binary.compatibility.validator)
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
|
`maven-publish`
|
||||||
|
signing
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
group = "app.revanced"
|
||||||
apply(plugin = "maven-publish")
|
|
||||||
|
|
||||||
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"])
|
||||||
|
}
|
||||||
|
|||||||
111
docs/1_patcher_intro.md
Normal file
111
docs/1_patcher_intro.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<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)
|
||||||
112
docs/2_1_setup.md
Normal file
112
docs/2_1_setup.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<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)
|
||||||
296
docs/2_2_1_fingerprinting.md
Normal file
296
docs/2_2_1_fingerprinting.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<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)
|
||||||
262
docs/2_2_patch_anatomy.md
Normal file
262
docs/2_2_patch_anatomy.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<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)
|
||||||
126
docs/2_patches_intro.md
Normal file
126
docs/2_patches_intro.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<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)
|
||||||
105
docs/3_structure_and_conventions.md
Normal file
105
docs/3_structure_and_conventions.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<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
Normal file
117
docs/4_apis.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# 💪 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
|
||||||
73
docs/README.md
Normal file
73
docs/README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<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,4 +1,3 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
version = 21.1.0-dev.5
|
||||||
version = 15.0.1
|
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
[versions]
|
[versions]
|
||||||
android = "4.1.1.4"
|
android = "4.1.1.4"
|
||||||
kotlin-reflect = "1.9.0"
|
apktool-lib = "2.10.1.1"
|
||||||
apktool-lib = "2.8.2-6"
|
binary-compatibility-validator = "0.18.1"
|
||||||
kotlin-test = "1.8.20-RC"
|
kotlin = "2.0.20"
|
||||||
kotlinx-coroutines-core = "1.7.1"
|
kotlinx-coroutines-core = "1.10.2"
|
||||||
multidexlib2 = "3.0.3.r2"
|
mockk = "1.14.5"
|
||||||
smali = "3.0.3"
|
multidexlib2 = "3.0.3.r3"
|
||||||
symbol-processing-api = "1.9.0-1.0.11"
|
# Tracking https://github.com/google/smali/issues/64.
|
||||||
|
#noinspection GradleDependency
|
||||||
|
smali = "3.0.9"
|
||||||
xpp3 = "1.1.4c"
|
xpp3 = "1.1.4c"
|
||||||
binary-compatibility-validator = "0.13.2"
|
|
||||||
kotlin-compile-testing-ksp = "1.5.0"
|
|
||||||
kotlinpoet-ksp = "1.14.2"
|
|
||||||
ksp = "1.9.0-1.0.11"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
android = { module = "com.google.android:android", version.ref = "android" }
|
android = { module = "com.google.android:android", version.ref = "android" }
|
||||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" }
|
|
||||||
apktool-lib = { module = "app.revanced:apktool-lib", version.ref = "apktool-lib" }
|
apktool-lib = { module = "app.revanced:apktool-lib", version.ref = "apktool-lib" }
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
|
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" }
|
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" }
|
multidexlib2 = { module = "app.revanced:multidexlib2", version.ref = "multidexlib2" }
|
||||||
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||||
symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbol-processing-api" }
|
|
||||||
xpp3 = { module = "xpp3:xpp3", version.ref = "xpp3" }
|
xpp3 = { module = "xpp3:xpp3", version.ref = "xpp3" }
|
||||||
kotlin-compile-testing = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "kotlin-compile-testing-ksp" }
|
|
||||||
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet-ksp" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
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,6 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
||||||
|
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,6 +15,8 @@
|
|||||||
# 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
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -83,7 +85,9 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -144,7 +148,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=SC3045
|
# shellcheck disable=SC2039,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
|
||||||
@@ -152,7 +156,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=SC3045
|
# shellcheck disable=SC2039,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
|
||||||
@@ -201,11 +205,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# 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, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|||||||
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem 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 ##########################################################################
|
||||||
@@ -43,11 +45,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.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|||||||
7350
package-lock.json
generated
7350
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-backmerge": "^3.1.0",
|
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||||
"@semantic-release/changelog": "^6.0.2",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.7.6",
|
"gradle-semantic-release-plugin": "^1.10.1",
|
||||||
"semantic-release": "^20.1.0"
|
"semantic-release": "^24.2.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
public abstract interface annotation class app/revanced/patcher/patch/annotation/CompatiblePackage : java/lang/annotation/Annotation {
|
|
||||||
public abstract fun name ()Ljava/lang/String;
|
|
||||||
public abstract fun versions ()[Ljava/lang/String;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract interface annotation class app/revanced/patcher/patch/annotation/Patch : java/lang/annotation/Annotation {
|
|
||||||
public abstract fun compatiblePackages ()[Lapp/revanced/patcher/patch/annotation/CompatiblePackage;
|
|
||||||
public abstract fun dependencies ()[Ljava/lang/Class;
|
|
||||||
public abstract fun description ()Ljava/lang/String;
|
|
||||||
public abstract fun name ()Ljava/lang/String;
|
|
||||||
public abstract fun requiresIntegrations ()Z
|
|
||||||
public abstract fun use ()Z
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessor : com/google/devtools/ksp/processing/SymbolProcessor {
|
|
||||||
public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessorProvider : com/google/devtools/ksp/processing/SymbolProcessorProvider {
|
|
||||||
public fun <init> ()V
|
|
||||||
public fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lapp/revanced/patcher/patch/annotation/processor/PatchProcessor;
|
|
||||||
public synthetic fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lcom/google/devtools/ksp/processing/SymbolProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm") version "1.9.0"
|
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.symbol.processing.api)
|
|
||||||
implementation(libs.kotlinpoet.ksp)
|
|
||||||
implementation(project(":revanced-patcher"))
|
|
||||||
|
|
||||||
testImplementation(libs.kotlin.test)
|
|
||||||
testImplementation(libs.kotlin.compile.testing)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin { jvmToolchain(11) }
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
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>("gpr") {
|
|
||||||
from(components["java"])
|
|
||||||
|
|
||||||
version = project.version.toString()
|
|
||||||
|
|
||||||
pom {
|
|
||||||
name = "ReVanced patch annotation processor"
|
|
||||||
description = "Annotation processor for patches."
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
rootProject.name = "revanced-patch-annotation-processor"
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation
|
|
||||||
|
|
||||||
import java.lang.annotation.Inherited
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation for [app.revanced.patcher.patch.Patch] classes.
|
|
||||||
*
|
|
||||||
* @param name The name of the patch. If empty, the patch will be unnamed.
|
|
||||||
* @param description The description of the patch. If empty, no description will be used.
|
|
||||||
* @param dependencies The patches this patch depends on.
|
|
||||||
* @param compatiblePackages The packages this patch is compatible with.
|
|
||||||
* @param use Whether this patch should be used.
|
|
||||||
* @param requiresIntegrations Whether this patch requires integrations.
|
|
||||||
*/
|
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Inherited
|
|
||||||
annotation class Patch(
|
|
||||||
val name: String = "",
|
|
||||||
val description: String = "",
|
|
||||||
val dependencies: Array<KClass<out app.revanced.patcher.patch.Patch<*>>> = [],
|
|
||||||
val compatiblePackages: Array<CompatiblePackage> = [],
|
|
||||||
val use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
val requiresIntegrations: Boolean = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A package that a [app.revanced.patcher.patch.Patch] is compatible with.
|
|
||||||
*
|
|
||||||
* @param name The name of the package.
|
|
||||||
* @param versions The versions of the package.
|
|
||||||
*/
|
|
||||||
annotation class CompatiblePackage(
|
|
||||||
val name: String,
|
|
||||||
val versions: Array<String> = [],
|
|
||||||
)
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import com.google.devtools.ksp.processing.CodeGenerator
|
|
||||||
import com.google.devtools.ksp.processing.Dependencies
|
|
||||||
import com.google.devtools.ksp.processing.Resolver
|
|
||||||
import com.google.devtools.ksp.processing.SymbolProcessor
|
|
||||||
import com.google.devtools.ksp.symbol.KSAnnotated
|
|
||||||
import com.google.devtools.ksp.symbol.KSAnnotation
|
|
||||||
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
|
||||||
import com.google.devtools.ksp.symbol.KSType
|
|
||||||
import com.google.devtools.ksp.validate
|
|
||||||
import com.squareup.kotlinpoet.*
|
|
||||||
import com.squareup.kotlinpoet.ksp.toClassName
|
|
||||||
import com.squareup.kotlinpoet.ksp.writeTo
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
class PatchProcessor internal constructor(
|
|
||||||
private val codeGenerator: CodeGenerator,
|
|
||||||
) : SymbolProcessor {
|
|
||||||
|
|
||||||
private fun KSAnnotated.isSubclassOf(cls: KClass<*>): Boolean {
|
|
||||||
if (this !is KSClassDeclaration) return false
|
|
||||||
|
|
||||||
if (qualifiedName?.asString() == cls.qualifiedName) return true
|
|
||||||
|
|
||||||
return superTypes.any { it.resolve().declaration.isSubclassOf(cls) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun process(resolver: Resolver): List<KSAnnotated> {
|
|
||||||
val patches = buildMap {
|
|
||||||
resolver.getSymbolsWithAnnotation(Patch::class.qualifiedName!!).filter {
|
|
||||||
// Do not check here if Patch is super of the class, because it is expensive.
|
|
||||||
// Check it later when processing.
|
|
||||||
it.validate() && it.isSubclassOf(app.revanced.patcher.patch.Patch::class)
|
|
||||||
}.map {
|
|
||||||
it as KSClassDeclaration
|
|
||||||
}.forEach { patchDeclaration ->
|
|
||||||
patchDeclaration.annotations.find {
|
|
||||||
it.annotationType.resolve().declaration.qualifiedName!!.asString() == Patch::class.qualifiedName!!
|
|
||||||
}?.let { annotation ->
|
|
||||||
fun KSAnnotation.property(name: String) =
|
|
||||||
arguments.find { it.name!!.asString() == name }?.value!!
|
|
||||||
|
|
||||||
val name =
|
|
||||||
annotation.property("name").toString().ifEmpty { null }
|
|
||||||
|
|
||||||
val description =
|
|
||||||
annotation.property("description").toString().ifEmpty { null }
|
|
||||||
|
|
||||||
val dependencies =
|
|
||||||
(annotation.property("dependencies") as List<KSType>).ifEmpty { null }
|
|
||||||
|
|
||||||
val compatiblePackages =
|
|
||||||
(annotation.property("compatiblePackages") as List<KSAnnotation>).ifEmpty { null }
|
|
||||||
|
|
||||||
val use =
|
|
||||||
annotation.property("use") as Boolean
|
|
||||||
|
|
||||||
val requiresIntegrations =
|
|
||||||
annotation.property("requiresIntegrations") as Boolean
|
|
||||||
|
|
||||||
// Data class for KotlinPoet
|
|
||||||
data class PatchData(
|
|
||||||
val name: String?,
|
|
||||||
val description: String?,
|
|
||||||
val dependencies: List<ClassName>?,
|
|
||||||
val compatiblePackages: List<CodeBlock>?,
|
|
||||||
val use: Boolean,
|
|
||||||
val requiresIntegrations: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
this[patchDeclaration] = PatchData(
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
dependencies?.map { dependency -> dependency.toClassName() },
|
|
||||||
compatiblePackages?.map {
|
|
||||||
val packageName = it.property("name")
|
|
||||||
val packageVersions = (it.property("versions") as List<String>).ifEmpty { null }
|
|
||||||
?.joinToString(", ") { version -> "\"$version\"" }
|
|
||||||
|
|
||||||
CodeBlock.of(
|
|
||||||
"%T(%S, %L)",
|
|
||||||
app.revanced.patcher.patch.Patch.CompatiblePackage::class,
|
|
||||||
packageName,
|
|
||||||
packageVersions?.let { "setOf($packageVersions)" },
|
|
||||||
)
|
|
||||||
},
|
|
||||||
use,
|
|
||||||
requiresIntegrations
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a patch depends on another, that is annotated, the dependency should be replaced with the generated patch,
|
|
||||||
// because the generated patch has all the necessary properties to invoke the super constructor with,
|
|
||||||
// unlike the annotated patch.
|
|
||||||
val dependencyResolutionMap = buildMap {
|
|
||||||
patches.values.filter { it.dependencies != null }.flatMap {
|
|
||||||
it.dependencies!!
|
|
||||||
}.distinct().forEach { dependency ->
|
|
||||||
patches.keys.find { it.qualifiedName?.asString() == dependency.toString() }
|
|
||||||
?.let { patch ->
|
|
||||||
this[dependency] = ClassName(
|
|
||||||
patch.packageName.asString(),
|
|
||||||
patch.simpleName.asString() + "Generated"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patches.forEach { (patchDeclaration, patchAnnotation) ->
|
|
||||||
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
|
||||||
|
|
||||||
val superClass = if (isBytecodePatch) {
|
|
||||||
BytecodePatch::class
|
|
||||||
} else {
|
|
||||||
ResourcePatch::class
|
|
||||||
}
|
|
||||||
|
|
||||||
val contextClass = if (isBytecodePatch) {
|
|
||||||
BytecodeContext::class
|
|
||||||
} else {
|
|
||||||
ResourceContext::class
|
|
||||||
}
|
|
||||||
|
|
||||||
val generatedPatchClassName = ClassName(
|
|
||||||
patchDeclaration.packageName.asString(),
|
|
||||||
patchDeclaration.simpleName.asString() + "Generated"
|
|
||||||
)
|
|
||||||
|
|
||||||
FileSpec.builder(generatedPatchClassName)
|
|
||||||
.addType(
|
|
||||||
TypeSpec.objectBuilder(generatedPatchClassName)
|
|
||||||
.superclass(superClass).apply {
|
|
||||||
patchAnnotation.name?.let { name ->
|
|
||||||
addSuperclassConstructorParameter("name = %S", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
patchAnnotation.description?.let { description ->
|
|
||||||
addSuperclassConstructorParameter("description = %S", description)
|
|
||||||
}
|
|
||||||
|
|
||||||
patchAnnotation.compatiblePackages?.let { compatiblePackages ->
|
|
||||||
addSuperclassConstructorParameter(
|
|
||||||
"compatiblePackages = setOf(%L)",
|
|
||||||
compatiblePackages.joinToString(", ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The generated patch always depends on the source patch.
|
|
||||||
addSuperclassConstructorParameter(
|
|
||||||
"dependencies = setOf(%L)",
|
|
||||||
buildList {
|
|
||||||
patchAnnotation.dependencies?.forEach { dependency ->
|
|
||||||
add("${(dependencyResolutionMap[dependency] ?: dependency)}::class")
|
|
||||||
}
|
|
||||||
|
|
||||||
add("${patchDeclaration.toClassName()}::class")
|
|
||||||
}.joinToString(", "),
|
|
||||||
)
|
|
||||||
|
|
||||||
addSuperclassConstructorParameter(
|
|
||||||
"use = %L", patchAnnotation.use
|
|
||||||
)
|
|
||||||
|
|
||||||
addSuperclassConstructorParameter(
|
|
||||||
"requiresIntegrations = %L",
|
|
||||||
patchAnnotation.requiresIntegrations
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.addFunction(
|
|
||||||
FunSpec.builder("execute")
|
|
||||||
.addModifiers(KModifier.OVERRIDE)
|
|
||||||
.addParameter("context", contextClass)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.addInitializerBlock(
|
|
||||||
CodeBlock.builder()
|
|
||||||
.add(
|
|
||||||
"%T.options.forEach { (_, option) ->",
|
|
||||||
patchDeclaration.toClassName()
|
|
||||||
)
|
|
||||||
.addStatement(
|
|
||||||
"options.register(option)"
|
|
||||||
)
|
|
||||||
.add(
|
|
||||||
"}"
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
).build().writeTo(
|
|
||||||
codeGenerator,
|
|
||||||
Dependencies(false, patchDeclaration.containingFile!!)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor
|
|
||||||
|
|
||||||
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
|
||||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
|
||||||
|
|
||||||
class PatchProcessorProvider : SymbolProcessorProvider {
|
|
||||||
override fun create(environment: SymbolProcessorEnvironment) =
|
|
||||||
PatchProcessor(environment.codeGenerator)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
app.revanced.patcher.patch.annotation.processor.PatchProcessorProvider
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import com.tschuchort.compiletesting.KotlinCompilation
|
|
||||||
import com.tschuchort.compiletesting.SourceFile
|
|
||||||
import com.tschuchort.compiletesting.kspWithCompilation
|
|
||||||
import com.tschuchort.compiletesting.symbolProcessorProviders
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
class TestPatchAnnotationProcessor {
|
|
||||||
// region Processing
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testProcessing() = assertEquals(
|
|
||||||
"Processable patch", compile(
|
|
||||||
getSourceFile(
|
|
||||||
"processing", "ProcessablePatch"
|
|
||||||
)
|
|
||||||
).loadPatch("$SAMPLE_PACKAGE.processing.ProcessablePatchGenerated").name
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun generateNullProperties() = compile(
|
|
||||||
getSourceFile(
|
|
||||||
"null", "NullPropertiesPatch"
|
|
||||||
)
|
|
||||||
).loadPatch("$SAMPLE_PACKAGE.null.NullPropertiesPatchGenerated").let {
|
|
||||||
assertNull(it.description) // Because no description was provided.
|
|
||||||
assertNull(it.compatiblePackages!!.first().versions) // Because no versions were provided.
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Dependencies
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDependencies() {
|
|
||||||
compile(
|
|
||||||
getSourceFile(
|
|
||||||
"dependencies", "DependentPatch"
|
|
||||||
), getSourceFile(
|
|
||||||
"dependencies", "DependencyPatch"
|
|
||||||
)
|
|
||||||
).let { result ->
|
|
||||||
result.loadPatch("$SAMPLE_PACKAGE.dependencies.DependentPatchGenerated").let {
|
|
||||||
// Dependency as well as the source class of the generated class.
|
|
||||||
assertEquals(
|
|
||||||
2,
|
|
||||||
it.dependencies!!.size
|
|
||||||
)
|
|
||||||
|
|
||||||
// The last dependency is always the source class of the generated class to respect
|
|
||||||
// order of dependencies.
|
|
||||||
assertEquals(
|
|
||||||
result.loadPatch("$SAMPLE_PACKAGE.dependencies.DependentPatch")::class,
|
|
||||||
it.dependencies!!.last()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Options
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOptions() {
|
|
||||||
val patch = compile(
|
|
||||||
getSourceFile(
|
|
||||||
"options", "OptionsPatch"
|
|
||||||
)
|
|
||||||
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
|
||||||
|
|
||||||
assert(patch.options.isNotEmpty())
|
|
||||||
assertEquals(patch.options["print"].title, "Print message")
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Limitations
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun failingManualDependency() = assertEquals(
|
|
||||||
1, // Generated patch is always dependent on source class.
|
|
||||||
compile(
|
|
||||||
getSourceFile(
|
|
||||||
"limitations/manualdependency", "DependentPatch"
|
|
||||||
), getSourceFile(
|
|
||||||
"limitations/manualdependency", "DependencyPatch"
|
|
||||||
)
|
|
||||||
).loadPatch("$SAMPLE_PACKAGE.limitations.manualdependency.DependentPatchGenerated").dependencies!!.size
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
private companion object Utils {
|
|
||||||
const val SAMPLE_PACKAGE = "app.revanced.patcher.patch.annotation.processor.samples"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a source file from the given sample and class name.
|
|
||||||
*
|
|
||||||
* @param sample The sample to get the source file from.
|
|
||||||
* @param className The name of the class to get the source file from.
|
|
||||||
* @return The source file.
|
|
||||||
*/
|
|
||||||
fun getSourceFile(sample: String, className: String): SourceFile {
|
|
||||||
val resourceName = "app/revanced/patcher/patch/annotation/processor/samples/$sample/$className.kt"
|
|
||||||
return SourceFile.kotlin(
|
|
||||||
"$className.kt",
|
|
||||||
TestPatchAnnotationProcessor::class.java.classLoader.getResourceAsStream(resourceName)
|
|
||||||
?.readAllBytes()
|
|
||||||
?.toString(Charsets.UTF_8)
|
|
||||||
?: error("Could not find resource $resourceName")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile the given source files and return the result.
|
|
||||||
*
|
|
||||||
* @param sourceFiles The source files to compile.
|
|
||||||
* @return The result of the compilation.
|
|
||||||
*/
|
|
||||||
fun compile(vararg sourceFiles: SourceFile) = KotlinCompilation().apply {
|
|
||||||
sources = sourceFiles.asList()
|
|
||||||
|
|
||||||
symbolProcessorProviders = listOf(PatchProcessorProvider())
|
|
||||||
|
|
||||||
// Required until https://github.com/tschuchortdev/kotlin-compile-testing/issues/312 closed.
|
|
||||||
kspWithCompilation = true
|
|
||||||
|
|
||||||
inheritClassPath = true
|
|
||||||
messageOutputStream = System.out
|
|
||||||
}.compile().also { result ->
|
|
||||||
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Class loading
|
|
||||||
|
|
||||||
fun KotlinCompilation.Result.loadPatch(name: String) = classLoader.loadClass(name).loadPatch()
|
|
||||||
|
|
||||||
fun Class<*>.loadPatch() = this.getField("INSTANCE").get(null) as Patch<*>
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch(name = "Dependency patch")
|
|
||||||
object DependencyPatch : ResourcePatch() {
|
|
||||||
override fun execute(context: ResourceContext) {}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch(
|
|
||||||
name = "Dependent patch",
|
|
||||||
dependencies = [DependencyPatch::class],
|
|
||||||
)
|
|
||||||
object DependentPatch : BytecodePatch() {
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch(name = "Dependency patch")
|
|
||||||
object DependencyPatch : ResourcePatch() {
|
|
||||||
override fun execute(context: ResourceContext) { }
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch(name = "Dependent patch")
|
|
||||||
object DependentPatch : BytecodePatch(
|
|
||||||
// Dependency will not be executed correctly if it is manually specified.
|
|
||||||
// The reason for this is that the dependency patch is annotated too,
|
|
||||||
// so the processor will generate a new patch class for it embedding the annotated information.
|
|
||||||
// Because the dependency is manually specified,
|
|
||||||
// the processor will not be able to change this dependency to the generated class,
|
|
||||||
// which means that the dependency will lose the annotated information.
|
|
||||||
dependencies = setOf(DependencyPatch::class)
|
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.`null`
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch(
|
|
||||||
"Patch with null properties",
|
|
||||||
compatiblePackages = [CompatiblePackage("com.google.android.youtube")],
|
|
||||||
)
|
|
||||||
object NullPropertiesPatch : BytecodePatch() {
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.options
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
|
||||||
|
|
||||||
@Patch(name = "Options patch")
|
|
||||||
object OptionsPatch : ResourcePatch() {
|
|
||||||
override fun execute(context: ResourceContext) {}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
private val printOption by stringPatchOption(
|
|
||||||
"print",
|
|
||||||
null,
|
|
||||||
"Print message",
|
|
||||||
"The message to print."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotation.processor.samples.processing
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
|
|
||||||
@Patch("Processable patch")
|
|
||||||
object ProcessablePatch : BytecodePatch() {
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm") version "1.9.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
|
||||||
implementation(libs.xpp3)
|
|
||||||
implementation(libs.smali)
|
|
||||||
implementation(libs.multidexlib2)
|
|
||||||
implementation(libs.apktool.lib)
|
|
||||||
implementation(libs.kotlin.reflect)
|
|
||||||
|
|
||||||
compileOnly(libs.android)
|
|
||||||
|
|
||||||
testImplementation(project(":revanced-patch-annotation-processor"))
|
|
||||||
testImplementation(libs.kotlin.test)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
expand("projectVersion" to project.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin { jvmToolchain(11) }
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
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>("gpr") {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
rootProject.name = "revanced-patcher"
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface IntegrationsConsumer {
|
|
||||||
fun acceptIntegrations(integrations: List<File>)
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import dalvik.system.DexClassLoader
|
|
||||||
import lanchon.multidexlib2.BasicDexFileNamer
|
|
||||||
import lanchon.multidexlib2.MultiDexIO
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.util.jar.JarFile
|
|
||||||
import java.util.logging.Logger
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of [Patch]es.
|
|
||||||
*/
|
|
||||||
typealias PatchSet = Set<Patch<*>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Patch] class.
|
|
||||||
*/
|
|
||||||
typealias PatchClass = KClass<out Patch<*>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A loader of [Patch]es from patch bundles.
|
|
||||||
* This will load all [Patch]es from the given patch bundles that have a name.
|
|
||||||
*
|
|
||||||
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
|
||||||
* @param classLoader The [ClassLoader] to use for loading the classes.
|
|
||||||
* @param patchBundles A set of patches to initialize this instance with.
|
|
||||||
*/
|
|
||||||
sealed class PatchBundleLoader private constructor(
|
|
||||||
classLoader: ClassLoader,
|
|
||||||
patchBundles: Array<out File>,
|
|
||||||
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
|
||||||
// This constructor parameter is unfortunately necessary,
|
|
||||||
// so that a reference to the mutable set is present in the constructor to be able to add patches to it.
|
|
||||||
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
|
||||||
private val patchSet: MutableSet<Patch<*>> = mutableSetOf()
|
|
||||||
) : PatchSet by patchSet {
|
|
||||||
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
|
||||||
|
|
||||||
init {
|
|
||||||
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
|
||||||
classLoader.loadClass(it)
|
|
||||||
}.filter {
|
|
||||||
Patch::class.java.isAssignableFrom(it)
|
|
||||||
}.mapNotNull { patchClass ->
|
|
||||||
patchClass.getInstance(logger, silent = true)
|
|
||||||
}.filter {
|
|
||||||
it.name != null
|
|
||||||
}.let { patches ->
|
|
||||||
patchSet.addAll(patches)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal companion object Utils {
|
|
||||||
/**
|
|
||||||
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
|
||||||
*
|
|
||||||
* @param logger The [Logger] to use for logging.
|
|
||||||
* @param silent Whether to suppress logging.
|
|
||||||
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
|
||||||
*/
|
|
||||||
internal fun Class<*>.getInstance(logger: Logger, silent: Boolean = false): Patch<*>? {
|
|
||||||
return try {
|
|
||||||
getField("INSTANCE").get(null)
|
|
||||||
} catch (exception: NoSuchFieldException) {
|
|
||||||
if (!silent) logger.fine(
|
|
||||||
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
|
||||||
"Will try to instantiate it."
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
getDeclaredConstructor().newInstance()
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
if (!silent) logger.severe(
|
|
||||||
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
|
||||||
"therefor cannot be instantiated and will be ignored."
|
|
||||||
)
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
} as Patch<*>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchBundleLoader] for JAR files.
|
|
||||||
*
|
|
||||||
* @param patchBundles The path to patch bundles of JAR format.
|
|
||||||
*/
|
|
||||||
class Jar(vararg patchBundles: File) : PatchBundleLoader(
|
|
||||||
URLClassLoader(patchBundles.map { it.toURI().toURL() }.toTypedArray()),
|
|
||||||
patchBundles,
|
|
||||||
{ patchBundle ->
|
|
||||||
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
|
||||||
.map { it.name.replace('/', '.').replace(".class", "") }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchBundleLoader] for [Dex] files.
|
|
||||||
*
|
|
||||||
* @param patchBundles The path to patch bundles of DEX format.
|
|
||||||
* @param optimizedDexDirectory The directory to store optimized DEX files in.
|
|
||||||
* This parameter is deprecated and has no effect since API level 26.
|
|
||||||
*/
|
|
||||||
class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader(
|
|
||||||
DexClassLoader(
|
|
||||||
patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, optimizedDexDirectory?.absolutePath,
|
|
||||||
null,
|
|
||||||
PatchBundleLoader::class.java.classLoader
|
|
||||||
),
|
|
||||||
patchBundles,
|
|
||||||
{ patchBundle ->
|
|
||||||
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
|
||||||
.map { classDef ->
|
|
||||||
classDef.type.substring(1, classDef.length - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
|
||||||
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface PatchExecutorFunction : Function<Boolean, Flow<PatchResult>>
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatchBundleLoader.Utils.getInstance
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
|
|
||||||
import app.revanced.patcher.patch.*
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.util.function.Supplier
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ReVanced Patcher.
|
|
||||||
*
|
|
||||||
* @param options The options for the patcher.
|
|
||||||
*/
|
|
||||||
class Patcher(
|
|
||||||
private val options: PatcherOptions
|
|
||||||
) : PatchExecutorFunction, PatchesConsumer, IntegrationsConsumer, Supplier<PatcherResult>, Closeable {
|
|
||||||
private val logger = Logger.getLogger(Patcher::class.java.name)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The context of ReVanced [Patcher].
|
|
||||||
* This holds the current state of the patcher.
|
|
||||||
*/
|
|
||||||
val context = PatcherContext(options)
|
|
||||||
|
|
||||||
init {
|
|
||||||
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.MANIFEST_ONLY)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix circular dependency detection.
|
|
||||||
// /**
|
|
||||||
// * Add [Patch]es to ReVanced [Patcher].
|
|
||||||
// * It is not guaranteed that all supplied [Patch]es will be accepted, if an exception is thrown.
|
|
||||||
// *
|
|
||||||
// * @param patches The [Patch]es to add.
|
|
||||||
// * @throws PatcherException.CircularDependencyException If a circular dependency is detected.
|
|
||||||
// */
|
|
||||||
/**
|
|
||||||
* Add [Patch]es to ReVanced [Patcher].
|
|
||||||
*
|
|
||||||
* @param patches The [Patch]es to add.
|
|
||||||
*/
|
|
||||||
@Suppress("NAME_SHADOWING")
|
|
||||||
override fun acceptPatches(patches: List<Patch<*>>) {
|
|
||||||
/**
|
|
||||||
* Add dependencies of a [Patch] recursively to [PatcherContext.allPatches].
|
|
||||||
* If a [Patch] is already in [PatcherContext.allPatches], it will not be added again.
|
|
||||||
*/
|
|
||||||
fun PatchClass.putDependenciesRecursively() {
|
|
||||||
if (context.allPatches.contains(this)) return
|
|
||||||
|
|
||||||
val dependency = this.java.getInstance(logger)!!
|
|
||||||
context.allPatches[this] = dependency
|
|
||||||
|
|
||||||
dependency.dependencies?.forEach { it.putDependenciesRecursively() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all patches and their dependencies to the context.
|
|
||||||
for (patch in patches) context.executablePatches.putIfAbsent(patch::class, patch) ?: run {
|
|
||||||
context.allPatches[patch::class] = patch
|
|
||||||
|
|
||||||
patch.dependencies?.forEach { it.putDependenciesRecursively() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Fix circular dependency detection.
|
|
||||||
val graph = mutableMapOf<PatchClass, MutableList<PatchClass>>()
|
|
||||||
fun PatchClass.visit() {
|
|
||||||
if (this in graph) return
|
|
||||||
|
|
||||||
val group = graph.getOrPut(this) { mutableListOf(this) }
|
|
||||||
|
|
||||||
val dependencies = context.allPatches[this]!!.manifest.dependencies ?: return
|
|
||||||
dependencies.forEach { dependency ->
|
|
||||||
if (group == graph[dependency])
|
|
||||||
throw PatcherException.CircularDependencyException(context.allPatches[this]!!.manifest.name)
|
|
||||||
|
|
||||||
graph[dependency] = group.apply { add(dependency) }
|
|
||||||
dependency.visit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if at least one patch or its dependencies matches the given predicate.
|
|
||||||
*
|
|
||||||
* @param predicate The predicate to match.
|
|
||||||
*/
|
|
||||||
fun Patch<*>.anyRecursively(predicate: (Patch<*>) -> Boolean): Boolean =
|
|
||||||
predicate(this) || dependencies?.any { dependency ->
|
|
||||||
context.allPatches[dependency]!!.anyRecursively(predicate)
|
|
||||||
} ?: false
|
|
||||||
|
|
||||||
context.allPatches.values.let { patches ->
|
|
||||||
// Determine, if resource patching is required.
|
|
||||||
for (patch in patches)
|
|
||||||
if (patch.anyRecursively { patch is ResourcePatch }) {
|
|
||||||
options.resourceDecodingMode = ResourceContext.ResourceDecodingMode.FULL
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine, if merging integrations is required.
|
|
||||||
for (patch in patches)
|
|
||||||
if (!patch.anyRecursively { it.requiresIntegrations }) {
|
|
||||||
context.bytecodeContext.integrations.merge = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add integrations to the [Patcher].
|
|
||||||
*
|
|
||||||
* @param integrations The integrations to add. Must be a DEX file or container of DEX files.
|
|
||||||
*/
|
|
||||||
override fun acceptIntegrations(integrations: List<File>) {
|
|
||||||
context.bytecodeContext.integrations.addAll(integrations)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute [Patch]es that were added to ReVanced [Patcher].
|
|
||||||
*
|
|
||||||
* @param returnOnError If true, ReVanced [Patcher] will return immediately if a [Patch] fails.
|
|
||||||
* @return A pair of the name of the [Patch] and its [PatchResult].
|
|
||||||
*/
|
|
||||||
override fun apply(returnOnError: Boolean) = flow {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a [Patch] and its dependencies recursively.
|
|
||||||
*
|
|
||||||
* @param patch The [Patch] to execute.
|
|
||||||
* @param executedPatches A map to prevent [Patch]es from being executed twice due to dependencies.
|
|
||||||
* @return The result of executing the [Patch].
|
|
||||||
*/
|
|
||||||
fun executePatch(
|
|
||||||
patch: Patch<*>,
|
|
||||||
executedPatches: LinkedHashMap<Patch<*>, PatchResult>
|
|
||||||
): PatchResult {
|
|
||||||
val patchName = patch.name ?: patch.toString()
|
|
||||||
|
|
||||||
executedPatches[patch]?.let { patchResult ->
|
|
||||||
patchResult.exception ?: return patchResult
|
|
||||||
|
|
||||||
// Return a new result with an exception indicating that the patch was not executed previously,
|
|
||||||
// because it is a dependency of another patch that failed.
|
|
||||||
return PatchResult(patch, PatchException("'$patchName' did not succeed previously"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively execute all dependency patches.
|
|
||||||
patch.dependencies?.forEach { dependencyClass ->
|
|
||||||
val dependency = context.allPatches[dependencyClass]!!
|
|
||||||
val result = executePatch(dependency, executedPatches)
|
|
||||||
|
|
||||||
result.exception?.let {
|
|
||||||
return PatchResult(
|
|
||||||
patch,
|
|
||||||
PatchException(
|
|
||||||
"'$patchName' depends on '${dependency.name ?: dependency}' " +
|
|
||||||
"that raised an exception:\n${it.stackTraceToString()}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
|
||||||
// TODO: Implement this in a more polymorphic way.
|
|
||||||
when (patch) {
|
|
||||||
is BytecodePatch -> {
|
|
||||||
patch.fingerprints.resolveUsingLookupMap(context.bytecodeContext)
|
|
||||||
patch.execute(context.bytecodeContext)
|
|
||||||
}
|
|
||||||
is ResourcePatch -> {
|
|
||||||
patch.execute(context.resourceContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PatchResult(patch)
|
|
||||||
} catch (exception: PatchException) {
|
|
||||||
PatchResult(patch, exception)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
PatchResult(patch, PatchException(exception))
|
|
||||||
}.also { executedPatches[patch] = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.bytecodeContext.integrations.merge) context.bytecodeContext.integrations.flush()
|
|
||||||
|
|
||||||
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
|
|
||||||
|
|
||||||
// Prevent from decoding the app manifest twice if it is not needed.
|
|
||||||
if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL)
|
|
||||||
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL)
|
|
||||||
|
|
||||||
logger.info("Executing patches")
|
|
||||||
|
|
||||||
val executedPatches = LinkedHashMap<Patch<*>, PatchResult>() // Key is name.
|
|
||||||
|
|
||||||
context.executablePatches.values.sortedBy { it.name }.forEach { patch ->
|
|
||||||
val patchResult = executePatch(patch, executedPatches)
|
|
||||||
|
|
||||||
// If the patch failed, emit the result, even if it is closeable.
|
|
||||||
// Results of executed patches that are closeable will be emitted later.
|
|
||||||
patchResult.exception?.let {
|
|
||||||
// Propagate exception to caller instead of wrapping it in a new exception.
|
|
||||||
emit(patchResult)
|
|
||||||
|
|
||||||
if (returnOnError) return@flow
|
|
||||||
} ?: run {
|
|
||||||
if (patch is Closeable) return@run
|
|
||||||
|
|
||||||
emit(patchResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
executedPatches.values
|
|
||||||
.filter { it.exception == null }
|
|
||||||
.filter { it.patch is Closeable }.asReversed().forEach { executedPatch ->
|
|
||||||
val patch = executedPatch.patch
|
|
||||||
|
|
||||||
val result = try {
|
|
||||||
(patch as Closeable).close()
|
|
||||||
|
|
||||||
executedPatch
|
|
||||||
} catch (exception: PatchException) {
|
|
||||||
PatchResult(patch, exception)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
PatchResult(patch, PatchException(exception))
|
|
||||||
}
|
|
||||||
|
|
||||||
result.exception?.let {
|
|
||||||
emit(
|
|
||||||
PatchResult(
|
|
||||||
patch,
|
|
||||||
PatchException(
|
|
||||||
"'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}",
|
|
||||||
result.exception
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (returnOnError) return@flow
|
|
||||||
} ?: run {
|
|
||||||
patch.name ?: return@run
|
|
||||||
|
|
||||||
emit(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() = MethodFingerprint.clearFingerprintResolutionLookupMaps()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile and save the patched APK file.
|
|
||||||
*
|
|
||||||
* @return The [PatcherResult] containing the patched input files.
|
|
||||||
*/
|
|
||||||
override fun get() = PatcherResult(
|
|
||||||
context.bytecodeContext.get(),
|
|
||||||
context.resourceContext.get(),
|
|
||||||
context.packageMetadata.apkInfo.doNotCompress?.toList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import brut.androlib.apk.ApkInfo
|
|
||||||
import brut.directory.ExtFile
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context for ReVanced [Patcher].
|
|
||||||
*
|
|
||||||
* @param options The [PatcherOptions] used to create this context.
|
|
||||||
*/
|
|
||||||
class PatcherContext internal constructor(options: PatcherOptions) {
|
|
||||||
/**
|
|
||||||
* [PackageMetadata] of the supplied [PatcherOptions.inputFile].
|
|
||||||
*/
|
|
||||||
val packageMetadata = PackageMetadata(ApkInfo(ExtFile(options.inputFile)))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The map of [Patch]es associated by their [PatchClass].
|
|
||||||
*/
|
|
||||||
internal val executablePatches = mutableMapOf<PatchClass, Patch<*>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The map of all [Patch]es and their dependencies associated by their [PatchClass].
|
|
||||||
*/
|
|
||||||
internal val allPatches = mutableMapOf<PatchClass, Patch<*>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [ResourceContext] of this [PatcherContext].
|
|
||||||
* This holds the current state of the resources.
|
|
||||||
*/
|
|
||||||
internal val resourceContext = ResourceContext(this, options)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [BytecodeContext] of this [PatcherContext].
|
|
||||||
* This holds the current state of the bytecode.
|
|
||||||
*/
|
|
||||||
internal val bytecodeContext = BytecodeContext(options) }
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown by ReVanced [Patcher].
|
|
||||||
*
|
|
||||||
* @param errorMessage The exception message.
|
|
||||||
* @param cause The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
sealed class PatcherException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
|
||||||
constructor(errorMessage: String) : this(errorMessage, null)
|
|
||||||
|
|
||||||
|
|
||||||
class CircularDependencyException internal constructor(dependant: String) : PatcherException(
|
|
||||||
"Patch '$dependant' causes a circular dependency"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.logging.impl.NopLogger
|
|
||||||
import brut.androlib.Config
|
|
||||||
import java.io.File
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for ReVanced [Patcher].
|
|
||||||
* @param inputFile The input file to patch.
|
|
||||||
* @param resourceCachePath The path to the directory to use for caching resources.
|
|
||||||
* @param aaptBinaryPath The path to a custom aapt binary.
|
|
||||||
* @param frameworkFileDirectory The path to the directory to cache the framework file in.
|
|
||||||
* @param unusedLogger The logger to use for logging.
|
|
||||||
*/
|
|
||||||
data class PatcherOptions
|
|
||||||
@Deprecated("Use the constructor without the logger parameter instead")
|
|
||||||
constructor(
|
|
||||||
internal val inputFile: File,
|
|
||||||
internal val resourceCachePath: File = File("revanced-resource-cache"),
|
|
||||||
internal val aaptBinaryPath: String? = null,
|
|
||||||
internal val frameworkFileDirectory: String? = null,
|
|
||||||
internal val unusedLogger: app.revanced.patcher.logging.Logger = NopLogger
|
|
||||||
) {
|
|
||||||
private val logger = Logger.getLogger(PatcherOptions::class.java.name)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mode to use for resource decoding.
|
|
||||||
* @see ResourceContext.ResourceDecodingMode
|
|
||||||
*/
|
|
||||||
internal var resourceDecodingMode = ResourceContext.ResourceDecodingMode.MANIFEST_ONLY
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The configuration to use for resource decoding and compiling.
|
|
||||||
*/
|
|
||||||
internal val resourceConfig = Config.getDefaultConfig().apply {
|
|
||||||
useAapt2 = true
|
|
||||||
aaptPath = aaptBinaryPath ?: ""
|
|
||||||
frameworkDirectory = frameworkFileDirectory
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for ReVanced [Patcher].
|
|
||||||
* @param inputFile The input file to patch.
|
|
||||||
* @param resourceCachePath The path to the directory to use for caching resources.
|
|
||||||
* @param aaptBinaryPath The path to a custom aapt binary.
|
|
||||||
* @param frameworkFileDirectory The path to the directory to cache the framework file in.
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
inputFile: File,
|
|
||||||
resourceCachePath: File = File("revanced-resource-cache"),
|
|
||||||
aaptBinaryPath: String? = null,
|
|
||||||
frameworkFileDirectory: String? = null,
|
|
||||||
) : this(
|
|
||||||
inputFile,
|
|
||||||
resourceCachePath,
|
|
||||||
aaptBinaryPath,
|
|
||||||
frameworkFileDirectory,
|
|
||||||
NopLogger
|
|
||||||
)
|
|
||||||
|
|
||||||
fun recreateResourceCacheDirectory() = resourceCachePath.also {
|
|
||||||
if (it.exists()) {
|
|
||||||
logger.info("Deleting existing resource cache directory")
|
|
||||||
|
|
||||||
if (!it.deleteRecursively())
|
|
||||||
logger.severe("Failed to delete existing resource cache directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
it.mkdirs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patcher.
|
|
||||||
* @param dexFiles The patched dex files.
|
|
||||||
* @param resourceFile File containing resources that need to be extracted into the APK.
|
|
||||||
* @param doNotCompress List of relative paths of files to exclude from compressing.
|
|
||||||
*/
|
|
||||||
data class PatcherResult(
|
|
||||||
val dexFiles: List<PatchedDexFile>,
|
|
||||||
val resourceFile: File?,
|
|
||||||
val doNotCompress: List<String>? = null
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* Wrapper for dex files.
|
|
||||||
* @param name The original name of the dex file.
|
|
||||||
* @param stream The dex file as [InputStream].
|
|
||||||
*/
|
|
||||||
class PatchedDexFile(val name: String, val stream: InputStream)
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface PatchesConsumer {
|
|
||||||
fun acceptPatches(patches: List<Patch<*>>)
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
package app.revanced.patcher.data
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatcherContext
|
|
||||||
import app.revanced.patcher.PatcherOptions
|
|
||||||
import app.revanced.patcher.PatcherResult
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.util.ClassMerger.merge
|
|
||||||
import app.revanced.patcher.util.ProxyClassList
|
|
||||||
import app.revanced.patcher.util.method.MethodWalker
|
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
|
||||||
import com.android.tools.smali.dexlib2.Opcodes
|
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
|
||||||
import com.android.tools.smali.dexlib2.iface.DexFile
|
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
|
||||||
import com.android.tools.smali.dexlib2.writer.io.MemoryDataStore
|
|
||||||
import lanchon.multidexlib2.BasicDexFileNamer
|
|
||||||
import lanchon.multidexlib2.DexIO
|
|
||||||
import lanchon.multidexlib2.MultiDexIO
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Flushable
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context for bytecode.
|
|
||||||
* This holds the current state of the bytecode.
|
|
||||||
*
|
|
||||||
* @param options The [PatcherOptions] used to create this context.
|
|
||||||
*/
|
|
||||||
class BytecodeContext internal constructor(private val options: PatcherOptions) :
|
|
||||||
Context<List<PatcherResult.PatchedDexFile>> {
|
|
||||||
private val logger = Logger.getLogger(BytecodeContext::class.java.name)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [Opcodes] of the supplied [PatcherOptions.inputFile].
|
|
||||||
*/
|
|
||||||
internal lateinit var opcodes: Opcodes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of classes.
|
|
||||||
*/
|
|
||||||
val classes by lazy {
|
|
||||||
ProxyClassList(
|
|
||||||
MultiDexIO.readDexFile(
|
|
||||||
true, options.inputFile, BasicDexFileNamer(), null, null
|
|
||||||
).also { opcodes = it.opcodes }.classes.toMutableSet()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [Integrations] of this [PatcherContext].
|
|
||||||
*/
|
|
||||||
internal val integrations = Integrations()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given class name.
|
|
||||||
*
|
|
||||||
* @param className The name of the class.
|
|
||||||
* @return A proxy for the first class that matches the class name.
|
|
||||||
*/
|
|
||||||
fun findClass(className: String) = findClass { it.type.contains(className) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given predicate.
|
|
||||||
*
|
|
||||||
* @param predicate A predicate to match the class.
|
|
||||||
* @return A proxy for the first class that matches the predicate.
|
|
||||||
*/
|
|
||||||
fun findClass(predicate: (ClassDef) -> Boolean) =
|
|
||||||
// if we already proxied the class matching the predicate...
|
|
||||||
classes.proxies.firstOrNull { predicate(it.immutableClass) } ?:
|
|
||||||
// else resolve the class to a proxy and return it, if the predicate is matching a class
|
|
||||||
classes.find(predicate)?.let { proxy(it) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Proxy a class.
|
|
||||||
* This will allow the class to be modified.
|
|
||||||
*
|
|
||||||
* @param classDef The class to proxy.
|
|
||||||
* @return A proxy for the class.
|
|
||||||
*/
|
|
||||||
fun proxy(classDef: ClassDef) = this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let {
|
|
||||||
ClassProxy(classDef).also { this.classes.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a [MethodWalker] instance for the current [BytecodeContext].
|
|
||||||
*
|
|
||||||
* @param startMethod The method to start at.
|
|
||||||
* @return A [MethodWalker] instance.
|
|
||||||
*/
|
|
||||||
fun toMethodWalker(startMethod: Method) = MethodWalker(this, startMethod)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The integrations of a [PatcherContext].
|
|
||||||
*/
|
|
||||||
internal inner class Integrations : MutableList<File> by mutableListOf(), Flushable {
|
|
||||||
/**
|
|
||||||
* Whether to merge integrations.
|
|
||||||
* Set to true, if the field requiresIntegrations of any supplied [Patch] is true.
|
|
||||||
*/
|
|
||||||
var merge = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge integrations into the [BytecodeContext] and flush all [Integrations].
|
|
||||||
*/
|
|
||||||
override fun flush() {
|
|
||||||
if (!merge) return
|
|
||||||
|
|
||||||
logger.info("Merging integrations")
|
|
||||||
|
|
||||||
// TODO: Multi-thread this.
|
|
||||||
this@Integrations.forEach { integrations ->
|
|
||||||
MultiDexIO.readDexFile(
|
|
||||||
true,
|
|
||||||
integrations, BasicDexFileNamer(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
).classes.forEach classDef@{ classDef ->
|
|
||||||
val existingClass = classes.find { it.type == classDef.type } ?: run {
|
|
||||||
logger.fine("Merging $classDef")
|
|
||||||
classes.add(classDef)
|
|
||||||
return@classDef
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.fine("$classDef exists. Adding missing methods and fields.")
|
|
||||||
|
|
||||||
existingClass.merge(classDef, this@BytecodeContext).let { mergedClass ->
|
|
||||||
// If the class was merged, replace the original class with the merged class.
|
|
||||||
if (mergedClass === existingClass) return@let
|
|
||||||
classes.apply { remove(existingClass); add(mergedClass) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile bytecode from the [BytecodeContext].
|
|
||||||
*
|
|
||||||
* @return The compiled bytecode.
|
|
||||||
*/
|
|
||||||
override fun get(): List<PatcherResult.PatchedDexFile> {
|
|
||||||
logger.info("Compiling modified dex files")
|
|
||||||
|
|
||||||
return mutableMapOf<String, MemoryDataStore>().apply {
|
|
||||||
MultiDexIO.writeDexFile(
|
|
||||||
true, -1, // Defaults to amount of available cores.
|
|
||||||
this, BasicDexFileNamer(), object : DexFile {
|
|
||||||
override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses)
|
|
||||||
override fun getOpcodes() = this@BytecodeContext.opcodes
|
|
||||||
}, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
|
|
||||||
)
|
|
||||||
}.map { PatcherResult.PatchedDexFile(it.key, it.value.readAt(0)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.data
|
|
||||||
|
|
||||||
import java.util.function.Supplier
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A common interface for contexts such as [ResourceContext] and [BytecodeContext].
|
|
||||||
*/
|
|
||||||
|
|
||||||
sealed interface Context<T> : Supplier<T>
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package app.revanced.patcher.data
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatcherContext
|
|
||||||
import app.revanced.patcher.PatcherOptions
|
|
||||||
import app.revanced.patcher.util.DomFileEditor
|
|
||||||
import brut.androlib.AaptInvoker
|
|
||||||
import brut.androlib.ApkDecoder
|
|
||||||
import brut.androlib.apk.UsesFramework
|
|
||||||
import brut.androlib.res.Framework
|
|
||||||
import brut.androlib.res.ResourcesDecoder
|
|
||||||
import brut.androlib.res.decoder.AndroidManifestResourceParser
|
|
||||||
import brut.androlib.res.decoder.XmlPullStreamDecoder
|
|
||||||
import brut.androlib.res.xml.ResXmlPatcher
|
|
||||||
import brut.directory.ExtFile
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context for resources.
|
|
||||||
* This holds the current state of the resources.
|
|
||||||
*
|
|
||||||
* @param context The [PatcherContext] to create the context for.
|
|
||||||
*/
|
|
||||||
class ResourceContext internal constructor(
|
|
||||||
private val context: PatcherContext,
|
|
||||||
private val options: PatcherOptions
|
|
||||||
) : Context<File?>, Iterable<File> {
|
|
||||||
private val logger = Logger.getLogger(ResourceContext::class.java.name)
|
|
||||||
|
|
||||||
val xmlEditor = XmlFileHolder()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode resources for the patcher.
|
|
||||||
*
|
|
||||||
* @param mode The [ResourceDecodingMode] to use when decoding.
|
|
||||||
*/
|
|
||||||
internal fun decodeResources(mode: ResourceDecodingMode) = with(context.packageMetadata.apkInfo) {
|
|
||||||
// Needed to decode resources.
|
|
||||||
val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this)
|
|
||||||
|
|
||||||
when (mode) {
|
|
||||||
ResourceDecodingMode.FULL -> {
|
|
||||||
val outDir = options.recreateResourceCacheDirectory()
|
|
||||||
|
|
||||||
logger.info("Decoding resources")
|
|
||||||
|
|
||||||
resourcesDecoder.decodeResources(outDir)
|
|
||||||
resourcesDecoder.decodeManifest(outDir)
|
|
||||||
|
|
||||||
// Needed to record uncompressed files.
|
|
||||||
val apkDecoder = ApkDecoder(options.resourceConfig, this)
|
|
||||||
apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping)
|
|
||||||
|
|
||||||
usesFramework = UsesFramework().apply {
|
|
||||||
ids = resourcesDecoder.resTable.listFramePackages().map { it.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDecodingMode.MANIFEST_ONLY -> {
|
|
||||||
logger.info("Decoding app manifest")
|
|
||||||
|
|
||||||
// Decode manually instead of using resourceDecoder.decodeManifest
|
|
||||||
// because it does not support decoding to an OutputStream.
|
|
||||||
XmlPullStreamDecoder(
|
|
||||||
AndroidManifestResourceParser(resourcesDecoder.resTable),
|
|
||||||
resourcesDecoder.resXmlSerializer
|
|
||||||
).decodeManifest(
|
|
||||||
apkFile.directory.getFileInput("AndroidManifest.xml"),
|
|
||||||
// Older Android versions do not support OutputStream.nullOutputStream()
|
|
||||||
object : OutputStream() {
|
|
||||||
override fun write(b: Int) { /* do nothing */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get the package name and version from the manifest using the XmlPullStreamDecoder.
|
|
||||||
// XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo.
|
|
||||||
context.packageMetadata.let { metadata ->
|
|
||||||
metadata.packageName = resourcesDecoder.resTable.packageRenamed
|
|
||||||
versionInfo.let {
|
|
||||||
metadata.packageVersion = it.versionName ?: it.versionCode
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The ResTable if flagged as sparse if the main package is not loaded, which is the case here,
|
|
||||||
because ResourcesDecoder.decodeResources loads the main package
|
|
||||||
and not XmlPullStreamDecoder.decodeManifest.
|
|
||||||
See ARSCDecoder.readTableType for more info.
|
|
||||||
|
|
||||||
Set this to false again to prevent the ResTable from being flagged as sparse falsely.
|
|
||||||
*/
|
|
||||||
metadata.apkInfo.sparseResources = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(path: String) = options.resourceCachePath.resolve(path)
|
|
||||||
|
|
||||||
override fun iterator() = options.resourceCachePath.walkTopDown().iterator()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile resources from the [ResourceContext].
|
|
||||||
*
|
|
||||||
* @return The compiled resources.
|
|
||||||
*/
|
|
||||||
override fun get(): File? {
|
|
||||||
var resourceFile: File? = null
|
|
||||||
|
|
||||||
if (options.resourceDecodingMode == ResourceDecodingMode.FULL) {
|
|
||||||
logger.info("Compiling modified resources")
|
|
||||||
|
|
||||||
val cacheDirectory = ExtFile(options.resourceCachePath)
|
|
||||||
val aaptFile = cacheDirectory.resolve("aapt_temp_file").also {
|
|
||||||
Files.deleteIfExists(it.toPath())
|
|
||||||
}.also { resourceFile = it }
|
|
||||||
|
|
||||||
try {
|
|
||||||
AaptInvoker(
|
|
||||||
options.resourceConfig, context.packageMetadata.apkInfo
|
|
||||||
).invokeAapt(aaptFile,
|
|
||||||
cacheDirectory.resolve("AndroidManifest.xml").also {
|
|
||||||
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
|
|
||||||
},
|
|
||||||
cacheDirectory.resolve("res"),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
context.packageMetadata.apkInfo.usesFramework.let { usesFramework ->
|
|
||||||
usesFramework.ids.map { id ->
|
|
||||||
Framework(options.resourceConfig).getFrameworkApk(id, usesFramework.tag)
|
|
||||||
}.toTypedArray()
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
cacheDirectory.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceFile
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of decoding the resources.
|
|
||||||
*/
|
|
||||||
internal enum class ResourceDecodingMode {
|
|
||||||
/**
|
|
||||||
* Decode all resources.
|
|
||||||
*/
|
|
||||||
FULL,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode the manifest file only.
|
|
||||||
*/
|
|
||||||
MANIFEST_ONLY,
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class XmlFileHolder {
|
|
||||||
operator fun get(inputStream: InputStream) =
|
|
||||||
DomFileEditor(inputStream)
|
|
||||||
|
|
||||||
operator fun get(path: String): DomFileEditor {
|
|
||||||
return DomFileEditor(this@ResourceContext[path])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.patcher.extensions
|
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import com.android.tools.smali.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
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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 {
|
|
||||||
// TODO: Make this a property.
|
|
||||||
/**
|
|
||||||
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
val MethodFingerprint.fuzzyPatternScanMethod
|
|
||||||
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced fingerprint.
|
|
||||||
* Can be a [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
interface Fingerprint
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
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
|
|
||||||
)
|
|
||||||
@@ -1,516 +0,0 @@
|
|||||||
package app.revanced.patcher.fingerprint.method.impl
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.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 com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
|
||||||
import com.android.tools.smali.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()) clearFingerprintResolutionLookupMaps()
|
|
||||||
|
|
||||||
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 Set<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
|
||||||
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
|
||||||
|
|
||||||
forEach { fingerprint ->
|
|
||||||
fingerprint.resolveUsingLookupMap(context)
|
|
||||||
}
|
|
||||||
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.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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patcher.logging
|
|
||||||
|
|
||||||
@Deprecated("This will be removed in a future release")
|
|
||||||
interface Logger {
|
|
||||||
fun error(msg: String) {}
|
|
||||||
fun warn(msg: String) {}
|
|
||||||
fun info(msg: String) {}
|
|
||||||
fun trace(msg: String) {}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package app.revanced.patcher.logging.impl
|
|
||||||
|
|
||||||
import app.revanced.patcher.logging.Logger
|
|
||||||
|
|
||||||
@Deprecated("This will be removed in a future release")
|
|
||||||
object NopLogger : Logger
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatchClass
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced [Patch] that works on [BytecodeContext].
|
|
||||||
*
|
|
||||||
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
|
||||||
* @param name The name of the patch.
|
|
||||||
* @param description The description of the patch.
|
|
||||||
* @param compatiblePackages The packages the patch is compatible with.
|
|
||||||
* @param dependencies The names of patches this patch depends on.
|
|
||||||
* @param use Weather or not the patch should be used.
|
|
||||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
|
||||||
*/
|
|
||||||
abstract class BytecodePatch(
|
|
||||||
internal val fingerprints: Set<MethodFingerprint> = emptySet(),
|
|
||||||
name: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
|
||||||
dependencies: Set<PatchClass>? = null,
|
|
||||||
use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
requiresIntegrations: Boolean = false,
|
|
||||||
) : Patch<BytecodeContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
@file:Suppress("MemberVisibilityCanBePrivate", "UNUSED_PARAMETER")
|
|
||||||
|
|
||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatchClass
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.patch.options.PatchOptions
|
|
||||||
import java.io.Closeable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced patch.
|
|
||||||
*
|
|
||||||
* If an implementation of [Patch] also implements [Closeable]
|
|
||||||
* it will be closed in reverse execution order of patches executed by ReVanced [Patcher].
|
|
||||||
*
|
|
||||||
* @param name The name of the patch.
|
|
||||||
* @param description The description of the patch.
|
|
||||||
* @param compatiblePackages The packages the patch is compatible with.
|
|
||||||
* @param dependencies The names of patches this patch depends on.
|
|
||||||
* @param use Weather or not the patch should be used.
|
|
||||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
|
||||||
* @param T The [Context] type this patch will work on.
|
|
||||||
*/
|
|
||||||
sealed class Patch<out T : Context<*>>(
|
|
||||||
val name: String? = null,
|
|
||||||
val description: String? = null,
|
|
||||||
val compatiblePackages: Set<CompatiblePackage>? = null,
|
|
||||||
val dependencies: Set<PatchClass>? = null,
|
|
||||||
val use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
val requiresIntegrations: Boolean = false,
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* The options of the patch associated by the options key.
|
|
||||||
*/
|
|
||||||
val options = PatchOptions()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The execution function of the patch.
|
|
||||||
*
|
|
||||||
* @param context The [Context] the patch will work on.
|
|
||||||
* @return The result of executing the patch.
|
|
||||||
*/
|
|
||||||
abstract fun execute(context: @UnsafeVariance T)
|
|
||||||
|
|
||||||
override fun hashCode() = name.hashCode()
|
|
||||||
|
|
||||||
override fun toString() = name ?: this::class.simpleName ?: "Unnamed patch"
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Patch<*>
|
|
||||||
|
|
||||||
return name == other.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A package a [Patch] is compatible with.
|
|
||||||
*
|
|
||||||
* @param name The name of the package.
|
|
||||||
* @param versions The versions of the package.
|
|
||||||
*/
|
|
||||||
class CompatiblePackage(
|
|
||||||
val name: String,
|
|
||||||
val versions: Set<String>? = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A result of executing a [Patch].
|
|
||||||
*
|
|
||||||
* @param patch The [Patch] that was executed.
|
|
||||||
* @param exception The [PatchException] thrown, if any.
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatchClass
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced [Patch] that works on [ResourceContext].
|
|
||||||
*
|
|
||||||
* @param name The name of the patch.
|
|
||||||
* @param description The description of the patch.
|
|
||||||
* @param compatiblePackages The packages the patch is compatible with.
|
|
||||||
* @param dependencies The names of patches this patch depends on.
|
|
||||||
* @param use Weather or not the patch should be used.
|
|
||||||
* @param requiresIntegrations Weather or not the patch requires integrations.
|
|
||||||
*/
|
|
||||||
abstract class ResourcePatch(
|
|
||||||
name: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
|
||||||
dependencies: Set<PatchClass>? = null,
|
|
||||||
use: Boolean = true,
|
|
||||||
// TODO: Remove this property, once integrations are coupled with patches.
|
|
||||||
requiresIntegrations: Boolean = false,
|
|
||||||
) : Patch<ResourceContext>(name, description, compatiblePackages, dependencies, use, requiresIntegrations)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Patch] option.
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @param validate The function to validate values of the option.
|
|
||||||
* @param T The value type of the option.
|
|
||||||
*/
|
|
||||||
abstract class PatchOption<T>(
|
|
||||||
val key: String,
|
|
||||||
default: T?,
|
|
||||||
val title: String?,
|
|
||||||
val description: String?,
|
|
||||||
val required: Boolean,
|
|
||||||
val validate: (T?) -> Boolean
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* The value of the [PatchOption].
|
|
||||||
*/
|
|
||||||
var value: T? = default
|
|
||||||
set(value) {
|
|
||||||
if (required && value == null) throw PatchOptionException.ValueRequiredException(this)
|
|
||||||
if (!validate(value)) throw PatchOptionException.ValueValidationException(value, this)
|
|
||||||
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
|
||||||
|
|
||||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when using [PatchOption]s.
|
|
||||||
*
|
|
||||||
* @param errorMessage The exception message.
|
|
||||||
*/
|
|
||||||
sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage, null) {
|
|
||||||
/**
|
|
||||||
* An exception thrown when a [PatchOption] is set to an invalid value.
|
|
||||||
*
|
|
||||||
* @param invalidType The type of the value that was passed.
|
|
||||||
* @param expectedType The type of the value that was expected.
|
|
||||||
*/
|
|
||||||
class InvalidValueTypeException(invalidType: String, expectedType: String) :
|
|
||||||
PatchOptionException("Type $expectedType was expected but received type $invalidType")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a value did not satisfy the value conditions specified by the [PatchOption].
|
|
||||||
*
|
|
||||||
* @param value The value that failed validation.
|
|
||||||
*/
|
|
||||||
class ValueValidationException(value: Any?, option: PatchOption<*>) :
|
|
||||||
Exception("The option value \"$value\" failed validation for ${option.key}")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a value is required but null was passed.
|
|
||||||
*
|
|
||||||
* @param option The [PatchOption] that requires a value.
|
|
||||||
*/
|
|
||||||
class ValueRequiredException(option: PatchOption<*>) :
|
|
||||||
Exception("The option ${option.key} requires a value, but null was passed")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a [PatchOption] is not found.
|
|
||||||
*
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
class PatchOptionNotFoundException(key: String)
|
|
||||||
: Exception("No option with key $key")
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map of [PatchOption]s associated by their keys.
|
|
||||||
*
|
|
||||||
* @param options The [PatchOption]s to initialize with.
|
|
||||||
*/
|
|
||||||
class PatchOptions internal constructor(
|
|
||||||
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf()
|
|
||||||
) : MutableMap<String, PatchOption<*>> by options {
|
|
||||||
/**
|
|
||||||
* Register a [PatchOption]. Acts like [MutableMap.put].
|
|
||||||
* @param value The [PatchOption] to register.
|
|
||||||
*/
|
|
||||||
fun register(value: PatchOption<*>) {
|
|
||||||
options[value.key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an option's value.
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param value The value.
|
|
||||||
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
|
||||||
*/
|
|
||||||
operator fun <T : Any> set(key: String, value: T?) {
|
|
||||||
val option = this[key]
|
|
||||||
|
|
||||||
try {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
(option as PatchOption<T>).value = value
|
|
||||||
} catch (e: ClassCastException) {
|
|
||||||
throw PatchOptionException.InvalidValueTypeException(
|
|
||||||
value?.let { it::class.java.name } ?: "null",
|
|
||||||
option.value?.let { it::class.java.name } ?: "null",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an option.
|
|
||||||
*/
|
|
||||||
override operator fun get(key: String) =
|
|
||||||
options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Boolean].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class BooleanPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Boolean?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Boolean?) -> Boolean
|
|
||||||
) : PatchOption<Boolean>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [BooleanPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [BooleanPatchOption].
|
|
||||||
*
|
|
||||||
* @see BooleanPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.booleanPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Boolean? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Boolean?) -> Boolean = { true }
|
|
||||||
) = BooleanPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Float].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class FloatPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Float?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Float?) -> Boolean
|
|
||||||
) : PatchOption<Float>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [FloatPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [FloatPatchOption].
|
|
||||||
*
|
|
||||||
* @see FloatPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.floatPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Float? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Float?) -> Boolean = { true }
|
|
||||||
) = FloatPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing an [Integer].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class IntPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Int?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Int?) -> Boolean
|
|
||||||
) : PatchOption<Int>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [IntPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [IntPatchOption].
|
|
||||||
*
|
|
||||||
* @see IntPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.intPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Int? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Int?) -> Boolean = { true }
|
|
||||||
) = IntPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Long].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class LongPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Long?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Long?) -> Boolean
|
|
||||||
) : PatchOption<Long>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [LongPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [LongPatchOption].
|
|
||||||
*
|
|
||||||
* @see LongPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.longPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Long? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Long?) -> Boolean = { true }
|
|
||||||
) = LongPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [String].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class StringPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: String?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (String?) -> Boolean
|
|
||||||
) : PatchOption<String>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [StringPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [StringPatchOption].
|
|
||||||
*
|
|
||||||
* @see StringPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.stringPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: String? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (String?) -> Boolean = { true }
|
|
||||||
) = StringPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types.array
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Boolean] array.
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class BooleanArrayPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Array<Boolean>?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Array<Boolean>?) -> Boolean
|
|
||||||
) : PatchOption<Array<Boolean>>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [BooleanArrayPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [BooleanArrayPatchOption].
|
|
||||||
*
|
|
||||||
* @see BooleanArrayPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.booleanArrayPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Array<Boolean>? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Array<Boolean>?) -> Boolean = { true }
|
|
||||||
) = BooleanArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types.array
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Float] array.
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class FloatArrayPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Array<Float>?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Array<Float>?) -> Boolean
|
|
||||||
) : PatchOption<Array<Float>>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [FloatArrayPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [FloatArrayPatchOption].
|
|
||||||
*
|
|
||||||
* @see FloatArrayPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.floatArrayPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Array<Float>? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Array<Float>?) -> Boolean = { true }
|
|
||||||
) = FloatArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types.array
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing an [Integer] array.
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class IntArrayPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Array<Int>?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Array<Int>?) -> Boolean
|
|
||||||
) : PatchOption<Array<Int>>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [IntArrayPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [IntArrayPatchOption].
|
|
||||||
*
|
|
||||||
* @see IntArrayPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.intArrayPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Array<Int>? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Array<Int>?) -> Boolean = { true }
|
|
||||||
) = IntArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types.array
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Long] array.
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class LongArrayPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Array<Long>?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Array<Long>?) -> Boolean
|
|
||||||
) : PatchOption<Array<Long>>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [LongArrayPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [LongArrayPatchOption].
|
|
||||||
*
|
|
||||||
* @see LongArrayPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.longArrayPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Array<Long>? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Array<Long>?) -> Boolean = { true }
|
|
||||||
) = LongArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options.types.array
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [String] array.
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class StringArrayPatchOption private constructor(
|
|
||||||
key: String,
|
|
||||||
default: Array<String>?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
required: Boolean,
|
|
||||||
validator: (Array<String>?) -> Boolean
|
|
||||||
) : PatchOption<Array<String>>(key, default, title, description, required, validator) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new [StringArrayPatchOption] and add it to the current [Patch].
|
|
||||||
*
|
|
||||||
* @param key The identifier.
|
|
||||||
* @param default The default value.
|
|
||||||
* @param title The title.
|
|
||||||
* @param description A description.
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
* @return The created [StringArrayPatchOption].
|
|
||||||
*
|
|
||||||
* @see StringArrayPatchOption
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
fun <T : Patch<*>> T.stringArrayPatchOption(
|
|
||||||
key: String,
|
|
||||||
default: Array<String>? = null,
|
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Array<String>?) -> Boolean = { true }
|
|
||||||
) = StringArrayPatchOption(key, default, title, description, required, validator).also { options.register(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import org.w3c.dom.Document
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
import javax.xml.transform.TransformerFactory
|
|
||||||
import javax.xml.transform.dom.DOMSource
|
|
||||||
import javax.xml.transform.stream.StreamResult
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for a file that can be edited as a dom document.
|
|
||||||
*
|
|
||||||
* This constructor does not check for locks to the file when writing.
|
|
||||||
* Use the secondary constructor.
|
|
||||||
*
|
|
||||||
* @param inputStream the input stream to read the xml file from.
|
|
||||||
* @param outputStream the output stream to write the xml file to. If null, the file will be read only.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class DomFileEditor internal constructor(
|
|
||||||
private val inputStream: InputStream,
|
|
||||||
private val outputStream: Lazy<OutputStream>? = null,
|
|
||||||
) : Closeable {
|
|
||||||
// path to the xml file to unlock the resource when closing the editor
|
|
||||||
private var filePath: String? = null
|
|
||||||
private var closed: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The document of the xml file
|
|
||||||
*/
|
|
||||||
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
|
||||||
.also(Document::normalize)
|
|
||||||
|
|
||||||
|
|
||||||
// lazily open an output stream
|
|
||||||
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
|
||||||
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
|
||||||
constructor(file: File) : this(file.inputStream(), lazy { file.outputStream() }) {
|
|
||||||
// increase the lock
|
|
||||||
locks.merge(file.path, 1, Integer::sum)
|
|
||||||
filePath = file.path
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the editor. Write backs and decreases the lock count.
|
|
||||||
*
|
|
||||||
* Will not write back to the file if the file is still locked.
|
|
||||||
*/
|
|
||||||
override fun close() {
|
|
||||||
if (closed) return
|
|
||||||
|
|
||||||
inputStream.close()
|
|
||||||
|
|
||||||
// if the output stream is not null, do not close it
|
|
||||||
outputStream?.let {
|
|
||||||
// prevent writing to same file, if it is being locked
|
|
||||||
// isLocked will be false if the editor was created through a stream
|
|
||||||
val isLocked = filePath?.let { path ->
|
|
||||||
val isLocked = locks[path]!! > 1
|
|
||||||
// decrease the lock count if the editor was opened for a file
|
|
||||||
locks.merge(path, -1, Integer::sum)
|
|
||||||
isLocked
|
|
||||||
} ?: false
|
|
||||||
|
|
||||||
// if unlocked, write back to the file
|
|
||||||
if (!isLocked) {
|
|
||||||
it.value.use { stream ->
|
|
||||||
val result = StreamResult(stream)
|
|
||||||
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.value.close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
// map of concurrent open files
|
|
||||||
val locks = mutableMapOf<String, Int>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
|
||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that represents a set of classes and proxies.
|
|
||||||
*
|
|
||||||
* @param classes The classes to be backed by proxies.
|
|
||||||
*/
|
|
||||||
class ProxyClassList internal constructor(classes: MutableSet<ClassDef>) : MutableSet<ClassDef> by classes {
|
|
||||||
internal val proxies = mutableListOf<ClassProxy>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a [ClassProxy].
|
|
||||||
*/
|
|
||||||
fun add(classProxy: ClassProxy) = proxies.add(classProxy)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all classes with their mutated versions.
|
|
||||||
*/
|
|
||||||
internal fun replaceClasses() = proxies.removeIf { proxy ->
|
|
||||||
// If the proxy is unused, return false to keep it in the proxies list.
|
|
||||||
if (!proxy.resolved) return@removeIf false
|
|
||||||
|
|
||||||
// If it has been used, replace the original class with the mutable class.
|
|
||||||
remove(proxy.immutableClass)
|
|
||||||
add(proxy.mutableClass)
|
|
||||||
|
|
||||||
// Return true to remove the proxy from the proxies list.
|
|
||||||
return@removeIf true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package app.revanced.patcher.util.method
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.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.findClass(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)
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
package app.revanced.patcher.extensions
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21s
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
private object InstructionExtensionsTest {
|
|
||||||
private lateinit var testMethod: MutableMethod
|
|
||||||
private lateinit var testMethodImplementation: MutableMethodImplementation
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
fun createTestMethod() = ImmutableMethod(
|
|
||||||
"TestClass;",
|
|
||||||
"testMethod",
|
|
||||||
null,
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC.value,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
MutableMethodImplementation(16).also { testMethodImplementation = it }.apply {
|
|
||||||
repeat(10) { i -> this.addInstruction(TestInstruction(i)) }
|
|
||||||
},
|
|
||||||
).let { testMethod = it.toMutable() }
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionsToImplementationIndexed() = applyToImplementation {
|
|
||||||
addInstructions(5, getTestInstructions(5..6)).also {
|
|
||||||
assertRegisterIs(5, 5)
|
|
||||||
assertRegisterIs(6, 6)
|
|
||||||
|
|
||||||
assertRegisterIs(5, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionsToImplementation() = applyToImplementation {
|
|
||||||
addInstructions(getTestInstructions(10..11)).also {
|
|
||||||
assertRegisterIs(10, 10)
|
|
||||||
assertRegisterIs(11, 11)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun removeInstructionsFromImplementationIndexed() = applyToImplementation {
|
|
||||||
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun removeInstructionsFromImplementation() = applyToImplementation {
|
|
||||||
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
|
||||||
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
|
||||||
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun replaceInstructionsInImplementationIndexed() = applyToImplementation {
|
|
||||||
replaceInstructions(5, getTestInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
assertRegisterIs(7, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionToMethodIndexed() = applyToMethod {
|
|
||||||
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionToMethod() = applyToMethod {
|
|
||||||
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSmaliInstructionToMethodIndexed() = applyToMethod {
|
|
||||||
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSmaliInstructionToMethod() = applyToMethod {
|
|
||||||
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionsToMethodIndexed() = applyToMethod {
|
|
||||||
addInstructions(5, getTestInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
|
|
||||||
assertRegisterIs(5, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addInstructionsToMethod() = applyToMethod {
|
|
||||||
addInstructions(getTestInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 10)
|
|
||||||
assertRegisterIs(1, 11)
|
|
||||||
|
|
||||||
assertRegisterIs(9, 9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSmaliInstructionsToMethodIndexed() = applyToMethod {
|
|
||||||
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
|
|
||||||
assertRegisterIs(5, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSmaliInstructionsToMethod() = applyToMethod {
|
|
||||||
addInstructions(getTestSmaliInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 10)
|
|
||||||
assertRegisterIs(1, 11)
|
|
||||||
|
|
||||||
assertRegisterIs(9, 9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod {
|
|
||||||
val label = ExternalLabel("testLabel", getInstruction(5))
|
|
||||||
|
|
||||||
addInstructionsWithLabels(
|
|
||||||
5,
|
|
||||||
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
|
|
||||||
label
|
|
||||||
).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
assertRegisterIs(5, 8)
|
|
||||||
|
|
||||||
val gotoTarget = getInstruction<BuilderOffsetInstruction>(7)
|
|
||||||
.target.location.instruction as OneRegisterInstruction
|
|
||||||
|
|
||||||
assertEquals(5, gotoTarget.registerA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun removeInstructionFromMethodIndexed() = applyToMethod {
|
|
||||||
removeInstruction(5).also {
|
|
||||||
assertRegisterIs(4, 4)
|
|
||||||
assertRegisterIs(6, 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun removeInstructionsFromMethodIndexed() = applyToMethod {
|
|
||||||
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun removeInstructionsFromMethod() = applyToMethod {
|
|
||||||
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
|
||||||
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
|
||||||
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun replaceInstructionInMethodIndexed() = applyToMethod {
|
|
||||||
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun replaceInstructionsInMethodIndexed() = applyToMethod {
|
|
||||||
replaceInstructions(5, getTestInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
assertRegisterIs(7, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod {
|
|
||||||
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
|
|
||||||
assertRegisterIs(0, 5)
|
|
||||||
assertRegisterIs(1, 6)
|
|
||||||
assertRegisterIs(7, 7)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Helper methods
|
|
||||||
|
|
||||||
private fun applyToImplementation(block: MutableMethodImplementation.() -> Unit) {
|
|
||||||
testMethodImplementation.apply(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyToMethod(block: MutableMethod.() -> Unit) {
|
|
||||||
testMethod.apply(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals(
|
|
||||||
register, getInstruction<OneRegisterInstruction>(atIndex).registerA
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) =
|
|
||||||
implementation!!.assertRegisterIs(register, atIndex)
|
|
||||||
|
|
||||||
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
|
|
||||||
|
|
||||||
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
|
|
||||||
|
|
||||||
private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") {
|
|
||||||
getTestSmaliInstruction(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
private class TestInstruction(register: Int) : BuilderInstruction21s(Opcode.CONST_16, register, 0)
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.options
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.options.types.BooleanPatchOption.Companion.booleanPatchOption
|
|
||||||
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
|
||||||
import app.revanced.patcher.patch.options.types.array.StringArrayPatchOption.Companion.stringArrayPatchOption
|
|
||||||
import org.junit.jupiter.api.assertDoesNotThrow
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
internal class PatchOptionsTest {
|
|
||||||
@Test
|
|
||||||
fun `should not fail because default value is unvalidated`() {
|
|
||||||
assertDoesNotThrow {
|
|
||||||
OptionsTestPatch.options["required"].value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should throw due to incorrect type`() {
|
|
||||||
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
|
||||||
OptionsTestPatch.options["bool"] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should be nullable`() {
|
|
||||||
OptionsTestPatch.options["bool"] = null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `option should not be found`() {
|
|
||||||
assertThrows<PatchOptionException.PatchOptionNotFoundException> {
|
|
||||||
OptionsTestPatch.options["this option does not exist"] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should be able to add options manually`() {
|
|
||||||
assertThrows<PatchOptionException.InvalidValueTypeException> {
|
|
||||||
OptionsTestPatch.options["array"] = OptionsTestPatch.stringArrayOption
|
|
||||||
}
|
|
||||||
assertDoesNotThrow {
|
|
||||||
OptionsTestPatch.options.register(OptionsTestPatch.stringArrayOption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private object OptionsTestPatch : BytecodePatch() {
|
|
||||||
private var stringOption by stringPatchOption("string", "default")
|
|
||||||
private var booleanOption by booleanPatchOption("bool", true)
|
|
||||||
private var requiredStringOption by stringPatchOption("required", "default", required = true)
|
|
||||||
private var nullDefaultRequiredOption by stringPatchOption("null", null, required = true)
|
|
||||||
|
|
||||||
val stringArrayOption = stringArrayPatchOption("array", arrayOf("1", "2"))
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.usage
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.PatchException
|
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
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.Companion.toMutable
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Format
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
|
||||||
import com.android.tools.smali.dexlib2.util.Preconditions
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@Patch(
|
|
||||||
name = "Example bytecode patch",
|
|
||||||
description = "Example demonstration of a bytecode patch.",
|
|
||||||
dependencies = [ExampleResourcePatch::class],
|
|
||||||
compatiblePackages = [CompatiblePackage("com.example.examplePackage", arrayOf("0.0.1", "0.0.2"))]
|
|
||||||
)
|
|
||||||
object ExampleBytecodePatch : BytecodePatch(setOf(ExampleFingerprint)) {
|
|
||||||
// Entry point of a patch. Supplied fingerprints are resolved at this point.
|
|
||||||
override fun execute(context: BytecodeContext) {
|
|
||||||
ExampleFingerprint.result?.let { result ->
|
|
||||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
|
||||||
// Get the start index of our opcode pattern.
|
|
||||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
|
||||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
|
||||||
|
|
||||||
result.mutableMethod.apply {
|
|
||||||
replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
|
||||||
|
|
||||||
// Store the fields initial value into the first virtual register.
|
|
||||||
replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
|
||||||
|
|
||||||
// Now let's create a new call to our method and print the return value!
|
|
||||||
// You can also use the smali compiler to create instructions.
|
|
||||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
|
||||||
//
|
|
||||||
// Control flow instructions are not supported as of now.
|
|
||||||
addInstructionsWithLabels(
|
|
||||||
startIndex + 2,
|
|
||||||
"""
|
|
||||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
|
||||||
move-result-object v1
|
|
||||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the class in which the method matching our fingerprint is defined in.
|
|
||||||
context.findClass(result.classDef.type)!!.mutableClass.apply {
|
|
||||||
// Add a new method that returns a string.
|
|
||||||
methods.add(
|
|
||||||
ImmutableMethod(
|
|
||||||
result.classDef.type,
|
|
||||||
"returnHello",
|
|
||||||
null,
|
|
||||||
"Ljava/lang/String;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
1,
|
|
||||||
ImmutableList.of(
|
|
||||||
BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
0,
|
|
||||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
|
||||||
),
|
|
||||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add a field in the main class.
|
|
||||||
// We will use this field in our method below to call println on.
|
|
||||||
// The field holds the Ljava/io/PrintStream->out; field.
|
|
||||||
fields.add(
|
|
||||||
ImmutableField(
|
|
||||||
type,
|
|
||||||
"dummyField",
|
|
||||||
"Ljava/io/PrintStream;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
ImmutableFieldEncodedValue(
|
|
||||||
ImmutableFieldReference(
|
|
||||||
"Ljava/lang/System;",
|
|
||||||
"out",
|
|
||||||
"Ljava/io/PrintStream;"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: throw PatchException("Fingerprint failed to resolve.")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace an existing instruction with a new one containing a reference to a new string.
|
|
||||||
* @param index The index of the instruction to replace.
|
|
||||||
* @param string The replacement string.
|
|
||||||
*/
|
|
||||||
private fun MutableMethod.replaceStringAt(index: Int, string: String) {
|
|
||||||
val instruction = getInstruction(index)
|
|
||||||
|
|
||||||
// Utility method of dexlib2.
|
|
||||||
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
|
||||||
|
|
||||||
// Cast this to an instruction of the format 21c.
|
|
||||||
// The instruction format can be found in the docs at
|
|
||||||
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
|
||||||
val strInstruction = instruction as Instruction21c
|
|
||||||
|
|
||||||
// In our case we want an instruction with the opcode CONST_STRING
|
|
||||||
// The format is 21c, so we create a new BuilderInstruction21c
|
|
||||||
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
|
||||||
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
|
||||||
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
|
||||||
replaceInstruction(
|
|
||||||
index,
|
|
||||||
"const-string ${strInstruction.registerA}, ${ImmutableStringReference(string)}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.usage
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
@FuzzyPatternScanMethod(2)
|
|
||||||
object ExampleFingerprint : MethodFingerprint(
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
listOf("[L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
null, // Matching unknown opcodes.
|
|
||||||
Opcode.INVOKE_STATIC, // This is intentionally wrong to test fuzzy matching.
|
|
||||||
Opcode.RETURN_VOID
|
|
||||||
),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.usage
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleResourcePatch : ResourcePatch() {
|
|
||||||
override fun execute(context: ResourceContext) {
|
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
|
||||||
val element = editor // regular DomFileEditor
|
|
||||||
.file
|
|
||||||
.getElementsByTagName("application")
|
|
||||||
.item(0) as Element
|
|
||||||
element
|
|
||||||
.setAttribute(
|
|
||||||
"exampleAttribute",
|
|
||||||
"exampleValue"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1 @@
|
|||||||
val githubUsername: String = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
|
rootProject.name = "revanced-patcher"
|
||||||
val githubPassword: String = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
google()
|
|
||||||
mavenLocal()
|
|
||||||
listOf("multidexlib2", "apktool").forEach { repo ->
|
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/$repo")
|
|
||||||
credentials {
|
|
||||||
username = githubUsername
|
|
||||||
password = githubPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include("revanced-patch-annotation-processor", "revanced-patcher")
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user