mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-14 15:13:13 +00:00
Compare commits
186 Commits
v15.0.0-de
...
v1.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d971239997 | ||
|
|
28b98478e4 | ||
|
|
83d608ac06 | ||
|
|
b369a30dd5 | ||
|
|
8442991290 | ||
|
|
a06c0db6a7 | ||
|
|
3f0c740200 | ||
|
|
545c5c144d | ||
|
|
0fa529fcdf | ||
|
|
7573db2575 | ||
|
|
70ca184cf9 | ||
|
|
72f16b7785 | ||
|
|
fc03639b26 | ||
|
|
88a85f94e7 | ||
|
|
45a167e785 | ||
|
|
699d8abf59 | ||
|
|
b58c718699 | ||
|
|
266d6810a9 | ||
|
|
40b1fa43e1 | ||
|
|
94f9594eed | ||
|
|
cff58ab180 | ||
|
|
989646b0b5 | ||
|
|
5c3fbaee7a | ||
|
|
08525e9c26 | ||
|
|
5630e49663 | ||
|
|
0543122427 | ||
|
|
0873703056 | ||
|
|
1a99ecaffe | ||
|
|
6726884be5 | ||
|
|
8b4f3947f8 | ||
|
|
4d74de4061 | ||
|
|
4fbee7d255 | ||
|
|
fd9f639605 | ||
|
|
9084ccc2a2 | ||
|
|
83a8a48176 | ||
|
|
66b08f8b3a | ||
|
|
e286ba5090 | ||
|
|
e5c054ac2f | ||
|
|
cb0741d05f | ||
|
|
38556d61ab | ||
|
|
ce8021b482 | ||
|
|
243dba7751 | ||
|
|
698f759979 | ||
|
|
1701da3dde | ||
|
|
37fa9949ec | ||
|
|
ac36d19693 | ||
|
|
c245edb0c5 | ||
|
|
1f7bf3ac6c | ||
|
|
bfeeaf4435 | ||
|
|
748d0abad0 | ||
|
|
569238ab76 | ||
|
|
23197879b2 | ||
|
|
305a81793a | ||
|
|
33f9211f98 | ||
|
|
864e38c069 | ||
|
|
659e1087c9 | ||
|
|
03700ffa51 | ||
|
|
ae06d826e8 | ||
|
|
5ca5188fc2 | ||
|
|
f88c11820d | ||
|
|
93e81ff047 | ||
|
|
d49df10a3c | ||
|
|
04b49b8b66 | ||
|
|
5ddc63f979 | ||
|
|
82b1e66d54 | ||
|
|
fd630cd429 | ||
|
|
f4a47d4dc8 | ||
|
|
3bfc24fc16 | ||
|
|
25bba2c1d8 | ||
|
|
4dea27e831 | ||
|
|
a0d6d46217 | ||
|
|
643a14e664 | ||
|
|
355e6d82cc | ||
|
|
df7503b47b | ||
|
|
a01dded092 | ||
|
|
9ae95174e6 | ||
|
|
e161f7fea4 | ||
|
|
200e3c9fdb | ||
|
|
f0f34031dd | ||
|
|
560c485ab0 | ||
|
|
cc5a414692 | ||
|
|
c2a334eb3f | ||
|
|
1b2fbbca26 | ||
|
|
4458141d6d | ||
|
|
8544fc4cbc | ||
|
|
a492808021 | ||
|
|
0204eee79e | ||
|
|
4022b8b847 | ||
|
|
8daf877fac | ||
|
|
7d38bb0baa | ||
|
|
5f71a342ac | ||
|
|
866b03af21 | ||
|
|
4c1a42b216 | ||
|
|
264989f488 | ||
|
|
4281546f69 | ||
|
|
af4f2396c7 | ||
|
|
147195647c | ||
|
|
433914feda | ||
|
|
622138736d | ||
|
|
aed4fd9a3c | ||
|
|
32e645850d | ||
|
|
e45fc02aae | ||
|
|
e0d29cf450 | ||
|
|
2b888e381c | ||
|
|
f72dd68ec5 | ||
|
|
3b68d5c65e | ||
|
|
eed1cfda7b | ||
|
|
8b70bb4290 | ||
|
|
dbda641d0c | ||
|
|
5ae5e98f1f | ||
|
|
1ba40ab1cb | ||
|
|
e9c119ebb1 | ||
|
|
1bd6d1d5b8 | ||
|
|
4e7378bd79 | ||
|
|
28ed4793e3 | ||
|
|
312235b194 | ||
|
|
6ab21e5891 | ||
|
|
db8d1150c3 | ||
|
|
8f778f38fe | ||
|
|
88a6a27302 | ||
|
|
a9e4e8ac32 | ||
|
|
d5e694c306 | ||
|
|
dde0a22642 | ||
|
|
9a67aa3ff4 | ||
|
|
e69708f21e | ||
|
|
c49071aff7 | ||
|
|
d15240d033 | ||
|
|
6767c8fbc1 | ||
|
|
4543b36616 | ||
|
|
ec6d462ade | ||
|
|
84bc7e0dc7 | ||
|
|
6ad51aad9a | ||
|
|
b711b8001e | ||
|
|
12c10d8c64 | ||
|
|
05e44007d8 | ||
|
|
dbafe2ab37 | ||
|
|
45a885dbde | ||
|
|
78235d1abe | ||
|
|
aec5eeb597 | ||
|
|
d98c9eeb30 | ||
|
|
f8e978af88 | ||
|
|
86cb053566 | ||
|
|
c1ccb70de4 | ||
|
|
bb42fa3c6f | ||
|
|
2d3c61113d | ||
|
|
6bc4e7eab7 | ||
|
|
be51f42710 | ||
|
|
fa0412985c | ||
|
|
0048788dd0 | ||
|
|
47eb493f54 | ||
|
|
6b1337e4fc | ||
|
|
f4589db3a9 | ||
|
|
1af31b2aa3 | ||
|
|
14f7667156 | ||
|
|
cd57a8c9a0 | ||
|
|
0d3beb353d | ||
|
|
ddef338631 | ||
|
|
fc4b673087 | ||
|
|
8d1bb5f3d9 | ||
|
|
c8a017a4c0 | ||
|
|
51fb59a43c | ||
|
|
a78715133c | ||
|
|
e8f6973938 | ||
|
|
3cb1e01587 | ||
|
|
cb4ee207e1 | ||
|
|
ca6b94d943 | ||
|
|
6cdb6887d4 | ||
|
|
ab6453ca8a | ||
|
|
e8182c17ad | ||
|
|
49beec9fc6 | ||
|
|
3ab42a932c | ||
|
|
4d98cbc9e8 | ||
|
|
87bbde5e06 | ||
|
|
8db8893ab1 | ||
|
|
00c6ab7faf | ||
|
|
460d62a24c | ||
|
|
89e4b9f762 | ||
|
|
a8fd7c00c3 | ||
|
|
1769132a9e | ||
|
|
6c0f0823c9 | ||
|
|
23e897a7a9 | ||
|
|
7e67daf878 | ||
|
|
593c83f29f | ||
|
|
72e123dd01 | ||
|
|
599a401ed9 | ||
|
|
3f8500b059 |
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# Linux start script should use lf
|
||||
/gradlew text eol=lf
|
||||
|
||||
# These are Windows script files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a very clearly broken issue.
|
||||
title: 'bug: <title>'
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced bug report
|
||||
|
||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Crash
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: How did you find the bug? Any additional details that might help?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Add the steps to reproduce this bug including your environment.
|
||||
placeholder: Step 1. Download some files. Step 2. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots or videos
|
||||
description: Add screenshots or videos that show the bug here.
|
||||
placeholder: Drag and drop the screenshots/videos into this box.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solution
|
||||
description: If applicable, add a possible solution.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I filled out all of the requested information in this issue properly.
|
||||
required: true
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 📃 Documentation
|
||||
url: https://github.com/revanced/revanced-documentation/
|
||||
about: Don't know how or where to start? Check out our documentation!
|
||||
- name: 🗨 Discussions
|
||||
url: https://github.com/revanced/revanced-suggestions/discussions
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed feature request.
|
||||
title: 'feat: <title>'
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced feature request
|
||||
|
||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Functionality
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue
|
||||
description: What is the current problem. Why does it require a feature request?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe your feature in detail. How does it solve the issue?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: Why should your feature should be considered?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I filled out all of the requested information in this issue properly.
|
||||
required: true
|
||||
2
.github/config.yml
vendored
2
.github/config.yml
vendored
@@ -1,2 +0,0 @@
|
||||
firstPRMergeComment: >
|
||||
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
||||
25
.github/workflows/pull_request.yml
vendored
25
.github/workflows/pull_request.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: PR to main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
||||
|
||||
jobs:
|
||||
pull-request:
|
||||
name: Open pull request
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Open pull request
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
destination_branch: 'main'
|
||||
pr_title: 'chore: ${{ env.MESSAGE }}'
|
||||
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
|
||||
pr_draft: true
|
||||
35
.github/workflows/release.yml
vendored
35
.github/workflows/release.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
@@ -10,36 +9,34 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# Make sure the release step uses its own credentials:
|
||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
path: |
|
||||
${{ runner.home }}/.gradle/caches
|
||||
${{ runner.home }}/.gradle/wrapper
|
||||
.gradle
|
||||
build
|
||||
node_modules
|
||||
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew build clean --no-daemon
|
||||
run: ./gradlew build
|
||||
- name: Setup semantic-release
|
||||
run: npm install
|
||||
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||
run: npm exec semantic-release
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: npx semantic-release
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -74,7 +74,6 @@ cmake-build-*/
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
.idea/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
@@ -116,9 +115,3 @@ gradle-app.setting
|
||||
|
||||
# Avoid ignoring test resources
|
||||
!src/test/resources/*
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# Gradle props, to avoid sharing the gpr key
|
||||
gradle.properties
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
7
.idea/discord.xml
generated
Normal file
7
.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
15
.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxProjectSettings">
|
||||
<option name="commitMessageIssueKeyValidationOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
<option name="commitMessageValidationEnabledOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
10
.idea/misc.xml
generated
Normal file
10
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="azul-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/vcs.xml
generated
Normal file
12
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CommitMessageInspectionProfile">
|
||||
<profile version="1.0">
|
||||
<inspection_tool class="CommitFormat" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CommitNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
22
.releaserc
22
.releaserc
@@ -7,13 +7,7 @@
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@semantic-release/commit-analyzer", {
|
||||
"releaseRules": [
|
||||
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"gradle-semantic-release-plugin",
|
||||
@@ -26,18 +20,6 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"@saithodev/semantic-release-backmerge",
|
||||
{
|
||||
backmergeBranches: [{"from": "main", "to": "dev"}],
|
||||
clearWorkspace: true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
successComment: false
|
||||
}
|
||||
]
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
|
||||
1519
CHANGELOG.md
1519
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1 @@
|
||||
# 💉 ReVanced Patcher
|
||||
|
||||
ReVanced Patcher used to patch Android applications.
|
||||
# Patcher
|
||||
|
||||
@@ -1,10 +1,74 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.0" apply false
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
kotlin("jvm") version "1.6.21"
|
||||
java
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply(plugin = "maven-publish")
|
||||
group = "app.revanced"
|
||||
|
||||
group = "app.revanced"
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
|
||||
credentials {
|
||||
// DO NOT set these variables in the project's gradle.properties.
|
||||
// Instead, you should set them in:
|
||||
// Windows: %homepath%\.gradle\gradle.properties
|
||||
// Linux: ~/.gradle/gradle.properties
|
||||
username =
|
||||
project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") // DO NOT CHANGE!
|
||||
password =
|
||||
project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") // DO NOT CHANGE!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
|
||||
api("xpp3:xpp3:1.1.4c")
|
||||
api("org.apktool:apktool-lib:2.6.1")
|
||||
api("app.revanced:multidexlib2:2.5.2.r2")
|
||||
api("org.smali:smali:2.5.2")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
implementation(kotlin("reflect"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events("PASSED", "SKIPPED", "FAILED")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
val isGitHubCI = System.getenv("GITHUB_ACTOR") != null
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
if (isGitHubCI) {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
publications {
|
||||
register<MavenPublication>("gpr") {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 15.0.0-dev.3
|
||||
version = 1.0.0-dev.18
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
[versions]
|
||||
android = "4.1.1.4"
|
||||
kotlin-reflect = "1.9.0"
|
||||
apktool-lib = "2.8.2-6"
|
||||
kotlin-test = "1.8.20-RC"
|
||||
kotlinx-coroutines-core = "1.7.1"
|
||||
multidexlib2 = "3.0.3.r2"
|
||||
smali = "3.0.3"
|
||||
symbol-processing-api = "1.9.0-1.0.11"
|
||||
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]
|
||||
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" }
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" }
|
||||
multidexlib2 = { module = "app.revanced:multidexlib2", version.ref = "multidexlib2" }
|
||||
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" }
|
||||
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]
|
||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
40
gradlew
vendored
Executable file → Normal file
40
gradlew
vendored
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright <EFBFBD> 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -32,10 +32,10 @@
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
|
||||
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
|
||||
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
|
||||
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,11 +80,14 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
@@ -130,29 +133,22 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -197,10 +193,6 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
@@ -213,12 +205,6 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,8 +25,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
6580
package-lock.json
generated
6580
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@saithodev/semantic-release-backmerge": "^3.1.0",
|
||||
"@semantic-release/changelog": "^6.0.2",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.7.6",
|
||||
"semantic-release": "^20.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,25 +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 <init> (Lcom/google/devtools/ksp/processing/CodeGenerator;Lcom/google/devtools/ksp/processing/KSPLogger;)V
|
||||
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,205 +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.*
|
||||
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(
|
||||
private val codeGenerator: CodeGenerator,
|
||||
private val logger: KSPLogger
|
||||
) : 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 executablePatches = 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>)
|
||||
.joinToString(", ") { version -> "\"$version\"" }
|
||||
|
||||
CodeBlock.of(
|
||||
"%T(%S, setOf(%L))",
|
||||
app.revanced.patcher.patch.Patch.CompatiblePackage::class,
|
||||
packageName,
|
||||
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,
|
||||
// unlike the annotated patch.
|
||||
val dependencyResolutionMap = buildMap {
|
||||
executablePatches.values.filter { it.dependencies != null }.flatMap {
|
||||
it.dependencies!!
|
||||
}.distinct().forEach { dependency ->
|
||||
executablePatches.keys.find { it.qualifiedName?.asString() == dependency.toString() }
|
||||
?.let { patch ->
|
||||
this[dependency] = ClassName(
|
||||
patch.packageName.asString(),
|
||||
patch.simpleName.asString() + "Generated"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executablePatches.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(", ")
|
||||
)
|
||||
}
|
||||
|
||||
patchAnnotation.dependencies?.let { dependencies ->
|
||||
addSuperclassConstructorParameter(
|
||||
"dependencies = setOf(%L)",
|
||||
buildList {
|
||||
addAll(dependencies)
|
||||
// Also add the source class of the generated class so that it is also executed.
|
||||
add(patchDeclaration.toClassName())
|
||||
}.joinToString(", ") { dependency ->
|
||||
"${(dependencyResolutionMap[dependency] ?: dependency)}::class"
|
||||
}
|
||||
)
|
||||
}
|
||||
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 { (key, 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, environment.logger)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
app.revanced.patcher.patch.annotation.processor.PatchProcessorProvider
|
||||
@@ -1,135 +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
|
||||
)
|
||||
|
||||
// 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() = assertNull(
|
||||
compile(
|
||||
getSourceFile(
|
||||
"limitations/manualdependency", "DependentPatch"
|
||||
), getSourceFile(
|
||||
"limitations/manualdependency", "DependencyPatch"
|
||||
)
|
||||
).loadPatch("$SAMPLE_PACKAGE.limitations.manualdependency.DependentPatchGenerated").dependencies
|
||||
)
|
||||
|
||||
// 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,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,904 +0,0 @@
|
||||
public abstract interface class app/revanced/patcher/IntegrationsConsumer {
|
||||
public abstract fun acceptIntegrations (Ljava/util/List;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/PackageMetadata {
|
||||
public final fun getPackageName ()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 synthetic fun <init> (Ljava/lang/ClassLoader;[Ljava/io/File;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun add (Lapp/revanced/patcher/patch/Patch;)Z
|
||||
public synthetic fun add (Ljava/lang/Object;)Z
|
||||
public fun addAll (Ljava/util/Collection;)Z
|
||||
public fun clear ()V
|
||||
public fun contains (Lapp/revanced/patcher/patch/Patch;)Z
|
||||
public final fun contains (Ljava/lang/Object;)Z
|
||||
public fun containsAll (Ljava/util/Collection;)Z
|
||||
public 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 get ()Lapp/revanced/patcher/PatcherResult;
|
||||
public synthetic fun get ()Ljava/lang/Object;
|
||||
public final fun getContext ()Lapp/revanced/patcher/PatcherContext;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/PatcherContext {
|
||||
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 fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;)V
|
||||
public synthetic fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
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 fun <init> (Ljava/lang/String;Ljava/io/InputStream;)V
|
||||
public final fun getName ()Ljava/lang/String;
|
||||
public final fun getStream ()Ljava/io/InputStream;
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/patcher/PatchesConsumer {
|
||||
public abstract fun acceptPatches (Ljava/util/List;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/data/BytecodeContext : app/revanced/patcher/data/Context {
|
||||
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 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 static final field INSTANCE Lapp/revanced/patcher/extensions/InstructionExtensions;
|
||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/util/List;)V
|
||||
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
||||
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;Ljava/util/List;)V
|
||||
public final fun addInstructionsWithLabels (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
|
||||
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Ljava/lang/Object;
|
||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Ljava/lang/Object;
|
||||
public final fun getInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
||||
public final fun removeInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;II)V
|
||||
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)V
|
||||
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;II)V
|
||||
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
||||
public final fun replaceInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/extensions/MethodFingerprintExtensions {
|
||||
public static final field INSTANCE Lapp/revanced/patcher/extensions/MethodFingerprintExtensions;
|
||||
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 final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult {
|
||||
public fun <init> (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;)V
|
||||
public final fun component1 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
||||
public final fun component2 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||
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 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 fun getStringsScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult {
|
||||
public fun <init> (IILjava/util/List;)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 fun <init> (Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;II)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 fun <init> (Ljava/util/List;)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 fun <init> (Ljava/lang/String;I)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 abstract fun error (Ljava/lang/String;)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 static fun error (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||
public static fun info (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||
public static fun trace (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||
public static fun warn (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/logging/impl/NopLogger : app/revanced/patcher/logging/Logger {
|
||||
public static final field INSTANCE Lapp/revanced/patcher/logging/impl/NopLogger;
|
||||
public fun error (Ljava/lang/String;)V
|
||||
public fun info (Ljava/lang/String;)V
|
||||
public fun trace (Ljava/lang/String;)V
|
||||
public fun warn (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V
|
||||
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 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;Ljava/util/Set;Ljava/util/Set;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public abstract fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
public final fun getCompatiblePackages ()Ljava/util/Set;
|
||||
public final fun getDependencies ()Ljava/util/Set;
|
||||
public final fun getDescription ()Ljava/lang/String;
|
||||
public final fun getName ()Ljava/lang/String;
|
||||
public final fun getOptions ()Lapp/revanced/patcher/patch/options/PatchOptions;
|
||||
public final fun getRequiresIntegrations ()Z
|
||||
public final fun getUse ()Z
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/Patch$CompatiblePackage {
|
||||
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun getName ()Ljava/lang/String;
|
||||
public final fun getVersions ()Ljava/util/Set;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchException : java/lang/Exception {
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
|
||||
public fun <init> (Ljava/lang/Throwable;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/patch/PatchResult {
|
||||
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
||||
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZ)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
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 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 getSize ()I
|
||||
public fun isEmpty ()Z
|
||||
public fun iterator ()Ljava/util/Iterator;
|
||||
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 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/util/method/MethodWalker {
|
||||
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
||||
public final fun nextMethod (IZ)Lapp/revanced/patcher/util/method/MethodWalker;
|
||||
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/util/proxy/ClassProxy {
|
||||
public final fun getImmutableClass ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
||||
public final fun getMutableClass ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation : com/android/tools/smali/dexlib2/base/BaseAnnotation {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Annotation;)V
|
||||
public fun getElements ()Ljava/util/Set;
|
||||
public fun getType ()Ljava/lang/String;
|
||||
public fun getVisibility ()I
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Annotation;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement : com/android/tools/smali/dexlib2/base/BaseAnnotationElement {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)V
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
||||
public final fun setName (Ljava/lang/String;)V
|
||||
public final fun setValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass : com/android/tools/smali/dexlib2/base/reference/BaseTypeReference, com/android/tools/smali/dexlib2/iface/ClassDef {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)V
|
||||
public final fun charAt (I)C
|
||||
public fun get (I)C
|
||||
public fun getAccessFlags ()I
|
||||
public fun getAnnotations ()Ljava/util/Set;
|
||||
public synthetic fun getDirectMethods ()Ljava/lang/Iterable;
|
||||
public fun getDirectMethods ()Ljava/util/Set;
|
||||
public synthetic fun getFields ()Ljava/lang/Iterable;
|
||||
public fun getFields ()Ljava/util/Set;
|
||||
public synthetic fun getInstanceFields ()Ljava/lang/Iterable;
|
||||
public fun getInstanceFields ()Ljava/util/Set;
|
||||
public fun getInterfaces ()Ljava/util/List;
|
||||
public fun getLength ()I
|
||||
public synthetic fun getMethods ()Ljava/lang/Iterable;
|
||||
public fun getMethods ()Ljava/util/Set;
|
||||
public fun getSourceFile ()Ljava/lang/String;
|
||||
public synthetic fun getStaticFields ()Ljava/lang/Iterable;
|
||||
public fun getStaticFields ()Ljava/util/Set;
|
||||
public fun getSuperclass ()Ljava/lang/String;
|
||||
public fun getType ()Ljava/lang/String;
|
||||
public synthetic fun getVirtualMethods ()Ljava/lang/Iterable;
|
||||
public fun getVirtualMethods ()Ljava/util/Set;
|
||||
public final fun length ()I
|
||||
public final fun setAccessFlags (I)V
|
||||
public final fun setSourceFile (Ljava/lang/String;)V
|
||||
public final fun setSuperClass (Ljava/lang/String;)V
|
||||
public final fun setType (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField : com/android/tools/smali/dexlib2/base/reference/BaseFieldReference, com/android/tools/smali/dexlib2/iface/Field {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Field;)V
|
||||
public fun getAccessFlags ()I
|
||||
public fun getAnnotations ()Ljava/util/Set;
|
||||
public fun getDefiningClass ()Ljava/lang/String;
|
||||
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
||||
public fun getInitialValue ()Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
||||
public synthetic fun getInitialValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun getType ()Ljava/lang/String;
|
||||
public final fun setAccessFlags (I)V
|
||||
public final fun setDefiningClass (Ljava/lang/String;)V
|
||||
public final fun setInitialValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
||||
public final fun setName (Ljava/lang/String;)V
|
||||
public final fun setType (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Field;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod : com/android/tools/smali/dexlib2/base/reference/BaseMethodReference, com/android/tools/smali/dexlib2/iface/Method {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Method;)V
|
||||
public fun getAccessFlags ()I
|
||||
public fun getAnnotations ()Ljava/util/Set;
|
||||
public fun getDefiningClass ()Ljava/lang/String;
|
||||
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
||||
public fun getImplementation ()Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;
|
||||
public synthetic fun getImplementation ()Lcom/android/tools/smali/dexlib2/iface/MethodImplementation;
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun getParameterTypes ()Ljava/util/List;
|
||||
public fun getParameters ()Ljava/util/List;
|
||||
public fun getReturnType ()Ljava/lang/String;
|
||||
public final fun setAccessFlags (I)V
|
||||
public final fun setDefiningClass (Ljava/lang/String;)V
|
||||
public final fun setName (Ljava/lang/String;)V
|
||||
public final fun setReturnType (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter : com/android/tools/smali/dexlib2/base/BaseMethodParameter, com/android/tools/smali/dexlib2/iface/MethodParameter {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)V
|
||||
public final fun charAt (I)C
|
||||
public fun get (I)C
|
||||
public fun getAnnotations ()Ljava/util/Set;
|
||||
public fun getLength ()I
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun getSignature ()Ljava/lang/String;
|
||||
public fun getType ()Ljava/lang/String;
|
||||
public final fun length ()I
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseAnnotationEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)V
|
||||
public fun getElements ()Ljava/util/Set;
|
||||
public fun getType ()Ljava/lang/String;
|
||||
public final fun setType (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseArrayEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)V
|
||||
public fun getValue ()Ljava/util/List;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseBooleanEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)V
|
||||
public fun getValue ()Z
|
||||
public final fun setValue (Z)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseByteEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)V
|
||||
public fun getValue ()B
|
||||
public final fun setValue (B)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseCharEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)V
|
||||
public fun getValue ()C
|
||||
public final fun setValue (C)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseDoubleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)V
|
||||
public fun getValue ()D
|
||||
public final fun setValue (D)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue;
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue : com/android/tools/smali/dexlib2/iface/value/EncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseEnumEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)V
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFieldEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)V
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
||||
public fun getValueType ()I
|
||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFloatEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)V
|
||||
public fun getValue ()F
|
||||
public final fun setValue (F)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseIntEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)V
|
||||
public fun getValue ()I
|
||||
public final fun setValue (I)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseLongEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)V
|
||||
public fun getValue ()J
|
||||
public final fun setValue (J)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)V
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;
|
||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodHandleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)V
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;
|
||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)V
|
||||
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;
|
||||
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseNullEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion;
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseShortEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)V
|
||||
public fun getValue ()S
|
||||
public final fun setValue (S)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseStringEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/StringEncodedValue;)V
|
||||
public fun getValue ()Ljava/lang/String;
|
||||
public final fun setValue (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion;
|
||||
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)V
|
||||
public fun getValue ()Ljava/lang/String;
|
||||
public final fun setValue (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion {
|
||||
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/smali/ExternalLabel {
|
||||
public fun <init> (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)V
|
||||
public final fun copy (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
||||
public static synthetic fun copy$default (Lapp/revanced/patcher/util/smali/ExternalLabel;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;ILjava/lang/Object;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler {
|
||||
public static final field Companion Lapp/revanced/patcher/util/smali/InlineSmaliCompiler$Companion;
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler$Companion {
|
||||
public final fun compile (Ljava/lang/String;Ljava/lang/String;IZ)Ljava/util/List;
|
||||
}
|
||||
|
||||
public final class app/revanced/patcher/util/smali/InlineSmaliCompilerKt {
|
||||
public static final fun toInstruction (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||
public static synthetic fun toInstruction$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||
public static final fun toInstructions (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
||||
public static synthetic fun toInstructions$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Ljava/util/List;
|
||||
}
|
||||
|
||||
@@ -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,14 +0,0 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import brut.androlib.apk.ApkInfo
|
||||
|
||||
/**
|
||||
* Metadata about a package.
|
||||
*/
|
||||
class PackageMetadata internal constructor(internal val apkInfo: ApkInfo) {
|
||||
lateinit var packageName: String
|
||||
internal set
|
||||
|
||||
lateinit var packageVersion: String
|
||||
internal set
|
||||
}
|
||||
@@ -1,122 +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.
|
||||
*/
|
||||
sealed class PatchBundleLoader private constructor(
|
||||
classLoader: ClassLoader,
|
||||
patchBundles: Array<out File>,
|
||||
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
||||
) : PatchSet by mutableSetOf() {
|
||||
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||
|
||||
init {
|
||||
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
||||
classLoader.loadClass(it)
|
||||
}.filter {
|
||||
it.isInstance(Patch::class.java)
|
||||
}.mapNotNull { patchClass ->
|
||||
patchClass.getInstance(logger)
|
||||
}.filter {
|
||||
it.name != null
|
||||
}.let { patches ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(this as MutableSet<Patch<*>>).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.
|
||||
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
||||
*/
|
||||
internal fun Class<*>.getInstance(logger: Logger): Patch<*>? {
|
||||
return try {
|
||||
getField("INSTANCE").get(null)
|
||||
} catch (exception: NoSuchFileException) {
|
||||
logger.fine(
|
||||
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
||||
"Will try to instantiate it."
|
||||
)
|
||||
|
||||
try {
|
||||
getDeclaredConstructor().newInstance()
|
||||
} catch (exception: Exception) {
|
||||
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,276 +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.Level
|
||||
import java.util.logging.LogManager
|
||||
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 {
|
||||
LogManager.getLogManager().let { manager ->
|
||||
// Disable root logger.
|
||||
manager.getLogger("").level = Level.OFF
|
||||
|
||||
// Enable ReVanced logging only.
|
||||
manager.loggerNames
|
||||
.toList()
|
||||
.filter { it.startsWith("app.revanced") }
|
||||
.map { manager.getLogger(it) }
|
||||
.forEach { it.level = Level.INFO }
|
||||
}
|
||||
|
||||
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) ?: {
|
||||
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
|
||||
|
||||
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 { dependencyName ->
|
||||
val dependency = context.executablePatches[dependencyName]!!
|
||||
val result = executePatch(dependency, executedPatches)
|
||||
|
||||
result.exception?.let {
|
||||
return PatchResult(
|
||||
patch,
|
||||
PatchException("'$patchName' depends on '${dependency}' that raised an exception: $it")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return try {
|
||||
// TODO: Implement this in a more polymorphic way.
|
||||
when (patch) {
|
||||
is BytecodePatch -> {
|
||||
patch.fingerprints.toList().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.map { it.value }.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",
|
||||
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.
|
||||
* True when any supplied [Patch] is annotated with [RequiresIntegrations].
|
||||
*/
|
||||
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,330 +0,0 @@
|
||||
package app.revanced.patcher.extensions
|
||||
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patcher.util.smali.toInstruction
|
||||
import app.revanced.patcher.util.smali.toInstructions
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
||||
import com.android.tools.smali.dexlib2.builder.Label
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.*
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
object InstructionExtensions {
|
||||
|
||||
/**
|
||||
* Add instructions to a method at the given index.
|
||||
*
|
||||
* @param index The index to add the instructions at.
|
||||
* @param instructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethodImplementation.addInstructions(
|
||||
index: Int,
|
||||
instructions: List<BuilderInstruction>
|
||||
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
||||
|
||||
/**
|
||||
* Add instructions to a method.
|
||||
* The instructions will be added at the end of the method.
|
||||
*
|
||||
* @param instructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
|
||||
instructions.forEach { this.addInstruction(it) }
|
||||
|
||||
/**
|
||||
* Remove instructions from a method at the given index.
|
||||
*
|
||||
* @param index The index to remove the instructions at.
|
||||
* @param count The amount of instructions to remove.
|
||||
*/
|
||||
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) = repeat(count) {
|
||||
removeInstruction(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first instructions from a method.
|
||||
*
|
||||
* @param count The amount of instructions to remove.
|
||||
*/
|
||||
fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count)
|
||||
|
||||
/**
|
||||
* Replace instructions at the given index with the given instructions.
|
||||
* The amount of instructions to replace is the amount of instructions in the given list.
|
||||
*
|
||||
* @param index The index to replace the instructions at.
|
||||
* @param instructions The instructions to replace the instructions with.
|
||||
*/
|
||||
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
||||
// Remove the instructions at the given index.
|
||||
removeInstructions(index, instructions.size)
|
||||
|
||||
// Add the instructions at the given index.
|
||||
addInstructions(index, instructions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an instruction to a method at the given index.
|
||||
*
|
||||
* @param index The index to add the instruction at.
|
||||
* @param instruction The instruction to add.
|
||||
*/
|
||||
fun MutableMethod.addInstruction(index: Int, instruction: BuilderInstruction) =
|
||||
implementation!!.addInstruction(index, instruction)
|
||||
|
||||
/**
|
||||
* Add an instruction to a method.
|
||||
*
|
||||
* @param instruction The instructions to add.
|
||||
*/
|
||||
fun MutableMethod.addInstruction(instruction: BuilderInstruction) =
|
||||
implementation!!.addInstruction(instruction)
|
||||
|
||||
/**
|
||||
* Add an instruction to a method at the given index.
|
||||
*
|
||||
* @param index The index to add the instruction at.
|
||||
* @param smaliInstructions The instruction to add.
|
||||
*/
|
||||
fun MutableMethod.addInstruction(index: Int, smaliInstructions: String) =
|
||||
implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
||||
|
||||
/**
|
||||
* Add an instruction to a method.
|
||||
*
|
||||
* @param smaliInstructions The instruction to add.
|
||||
*/
|
||||
fun MutableMethod.addInstruction(smaliInstructions: String) =
|
||||
implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
||||
|
||||
|
||||
/**
|
||||
* Add instructions to a method at the given index.
|
||||
*
|
||||
* @param index The index to add the instructions at.
|
||||
* @param instructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethod.addInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||
implementation!!.addInstructions(index, instructions)
|
||||
|
||||
/**
|
||||
* Add instructions to a method.
|
||||
*
|
||||
* @param instructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) =
|
||||
implementation!!.addInstructions(instructions)
|
||||
|
||||
/**
|
||||
* Add instructions to a method.
|
||||
*
|
||||
* @param smaliInstructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) =
|
||||
implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
||||
|
||||
/**
|
||||
* Add instructions to a method.
|
||||
*
|
||||
* @param smaliInstructions The instructions to add.
|
||||
*/
|
||||
fun MutableMethod.addInstructions(smaliInstructions: String) =
|
||||
implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
||||
|
||||
/**
|
||||
* Add instructions to a method at the given index.
|
||||
*
|
||||
* @param index The index to add the instructions at.
|
||||
* @param smaliInstructions The instructions to add.
|
||||
* @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions].
|
||||
*/
|
||||
// Special function for adding instructions with external labels.
|
||||
fun MutableMethod.addInstructionsWithLabels(
|
||||
index: Int,
|
||||
smaliInstructions: String,
|
||||
vararg externalLabels: ExternalLabel
|
||||
) {
|
||||
// Create reference dummy instructions for the instructions.
|
||||
val nopSmali = StringBuilder(smaliInstructions).also { builder ->
|
||||
externalLabels.forEach { (name, _) ->
|
||||
builder.append("\n:$name\nnop")
|
||||
}
|
||||
}.toString()
|
||||
|
||||
// Compile the instructions with the dummy labels
|
||||
val compiledInstructions = nopSmali.toInstructions(this)
|
||||
|
||||
// Add the compiled list of instructions to the method.
|
||||
addInstructions(
|
||||
index,
|
||||
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size)
|
||||
)
|
||||
|
||||
implementation!!.apply {
|
||||
this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size)
|
||||
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
|
||||
// If the compiled instruction is not an offset instruction, skip it.
|
||||
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
||||
|
||||
/**
|
||||
* Creates a new label for the instruction
|
||||
* and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
||||
*/
|
||||
fun Instruction.makeNewLabel() {
|
||||
fun replaceOffset(
|
||||
i: BuilderOffsetInstruction, label: Label
|
||||
): BuilderOffsetInstruction {
|
||||
return when (i) {
|
||||
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
||||
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
||||
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
||||
is BuilderInstruction22t -> BuilderInstruction22t(
|
||||
i.opcode,
|
||||
i.registerA,
|
||||
i.registerB,
|
||||
label
|
||||
)
|
||||
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
||||
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
||||
else -> throw IllegalStateException(
|
||||
"A non-offset instruction was given, this should never happen!"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the final label.
|
||||
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
||||
|
||||
// Create the final instruction with the new label.
|
||||
val newInstruction = replaceOffset(
|
||||
compiledInstruction, label
|
||||
)
|
||||
|
||||
// Replace the instruction pointing to the dummy label
|
||||
// with the new instruction pointing to the real instruction.
|
||||
replaceInstruction(index + compiledInstructionIndex, newInstruction)
|
||||
}
|
||||
|
||||
// If the compiled instruction targets its own instruction,
|
||||
// which means it points to some of its own, simply an offset has to be applied.
|
||||
val labelIndex = compiledInstruction.target.location.index
|
||||
if (labelIndex < compiledInstructions.size - externalLabels.size) {
|
||||
// Get the targets index (insertion index + the index of the dummy instruction).
|
||||
this.instructions[index + labelIndex].makeNewLabel()
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
// Since the compiled instruction points to a dummy instruction,
|
||||
// we can find the real instruction which it was created for by calculation.
|
||||
|
||||
// Get the index of the instruction in the externalLabels list
|
||||
// which the dummy instruction was created for.
|
||||
// This works because we created the dummy instructions in the same order as the externalLabels list.
|
||||
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
|
||||
instruction.makeNewLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an instruction at the given index.
|
||||
*
|
||||
* @param index The index to remove the instruction at.
|
||||
*/
|
||||
fun MutableMethod.removeInstruction(index: Int) =
|
||||
implementation!!.removeInstruction(index)
|
||||
|
||||
/**
|
||||
* Remove instructions at the given index.
|
||||
*
|
||||
* @param index The index to remove the instructions at.
|
||||
* @param count The amount of instructions to remove.
|
||||
*/
|
||||
fun MutableMethod.removeInstructions(index: Int, count: Int) =
|
||||
implementation!!.removeInstructions(index, count)
|
||||
|
||||
/**
|
||||
* Remove instructions at the given index.
|
||||
*
|
||||
* @param count The amount of instructions to remove.
|
||||
*/
|
||||
fun MutableMethod.removeInstructions(count: Int) =
|
||||
implementation!!.removeInstructions(count)
|
||||
|
||||
/**
|
||||
* Replace an instruction at the given index.
|
||||
*
|
||||
* @param index The index to replace the instruction at.
|
||||
* @param instruction The instruction to replace the instruction with.
|
||||
*/
|
||||
fun MutableMethod.replaceInstruction(index: Int, instruction: BuilderInstruction) =
|
||||
implementation!!.replaceInstruction(index, instruction)
|
||||
|
||||
/**
|
||||
* Replace an instruction at the given index.
|
||||
*
|
||||
* @param index The index to replace the instruction at.
|
||||
* @param smaliInstruction The smali instruction to replace the instruction with.
|
||||
*/
|
||||
fun MutableMethod.replaceInstruction(index: Int, smaliInstruction: String) =
|
||||
implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
||||
|
||||
/**
|
||||
* Replace instructions at the given index.
|
||||
*
|
||||
* @param index The index to replace the instructions at.
|
||||
* @param instructions The instructions to replace the instructions with.
|
||||
*/
|
||||
fun MutableMethod.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||
implementation!!.replaceInstructions(index, instructions)
|
||||
|
||||
/**
|
||||
* Replace instructions at the given index.
|
||||
*
|
||||
* @param index The index to replace the instructions at.
|
||||
* @param smaliInstructions The smali instructions to replace the instructions with.
|
||||
*/
|
||||
fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) =
|
||||
implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
||||
|
||||
/**
|
||||
* Get an instruction at the given index.
|
||||
*
|
||||
* @param index The index to get the instruction at.
|
||||
* @return The instruction.
|
||||
*/
|
||||
fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index]
|
||||
|
||||
/**
|
||||
* Get an instruction at the given index.
|
||||
*
|
||||
* @param index The index to get the instruction at.
|
||||
* @param T The type of instruction to return.
|
||||
* @return The instruction.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
||||
|
||||
/**
|
||||
* Get an instruction at the given index.
|
||||
* @param index The index to get the instruction at.
|
||||
* @return The instruction.
|
||||
*/
|
||||
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = implementation!!.getInstruction(index)
|
||||
|
||||
/**
|
||||
* Get an instruction at the given index.
|
||||
* @param index The index to get the instruction at.
|
||||
* @param T The type of instruction to return.
|
||||
* @return The instruction.
|
||||
*/
|
||||
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
|
||||
|
||||
/**
|
||||
* Get the instructions of a method.
|
||||
* @return The instructions.
|
||||
*/
|
||||
fun MutableMethod.getInstructions(): MutableList<BuilderInstruction> = implementation!!.instructions
|
||||
}
|
||||
@@ -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,513 +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 List<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
||||
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
||||
|
||||
for (fingerprint in this) {
|
||||
fingerprint.resolveUsingLookupMap(context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
|
||||
*
|
||||
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
|
||||
* amount of time because they are resolved in sequence.
|
||||
*
|
||||
* For apps with many fingerprints, resolving performance can be improved by:
|
||||
* - Slowest: Specify [opcodes] and nothing else.
|
||||
* - Fast: Specify [accessFlags], [returnType].
|
||||
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||
*/
|
||||
internal fun MethodFingerprint.resolveUsingLookupMap(context: BytecodeContext): Boolean {
|
||||
/**
|
||||
* Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
||||
*
|
||||
* @return A list of [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
|
||||
*/
|
||||
fun MethodFingerprint.methodStringsLookup(): List<MethodClassPair>? {
|
||||
strings?.forEach {
|
||||
val methods = methodStringsLookupMap[it]
|
||||
if (methods != null) return methods
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
|
||||
*
|
||||
* @return A list of [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
|
||||
*/
|
||||
fun MethodFingerprint.methodSignatureLookup(): List<MethodClassPair> {
|
||||
if (accessFlags == null) return methods
|
||||
|
||||
var returnTypeValue = returnType
|
||||
if (returnTypeValue == null) {
|
||||
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||
// Constructors always have void return type
|
||||
returnTypeValue = "V"
|
||||
} else {
|
||||
return methods
|
||||
}
|
||||
}
|
||||
|
||||
val key = buildString {
|
||||
append(accessFlags)
|
||||
append(returnTypeValue.first())
|
||||
if (parameters != null) appendParameters(parameters)
|
||||
}
|
||||
return methodSignatureLookupMap[key] ?: return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] using a list of [MethodClassPair].
|
||||
*
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun MethodFingerprint.resolveUsingMethodClassPair(classMethods: Iterable<MethodClassPair>): Boolean {
|
||||
classMethods.forEach { classAndMethod ->
|
||||
if (resolve(context, classAndMethod.first, classAndMethod.second)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
val methodsWithSameStrings = methodStringsLookup()
|
||||
if (methodsWithSameStrings != null) if (resolveUsingMethodClassPair(methodsWithSameStrings)) return true
|
||||
|
||||
// No strings declared or none matched (partial matches are allowed).
|
||||
// Use signature matching.
|
||||
return resolveUsingMethodClassPair(methodSignatureLookup())
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
|
||||
*
|
||||
* @param classes The classes on which to resolve the [MethodFingerprint] in.
|
||||
* @param context The [BytecodeContext] to host proxies.
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) {
|
||||
for (fingerprint in this) // For each fingerprint...
|
||||
classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint
|
||||
if (fingerprint.resolve(context, classDef))
|
||||
break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint.
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] against a [ClassDef].
|
||||
*
|
||||
* @param forClass The class on which to resolve the [MethodFingerprint] in.
|
||||
* @param context The [BytecodeContext] to host proxies.
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun MethodFingerprint.resolve(context: BytecodeContext, forClass: ClassDef): Boolean {
|
||||
for (method in forClass.methods)
|
||||
if (this.resolve(context, method, forClass))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] against a [Method].
|
||||
*
|
||||
* @param method The class on which to resolve the [MethodFingerprint] in.
|
||||
* @param forClass The class on which to resolve the [MethodFingerprint].
|
||||
* @param context The [BytecodeContext] to host proxies.
|
||||
* @return True if the resolution was successful or if the fingerprint is already resolved, false otherwise.
|
||||
*/
|
||||
fun MethodFingerprint.resolve(context: BytecodeContext, method: Method, forClass: ClassDef): Boolean {
|
||||
val methodFingerprint = this
|
||||
|
||||
if (methodFingerprint.result != null) return true
|
||||
|
||||
if (methodFingerprint.returnType != null && !method.returnType.startsWith(methodFingerprint.returnType))
|
||||
return false
|
||||
|
||||
if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags)
|
||||
return false
|
||||
|
||||
|
||||
fun parametersEqual(
|
||||
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
||||
): Boolean {
|
||||
if (parameters1.count() != parameters2.count()) return false
|
||||
val iterator1 = parameters1.iterator()
|
||||
parameters2.forEach {
|
||||
if (!it.startsWith(iterator1.next())) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (methodFingerprint.parameters != null && !parametersEqual(
|
||||
methodFingerprint.parameters, // TODO: parseParameters()
|
||||
method.parameterTypes
|
||||
)
|
||||
) return false
|
||||
|
||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass))
|
||||
return false
|
||||
|
||||
val stringsScanResult: StringsScanResult? =
|
||||
if (methodFingerprint.strings != null) {
|
||||
StringsScanResult(
|
||||
buildList {
|
||||
val implementation = method.implementation ?: return false
|
||||
|
||||
val stringsList = methodFingerprint.strings.toMutableList()
|
||||
|
||||
implementation.instructions.forEachIndexed { instructionIndex, instruction ->
|
||||
if (
|
||||
instruction.opcode != Opcode.CONST_STRING &&
|
||||
instruction.opcode != Opcode.CONST_STRING_JUMBO
|
||||
) return@forEachIndexed
|
||||
|
||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||
val index = stringsList.indexOfFirst(string::contains)
|
||||
if (index == -1) return@forEachIndexed
|
||||
|
||||
add(
|
||||
StringMatch(
|
||||
string,
|
||||
instructionIndex
|
||||
)
|
||||
)
|
||||
stringsList.removeAt(index)
|
||||
}
|
||||
|
||||
if (stringsList.isNotEmpty()) return false
|
||||
}
|
||||
)
|
||||
} else null
|
||||
|
||||
val patternScanResult = if (methodFingerprint.opcodes != null) {
|
||||
method.implementation?.instructions ?: return false
|
||||
|
||||
fun Method.patternScan(
|
||||
fingerprint: MethodFingerprint
|
||||
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
||||
val instructions = this.implementation!!.instructions
|
||||
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
|
||||
|
||||
val pattern = fingerprint.opcodes!!
|
||||
val instructionLength = instructions.count()
|
||||
val patternLength = pattern.count()
|
||||
|
||||
for (index in 0 until instructionLength) {
|
||||
var patternIndex = 0
|
||||
var threshold = fingerprintFuzzyPatternScanThreshold
|
||||
|
||||
while (index + patternIndex < instructionLength) {
|
||||
val originalOpcode = instructions.elementAt(index + patternIndex).opcode
|
||||
val patternOpcode = pattern.elementAt(patternIndex)
|
||||
|
||||
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
|
||||
// reaching maximum threshold (0) means,
|
||||
// the pattern does not match to the current instructions
|
||||
if (threshold-- == 0) break
|
||||
}
|
||||
|
||||
if (patternIndex < patternLength - 1) {
|
||||
// if the entire pattern has not been scanned yet
|
||||
// continue the scan
|
||||
patternIndex++
|
||||
continue
|
||||
}
|
||||
// the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
|
||||
val result =
|
||||
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
||||
index,
|
||||
index + patternIndex
|
||||
)
|
||||
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
|
||||
result.warnings = result.createWarnings(pattern, instructions)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
method.patternScan(methodFingerprint) ?: return false
|
||||
} else null
|
||||
|
||||
methodFingerprint.result = MethodFingerprintResult(
|
||||
method,
|
||||
forClass,
|
||||
MethodFingerprintResult.MethodFingerprintScanResult(
|
||||
patternScanResult,
|
||||
stringsScanResult
|
||||
),
|
||||
context
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings(
|
||||
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
||||
) = buildList {
|
||||
for ((patternIndex, instructionIndex) in (this@createWarnings.startIndex until this@createWarnings.endIndex).withIndex()) {
|
||||
val originalOpcode = instructions.elementAt(instructionIndex).opcode
|
||||
val patternOpcode = pattern.elementAt(patternIndex)
|
||||
|
||||
if (patternOpcode == null || patternOpcode.ordinal == originalOpcode.ordinal) continue
|
||||
|
||||
this.add(
|
||||
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.Warning(
|
||||
originalOpcode,
|
||||
patternOpcode,
|
||||
instructionIndex,
|
||||
patternIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the result of a [MethodFingerprintResult].
|
||||
*
|
||||
* @param method The matching method.
|
||||
* @param classDef The [ClassDef] that contains the matching [method].
|
||||
* @param scanResult The result of scanning for the [MethodFingerprint].
|
||||
* @param context The [BytecodeContext] this [MethodFingerprintResult] is attached to, to create proxies.
|
||||
*/
|
||||
data class MethodFingerprintResult(
|
||||
val method: Method,
|
||||
val classDef: ClassDef,
|
||||
val scanResult: MethodFingerprintScanResult,
|
||||
internal val context: BytecodeContext
|
||||
) {
|
||||
/**
|
||||
* Returns a mutable clone of [classDef]
|
||||
*
|
||||
* Please note, this method allocates a [ClassProxy].
|
||||
* Use [classDef] where possible.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val mutableClass by lazy { context.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,222 +0,0 @@
|
||||
package app.revanced.patcher.util
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.traverseClassHierarchy
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||
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.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import java.util.logging.Logger
|
||||
import kotlin.reflect.KFunction2
|
||||
|
||||
/**
|
||||
* Experimental class to merge a [ClassDef] with another.
|
||||
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
||||
*/
|
||||
internal object ClassMerger {
|
||||
private val logger = Logger.getLogger(ClassMerger::class.java.name)
|
||||
|
||||
/**
|
||||
* Merge a class with [otherClass].
|
||||
*
|
||||
* @param otherClass The class to merge with
|
||||
* @param context The context to traverse the class hierarchy in.
|
||||
* @return The merged class or the original class if no merge was needed.
|
||||
*/
|
||||
fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext) = this
|
||||
//.fixFieldAccess(otherClass)
|
||||
//.fixMethodAccess(otherClass)
|
||||
.addMissingFields(otherClass)
|
||||
.addMissingMethods(otherClass)
|
||||
.publicize(otherClass, context)
|
||||
|
||||
/**
|
||||
* Add methods which are missing but existing in [fromClass].
|
||||
*
|
||||
* @param fromClass The class to add missing methods from.
|
||||
*/
|
||||
private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef {
|
||||
val missingMethods = fromClass.methods.let { fromMethods ->
|
||||
methods.filterNot { method ->
|
||||
fromMethods.any { fromMethod ->
|
||||
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingMethods.isEmpty()) return this
|
||||
|
||||
logger.fine("Found ${missingMethods.size} missing methods")
|
||||
|
||||
return asMutableClass().apply {
|
||||
methods.addAll(missingMethods.map { it.toMutable() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields which are missing but existing in [fromClass].
|
||||
*
|
||||
* @param fromClass The class to add missing fields from.
|
||||
*/
|
||||
private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef {
|
||||
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
||||
fromField.name == field.name
|
||||
}
|
||||
|
||||
if (missingFields.isEmpty()) return this
|
||||
|
||||
logger.fine("Found ${missingFields.size} missing fields")
|
||||
|
||||
return asMutableClass().apply {
|
||||
fields.addAll(missingFields.map { it.toMutable() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a class and its super class public recursively.
|
||||
* @param reference The class to check the [AccessFlags] of.
|
||||
* @param context The context to traverse the class hierarchy in.
|
||||
*/
|
||||
private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext) =
|
||||
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
||||
this.asMutableClass().apply {
|
||||
context.traverseClassHierarchy(this) {
|
||||
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||
|
||||
logger.fine("Publicizing ${this.type}")
|
||||
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
}
|
||||
else this
|
||||
|
||||
/**
|
||||
* Publicize fields if they are public in [reference].
|
||||
*
|
||||
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||
*/
|
||||
private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef {
|
||||
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
||||
if (field.name != referenceField.name) return@filterAny false
|
||||
|
||||
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
||||
}
|
||||
|
||||
if (brokenFields.isEmpty()) return this
|
||||
|
||||
logger.fine("Found ${brokenFields.size} broken fields")
|
||||
|
||||
/**
|
||||
* Make a field public.
|
||||
*/
|
||||
fun MutableField.publicize() {
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
|
||||
return asMutableClass().apply {
|
||||
fields.filter { brokenFields.contains(it) }.forEach(MutableField::publicize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publicize methods if they are public in [reference].
|
||||
*
|
||||
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||
*/
|
||||
private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef {
|
||||
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||
|
||||
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
||||
}
|
||||
|
||||
if (brokenMethods.isEmpty()) return this
|
||||
|
||||
logger.fine("Found ${brokenMethods.size} methods")
|
||||
|
||||
/**
|
||||
* Make a method public.
|
||||
*/
|
||||
fun MutableMethod.publicize() {
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
|
||||
return asMutableClass().apply {
|
||||
methods.filter { brokenMethods.contains(it) }.forEach(MutableMethod::publicize)
|
||||
}
|
||||
}
|
||||
|
||||
private object Utils {
|
||||
/**
|
||||
* traverse the class hierarchy starting from the given root class
|
||||
*
|
||||
* @param targetClass the class to start traversing the class hierarchy from
|
||||
* @param callback function that is called for every class in the hierarchy
|
||||
*/
|
||||
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||
callback(targetClass)
|
||||
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||
traverseClassHierarchy(it, callback)
|
||||
}
|
||||
}
|
||||
|
||||
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
||||
|
||||
/**
|
||||
* Check if the [AccessFlags.PUBLIC] flag is set.
|
||||
*
|
||||
* @return True, if the flag is set.
|
||||
*/
|
||||
fun Int.isPublic() = AccessFlags.PUBLIC.isSet(this)
|
||||
|
||||
/**
|
||||
* Make [AccessFlags] public.
|
||||
*
|
||||
* @return The new [AccessFlags].
|
||||
*/
|
||||
fun Int.toPublic() = this.or(AccessFlags.PUBLIC).and(AccessFlags.PRIVATE.value.inv())
|
||||
|
||||
/**
|
||||
* Filter [this] on [needles] matching the given [predicate].
|
||||
*
|
||||
* @param needles The needles to filter [this] with.
|
||||
* @param predicate The filter.
|
||||
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||
*/
|
||||
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
||||
|
||||
/**
|
||||
* Filter [this] on [needles] not matching the given [predicate].
|
||||
*
|
||||
* @param needles The needles to filter [this] with.
|
||||
* @param predicate The filter.
|
||||
* @return The [this] filtered on [needles] not matching the given [predicate].
|
||||
*/
|
||||
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
||||
|
||||
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
||||
haystack: Iterable<HayType>,
|
||||
needles: Iterable<NeedleType>,
|
||||
predicate: (HayType, NeedleType) -> Boolean
|
||||
) = this(haystack) { hay ->
|
||||
needles.any { needle ->
|
||||
predicate(hay, needle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,33 +0,0 @@
|
||||
package app.revanced.patcher.util.proxy
|
||||
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
/**
|
||||
* A proxy class for a [ClassDef].
|
||||
*
|
||||
* A class proxy simply holds a reference to the original class
|
||||
* and allocates a mutable clone for the original class if needed.
|
||||
* @param immutableClass The class to proxy.
|
||||
*/
|
||||
class ClassProxy internal constructor(
|
||||
val immutableClass: ClassDef,
|
||||
) {
|
||||
/**
|
||||
* Weather the proxy was actually used.
|
||||
*/
|
||||
internal var resolved = false
|
||||
|
||||
/**
|
||||
* The mutable clone of the original class.
|
||||
*
|
||||
* Note: This is only allocated if the proxy is actually used.
|
||||
*/
|
||||
val mutableClass by lazy {
|
||||
resolved = true
|
||||
if (immutableClass is MutableClass) {
|
||||
immutableClass
|
||||
} else
|
||||
MutableClass(immutableClass)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package app.revanced.patcher.util.smali
|
||||
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
/**
|
||||
* A class that represents a label for an instruction.
|
||||
* @param name The label name.
|
||||
* @param instruction The instruction that this label is for.
|
||||
*/
|
||||
data class ExternalLabel(internal val name: String, internal val instruction: Instruction)
|
||||
@@ -1,84 +0,0 @@
|
||||
package app.revanced.patcher.util.smali
|
||||
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import org.antlr.runtime.CommonTokenStream
|
||||
import org.antlr.runtime.TokenSource
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcodes
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder
|
||||
import com.android.tools.smali.smali.LexerErrorInterface
|
||||
import com.android.tools.smali.smali.smaliFlexLexer
|
||||
import com.android.tools.smali.smali.smaliParser
|
||||
import com.android.tools.smali.smali.smaliTreeWalker
|
||||
import java.io.InputStreamReader
|
||||
|
||||
private const val METHOD_TEMPLATE = """
|
||||
.class LInlineCompiler;
|
||||
.super Ljava/lang/Object;
|
||||
.method %s dummyMethod(%s)V
|
||||
.registers %d
|
||||
%s
|
||||
.end method
|
||||
"""
|
||||
|
||||
class InlineSmaliCompiler {
|
||||
companion object {
|
||||
/**
|
||||
* Compiles a string of Smali code to a list of instructions.
|
||||
* Special registers (such as p0, p1) will only work correctly
|
||||
* if the parameters and registers of the method are passed.
|
||||
*/
|
||||
fun compile(
|
||||
instructions: String, parameters: String, registers: Int, forStaticMethod: Boolean
|
||||
): List<BuilderInstruction> {
|
||||
val input = METHOD_TEMPLATE.format(
|
||||
if (forStaticMethod) {
|
||||
"static"
|
||||
} else {
|
||||
""
|
||||
}, parameters, registers, instructions
|
||||
)
|
||||
val reader = InputStreamReader(input.byteInputStream())
|
||||
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
|
||||
val tokens = CommonTokenStream(lexer as TokenSource)
|
||||
val parser = smaliParser(tokens)
|
||||
val result = parser.smali_file()
|
||||
if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) {
|
||||
throw IllegalStateException(
|
||||
"Encountered ${parser.numberOfSyntaxErrors} parser syntax errors and ${lexer.numberOfSyntaxErrors} lexer syntax errors!"
|
||||
)
|
||||
}
|
||||
val treeStream = CommonTreeNodeStream(result.tree)
|
||||
treeStream.tokenStream = tokens
|
||||
val dexGen = smaliTreeWalker(treeStream)
|
||||
dexGen.setDexBuilder(DexBuilder(Opcodes.getDefault()))
|
||||
val classDef = dexGen.smali_file()
|
||||
return classDef.methods.first().implementation!!.instructions.map { it as BuilderInstruction }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile lines of Smali code to a list of instructions.
|
||||
*
|
||||
* Note: Adding compiled instructions to an existing method with
|
||||
* offset instructions WITHOUT specifying a parent method will not work.
|
||||
* @param method The method to compile the instructions against.
|
||||
* @returns A list of instructions.
|
||||
*/
|
||||
fun String.toInstructions(method: MutableMethod? = null): List<BuilderInstruction> {
|
||||
return InlineSmaliCompiler.compile(this,
|
||||
method?.parameters?.joinToString("") { it } ?: "",
|
||||
method?.implementation?.registerCount ?: 1,
|
||||
method?.let { AccessFlags.STATIC.isSet(it.accessFlags) } ?: true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a line of Smali code to an instruction.
|
||||
* @param templateMethod The method to compile the instructions against.
|
||||
* @return The instruction.
|
||||
*/
|
||||
fun String.toInstruction(templateMethod: MutableMethod? = null) = this.toInstructions(templateMethod).first()
|
||||
@@ -1 +0,0 @@
|
||||
version=${projectVersion}
|
||||
@@ -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,105 +0,0 @@
|
||||
package app.revanced.patcher.util.smali
|
||||
|
||||
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.newLabel
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import java.util.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
internal class InlineSmaliCompilerTest {
|
||||
@Test
|
||||
fun `compiler should output valid instruction`() {
|
||||
val want = BuilderInstruction21c(Opcode.CONST_STRING, 0, ImmutableStringReference("Test")) as BuilderInstruction
|
||||
val have = "const-string v0, \"Test\"".toInstruction()
|
||||
instructionEquals(want, have)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `compiler should support branching with own branches`() {
|
||||
val method = createMethod()
|
||||
val insnAmount = 8
|
||||
val insnIndex = insnAmount - 2
|
||||
val targetIndex = insnIndex - 1
|
||||
|
||||
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
||||
Arrays.fill(it, "const/4 v0, 0x0")
|
||||
}.joinToString("\n"))
|
||||
method.addInstructionsWithLabels(
|
||||
targetIndex,
|
||||
"""
|
||||
:test
|
||||
const/4 v0, 0x1
|
||||
if-eqz v0, :test
|
||||
"""
|
||||
)
|
||||
|
||||
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||
assertEquals(targetIndex, insn.target.location.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `compiler should support branching to outside branches`() {
|
||||
val method = createMethod()
|
||||
val insnIndex = 3
|
||||
val labelIndex = 1
|
||||
|
||||
method.addInstructions(
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
const/4 v0, 0x0
|
||||
"""
|
||||
)
|
||||
|
||||
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
|
||||
|
||||
method.addInstructionsWithLabels(
|
||||
method.implementation!!.instructions.size,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
if-eqz v0, :test
|
||||
return-void
|
||||
""",
|
||||
ExternalLabel("test", method.getInstruction(1))
|
||||
)
|
||||
|
||||
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||
assertTrue(insn.target.isPlaced, "Label was not placed")
|
||||
assertEquals(labelIndex, insn.target.location.index)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun createMethod(
|
||||
name: String = "dummy",
|
||||
returnType: String = "V",
|
||||
accessFlags: Int = AccessFlags.STATIC.value,
|
||||
registerCount: Int = 1,
|
||||
) = ImmutableMethod(
|
||||
"Ldummy;",
|
||||
name,
|
||||
emptyList(), // parameters
|
||||
returnType,
|
||||
accessFlags,
|
||||
emptySet(),
|
||||
emptySet(),
|
||||
MutableMethodImplementation(registerCount)
|
||||
).toMutable()
|
||||
|
||||
private fun instructionEquals(want: BuilderInstruction, have: BuilderInstruction) {
|
||||
assertEquals(want.opcode, have.opcode)
|
||||
assertEquals(want.format, have.format)
|
||||
assertEquals(want.codeUnits, have.codeUnits)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1 @@
|
||||
val githubUsername: String = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
|
||||
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")
|
||||
rootProject.name = "revanced-patcher"
|
||||
|
||||
244
src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
244
src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
@@ -0,0 +1,244 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.data.PatcherData
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.data.implementation.findIndexed
|
||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.extensions.nullOutputStream
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.signature.implementation.method.resolver.MethodSignatureResolver
|
||||
import app.revanced.patcher.util.ListBackedSet
|
||||
import brut.androlib.Androlib
|
||||
import brut.androlib.meta.UsesFramework
|
||||
import brut.androlib.res.AndrolibResources
|
||||
import brut.androlib.res.data.ResPackage
|
||||
import brut.androlib.res.decoder.AXmlResourceParser
|
||||
import brut.androlib.res.decoder.ResAttrDecoder
|
||||
import brut.androlib.res.decoder.XmlPullStreamDecoder
|
||||
import brut.directory.ExtFile
|
||||
import lanchon.multidexlib2.BasicDexFileNamer
|
||||
import lanchon.multidexlib2.DexIO
|
||||
import lanchon.multidexlib2.MultiDexIO
|
||||
import org.jf.dexlib2.Opcodes
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.DexFile
|
||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||
import java.io.File
|
||||
|
||||
val NAMER = BasicDexFileNamer()
|
||||
|
||||
/**
|
||||
* The ReVanced Patcher.
|
||||
* @param options The options for the patcher.
|
||||
*/
|
||||
class Patcher(
|
||||
private val options: PatcherOptions
|
||||
) {
|
||||
val packageVersion: String
|
||||
val packageName: String
|
||||
|
||||
private lateinit var usesFramework: UsesFramework
|
||||
private val patcherData: PatcherData
|
||||
private val opcodes: Opcodes
|
||||
|
||||
init {
|
||||
val extFileInput = ExtFile(options.inputFile)
|
||||
val outDir = File(options.resourceCacheDirectory)
|
||||
|
||||
if (outDir.exists()) outDir.deleteRecursively()
|
||||
outDir.mkdir()
|
||||
|
||||
// load the resource table from the input file
|
||||
val androlib = Androlib()
|
||||
val resourceTable = androlib.getResTable(extFileInput, true)
|
||||
|
||||
if (options.patchResources) {
|
||||
// 1. decode resources to cache directory
|
||||
androlib.decodeManifestWithResources(extFileInput, outDir, resourceTable)
|
||||
androlib.decodeResourcesFull(extFileInput, outDir, resourceTable)
|
||||
|
||||
// 2. read framework ids from the resource table
|
||||
usesFramework = UsesFramework()
|
||||
usesFramework.ids = resourceTable.listFramePackages().map { it.id }.sorted()
|
||||
} else {
|
||||
// create decoder for the resource table
|
||||
val decoder = ResAttrDecoder()
|
||||
decoder.currentPackage = ResPackage(resourceTable, 0, null)
|
||||
|
||||
// create xml parser with the decoder
|
||||
val axmlParser = AXmlResourceParser()
|
||||
axmlParser.attrDecoder = decoder
|
||||
|
||||
// parse package information with the decoder and parser which will set required values in the resource table
|
||||
// instead of decodeManifest another more low level solution can be created to make it faster/better
|
||||
XmlPullStreamDecoder(
|
||||
axmlParser, AndrolibResources().resXmlSerializer
|
||||
).decodeManifest(
|
||||
extFileInput.directory.getFileInput("AndroidManifest.xml"), nullOutputStream
|
||||
)
|
||||
}
|
||||
|
||||
// set package information
|
||||
packageVersion = resourceTable.versionInfo.versionName
|
||||
packageName = resourceTable.currentResPackage.name
|
||||
// read dex files
|
||||
val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null)
|
||||
opcodes = dexFile.opcodes
|
||||
|
||||
// save to patcher data
|
||||
patcherData = PatcherData(dexFile.classes.toMutableList(), options.resourceCacheDirectory)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional dex file container to the patcher.
|
||||
* @param files The dex file containers to add to the patcher.
|
||||
* @param allowedOverwrites A list of class types that are allowed to be overwritten.
|
||||
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
|
||||
*/
|
||||
fun addFiles(
|
||||
files: Iterable<File>, allowedOverwrites: Iterable<String> = emptyList(), throwOnDuplicates: Boolean = false
|
||||
) {
|
||||
for (file in files) {
|
||||
val dexFile = MultiDexIO.readDexFile(true, file, NAMER, null, null)
|
||||
for (classDef in dexFile.classes) {
|
||||
val e = patcherData.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type }
|
||||
if (e != null) {
|
||||
if (throwOnDuplicates) {
|
||||
throw Exception("Class ${classDef.type} has already been added to the patcher.")
|
||||
}
|
||||
val (_, idx) = e
|
||||
if (allowedOverwrites.contains(classDef.type)) {
|
||||
patcherData.bytecodeData.classes.internalClasses[idx] = classDef
|
||||
}
|
||||
continue
|
||||
}
|
||||
patcherData.bytecodeData.classes.internalClasses.add(classDef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the patched dex file.
|
||||
*/
|
||||
fun save(): Map<String, MemoryDataStore> {
|
||||
val newDexFile = object : DexFile {
|
||||
override fun getClasses(): Set<ClassDef> {
|
||||
patcherData.bytecodeData.classes.applyProxies()
|
||||
return ListBackedSet(patcherData.bytecodeData.classes.internalClasses)
|
||||
}
|
||||
|
||||
override fun getOpcodes(): Opcodes {
|
||||
return this@Patcher.opcodes
|
||||
}
|
||||
}
|
||||
|
||||
// build modified resources
|
||||
if (options.patchResources) {
|
||||
val extDir = ExtFile(options.resourceCacheDirectory)
|
||||
|
||||
// TODO: figure out why a new instance of Androlib is necessary here
|
||||
Androlib().buildResources(extDir, usesFramework)
|
||||
}
|
||||
|
||||
// write dex modified files
|
||||
val output = mutableMapOf<String, MemoryDataStore>()
|
||||
MultiDexIO.writeDexFile(
|
||||
true, -1, // core count
|
||||
output, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
|
||||
)
|
||||
return output
|
||||
}
|
||||
|
||||
/**
|
||||
* Add [Patch]es to the patcher.
|
||||
* @param patches [Patch]es The patches to add.
|
||||
*/
|
||||
fun addPatches(patches: Iterable<Class<out Patch<Data>>>) {
|
||||
patcherData.patches.addAll(patches)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a [patch] and its dependencies recursively.
|
||||
* @param patch The [patch] to apply.
|
||||
* @param appliedPatches A list of [patch] names, to prevent applying [patch]es twice.
|
||||
* @return The result of executing the [patch].
|
||||
*/
|
||||
private fun applyPatch(
|
||||
patch: Class<out Patch<Data>>, appliedPatches: MutableList<String>
|
||||
): PatchResult {
|
||||
val patchName = patch.patchName
|
||||
|
||||
// if the patch has already applied silently skip it
|
||||
if (appliedPatches.contains(patchName)) return PatchResultSuccess()
|
||||
appliedPatches.add(patchName)
|
||||
|
||||
// recursively apply all dependency patches
|
||||
patch.dependencies?.forEach {
|
||||
val patchDependency = it.java
|
||||
|
||||
val result = applyPatch(patchDependency, appliedPatches)
|
||||
if (result.isSuccess()) return@forEach
|
||||
|
||||
val errorMessage = result.error()!!.message
|
||||
return PatchResultError("$patchName depends on ${patchDependency.patchName} but the following error was raised: $errorMessage")
|
||||
}
|
||||
|
||||
val patchInstance = patch.getDeclaredConstructor().newInstance()
|
||||
|
||||
// if the current patch is a resource patch but resource patching is disabled, return an error
|
||||
val isResourcePatch = patchInstance is ResourcePatch
|
||||
if (!options.patchResources && isResourcePatch) return PatchResultError("$patchName is a resource patch, but resource patching is disabled.")
|
||||
|
||||
// TODO: find a solution for this
|
||||
val data = if (isResourcePatch) {
|
||||
patcherData.resourceData
|
||||
} else {
|
||||
MethodSignatureResolver(
|
||||
patcherData.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures
|
||||
).resolve(patcherData)
|
||||
patcherData.bytecodeData
|
||||
}
|
||||
|
||||
return try {
|
||||
patchInstance.execute(data)
|
||||
} catch (e: Exception) {
|
||||
PatchResultError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply patches loaded into the patcher.
|
||||
* @param stopOnError If true, the patches will stop on the first error.
|
||||
* @return A map of [PatchResultSuccess]. If the [Patch] was successfully applied,
|
||||
* [PatchResultSuccess] will always be returned to the wrapping Result object.
|
||||
* If the [Patch] failed to apply, an Exception will always be returned to the wrapping Result object.
|
||||
*/
|
||||
fun applyPatches(
|
||||
stopOnError: Boolean = false, callback: (String) -> Unit = {}
|
||||
): Map<String, Result<PatchResultSuccess>> {
|
||||
val appliedPatches = mutableListOf<String>()
|
||||
|
||||
return buildMap {
|
||||
for (patch in patcherData.patches) {
|
||||
val result = applyPatch(patch, appliedPatches)
|
||||
|
||||
val name = patch.patchName
|
||||
callback(name)
|
||||
|
||||
this[name] = if (result.isSuccess()) {
|
||||
Result.success(result.success()!!)
|
||||
} else {
|
||||
Result.failure(result.error()!!)
|
||||
}
|
||||
|
||||
if (stopOnError && result.isError()) break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user