mirror of
https://github.com/ReVanced/revanced-cli.git
synced 2026-01-11 22:06:20 +00:00
Compare commits
190 Commits
v2.20.2-de
...
v4.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1c4d8df3e | ||
|
|
89acffe788 | ||
|
|
64d9127291 | ||
|
|
e3c55507cf | ||
|
|
64afc95a81 | ||
|
|
022fd230f6 | ||
|
|
a177693ece | ||
|
|
4b6dbffd7b | ||
|
|
78e89dd3bf | ||
|
|
a08530d47b | ||
|
|
eec2234226 | ||
|
|
d5ea5a0ab1 | ||
|
|
6fc3eebc21 | ||
|
|
c84041f942 | ||
|
|
995f2ec99b | ||
|
|
84b24f1b6f | ||
|
|
95a974a5ac | ||
|
|
ee6d883777 | ||
|
|
b65e4083d6 | ||
|
|
e46d855643 | ||
|
|
80a8d88406 | ||
|
|
50c0f98ce5 | ||
|
|
77d91735ff | ||
|
|
3846f721ca | ||
|
|
f199298317 | ||
|
|
592dc1c64a | ||
|
|
8da0c2bdfe | ||
|
|
e8e69aaf5a | ||
|
|
157278c9ba | ||
|
|
049a385473 | ||
|
|
e7c9da7c33 | ||
|
|
67bf8c1849 | ||
|
|
075f6ad565 | ||
|
|
6942b22a68 | ||
|
|
ac5742dd6c | ||
|
|
a022febd0c | ||
|
|
978032cce1 | ||
|
|
52c5259451 | ||
|
|
43fc20d90e | ||
|
|
e3851d58c8 | ||
|
|
3e13fb5d56 | ||
|
|
db50cee12c | ||
|
|
578e16b099 | ||
|
|
1319ab7629 | ||
|
|
2a08af3cb9 | ||
|
|
7794327a11 | ||
|
|
2b77608651 | ||
|
|
d09aca65df | ||
|
|
c8d4f56d61 | ||
|
|
c67e3c70c7 | ||
|
|
05878a6e06 | ||
|
|
29bc0e8df8 | ||
|
|
c4a89e39b9 | ||
|
|
847e384d9e | ||
|
|
96c196dcb1 | ||
|
|
dfd535c201 | ||
|
|
2b6051e7f3 | ||
|
|
0304988733 | ||
|
|
ca809f0948 | ||
|
|
5d50d1a622 | ||
|
|
83c28d9f71 | ||
|
|
93cbcc28aa | ||
|
|
3b3f7c7a7a | ||
|
|
bbb1a63abd | ||
|
|
f7fcbc55bb | ||
|
|
9fe5a0b6d9 | ||
|
|
4f2d2568d3 | ||
|
|
a3d8f004ec | ||
|
|
41ffc99ad0 | ||
|
|
1121376018 | ||
|
|
48e1689223 | ||
|
|
7580f5c2fc | ||
|
|
dd6c1392eb | ||
|
|
2a3dbafd17 | ||
|
|
9e39a6f8e4 | ||
|
|
2d5a7fdf1e | ||
|
|
be5d812dff | ||
|
|
c93186fb97 | ||
|
|
3a198052bb | ||
|
|
0f3e090418 | ||
|
|
6aed946183 | ||
|
|
924c1f80ec | ||
|
|
8dd709b6ef | ||
|
|
139e7facac | ||
|
|
1d26e572f7 | ||
|
|
2c7fcaf4ad | ||
|
|
52c3be23f2 | ||
|
|
3dd875d14c | ||
|
|
0350b7f1a2 | ||
|
|
a3d8705e89 | ||
|
|
a536c9f815 | ||
|
|
11c3a6cfd4 | ||
|
|
a5851f0c1a | ||
|
|
963ae3a5fa | ||
|
|
93f338a731 | ||
|
|
7dcf15e03b | ||
|
|
41898d7547 | ||
|
|
45dd15f679 | ||
|
|
c2da04a834 | ||
|
|
f556909b9e | ||
|
|
1fa3dd7b8c | ||
|
|
1fcc591222 | ||
|
|
1fd3b83d46 | ||
|
|
7ec9040d41 | ||
|
|
44cc88b25d | ||
|
|
97548f80b3 | ||
|
|
d766f0e229 | ||
|
|
b0e748daff | ||
|
|
ba758f00f4 | ||
|
|
a9c2a5f096 | ||
|
|
0dcd838de3 | ||
|
|
a7290353bf | ||
|
|
47a20afd2d | ||
|
|
f8972eac3e | ||
|
|
9edbbf3163 | ||
|
|
32da961d57 | ||
|
|
8a5daab2a3 | ||
|
|
c0cc909626 | ||
|
|
b74213f66e | ||
|
|
fe75d4ab87 | ||
|
|
07da528ce2 | ||
|
|
ef5fa9b4c9 | ||
|
|
fc359923e3 | ||
|
|
2e3cd90537 | ||
|
|
7174364ef8 | ||
|
|
4c9d414228 | ||
|
|
5e73e6d2de | ||
|
|
667ca3051c | ||
|
|
9220642a7b | ||
|
|
3f47235c64 | ||
|
|
83b475c2e3 | ||
|
|
4105b638c0 | ||
|
|
f9cf7d21b7 | ||
|
|
0a758d86df | ||
|
|
83f99e29ec | ||
|
|
00410d54c0 | ||
|
|
5eaad33dc1 | ||
|
|
fa85482c4a | ||
|
|
caed39af7f | ||
|
|
20e1fdc18d | ||
|
|
19cd541e9a | ||
|
|
f6c221d72d | ||
|
|
9c148c96bf | ||
|
|
2c5b53c7e9 | ||
|
|
77d9cb98d1 | ||
|
|
38677e2319 | ||
|
|
e5e768fa34 | ||
|
|
9082181ce5 | ||
|
|
542580ecff | ||
|
|
26112a4fd2 | ||
|
|
553fbdbb8f | ||
|
|
be429a4770 | ||
|
|
8c391cecce | ||
|
|
bd14bdb5ea | ||
|
|
d0fc886428 | ||
|
|
1ccbed8d17 | ||
|
|
3b89fb7bc2 | ||
|
|
16bb1a60fc | ||
|
|
5f1843771e | ||
|
|
9de90703c8 | ||
|
|
1201f9edc5 | ||
|
|
9c8f9608b2 | ||
|
|
be90d2e360 | ||
|
|
79dc99aa9b | ||
|
|
260ff00951 | ||
|
|
cfe4c5ed0e | ||
|
|
7133242691 | ||
|
|
55750ce16a | ||
|
|
6003e68d47 | ||
|
|
9f8c2aeedf | ||
|
|
0a16bc849b | ||
|
|
ecd5147590 | ||
|
|
ca255c9e1b | ||
|
|
e9aa490355 | ||
|
|
69fe59f97b | ||
|
|
995e1712bc | ||
|
|
a236f10bb0 | ||
|
|
4a6134ea60 | ||
|
|
b084024742 | ||
|
|
2ef48af1b3 | ||
|
|
da2c91874d | ||
|
|
379687c814 | ||
|
|
2f5577e464 | ||
|
|
6962fc2f4c | ||
|
|
a6cef26210 | ||
|
|
b4892c4413 | ||
|
|
90368405ad | ||
|
|
26967959e2 | ||
|
|
eac6f6fbe3 | ||
|
|
af4ec43352 |
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
73
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
73
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,73 +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-cli/labels/bug).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Error while running the CLI
|
|
||||||
- Error at runtime
|
|
||||||
- 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
|
|
||||||
49
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
49
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name: 🐞 Bug report
|
||||||
|
description: Report a bug or an issue.
|
||||||
|
title: 'bug: '
|
||||||
|
labels: ['Bug report']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# ReVanced CLI bug report
|
||||||
|
|
||||||
|
Please check for existing bug reports
|
||||||
|
[here](https://github.com/ReVanced/revanced-cli/labels/Bug%20report)
|
||||||
|
before creating a new one.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Bug description
|
||||||
|
description: |
|
||||||
|
- Describe your bug in detail
|
||||||
|
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
|
||||||
|
- Add images and videos if possible
|
||||||
|
- List used patches if applicable
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Error logs
|
||||||
|
description: Exceptions can be captured by running `logcat | grep AndroidRuntime` in a shell.
|
||||||
|
render: shell
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Solution
|
||||||
|
description: If applicable, add a possible solution to the bug.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add additional context here.
|
||||||
|
- type: checkboxes
|
||||||
|
id: acknowledgements
|
||||||
|
attributes:
|
||||||
|
label: Acknowledgements
|
||||||
|
description: Your issue will be closed if you don't follow the checklist below.
|
||||||
|
options:
|
||||||
|
- label: This request is not a duplicate of an existing issue.
|
||||||
|
required: true
|
||||||
|
- label: I have chosen an appropriate title.
|
||||||
|
required: true
|
||||||
|
- label: All requested information has been provided properly.
|
||||||
|
required: true
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
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
|
- name: 🗨 Discussions
|
||||||
url: https://github.com/revanced/revanced-suggestions/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!
|
about: Have something unspecific to ReVanced CLI in mind? 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-cli/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
|
|
||||||
45
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
45
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: ⭐ Feature request
|
||||||
|
description: Create a detailed request for a new feature.
|
||||||
|
title: 'feat: '
|
||||||
|
labels: ['Feature request']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# ReVanced CLI feature request
|
||||||
|
|
||||||
|
Please check for existing feature requests
|
||||||
|
[here](https://github.com/ReVanced/revanced-cli/labels/Feature%20request)
|
||||||
|
before creating a new one.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Feature description
|
||||||
|
description: |
|
||||||
|
- Describe your feature in detail
|
||||||
|
- Add images, videos, links, examples, references, etc. if possible
|
||||||
|
- Add the target application name in case you request a new patch
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Motivation
|
||||||
|
description: |
|
||||||
|
A strong motivation is necessary for a feature request to be considered.
|
||||||
|
|
||||||
|
- Why should this feature be implemented?
|
||||||
|
- What is the explicit use case?
|
||||||
|
- What are the benefits?
|
||||||
|
- What makes this feature important?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: acknowledgements
|
||||||
|
attributes:
|
||||||
|
label: Acknowledgements
|
||||||
|
description: Your issue will be closed if you don't follow the checklist below.
|
||||||
|
options:
|
||||||
|
- label: This request is not a duplicate of an existing issue.
|
||||||
|
required: true
|
||||||
|
- label: I have chosen an appropriate title.
|
||||||
|
required: true
|
||||||
|
- label: All requested information has been provided properly.
|
||||||
|
required: true
|
||||||
2
.github/config.yml
vendored
Normal file
2
.github/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
firstPRMergeComment: >
|
||||||
|
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
||||||
6
.github/workflows/pull_request.yml
vendored
6
.github/workflows/pull_request.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: PR to main
|
name: Open a PR to main
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
MESSAGE: Merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pull-request:
|
pull-request:
|
||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Open pull request
|
- name: Open pull request
|
||||||
uses: repo-sync/pull-request@v2
|
uses: repo-sync/pull-request@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
@@ -17,23 +17,27 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# Make sure the release step uses its own credentials:
|
# Make sure the release step uses its own credentials:
|
||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup JDK
|
- name: Cache
|
||||||
uses: actions/setup-java@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
path: |
|
||||||
distribution: 'zulu'
|
${{ runner.home }}/.gradle/caches
|
||||||
cache: gradle
|
${{ runner.home }}/.gradle/wrapper
|
||||||
- name: Setup Node.js
|
.gradle
|
||||||
uses: actions/setup-node@v3
|
build
|
||||||
with:
|
node_modules
|
||||||
node-version: "18"
|
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||||
cache: 'npm'
|
- name: Build
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Cleaning is necessary to avoid uploading two identical artifacts with different versions
|
||||||
|
run: ./gradlew clean --no-daemon
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install
|
run: npm install
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
1
.github/workflows/update-documentation.yml
vendored
1
.github/workflows/update-documentation.yml
vendored
@@ -9,6 +9,7 @@ jobs:
|
|||||||
trigger:
|
trigger:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Dispatch event to documentation repository
|
name: Dispatch event to documentation repository
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
steps:
|
steps:
|
||||||
- uses: peter-evans/repository-dispatch@v2
|
- uses: peter-evans/repository-dispatch@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -7,7 +7,13 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@semantic-release/commit-analyzer",
|
[
|
||||||
|
"@semantic-release/commit-analyzer", {
|
||||||
|
"releaseRules": [
|
||||||
|
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"@semantic-release/release-notes-generator",
|
"@semantic-release/release-notes-generator",
|
||||||
"@semantic-release/changelog",
|
"@semantic-release/changelog",
|
||||||
"gradle-semantic-release-plugin",
|
"gradle-semantic-release-plugin",
|
||||||
|
|||||||
363
CHANGELOG.md
363
CHANGELOG.md
@@ -1,3 +1,366 @@
|
|||||||
|
# [4.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v4.0.0-dev.4...v4.0.0-dev.5) (2023-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Only set options for filtered patches ([64d9127](https://github.com/ReVanced/revanced-cli/commit/64d9127291ea9a8abe21a0e82721721495094472))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Do not check, if the options file exists twice ([e3c5550](https://github.com/ReVanced/revanced-cli/commit/e3c55507cf52e697b9ce9d59decc1cbe4cfe5b43))
|
||||||
|
|
||||||
|
# [4.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v4.0.0-dev.3...v4.0.0-dev.4) (2023-10-01)
|
||||||
|
|
||||||
|
# [4.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v4.0.0-dev.2...v4.0.0-dev.3) (2023-09-27)
|
||||||
|
|
||||||
|
# [4.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v4.0.0-dev.1...v4.0.0-dev.2) (2023-09-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Improve option descriptions ([d5ea5a0](https://github.com/ReVanced/revanced-cli/commit/d5ea5a0ab1cc015730063e5be94ee18bd88cc906))
|
||||||
|
|
||||||
|
# [4.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.2-dev.1...v4.0.0-dev.1) (2023-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Check, if mounting is possible ([3e13fb5](https://github.com/ReVanced/revanced-cli/commit/3e13fb5d56eb2a90c2a4a1ddfc05852b1f70add5))
|
||||||
|
* Delete temporal files if it exists ([a022feb](https://github.com/ReVanced/revanced-cli/commit/a022febd0c70807ddc3fa9948207a2a70d5191da))
|
||||||
|
* Do not sign if mounting ([578e16b](https://github.com/ReVanced/revanced-cli/commit/578e16b099fddfd2bb56accb225d04dfcd409b0c))
|
||||||
|
* Filter logs correctly ([43fc20d](https://github.com/ReVanced/revanced-cli/commit/43fc20d90e0a694b231b17bb7d9ecfa22bb5d9a0))
|
||||||
|
* Log logs with levels over warning to error output stream ([075f6ad](https://github.com/ReVanced/revanced-cli/commit/075f6ad56528a667dca1f0bed704cf7e5381026f))
|
||||||
|
* Only open files for reading and writing if writeable ([3846f72](https://github.com/ReVanced/revanced-cli/commit/3846f721ca015ab39a7e4b8d3f3d61163a6f1650))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add function to get the most common compatible version ([77d9173](https://github.com/ReVanced/revanced-cli/commit/77d91735ffbbd6e733f08176f535bfd39ece0c29))
|
||||||
|
* Add option to filter patches to be listed by package name ([50c0f98](https://github.com/ReVanced/revanced-cli/commit/50c0f98ce5927e07839437a2e550aa85f5a7e62d))
|
||||||
|
* Add option to warn about patches not being found in supplied patch bundles ([e46d855](https://github.com/ReVanced/revanced-cli/commit/e46d85564320f46c6faa54b2d3fa7fca3fa60019))
|
||||||
|
* Add ReVanced Library subproject ([#265](https://github.com/ReVanced/revanced-cli/issues/265)) ([157278c](https://github.com/ReVanced/revanced-cli/commit/157278c9ba25f0f786c5fe58e3e23f6890107118))
|
||||||
|
* Do not format patch names ([80a8d88](https://github.com/ReVanced/revanced-cli/commit/80a8d88406b2b04d13dca4fb0d7d7d62e1910317))
|
||||||
|
* Extend signing API ([592dc1c](https://github.com/ReVanced/revanced-cli/commit/592dc1c64ae4078e73bb71eba11380b301c79dea))
|
||||||
|
* Log stacktrace in new line ([c67e3c7](https://github.com/ReVanced/revanced-cli/commit/c67e3c70c7eaa514cde1bebe775a2216bc4a6074))
|
||||||
|
* Use ReVanced Library in ReVanced CLI ([7794327](https://github.com/ReVanced/revanced-cli/commit/7794327a11e8a0e0f28176cd45fad797b924c45f))
|
||||||
|
* Word log message better ([6942b22](https://github.com/ReVanced/revanced-cli/commit/6942b22a68de5e991987ea89882915917aec93a3))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes many signatures of existing APIs and adds new functions for signing
|
||||||
|
* This changes the log handler signature
|
||||||
|
|
||||||
|
# [3.2.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.2-dev.1...v3.2.0-dev.1) (2023-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Log stacktrace in new line ([c67e3c7](https://github.com/ReVanced/revanced-cli/commit/c67e3c70c7eaa514cde1bebe775a2216bc4a6074))
|
||||||
|
|
||||||
|
## [3.1.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.1...v3.1.2-dev.1) (2023-09-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Log correct options command ([#262](https://github.com/ReVanced/revanced-cli/issues/262)) ([96c196d](https://github.com/ReVanced/revanced-cli/commit/96c196dcb14e37ad91b751af61ee8382547c1ca3))
|
||||||
|
|
||||||
|
## [3.1.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1) (2023-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Create options if it does not exist when updating them ([ca809f0](https://github.com/ReVanced/revanced-cli/commit/ca809f0948379e3a825f24d7a49aba8b6b8767d1))
|
||||||
|
|
||||||
|
## [3.1.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1-dev.1) (2023-09-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Create options if it does not exist when updating them ([ca809f0](https://github.com/ReVanced/revanced-cli/commit/ca809f0948379e3a825f24d7a49aba8b6b8767d1))
|
||||||
|
|
||||||
|
# [3.1.0](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.1.0) (2023-08-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
|
||||||
|
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
|
||||||
|
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
|
||||||
|
|
||||||
|
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.2...v3.1.0-dev.1) (2023-08-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
|
||||||
|
|
||||||
|
## [3.0.2-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.1...v3.0.2-dev.2) (2023-08-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
|
||||||
|
|
||||||
|
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.0.2-dev.1) (2023-08-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
|
||||||
|
|
||||||
|
## [3.0.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1) (2023-08-28)
|
||||||
|
|
||||||
|
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
|
||||||
|
|
||||||
|
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
|
||||||
|
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
|
||||||
|
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
|
||||||
|
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3))
|
||||||
|
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
|
||||||
|
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
|
||||||
|
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
|
||||||
|
* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e))
|
||||||
|
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
|
||||||
|
* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c))
|
||||||
|
* use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b))
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
|
||||||
|
* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6))
|
||||||
|
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
|
||||||
|
* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83))
|
||||||
|
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
|
||||||
|
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c))
|
||||||
|
* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131))
|
||||||
|
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
|
||||||
|
* use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580))
|
||||||
|
* use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6))
|
||||||
|
* use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b))
|
||||||
|
* use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This introduces major changes to how ReVanced CLI is used from the command line.
|
||||||
|
|
||||||
|
# [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
|
||||||
|
|
||||||
|
# [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
|
||||||
|
|
||||||
|
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
|
||||||
|
|
||||||
|
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
|
||||||
|
|
||||||
|
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24)
|
||||||
|
|
||||||
|
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
|
||||||
|
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
|
||||||
|
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
|
||||||
|
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
|
||||||
|
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
|
||||||
|
|
||||||
|
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
|
||||||
|
|
||||||
|
# [3.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-08-23)
|
||||||
|
|
||||||
|
# [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c))
|
||||||
|
|
||||||
|
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.5...v3.0.0-dev.1) (2023-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3))
|
||||||
|
* use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b))
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6))
|
||||||
|
* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131))
|
||||||
|
* use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580))
|
||||||
|
* use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6))
|
||||||
|
* use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b))
|
||||||
|
* use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This introduces major changes to how ReVanced CLI is used from the command line.
|
||||||
|
|
||||||
|
# [2.23.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.4...v2.23.0-dev.5) (2023-08-14)
|
||||||
|
|
||||||
|
# [2.23.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.3...v2.23.0-dev.4) (2023-08-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c))
|
||||||
|
|
||||||
|
# [2.23.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.2...v2.23.0-dev.3) (2023-08-03)
|
||||||
|
|
||||||
|
# [2.23.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.1...v2.23.0-dev.2) (2023-08-03)
|
||||||
|
|
||||||
|
# [2.23.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.1-dev.1...v2.23.0-dev.1) (2023-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83))
|
||||||
|
|
||||||
|
## [2.22.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v2.22.1-dev.1) (2023-07-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e))
|
||||||
|
|
||||||
|
# [2.22.0](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0) (2023-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* use new patch naming convention ([f6c221d](https://github.com/revanced/revanced-cli/commit/f6c221d72dc43ebea00e5eba6bfa02751ae8ad77))
|
||||||
|
|
||||||
|
# [2.22.0-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0-dev.1) (2023-07-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* use new patch naming convention ([e4908c7](https://github.com/revanced/revanced-cli/commit/e4908c71051a535f8ce3406427cebbb0941464df))
|
||||||
|
|
||||||
|
## [2.21.5](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5) (2023-07-01)
|
||||||
|
|
||||||
|
## [2.21.5-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.5-dev.1...v2.21.5-dev.2) (2023-07-01)
|
||||||
|
|
||||||
|
## [2.21.5-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5-dev.1) (2023-06-27)
|
||||||
|
|
||||||
|
## [2.21.4](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4) (2023-06-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove duplicate options entries. ([d0fc886](https://github.com/revanced/revanced-cli/commit/d0fc8864286adc2677f91a319a11a90272c1001d))
|
||||||
|
|
||||||
|
## [2.21.4-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4-dev.1) (2023-06-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove duplicate options entries. ([d0fc886](https://github.com/revanced/revanced-cli/commit/d0fc8864286adc2677f91a319a11a90272c1001d))
|
||||||
|
|
||||||
|
## [2.21.3](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3) (2023-06-12)
|
||||||
|
|
||||||
|
## [2.21.3-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3-dev.1) (2023-06-07)
|
||||||
|
|
||||||
|
## [2.21.2](https://github.com/revanced/revanced-cli/compare/v2.21.1...v2.21.2) (2023-05-24)
|
||||||
|
|
||||||
|
## [2.21.2-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.2-dev.1...v2.21.2-dev.2) (2023-05-15)
|
||||||
|
|
||||||
|
## [2.21.2-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.1...v2.21.2-dev.1) (2023-05-07)
|
||||||
|
|
||||||
|
## [2.21.1](https://github.com/revanced/revanced-cli/compare/v2.21.0...v2.21.1) (2023-05-06)
|
||||||
|
|
||||||
|
## [2.21.1-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.0...v2.21.1-dev.1) (2023-05-06)
|
||||||
|
|
||||||
|
# [2.21.0](https://github.com/revanced/revanced-cli/compare/v2.20.2...v2.21.0) (2023-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tests:** set order of tests ([2ef48af](https://github.com/revanced/revanced-cli/commit/2ef48af1b339ab729a05d69cb0c8c1ee1e3ab486))
|
||||||
|
* use working JADB dependency version ([#222](https://github.com/revanced/revanced-cli/issues/222)) ([da2c918](https://github.com/revanced/revanced-cli/commit/da2c91874d5623402febfcc0677ada3d648565e1))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add appreciation message for new contributors ([6962fc2](https://github.com/revanced/revanced-cli/commit/6962fc2f4c0f0c96e88a823be64f8ebd1312ee17))
|
||||||
|
|
||||||
|
# [2.21.0-dev.1](https://github.com/revanced/revanced-cli/compare/v2.20.2...v2.21.0-dev.1) (2023-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tests:** set order of tests ([2ef48af](https://github.com/revanced/revanced-cli/commit/2ef48af1b339ab729a05d69cb0c8c1ee1e3ab486))
|
||||||
|
* use working JADB dependency version ([#222](https://github.com/revanced/revanced-cli/issues/222)) ([da2c918](https://github.com/revanced/revanced-cli/commit/da2c91874d5623402febfcc0677ada3d648565e1))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add appreciation message for new contributors ([6962fc2](https://github.com/revanced/revanced-cli/commit/6962fc2f4c0f0c96e88a823be64f8ebd1312ee17))
|
||||||
|
|
||||||
|
## [2.20.2](https://github.com/revanced/revanced-cli/compare/v2.20.1...v2.20.2) (2023-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* correct spelling mistake ([31fb316](https://github.com/revanced/revanced-cli/commit/31fb3166d922ae1f568f52e44cbe726dd1c891a4))
|
||||||
|
|
||||||
## [2.20.2-dev.1](https://github.com/revanced/revanced-cli/compare/v2.20.1...v2.20.2-dev.1) (2023-04-03)
|
## [2.20.2-dev.1](https://github.com/revanced/revanced-cli/compare/v2.20.1...v2.20.2-dev.1) (2023-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
79
CONTRIBUTING.md
Normal file
79
CONTRIBUTING.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<p align="center">
|
||||||
|
<picture>
|
||||||
|
<source
|
||||||
|
width="256px"
|
||||||
|
media="(prefers-color-scheme: dark)"
|
||||||
|
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||||
|
>
|
||||||
|
</picture>
|
||||||
|
<br>
|
||||||
|
<a href="https://revanced.app/">
|
||||||
|
<img height="24px" src="assets/revanced-logo/revanced-logo-round.svg" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/revanced">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||||
|
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="http://revanced.app/discord">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://reddit.com/r/revancedapp">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://t.me/app_revanced">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/revancedapp">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032018-6da37214-7474-4641-a1da-7af7db3a31cd.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.youtube.com/@ReVanced">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Continuing the legacy of Vanced
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# 📙 ReVanced CLI contribution guidelines
|
||||||
|
|
||||||
|
This document describes how to contribute to ReVanced CLI.
|
||||||
|
|
||||||
|
## 📖 Resources to help you get started
|
||||||
|
|
||||||
|
* The [documentation](/docs) explains how to use ReVanced CLI
|
||||||
|
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
||||||
|
* [Issues](https://github.com/ReVanced/revanced-cli/issues) are where we keep track of bugs and feature requests
|
||||||
|
|
||||||
|
## 🙏 Submitting a feature request
|
||||||
|
|
||||||
|
Features can be requested by opening an issue using the
|
||||||
|
[Feature request issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Feature+request&projects=&template=feature-request.yml&title=feat%3A+).
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced CLI.
|
||||||
|
> Good motivation has to be provided for a request to be accepted.
|
||||||
|
|
||||||
|
## 🐞 Submitting a bug report
|
||||||
|
|
||||||
|
If you encounter a bug while using ReVanced CLI, open an issue using the
|
||||||
|
[Bug report issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Bug+report&projects=&template=bug-report.yml&title=bug%3A+).
|
||||||
|
|
||||||
|
## 📝 How to contribute
|
||||||
|
|
||||||
|
1. Before contributing, it is recommended to open an issue to discuss your change
|
||||||
|
with the maintainers of ReVanced CLI. This will help you determine whether your change is acceptable
|
||||||
|
and whether it is worth your time to implement it
|
||||||
|
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
||||||
|
3. Commit your changes.
|
||||||
|
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
||||||
|
that your pull request closes in the description of your pull request
|
||||||
|
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
||||||
|
it will be merged into the `dev` branch and will be included in the next release of ReVanced CLI
|
||||||
|
|
||||||
|
❤️ Thank you for considering contributing to ReVanced CLI,
|
||||||
|
ReVanced
|
||||||
75
README.md
Normal file
75
README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<p align="center">
|
||||||
|
<picture>
|
||||||
|
<source
|
||||||
|
width="256px"
|
||||||
|
media="(prefers-color-scheme: dark)"
|
||||||
|
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||||
|
>
|
||||||
|
</picture>
|
||||||
|
<br>
|
||||||
|
<a href="https://revanced.app/">
|
||||||
|
<img height="24px" src="assets/revanced-logo/revanced-logo-round.svg" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/revanced">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||||
|
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="http://revanced.app/discord">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://reddit.com/r/revancedapp">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://t.me/app_revanced">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/revancedapp">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032018-6da37214-7474-4641-a1da-7af7db3a31cd.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.youtube.com/@ReVanced">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Continuing the legacy of Vanced
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
# 💻 ReVanced CLI
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
Command line application to use ReVanced.
|
||||||
|
|
||||||
|
## ❓ About
|
||||||
|
|
||||||
|
ReVanced CLI is a command line application to patch apps using ReVanced.
|
||||||
|
ReVanced CLI also comes with commands to uninstall or install patched apps and list patches from supplied patch bundles.
|
||||||
|
|
||||||
|
## 🚀 Download
|
||||||
|
|
||||||
|
You can download the most recent version of ReVanced CLI from
|
||||||
|
[here](https://github.com/ReVanced/revanced-cli/releases/latest). Learn how to use ReVanced CLI by following the [documentation](/docs).
|
||||||
|
|
||||||
|
## 📚 Everything else
|
||||||
|
|
||||||
|
### 📙 Contributing
|
||||||
|
|
||||||
|
Thank you for considering contributing to ReVanced CLI.
|
||||||
|
You can find the contribution guidelines [here](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### 🛠️ Building
|
||||||
|
|
||||||
|
In order to build ReVanced CLI, you can follow the [documentation](/docs).
|
||||||
|
|
||||||
|
## 📜 Licence
|
||||||
|
|
||||||
|
ReVanced CLI is licensed under the GPLv3 licence. Please see the [licence file](LICENSE) for more information.
|
||||||
|
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced CLI as long as you track changes/dates in source files.
|
||||||
|
Any modifications to ReVanced CLI must also be made available under the GPL along with build & install instructions.
|
||||||
@@ -1,45 +1,36 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.10"
|
kotlin("jvm") version "1.9.0"
|
||||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
alias(libs.plugins.shadow)
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
|
|
||||||
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
|
|
||||||
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
mavenLocal()
|
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
|
||||||
credentials {
|
|
||||||
username = githubUsername
|
|
||||||
password = githubPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maven { url = uri("https://jitpack.io") }
|
|
||||||
google()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.10")
|
implementation(libs.revanced.patcher)
|
||||||
|
implementation(libs.revanced.library)
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
implementation(libs.picocli)
|
||||||
|
|
||||||
implementation("app.revanced:revanced-patcher:7.0.0")
|
testImplementation(libs.kotlin.test)
|
||||||
implementation("info.picocli:picocli:4.7.1")
|
|
||||||
implementation("com.github.revanced:jadb:master-SNAPSHOT") // updated fork
|
|
||||||
implementation("com.android.tools.build:apksig:7.2.2")
|
|
||||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
|
||||||
implementation("cc.ekblad:4koma:1.1.0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin { jvmToolchain(11) }
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
build {
|
test {
|
||||||
dependsOn(shadowJar)
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
expand("projectVersion" to project.version)
|
||||||
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes("Main-Class" to "app.revanced.cli.main.MainKt")
|
attributes("Main-Class" to "app.revanced.cli.command.MainCommandKt")
|
||||||
}
|
}
|
||||||
minimize {
|
minimize {
|
||||||
exclude(dependency("org.jetbrains.kotlin:.*"))
|
exclude(dependency("org.jetbrains.kotlin:.*"))
|
||||||
@@ -47,12 +38,26 @@ tasks {
|
|||||||
exclude(dependency("app.revanced:.*"))
|
exclude(dependency("app.revanced:.*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dummy task to fix the Gradle semantic-release plugin.
|
|
||||||
// Remove this if you forked it to support building only.
|
build {
|
||||||
// Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
|
dependsOn(shadowJar)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dummy task to hack gradle-semantic-release-plugin to release this project.
|
||||||
|
|
||||||
|
Explanation:
|
||||||
|
SemVer is a standard for versioning libraries.
|
||||||
|
For that reason the semantic-release plugin uses the "publish" task to publish libraries.
|
||||||
|
However, this subproject is not a library, and the "publish" task is not available for this subproject.
|
||||||
|
Because semantic-release is not designed to handle this case, we need to hack it.
|
||||||
|
|
||||||
|
RE: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
|
||||||
|
*/
|
||||||
|
|
||||||
register<DefaultTask>("publish") {
|
register<DefaultTask>("publish") {
|
||||||
group = "publish"
|
group = "publishing"
|
||||||
description = "Dummy task"
|
description = "Dummy task to hack gradle-semantic-release-plugin to release ReVanced CLI"
|
||||||
dependsOn(build)
|
dependsOn(build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# 💼 Prerequisites
|
# 💼 Prerequisites
|
||||||
|
|
||||||
To use the ReVanced CLI, you will need to fulfill certain requirements.
|
To use ReVanced CLI, you will need to fulfil specific requirements.
|
||||||
|
|
||||||
## 🤝 Requirements
|
## 🤝 Requirements
|
||||||
|
|
||||||
- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb), the command-line tool that lets
|
- Java SDK 11 (Azul Zulu JDK or OpenJDK)
|
||||||
you communicate with a device (optional).
|
- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to install the patched APK file on your device
|
||||||
- A x86/x86_64 host (or a custom AAPT binary for your architecture)
|
- An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7)
|
||||||
- Zulu OpenJDK 17
|
- ReVanced Patches
|
||||||
- An APK file (e.g. YouTube v17.49.37 or YouTube Music v5.36.51)
|
- ReVanced Integrations, if the patches require it
|
||||||
|
|
||||||
## ⏭️ Whats next
|
## ⏭️ Whats next
|
||||||
|
|
||||||
The next section will show, how to use the [ReVanced CLI](https://github.com/revanced/revanced-cli).
|
The following section will show you how to use ReVanced CLI.
|
||||||
|
|
||||||
Continue: [🛠️ Using the ReVanced CLI](1_usage.md)
|
Continue: [🛠️ Using ReVanced CLI](1_usage.md)
|
||||||
|
|||||||
119
docs/1_usage.md
119
docs/1_usage.md
@@ -1,74 +1,111 @@
|
|||||||
# 🛠️ Using the ReVanced CLI
|
# 🛠️ Using ReVanced CLI
|
||||||
|
|
||||||
Lean how to use the ReVanced CLI.
|
Learn how to ReVanced CLI.
|
||||||
|
|
||||||
## ⚡ Setup (optional)
|
## ⚡ Setup ADB
|
||||||
|
|
||||||
1. Make sure your device is connected
|
1. Ensure that ADB is working
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb shell exit
|
adb shell exit
|
||||||
```
|
```
|
||||||
|
|
||||||
If you plan to use the root variant, check if you have root access
|
Optionally, you can install the patched APK file on your device by mounting it on top of the original APK file.
|
||||||
|
You will need root permissions for this. Check if you have root permissions by running the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb shell su -c exit
|
adb shell su -c exit
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Copy the ADB device name
|
2. Get your device's serial
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb devices
|
adb devices
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔨 ReVanced CLI Usage
|
## 🔨 Using ReVanced CLI
|
||||||
|
|
||||||
- ### Show all available options for the ReVanced CLI
|
- ### ⚙️ Show all available options for ReVanced CLI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar revanced-cli.jar -h
|
java -jar revanced-cli.jar -h
|
||||||
```
|
```
|
||||||
|
|
||||||
- ### List all available patches from supplied patch bundles
|
- ### 📃 List patches from supplied patch bundles
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar revanced-cli.jar \
|
java -jar revanced-cli.jar list-patches \
|
||||||
-b revanced-patches.jar \
|
--with-packages \
|
||||||
-l
|
--with-versions \
|
||||||
|
--with-options \
|
||||||
|
revanced-patches.jar [<patch-bundle> ...]
|
||||||
```
|
```
|
||||||
|
|
||||||
- ### Use the ReVanced CLI without root permissions
|
- ### ⚙️ Generate options from patches using ReVanced CLI
|
||||||
|
|
||||||
|
This will generate an `options.json` file for the patches from a list of supplied patch bundles.
|
||||||
|
The file can be supplied to ReVanced CLI later on.
|
||||||
|
|
||||||
|
- ```bash
|
||||||
|
java -jar revanced-cli.jar options \
|
||||||
|
--path options.json \
|
||||||
|
--overwrite \
|
||||||
|
revanced-patches.jar [<patch-bundle> ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: A default `options.json` file will be automatically generated, if it does not exist
|
||||||
|
without any need for intervention when using the `patch` command.
|
||||||
|
|
||||||
|
- ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions
|
||||||
|
|
||||||
|
This will install the patched APK file regularly on your device.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
java -jar revanced-cli.jar \
|
java -jar revanced-cli.jar patch \
|
||||||
|
--patch-bundle revanced-patches.jar \
|
||||||
|
--out output.apk \
|
||||||
|
--device-serial <device-serial> \
|
||||||
|
input.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
- ### 👾 Use ReVanced CLI to patch an APK file but install with root permissions
|
||||||
|
|
||||||
|
This will install the patched APK file on your device by mounting it on top of the original APK file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb install input.apk
|
||||||
|
java -jar revanced-cli.jar patch \
|
||||||
|
--patch-bundle revanced-patches.jar \
|
||||||
|
--include some-other-patch \
|
||||||
|
--exclude some-patch \
|
||||||
|
--out patched-output.apk \
|
||||||
|
--device-serial <device-serial> \
|
||||||
|
--mount \
|
||||||
|
input.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: Some patches may require integrations
|
||||||
|
such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations).
|
||||||
|
Supply them with the option `--merge`. If any patches accepted by ReVanced Patcher require ReVanced Integrations,
|
||||||
|
they will be merged into the APK file automatically.
|
||||||
|
|
||||||
|
- ### 🗑️ Uninstall a patched APK file
|
||||||
|
```bash
|
||||||
|
java -jar revanced-cli.jar utility uninstall \
|
||||||
|
--package-name <package-name> \
|
||||||
|
<device-serial>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: You can unmount an APK file
|
||||||
|
with the option `--unmount`.
|
||||||
|
|
||||||
|
- ### ️ ⚙️ Manually install an APK file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -jar revanced-cli.jar utility install \
|
||||||
-a input.apk \
|
-a input.apk \
|
||||||
-o patched-output.apk \
|
<device-serial>
|
||||||
-b revanced-patches.jar
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- ### Mount the patched application with root permissions over the installed application
|
> **Note**: You can mount an APK file
|
||||||
|
by supplying the package name of the app to mount the supplied APK file to over the option `--mount`.
|
||||||
```bash
|
|
||||||
adb install input.apk # make sure the same version is installed
|
|
||||||
java -jar revanced-cli.jar \
|
|
||||||
-a input.apk \
|
|
||||||
-d device-name \
|
|
||||||
-o patched-output.apk \
|
|
||||||
-b revanced-patches.jar \
|
|
||||||
-e microg-support \
|
|
||||||
--mount
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
>
|
|
||||||
> - If you want to exclude patches, you can use the option `-e`. In the case of YouTube, you can exclude
|
|
||||||
the `microg-support` patch from [ReVanced Patches](https://github.com/revanced/revanced-patches) with the
|
|
||||||
option `-e microg-support` when mounting for example.
|
|
||||||
>
|
|
||||||
> - Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also might require
|
|
||||||
[ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`.
|
|
||||||
> The integrations will be merged, if necessary automatically, if supplied.
|
|
||||||
>
|
|
||||||
> - If you supplied a device with the option `-d`, the patched application will be automatically installed on the
|
|
||||||
device.
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# 💻 Documentation and guides of ReVanced CLI
|
# 💻 Documentation and guides of ReVanced CLI
|
||||||
|
|
||||||
This documentation explains how to use the [ReVanced CLI](https://github.com/revanced/revanced-cli).
|
This documentation explains how to use [ReVanced CLI](https://github.com/revanced/revanced-cli).
|
||||||
|
|
||||||
## 📖 Table of contents
|
## 📖 Table of contents
|
||||||
|
|
||||||
1. [💼 Prerequisites](0_prerequisites.md)
|
1. [💼 Prerequisites](0_prerequisites.md)
|
||||||
2. [🛠️ Using the ReVanced CLI](1_usage.md)
|
2. [🛠️ Using ReVanced CLI](1_usage.md)
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
|
org.gradle.parallel = true
|
||||||
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 2.20.2-dev.1
|
version = 4.0.0-dev.5
|
||||||
|
|||||||
17
gradle/libs.versions.toml
Normal file
17
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[versions]
|
||||||
|
shadow = "8.1.1"
|
||||||
|
kotlin-test = "1.8.20-RC"
|
||||||
|
kotlinx-coroutines-core = "1.7.3"
|
||||||
|
picocli = "4.7.3"
|
||||||
|
revanced-patcher = "16.0.0"
|
||||||
|
revanced-library = "1.1.1"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
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" }
|
||||||
|
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
|
||||||
|
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||||
|
revanced-library = { module = "app.revanced:revanced-library", version.ref = "revanced-library" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||||
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,5 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
293
gradlew
vendored
293
gradlew
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,67 +17,98 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# 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».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# 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
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
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.
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MSYS* | MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -98,88 +129,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
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.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
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
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
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.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
@@ -1 +1,23 @@
|
|||||||
|
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()
|
||||||
|
mavenLocal()
|
||||||
|
google()
|
||||||
|
maven { url = uri("https://jitpack.io") }
|
||||||
|
listOf("revanced-patcher", "jadb").forEach { repo ->
|
||||||
|
maven {
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/$repo")
|
||||||
|
credentials {
|
||||||
|
username = githubUsername
|
||||||
|
password = githubPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rootProject.name = "revanced-cli"
|
rootProject.name = "revanced-cli"
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package app.revanced.cli.aligning
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import app.revanced.patcher.PatcherResult
|
|
||||||
import app.revanced.utils.signing.align.ZipAligner
|
|
||||||
import app.revanced.utils.signing.align.zip.ZipFile
|
|
||||||
import app.revanced.utils.signing.align.zip.structures.ZipEntry
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object Aligning {
|
|
||||||
fun align(result: PatcherResult, inputFile: File, outputFile: File) {
|
|
||||||
logger.info("Aligning ${inputFile.name} to ${outputFile.name}")
|
|
||||||
|
|
||||||
if (outputFile.exists()) outputFile.delete()
|
|
||||||
|
|
||||||
ZipFile(outputFile).use { file ->
|
|
||||||
result.dexFiles.forEach {
|
|
||||||
file.addEntryCompressData(
|
|
||||||
ZipEntry.createWithName(it.name),
|
|
||||||
it.stream.readBytes()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.resourceFile?.let {
|
|
||||||
file.copyEntriesFromFileAligned(
|
|
||||||
ZipFile(it),
|
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
file.copyEntriesFromFileAligned(
|
|
||||||
ZipFile(inputFile),
|
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package app.revanced.cli.command
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchBundleLoader
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import picocli.CommandLine.Help.Visibility.ALWAYS
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|
||||||
|
@Command(name = "list-patches", description = ["List patches from supplied patch bundles"])
|
||||||
|
internal object ListPatchesCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
|
||||||
|
|
||||||
|
@Parameters(
|
||||||
|
description = ["Paths to patch bundles"], arity = "1..*"
|
||||||
|
)
|
||||||
|
private lateinit var patchBundles: Array<File>
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var withDescriptions: Boolean = true
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-p", "--with-packages"],
|
||||||
|
description = ["List the packages the patches are compatible with"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var withPackages: Boolean = false
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-v", "--with-versions"],
|
||||||
|
description = ["List the versions of the apps the patches are compatible with"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var withVersions: Boolean = false
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var withOptions: Boolean = false
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-f", "--filter-package-name"], description = ["Filter patches by package name"]
|
||||||
|
)
|
||||||
|
private var packageName: String? = null
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
fun Patch.CompatiblePackage.buildString() = buildString {
|
||||||
|
if (withVersions && versions != null) {
|
||||||
|
appendLine("Package name: $name")
|
||||||
|
appendLine("Compatible versions:")
|
||||||
|
append(versions!!.joinToString("\n") { version -> version }.prependIndent("\t"))
|
||||||
|
} else append("Package name: $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PatchOption<*>.buildString() = buildString {
|
||||||
|
appendLine("Title: $title")
|
||||||
|
appendLine("Description: $description")
|
||||||
|
|
||||||
|
value?.let {
|
||||||
|
appendLine("Key: $key")
|
||||||
|
append("Value: $it")
|
||||||
|
} ?: append("Key: $key")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Patch<*>.buildString() = buildString {
|
||||||
|
append("Name: $name")
|
||||||
|
|
||||||
|
if (withDescriptions) append("\nDescription: $description")
|
||||||
|
|
||||||
|
if (withOptions && options.isNotEmpty()) {
|
||||||
|
appendLine("\nOptions:")
|
||||||
|
append(
|
||||||
|
options.values.joinToString("\n\n") { option ->
|
||||||
|
option.buildString()
|
||||||
|
}.prependIndent("\t")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withPackages && compatiblePackages != null) {
|
||||||
|
appendLine("\nCompatible packages:")
|
||||||
|
append(
|
||||||
|
compatiblePackages!!.joinToString("\n") { it.buildString() }.prependIndent("\t")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Patch<*>.anyPackageName(name: String) = compatiblePackages?.any { it.name == name } == true
|
||||||
|
|
||||||
|
val patches = PatchBundleLoader.Jar(*patchBundles)
|
||||||
|
|
||||||
|
val filtered = packageName?.let { patches.filter { patch -> patch.anyPackageName(it) } } ?: patches
|
||||||
|
|
||||||
|
if (filtered.isNotEmpty()) logger.info(filtered.joinToString("\n\n") { it.buildString() })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,253 +1,39 @@
|
|||||||
package app.revanced.cli.command
|
package app.revanced.cli.command
|
||||||
|
|
||||||
import app.revanced.cli.aligning.Aligning
|
import app.revanced.cli.command.utility.UtilityCommand
|
||||||
import app.revanced.cli.logging.impl.DefaultCliLogger
|
import app.revanced.library.logging.Logger
|
||||||
import app.revanced.cli.patcher.Patcher
|
import picocli.CommandLine
|
||||||
import app.revanced.cli.patcher.logging.impl.PatcherLogger
|
import picocli.CommandLine.Command
|
||||||
import app.revanced.cli.signing.Signing
|
import picocli.CommandLine.IVersionProvider
|
||||||
import app.revanced.cli.signing.SigningOptions
|
import java.util.*
|
||||||
import app.revanced.patcher.PatcherOptions
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.description
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
|
||||||
import app.revanced.patcher.util.patch.PatchBundle
|
|
||||||
import app.revanced.utils.OptionsLoader
|
|
||||||
import app.revanced.utils.adb.Adb
|
|
||||||
import picocli.CommandLine.*
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Files
|
|
||||||
|
|
||||||
private class CLIVersionProvider : IVersionProvider {
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
Logger.setDefault()
|
||||||
|
CommandLine(MainCommand).execute(*args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private object CLIVersionProvider : IVersionProvider {
|
||||||
override fun getVersion() = arrayOf(
|
override fun getVersion() = arrayOf(
|
||||||
MainCommand::class.java.`package`.implementationVersion ?: "unknown"
|
MainCommand::class.java.getResourceAsStream(
|
||||||
)
|
"/app/revanced/cli/version.properties"
|
||||||
|
)?.use { stream ->
|
||||||
|
Properties().apply { load(stream) }.let {
|
||||||
|
"ReVanced CLI v${it.getProperty("version")}"
|
||||||
|
}
|
||||||
|
} ?: "ReVanced CLI")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "ReVanced-CLI",
|
name = "revanced-cli",
|
||||||
|
description = ["Command line application to use ReVanced"],
|
||||||
mixinStandardHelpOptions = true,
|
mixinStandardHelpOptions = true,
|
||||||
versionProvider = CLIVersionProvider::class
|
versionProvider = CLIVersionProvider::class,
|
||||||
|
subcommands = [
|
||||||
|
ListPatchesCommand::class,
|
||||||
|
PatchCommand::class,
|
||||||
|
OptionsCommand::class,
|
||||||
|
UtilityCommand::class,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
internal object MainCommand : Runnable {
|
private object MainCommand
|
||||||
val logger = DefaultCliLogger()
|
|
||||||
|
|
||||||
@ArgGroup(exclusive = false, multiplicity = "1")
|
|
||||||
lateinit var args: Args
|
|
||||||
|
|
||||||
class Args {
|
|
||||||
@Option(names = ["-a", "--apk"], description = ["Input APK file to be patched"], required = true)
|
|
||||||
lateinit var inputFile: File
|
|
||||||
|
|
||||||
@Option(names = ["--uninstall"], description = ["Uninstall the mount variant"])
|
|
||||||
var uninstall: Boolean = false
|
|
||||||
|
|
||||||
@Option(
|
|
||||||
names = ["-d", "--deploy-on"],
|
|
||||||
description = ["If specified, deploy to device over ADB with given name"]
|
|
||||||
)
|
|
||||||
var deploy: String? = null
|
|
||||||
|
|
||||||
@ArgGroup(exclusive = false)
|
|
||||||
var patchArgs: PatchArgs? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
class PatchArgs {
|
|
||||||
@Option(names = ["-b", "--bundle"], description = ["One or more bundles of patches"], required = true)
|
|
||||||
var patchBundles = arrayOf<String>()
|
|
||||||
|
|
||||||
@Option(names = ["--options"], description = ["Configuration file for all patch options"])
|
|
||||||
var options: File = File("options.toml")
|
|
||||||
|
|
||||||
@ArgGroup(exclusive = false)
|
|
||||||
var listingArgs: ListingArgs? = null
|
|
||||||
|
|
||||||
@ArgGroup(exclusive = false)
|
|
||||||
var patchingArgs: PatchingArgs? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
class ListingArgs {
|
|
||||||
@Option(names = ["-l", "--list"], description = ["List patches"], required = true)
|
|
||||||
var listOnly: Boolean = false
|
|
||||||
|
|
||||||
@Option(names = ["--with-versions"], description = ["List patches with compatible versions"])
|
|
||||||
var withVersions: Boolean = false
|
|
||||||
|
|
||||||
@Option(names = ["--with-packages"], description = ["List patches with compatible packages"])
|
|
||||||
var withPackages: Boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class PatchingArgs {
|
|
||||||
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
|
|
||||||
lateinit var outputPath: String
|
|
||||||
|
|
||||||
@Option(names = ["-e", "--exclude"], description = ["Explicitly exclude patches"])
|
|
||||||
var excludedPatches = arrayOf<String>()
|
|
||||||
|
|
||||||
@Option(
|
|
||||||
names = ["--exclusive"],
|
|
||||||
description = ["Only installs the patches you include, not including any patch by default"]
|
|
||||||
)
|
|
||||||
var exclusive = false
|
|
||||||
|
|
||||||
@Option(names = ["-i", "--include"], description = ["Include patches"])
|
|
||||||
var includedPatches = arrayOf<String>()
|
|
||||||
|
|
||||||
@Option(names = ["--experimental"], description = ["Disable patch version compatibility patch"])
|
|
||||||
var experimental: Boolean = false
|
|
||||||
|
|
||||||
@Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"])
|
|
||||||
var mergeFiles = listOf<File>()
|
|
||||||
|
|
||||||
@Option(names = ["--mount"], description = ["If specified, instead of installing, mount"])
|
|
||||||
var mount: Boolean = false
|
|
||||||
|
|
||||||
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
|
|
||||||
var cn = "ReVanced"
|
|
||||||
|
|
||||||
@Option(names = ["--keystore"], description = ["File path to your keystore"])
|
|
||||||
var keystorePath: String? = null
|
|
||||||
|
|
||||||
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
|
|
||||||
var password = "ReVanced"
|
|
||||||
|
|
||||||
@Option(names = ["-t", "--temp-dir"], description = ["Temporary resource cache directory"])
|
|
||||||
var cacheDirectory = "revanced-cache"
|
|
||||||
|
|
||||||
@Option(
|
|
||||||
names = ["-c", "--clean"],
|
|
||||||
description = ["Clean the temporary resource cache directory. This will be done anyways when running the patcher"]
|
|
||||||
)
|
|
||||||
var clean: Boolean = false
|
|
||||||
|
|
||||||
@Option(names = ["--custom-aapt2-binary"], description = ["Path to custom aapt2 binary"])
|
|
||||||
var aaptPath: String = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun run() {
|
|
||||||
if (args.patchArgs?.listingArgs?.listOnly == true) return printListOfPatches()
|
|
||||||
if (args.uninstall) return uninstall()
|
|
||||||
|
|
||||||
val pArgs = this.args.patchArgs?.patchingArgs ?: return
|
|
||||||
val outputFile = File(pArgs.outputPath) // the file to write to
|
|
||||||
|
|
||||||
val allPatches = args.patchArgs!!.patchBundles.flatMap { bundle ->
|
|
||||||
PatchBundle.Jar(bundle).loadPatches()
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionsLoader.init(args.patchArgs!!.options, allPatches)
|
|
||||||
|
|
||||||
val patcher = app.revanced.patcher.Patcher(
|
|
||||||
PatcherOptions(
|
|
||||||
args.inputFile.also { if (!it.exists()) return logger.error("Input file ${args.inputFile} does not exist.") },
|
|
||||||
pArgs.cacheDirectory,
|
|
||||||
pArgs.aaptPath,
|
|
||||||
pArgs.cacheDirectory,
|
|
||||||
PatcherLogger
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// prepare adb
|
|
||||||
val adb: Adb? = args.deploy?.let {
|
|
||||||
Adb(outputFile, patcher.context.packageMetadata.packageName, args.deploy!!, !pArgs.mount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the patcher
|
|
||||||
val result = Patcher.start(patcher, allPatches)
|
|
||||||
|
|
||||||
val cacheDirectory = File(pArgs.cacheDirectory)
|
|
||||||
|
|
||||||
// align the file
|
|
||||||
val alignedFile = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
|
|
||||||
Aligning.align(result, args.inputFile, alignedFile)
|
|
||||||
|
|
||||||
// sign the file
|
|
||||||
val finalFile = if (!pArgs.mount) {
|
|
||||||
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
|
|
||||||
Signing.sign(
|
|
||||||
alignedFile,
|
|
||||||
signedOutput,
|
|
||||||
SigningOptions(
|
|
||||||
pArgs.cn,
|
|
||||||
pArgs.password,
|
|
||||||
pArgs.keystorePath ?: outputFile.absoluteFile.parentFile
|
|
||||||
.resolve("${outputFile.nameWithoutExtension}.keystore")
|
|
||||||
.canonicalPath
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
signedOutput
|
|
||||||
} else
|
|
||||||
alignedFile
|
|
||||||
|
|
||||||
// finally copy to the specified output file
|
|
||||||
logger.info("Copying ${finalFile.name} to ${outputFile.name}")
|
|
||||||
finalFile.copyTo(outputFile, overwrite = true)
|
|
||||||
|
|
||||||
// clean up the cache directory if needed
|
|
||||||
if (pArgs.clean)
|
|
||||||
cleanUp(pArgs.cacheDirectory)
|
|
||||||
|
|
||||||
// deploy if specified
|
|
||||||
adb?.deploy()
|
|
||||||
|
|
||||||
if (pArgs.clean && args.deploy != null) Files.delete(outputFile.toPath())
|
|
||||||
|
|
||||||
logger.info("Finished")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun cleanUp(cacheDirectory: String) {
|
|
||||||
val result = if (File(cacheDirectory).deleteRecursively())
|
|
||||||
"Cleaned up cache directory"
|
|
||||||
else
|
|
||||||
"Failed to clean up cache directory"
|
|
||||||
logger.info(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun uninstall() {
|
|
||||||
val adb: Adb? = args.deploy?.let {
|
|
||||||
Adb(
|
|
||||||
File("placeholder_file"),
|
|
||||||
app.revanced.patcher.Patcher(PatcherOptions(args.inputFile, "")).context.packageMetadata.packageName,
|
|
||||||
args.deploy!!,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
adb?.uninstall()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun printListOfPatches() {
|
|
||||||
val logged = mutableListOf<String>()
|
|
||||||
for (patchBundlePath in args.patchArgs?.patchBundles!!) for (patch in PatchBundle.Jar(patchBundlePath)
|
|
||||||
.loadPatches()) {
|
|
||||||
if (patch.patchName in logged) continue
|
|
||||||
for (compatiblePackage in patch.compatiblePackages ?: continue) {
|
|
||||||
val packageEntryStr = buildString {
|
|
||||||
// Add package if flag is set
|
|
||||||
if (args.patchArgs?.listingArgs?.withPackages == true) {
|
|
||||||
val packageName = compatiblePackage.name.substringAfterLast(".").padStart(10)
|
|
||||||
append(packageName)
|
|
||||||
append("\t")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add patch name
|
|
||||||
val patchName = patch.patchName.padStart(25)
|
|
||||||
append(patchName)
|
|
||||||
|
|
||||||
// Add description if flag is set.
|
|
||||||
append("\t")
|
|
||||||
append(patch.description)
|
|
||||||
|
|
||||||
// Add compatible versions, if flag is set
|
|
||||||
if (args.patchArgs?.listingArgs?.withVersions == true) {
|
|
||||||
val compatibleVersions = compatiblePackage.versions.joinToString(separator = ", ")
|
|
||||||
append("\t")
|
|
||||||
append(compatibleVersions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logged.add(patch.patchName)
|
|
||||||
logger.info(packageEntryStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt
Normal file
54
src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package app.revanced.cli.command
|
||||||
|
|
||||||
|
import app.revanced.library.Options
|
||||||
|
import app.revanced.library.Options.setOptions
|
||||||
|
import app.revanced.patcher.PatchBundleLoader
|
||||||
|
import picocli.CommandLine
|
||||||
|
import picocli.CommandLine.Help.Visibility.ALWAYS
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "options",
|
||||||
|
description = ["Generate options file from patches"],
|
||||||
|
)
|
||||||
|
internal object OptionsCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(OptionsCommand::class.java.name)
|
||||||
|
|
||||||
|
@CommandLine.Parameters(
|
||||||
|
description = ["Paths to patch bundles"], arity = "1..*"
|
||||||
|
)
|
||||||
|
private lateinit var patchBundles: Array<File>
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var filePath: File = File("options.json")
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var overwrite: Boolean = false
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-u", "--update"],
|
||||||
|
description = ["Update existing options by adding missing and removing non-existent options"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var update: Boolean = false
|
||||||
|
|
||||||
|
override fun run() = try {
|
||||||
|
PatchBundleLoader.Jar(*patchBundles).let { patches ->
|
||||||
|
val exists = filePath.exists()
|
||||||
|
if (!exists || overwrite) {
|
||||||
|
if (exists && update) patches.setOptions(filePath)
|
||||||
|
|
||||||
|
Options.serialize(patches, prettyPrint = true).let(filePath::writeText)
|
||||||
|
} else throw OptionsFileAlreadyExistsException()
|
||||||
|
}
|
||||||
|
} catch (ex: OptionsFileAlreadyExistsException) {
|
||||||
|
logger.severe("Options file already exists, use --overwrite to override it")
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionsFileAlreadyExistsException : Exception()
|
||||||
|
}
|
||||||
331
src/main/kotlin/app/revanced/cli/command/PatchCommand.kt
Normal file
331
src/main/kotlin/app/revanced/cli/command/PatchCommand.kt
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
package app.revanced.cli.command
|
||||||
|
|
||||||
|
import app.revanced.library.ApkUtils
|
||||||
|
import app.revanced.library.Options
|
||||||
|
import app.revanced.library.Options.setOptions
|
||||||
|
import app.revanced.library.adb.AdbManager
|
||||||
|
import app.revanced.patcher.PatchBundleLoader
|
||||||
|
import app.revanced.patcher.PatchSet
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
import app.revanced.patcher.PatcherOptions
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import picocli.CommandLine
|
||||||
|
import picocli.CommandLine.Help.Visibility.ALWAYS
|
||||||
|
import picocli.CommandLine.Model.CommandSpec
|
||||||
|
import picocli.CommandLine.Spec
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "patch", description = ["Patch an APK file"]
|
||||||
|
)
|
||||||
|
internal object PatchCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(PatchCommand::class.java.name)
|
||||||
|
|
||||||
|
@Spec
|
||||||
|
lateinit var spec: CommandSpec // injected by picocli
|
||||||
|
|
||||||
|
private lateinit var apk: File
|
||||||
|
|
||||||
|
private var integrations = listOf<File>()
|
||||||
|
|
||||||
|
private var patchBundles = emptyList<File>()
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-i", "--include"], description = ["List of patches to include"]
|
||||||
|
)
|
||||||
|
private var includedPatches = arrayOf<String>()
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-e", "--exclude"], description = ["List of patches to exclude"]
|
||||||
|
)
|
||||||
|
private var excludedPatches = arrayOf<String>()
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var optionsFile: File = File("options.json")
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--exclusive"],
|
||||||
|
description = ["Only include patches that are explicitly specified to be included"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var exclusive = false
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-f","--force"],
|
||||||
|
description = ["Bypass compatibility checks for the supplied APK's version"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var force: Boolean = false
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true
|
||||||
|
)
|
||||||
|
private lateinit var outputFilePath: File
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var deviceSerial: String? = null
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var mount: Boolean = false
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"],
|
||||||
|
)
|
||||||
|
private var keystoreFilePath: File? = null
|
||||||
|
|
||||||
|
// key store password
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--keystore-password"],
|
||||||
|
description = ["The password of the keystore to sign the patched APK file with"],
|
||||||
|
)
|
||||||
|
private var keyStorePassword: String? = null // Empty password by default
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--alias"], description = ["The alias of the key from the keystore to sign the patched APK file with"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var alias = "ReVanced Key"
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--keystore-entry-password"],
|
||||||
|
description = ["The password of the entry from the keystore for the key to sign the patched APK file with"]
|
||||||
|
)
|
||||||
|
private var password = "" // Empty password by default
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--signer"], description = ["The name of the signer to sign the patched APK file with"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var signer = "ReVanced"
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-r", "--resource-cache"],
|
||||||
|
description = ["Path to temporary resource cache directory"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var resourceCachePath = File("revanced-resource-cache")
|
||||||
|
|
||||||
|
private var aaptBinaryPath: File? = null
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-p", "--purge"],
|
||||||
|
description = ["Purge the temporary resource cache directory after patching"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var purge: Boolean = false
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-w", "--warn"],
|
||||||
|
description = ["Warn if a patch can not be found in the supplied patch bundles"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var warn: Boolean = false
|
||||||
|
|
||||||
|
@CommandLine.Parameters(
|
||||||
|
description = ["APK file to be patched"], arity = "1..1"
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
private fun setApk(apk: File) {
|
||||||
|
if (!apk.exists()) throw CommandLine.ParameterException(
|
||||||
|
spec.commandLine(),
|
||||||
|
"APK file ${apk.name} does not exist"
|
||||||
|
)
|
||||||
|
this.apk = apk
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
private fun setIntegrations(integrations: Array<File>) {
|
||||||
|
integrations.firstOrNull { !it.exists() }?.let {
|
||||||
|
throw CommandLine.ParameterException(spec.commandLine(), "Integrations file ${it.name} does not exist")
|
||||||
|
}
|
||||||
|
this.integrations += integrations
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
private fun setPatchBundles(patchBundles: Array<File>) {
|
||||||
|
patchBundles.firstOrNull { !it.exists() }?.let {
|
||||||
|
throw CommandLine.ParameterException(spec.commandLine(), "Patch bundle ${it.name} does not exist")
|
||||||
|
}
|
||||||
|
this.patchBundles = patchBundles.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandLine.Option(
|
||||||
|
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"]
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
private fun setAaptBinaryPath(aaptBinaryPath: File) {
|
||||||
|
if (!aaptBinaryPath.exists()) throw CommandLine.ParameterException(
|
||||||
|
spec.commandLine(),
|
||||||
|
"AAPT binary ${aaptBinaryPath.name} does not exist"
|
||||||
|
)
|
||||||
|
this.aaptBinaryPath = aaptBinaryPath
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
val adbManager = deviceSerial?.let { serial -> AdbManager.getAdbManager(serial, mount) }
|
||||||
|
|
||||||
|
// region Load patches
|
||||||
|
|
||||||
|
logger.info("Loading patches")
|
||||||
|
|
||||||
|
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())
|
||||||
|
|
||||||
|
// Warn if a patch can not be found in the supplied patch bundles.
|
||||||
|
if (warn) patches.map { it.name }.toHashSet().let { availableNames ->
|
||||||
|
arrayOf(*includedPatches, *excludedPatches).filter { name ->
|
||||||
|
!availableNames.contains(name)
|
||||||
|
}
|
||||||
|
}.let { unknownPatches ->
|
||||||
|
if (unknownPatches.isEmpty()) return@let
|
||||||
|
logger.warning("Unknown input of patches:\n${unknownPatches.joinToString("\n")}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
Patcher(
|
||||||
|
PatcherOptions(
|
||||||
|
apk,
|
||||||
|
resourceCachePath,
|
||||||
|
aaptBinaryPath?.path,
|
||||||
|
resourceCachePath.absolutePath,
|
||||||
|
)
|
||||||
|
).use { patcher ->
|
||||||
|
val filteredPatches = patcher.filterPatchSelection(patches).also { patches ->
|
||||||
|
logger.info("Setting patch options")
|
||||||
|
|
||||||
|
if (optionsFile.exists()) patches.setOptions(optionsFile)
|
||||||
|
else Options.serialize(patches, prettyPrint = true).let(optionsFile::writeText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Patch
|
||||||
|
|
||||||
|
val patcherResult = patcher.apply {
|
||||||
|
acceptIntegrations(integrations)
|
||||||
|
acceptPatches(filteredPatches.toList())
|
||||||
|
|
||||||
|
// Execute patches.
|
||||||
|
runBlocking {
|
||||||
|
apply(false).collect { patchResult ->
|
||||||
|
patchResult.exception?.let {
|
||||||
|
StringWriter().use { writer ->
|
||||||
|
it.printStackTrace(PrintWriter(writer))
|
||||||
|
logger.severe("${patchResult.patch.name} failed:\n$writer")
|
||||||
|
}
|
||||||
|
} ?: logger.info("${patchResult.patch.name} succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.get()
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Save
|
||||||
|
|
||||||
|
val tempFile = resourceCachePath.resolve(apk.name).apply {
|
||||||
|
ApkUtils.copyAligned(apk, this, patcherResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keystoreFilePath = keystoreFilePath ?: outputFilePath.absoluteFile.parentFile
|
||||||
|
.resolve("${outputFilePath.nameWithoutExtension}.keystore")
|
||||||
|
|
||||||
|
if (!mount) ApkUtils.sign(
|
||||||
|
tempFile,
|
||||||
|
outputFilePath,
|
||||||
|
ApkUtils.SigningOptions(
|
||||||
|
keystoreFilePath,
|
||||||
|
keyStorePassword,
|
||||||
|
alias,
|
||||||
|
password,
|
||||||
|
signer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Install
|
||||||
|
|
||||||
|
adbManager?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName))
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
if (purge) {
|
||||||
|
logger.info("Purging temporary files")
|
||||||
|
purge(resourceCachePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the patches to be added to the patcher. The filter is based on the following:
|
||||||
|
*
|
||||||
|
* @param patches The patches to filter.
|
||||||
|
* @return The filtered patches.
|
||||||
|
*/
|
||||||
|
private fun Patcher.filterPatchSelection(patches: PatchSet): PatchSet = buildSet {
|
||||||
|
val packageName = context.packageMetadata.packageName
|
||||||
|
val packageVersion = context.packageMetadata.packageVersion
|
||||||
|
|
||||||
|
patches.forEach patch@{ patch ->
|
||||||
|
val patchName = patch.name!!
|
||||||
|
|
||||||
|
val explicitlyExcluded = excludedPatches.contains(patchName)
|
||||||
|
if (explicitlyExcluded) return@patch logger.info("Excluding $patchName")
|
||||||
|
|
||||||
|
// Make sure the patch is compatible with the supplied APK files package name and version.
|
||||||
|
patch.compatiblePackages?.let { packages ->
|
||||||
|
packages.singleOrNull { it.name == packageName }?.let { `package` ->
|
||||||
|
val matchesVersion = force || `package`.versions?.let {
|
||||||
|
it.any { version -> version == packageVersion }
|
||||||
|
} ?: true
|
||||||
|
|
||||||
|
if (!matchesVersion) return@patch logger.warning(
|
||||||
|
"$patchName is incompatible with version $packageVersion. "
|
||||||
|
+ "This patch is only compatible with version "
|
||||||
|
+ packages.joinToString(";") { pkg ->
|
||||||
|
"${pkg.name}: ${pkg.versions!!.joinToString(", ")}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} ?: return@patch logger.fine(
|
||||||
|
"$patchName is incompatible with $packageName. "
|
||||||
|
+ "This patch is only compatible with "
|
||||||
|
+ packages.joinToString(", ") { `package` -> `package`.name })
|
||||||
|
|
||||||
|
return@let
|
||||||
|
} ?: logger.fine("$patchName has no constraint on packages.")
|
||||||
|
|
||||||
|
// If the patch is implicitly used, it will be only included if [exclusive] is false.
|
||||||
|
val implicitlyIncluded = !exclusive && patch.use
|
||||||
|
// If the patch is explicitly used, it will be included even if [exclusive] is false.
|
||||||
|
val explicitlyIncluded = includedPatches.contains(patchName)
|
||||||
|
|
||||||
|
val included = implicitlyIncluded || explicitlyIncluded
|
||||||
|
if (!included) return@patch logger.info("$patchName excluded") // Case 1.
|
||||||
|
|
||||||
|
logger.fine("Adding $patchName")
|
||||||
|
|
||||||
|
add(patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun purge(resourceCachePath: File) {
|
||||||
|
val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory"
|
||||||
|
else "Failed to purge resource cache directory"
|
||||||
|
logger.info(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
|
import app.revanced.library.adb.AdbManager
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "install", description = ["Install an APK file to devices with the supplied ADB device serials"]
|
||||||
|
)
|
||||||
|
internal object InstallCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(InstallCommand::class.java.name)
|
||||||
|
|
||||||
|
@Parameters(
|
||||||
|
description = ["ADB device serials"], arity = "1..*"
|
||||||
|
)
|
||||||
|
private lateinit var deviceSerials: Array<String>
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-a", "--apk"], description = ["APK file to be installed"], required = true
|
||||||
|
)
|
||||||
|
private lateinit var apk: File
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-m", "--mount"],
|
||||||
|
description = ["Mount the supplied APK file over the app with the supplied package name"],
|
||||||
|
)
|
||||||
|
private var packageName: String? = null
|
||||||
|
|
||||||
|
override fun run() = deviceSerials.forEach { deviceSerial ->
|
||||||
|
try {
|
||||||
|
AdbManager.getAdbManager(deviceSerial, packageName != null).install(AdbManager.Apk(apk, packageName))
|
||||||
|
} catch (e: AdbManager.DeviceNotFoundException) {
|
||||||
|
logger.severe(e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
|
import app.revanced.library.adb.AdbManager
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import picocli.CommandLine.Help.Visibility.ALWAYS
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "uninstall",
|
||||||
|
description = ["Uninstall a patched app from the devices with the supplied ADB device serials"]
|
||||||
|
)
|
||||||
|
internal object UninstallCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(UninstallCommand::class.java.name)
|
||||||
|
|
||||||
|
@Parameters(description = ["ADB device serials"], arity = "1..*")
|
||||||
|
private lateinit var deviceSerials: Array<String>
|
||||||
|
|
||||||
|
@Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true)
|
||||||
|
private lateinit var packageName: String
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-u", "--unmount"],
|
||||||
|
description = ["Uninstall by unmounting the patched APK file"],
|
||||||
|
showDefaultValue = ALWAYS
|
||||||
|
)
|
||||||
|
private var unmount: Boolean = false
|
||||||
|
|
||||||
|
override fun run() = deviceSerials.forEach { deviceSerial ->
|
||||||
|
try {
|
||||||
|
AdbManager.getAdbManager(deviceSerial, unmount).uninstall(packageName)
|
||||||
|
} catch (e: AdbManager.DeviceNotFoundException) {
|
||||||
|
logger.severe(e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "utility",
|
||||||
|
description = ["Commands for utility purposes"],
|
||||||
|
subcommands = [InstallCommand::class, UninstallCommand::class],
|
||||||
|
)
|
||||||
|
internal object UtilityCommand
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.cli.logging
|
|
||||||
|
|
||||||
internal interface CliLogger {
|
|
||||||
fun error(msg: String)
|
|
||||||
fun info(msg: String)
|
|
||||||
fun trace(msg: String)
|
|
||||||
fun warn(msg: String)
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package app.revanced.cli.logging.impl
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand
|
|
||||||
import app.revanced.cli.logging.CliLogger
|
|
||||||
import java.util.logging.Logger
|
|
||||||
import java.util.logging.SimpleFormatter
|
|
||||||
|
|
||||||
internal class DefaultCliLogger(
|
|
||||||
private val logger: Logger = Logger.getLogger(MainCommand::class.java.name),
|
|
||||||
private val errorLogger: Logger = Logger.getLogger(logger.name + "Err")
|
|
||||||
) : CliLogger {
|
|
||||||
|
|
||||||
init {
|
|
||||||
logger.useParentHandlers = false
|
|
||||||
if (logger.handlers.isEmpty()) {
|
|
||||||
logger.addHandler(FlushingStreamHandler(System.out, SimpleFormatter()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun error(msg: String) = errorLogger.severe(msg)
|
|
||||||
override fun info(msg: String) = logger.info(msg)
|
|
||||||
override fun trace(msg: String) = logger.finest(msg)
|
|
||||||
override fun warn(msg: String) = errorLogger.warning(msg)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.cli.logging.impl
|
|
||||||
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.util.logging.Formatter
|
|
||||||
import java.util.logging.LogRecord
|
|
||||||
import java.util.logging.StreamHandler
|
|
||||||
|
|
||||||
internal class FlushingStreamHandler(out: OutputStream, format: Formatter) : StreamHandler(out, format) {
|
|
||||||
override fun publish(record: LogRecord) {
|
|
||||||
super.publish(record)
|
|
||||||
flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package app.revanced.cli.main
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand
|
|
||||||
import picocli.CommandLine
|
|
||||||
|
|
||||||
internal fun main(args: Array<String>) {
|
|
||||||
CommandLine(MainCommand).execute(*args)
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package app.revanced.cli.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.PatcherResult
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.utils.patcher.addPatchesFiltered
|
|
||||||
import app.revanced.utils.patcher.applyPatchesVerbose
|
|
||||||
import app.revanced.utils.patcher.mergeFiles
|
|
||||||
|
|
||||||
internal object Patcher {
|
|
||||||
internal fun start(
|
|
||||||
patcher: app.revanced.patcher.Patcher,
|
|
||||||
allPatches: List<Class<out Patch<Context>>>
|
|
||||||
): PatcherResult {
|
|
||||||
// merge files like necessary integrations
|
|
||||||
patcher.mergeFiles()
|
|
||||||
// add patches, but filter incompatible or excluded patches
|
|
||||||
patcher.addPatchesFiltered(allPatches)
|
|
||||||
// apply patches
|
|
||||||
patcher.applyPatchesVerbose()
|
|
||||||
|
|
||||||
return patcher.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.cli.patcher.logging.impl
|
|
||||||
|
|
||||||
import app.revanced.cli.logging.impl.DefaultCliLogger
|
|
||||||
import java.util.logging.Logger
|
|
||||||
|
|
||||||
internal object PatcherLogger : app.revanced.patcher.logging.Logger{
|
|
||||||
private val logger = DefaultCliLogger(Logger.getLogger(app.revanced.patcher.Patcher::class.java.name))
|
|
||||||
|
|
||||||
override fun error(msg: String) = logger.error(msg)
|
|
||||||
override fun info(msg: String) = logger.info(msg)
|
|
||||||
override fun warn(msg: String)= logger.warn(msg)
|
|
||||||
override fun trace(msg: String)= logger.trace(msg)
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.cli.signing
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import app.revanced.utils.signing.Signer
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object Signing {
|
|
||||||
fun sign(alignedFile: File, signedOutput: File, signingOptions: SigningOptions) {
|
|
||||||
logger.info("Signing ${alignedFile.name} to ${signedOutput.name}")
|
|
||||||
Signer(signingOptions).signApk(alignedFile, signedOutput)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package app.revanced.cli.signing
|
|
||||||
|
|
||||||
data class SigningOptions(
|
|
||||||
val cn: String,
|
|
||||||
val password: String,
|
|
||||||
val keyStoreFilePath: String
|
|
||||||
)
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package app.revanced.utils
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.options
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import cc.ekblad.toml.encodeToString
|
|
||||||
import cc.ekblad.toml.model.TomlValue
|
|
||||||
import cc.ekblad.toml.serialization.from
|
|
||||||
import cc.ekblad.toml.tomlMapper
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
private typealias PatchList = List<Class<out Patch<Context>>>
|
|
||||||
private typealias OptionsMap = MutableMap<String, MutableMap<String, Any>>
|
|
||||||
|
|
||||||
object OptionsLoader {
|
|
||||||
@JvmStatic
|
|
||||||
private val mapper = tomlMapper {}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun init(file: File, patches: PatchList) {
|
|
||||||
if (!file.exists()) file.createNewFile()
|
|
||||||
val map = mapper.decodeWithDefaults(generateDefaults(patches), TomlValue.from(file.toPath()))
|
|
||||||
readAndSet(map, patches)
|
|
||||||
save(map, file)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readAndSet(map: OptionsMap, patches: PatchList) {
|
|
||||||
for ((patchName, options) in map) {
|
|
||||||
val patch = patches.find { it.patchName == patchName } ?: continue
|
|
||||||
val patchOptions = patch.options ?: continue
|
|
||||||
for ((key, value) in options) {
|
|
||||||
if (value == "null") { // backwards compatibility, subject to removal
|
|
||||||
options.remove(key)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
patchOptions[key] = value
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.error("Error while setting option $key for patch $patchName: ${e.message}")
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun save(map: OptionsMap, file: File) {
|
|
||||||
val toml = mapper.encodeToString(map)
|
|
||||||
file.writeText(
|
|
||||||
"""
|
|
||||||
# A list of options for each patch.
|
|
||||||
# This file does not contain all options by default.
|
|
||||||
# Run the CLI with the "--list --with-options" flags to see all available options.
|
|
||||||
# You can also run the CLI with the aforementioned flags and a patch name to see all available options for that patch.
|
|
||||||
# To set an option, add a line with the format "option = value" or set the value if the option already exists.
|
|
||||||
# To reset an option to its default value, delete the line.
|
|
||||||
# To reset all options to their default values, delete this file.
|
|
||||||
#
|
|
||||||
# This file was generated by ReVanced Patcher version ${Patcher.version}.
|
|
||||||
""".trimIndent() + "\n\n$toml"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateDefaults(patches: PatchList) = buildMap {
|
|
||||||
for (patch in patches) {
|
|
||||||
val options = patch.options ?: continue
|
|
||||||
if (!options.iterator().hasNext()) continue
|
|
||||||
put(patch.patchName, buildMap {
|
|
||||||
for (option in options) {
|
|
||||||
put(option.key, option.value ?: continue)
|
|
||||||
}
|
|
||||||
} as MutableMap)
|
|
||||||
}
|
|
||||||
} as MutableMap
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package app.revanced.utils.adb
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import se.vidstige.jadb.JadbConnection
|
|
||||||
import se.vidstige.jadb.JadbDevice
|
|
||||||
import se.vidstige.jadb.managers.PackageManager
|
|
||||||
import java.io.File
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
internal class Adb(
|
|
||||||
private val file: File,
|
|
||||||
private val packageName: String,
|
|
||||||
deviceName: String,
|
|
||||||
private val modeInstall: Boolean = false,
|
|
||||||
private val logging: Boolean = true
|
|
||||||
) {
|
|
||||||
private val device: JadbDevice
|
|
||||||
|
|
||||||
init {
|
|
||||||
device = JadbConnection().devices.let { device -> device.find { it.serial == deviceName } ?: device.first() }
|
|
||||||
?: throw IllegalArgumentException("No such device with name $deviceName")
|
|
||||||
|
|
||||||
if (!modeInstall && device.run("su -h", false) != 0)
|
|
||||||
throw IllegalArgumentException("Root required on $deviceName. Task failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.replacePlaceholder(with: String? = null): String {
|
|
||||||
return this.replace(Constants.PLACEHOLDER, with ?: packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun deploy() {
|
|
||||||
if (modeInstall) {
|
|
||||||
logger.info("Installing without mounting")
|
|
||||||
|
|
||||||
PackageManager(device).install(file)
|
|
||||||
} else {
|
|
||||||
logger.info("Installing by mounting")
|
|
||||||
|
|
||||||
// push patched file
|
|
||||||
device.copy(Constants.PATH_INIT_PUSH, file)
|
|
||||||
|
|
||||||
// create revanced folder path
|
|
||||||
device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}")
|
|
||||||
|
|
||||||
// prepare mounting the apk
|
|
||||||
device.run(Constants.COMMAND_PREPARE_MOUNT_APK.replacePlaceholder())
|
|
||||||
|
|
||||||
// push mount script
|
|
||||||
device.createFile(
|
|
||||||
Constants.PATH_INIT_PUSH,
|
|
||||||
Constants.CONTENT_MOUNT_SCRIPT.replacePlaceholder()
|
|
||||||
)
|
|
||||||
// install mount script
|
|
||||||
device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder())
|
|
||||||
|
|
||||||
// unmount the apk for sanity
|
|
||||||
device.run(Constants.COMMAND_UMOUNT.replacePlaceholder())
|
|
||||||
// mount the apk
|
|
||||||
device.run(Constants.PATH_MOUNT.replacePlaceholder())
|
|
||||||
|
|
||||||
// relaunch app
|
|
||||||
device.run(Constants.COMMAND_RESTART.replacePlaceholder())
|
|
||||||
|
|
||||||
// log the app
|
|
||||||
log()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun uninstall() {
|
|
||||||
logger.info("Uninstalling by unmounting")
|
|
||||||
|
|
||||||
// unmount the apk
|
|
||||||
device.run(Constants.COMMAND_UMOUNT.replacePlaceholder())
|
|
||||||
|
|
||||||
// delete revanced app
|
|
||||||
device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_REVANCED_APP).replacePlaceholder())
|
|
||||||
|
|
||||||
// delete mount script
|
|
||||||
device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_MOUNT).replacePlaceholder())
|
|
||||||
|
|
||||||
logger.info("Finished uninstalling")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun log() {
|
|
||||||
val executor = Executors.newSingleThreadExecutor()
|
|
||||||
val pipe = if (logging) {
|
|
||||||
ProcessBuilder.Redirect.INHERIT
|
|
||||||
} else {
|
|
||||||
ProcessBuilder.Redirect.PIPE
|
|
||||||
}
|
|
||||||
|
|
||||||
val process = device.buildCommand(Constants.COMMAND_LOGCAT.replacePlaceholder())
|
|
||||||
.redirectOutput(pipe)
|
|
||||||
.redirectError(pipe)
|
|
||||||
.useExecutor(executor)
|
|
||||||
.start()
|
|
||||||
|
|
||||||
Thread.sleep(500) // give the app some time to start up.
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
while (device.run("${Constants.COMMAND_PID_OF} $packageName") == 0) {
|
|
||||||
Thread.sleep(1000)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw RuntimeException("An error occurred while monitoring the state of app", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("Stopped logging because the app was closed")
|
|
||||||
process.destroy()
|
|
||||||
executor.shutdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package app.revanced.utils.adb
|
|
||||||
|
|
||||||
import se.vidstige.jadb.JadbDevice
|
|
||||||
import se.vidstige.jadb.RemoteFile
|
|
||||||
import se.vidstige.jadb.ShellProcessBuilder
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder {
|
|
||||||
if (su) {
|
|
||||||
return shellProcessBuilder("su -c \'$command\'")
|
|
||||||
}
|
|
||||||
|
|
||||||
val args = command.split(" ") as ArrayList<String>
|
|
||||||
val cmd = args.removeFirst()
|
|
||||||
|
|
||||||
return shellProcessBuilder(cmd, *args.toTypedArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun JadbDevice.run(command: String, su: Boolean = true): Int {
|
|
||||||
return this.buildCommand(command, su).start().waitFor()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun JadbDevice.copy(targetPath: String, file: File) {
|
|
||||||
push(file, RemoteFile(targetPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun JadbDevice.createFile(targetFile: String, content: String) {
|
|
||||||
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package app.revanced.utils.adb
|
|
||||||
|
|
||||||
internal object Constants {
|
|
||||||
// template placeholder to replace a string in commands
|
|
||||||
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
|
|
||||||
|
|
||||||
// utility commands
|
|
||||||
private const val COMMAND_CHMOD_MOUNT = "chmod +x"
|
|
||||||
internal const val COMMAND_PID_OF = "pidof -s"
|
|
||||||
internal const val COMMAND_CREATE_DIR = "mkdir -p"
|
|
||||||
internal const val COMMAND_LOGCAT = "logcat -c && logcat | grep AndroidRuntime"
|
|
||||||
internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | xargs am start -n && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)"
|
|
||||||
|
|
||||||
// default mount file name
|
|
||||||
private const val NAME_MOUNT_SCRIPT = "mount_revanced_$PLACEHOLDER.sh"
|
|
||||||
|
|
||||||
// initial directory to push files to via adb push
|
|
||||||
internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete"
|
|
||||||
|
|
||||||
// revanced path
|
|
||||||
internal const val PATH_REVANCED = "/data/adb/revanced/"
|
|
||||||
|
|
||||||
// revanced apk path
|
|
||||||
internal const val PATH_REVANCED_APP = "$PATH_REVANCED$PLACEHOLDER.apk"
|
|
||||||
|
|
||||||
// delete command
|
|
||||||
internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER"
|
|
||||||
|
|
||||||
// mount script path
|
|
||||||
internal const val PATH_MOUNT = "/data/adb/service.d/$NAME_MOUNT_SCRIPT"
|
|
||||||
|
|
||||||
// move to revanced apk path & set permissions
|
|
||||||
internal const val COMMAND_PREPARE_MOUNT_APK =
|
|
||||||
"base_path=\"$PATH_REVANCED_APP\" && mv $PATH_INIT_PUSH ${'$'}base_path && chmod 644 ${'$'}base_path && chown system:system ${'$'}base_path && chcon u:object_r:apk_data_file:s0 ${'$'}base_path"
|
|
||||||
|
|
||||||
// unmount command
|
|
||||||
internal const val COMMAND_UMOUNT =
|
|
||||||
"grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done"
|
|
||||||
|
|
||||||
// install mount script & set permissions
|
|
||||||
internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && $COMMAND_CHMOD_MOUNT $PATH_MOUNT"
|
|
||||||
|
|
||||||
// mount script
|
|
||||||
internal val CONTENT_MOUNT_SCRIPT =
|
|
||||||
"""
|
|
||||||
#!/system/bin/sh
|
|
||||||
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
|
|
||||||
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
|
|
||||||
while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
|
|
||||||
|
|
||||||
base_path="$PATH_REVANCED_APP"
|
|
||||||
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
|
|
||||||
|
|
||||||
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
|
|
||||||
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package app.revanced.utils.patcher
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand
|
|
||||||
import app.revanced.cli.command.MainCommand.args
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.include
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
fun Patcher.addPatchesFiltered(allPatches: List<Class<out Patch<Context>>>) {
|
|
||||||
val packageName = this.context.packageMetadata.packageName
|
|
||||||
val packageVersion = this.context.packageMetadata.packageVersion
|
|
||||||
|
|
||||||
val includedPatches = mutableListOf<Class<out Patch<Context>>>()
|
|
||||||
allPatches.forEach patchLoop@{ patch ->
|
|
||||||
val compatiblePackages = patch.compatiblePackages
|
|
||||||
val patchName = patch.patchName
|
|
||||||
val args = MainCommand.args.patchArgs?.patchingArgs!!
|
|
||||||
|
|
||||||
val prefix = "Skipping $patchName"
|
|
||||||
|
|
||||||
if (compatiblePackages == null) logger.trace("$patchName: No constraint on packages.")
|
|
||||||
else {
|
|
||||||
if (!compatiblePackages.any { it.name == packageName }) {
|
|
||||||
logger.trace("$prefix: Incompatible with $packageName. This patch is only compatible with ${
|
|
||||||
compatiblePackages.joinToString(
|
|
||||||
", "
|
|
||||||
) { it.name }
|
|
||||||
}")
|
|
||||||
return@patchLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) {
|
|
||||||
val compatibleWith = compatiblePackages.joinToString(";") { _package ->
|
|
||||||
"${_package.name}: ${_package.versions.joinToString(", ")}"
|
|
||||||
}
|
|
||||||
logger.warn("$prefix: Incompatible with version $packageVersion. This patch is only compatible with $compatibleWith")
|
|
||||||
return@patchLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.excludedPatches.contains(patchName)) {
|
|
||||||
logger.info("$prefix: Manually excluded")
|
|
||||||
return@patchLoop
|
|
||||||
} else if ((!patch.include || args.exclusive) && !args.includedPatches.contains(patchName)) {
|
|
||||||
logger.info("$prefix: Excluded by default")
|
|
||||||
return@patchLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trace("Adding $patchName")
|
|
||||||
includedPatches.add(patch)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addPatches(includedPatches)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Patcher.applyPatchesVerbose() {
|
|
||||||
this.executePatches().forEach { (patch, result) ->
|
|
||||||
if (result.isSuccess) {
|
|
||||||
logger.info("$patch succeeded")
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
logger.error("$patch failed:")
|
|
||||||
result.exceptionOrNull()!!.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Patcher.mergeFiles() {
|
|
||||||
this.addIntegrations(args.patchArgs?.patchingArgs!!.mergeFiles) { file ->
|
|
||||||
logger.info("Merging $file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package app.revanced.utils.signing
|
|
||||||
|
|
||||||
import app.revanced.cli.command.MainCommand.logger
|
|
||||||
import app.revanced.cli.signing.SigningOptions
|
|
||||||
import com.android.apksig.ApkSigner
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
|
||||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
|
||||||
import org.bouncycastle.operator.ContentSigner
|
|
||||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.math.BigInteger
|
|
||||||
import java.security.*
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class Signer(
|
|
||||||
private val signingOptions: SigningOptions
|
|
||||||
) {
|
|
||||||
private val passwordCharArray = signingOptions.password.toCharArray()
|
|
||||||
private fun newKeystore(out: File) {
|
|
||||||
val (publicKey, privateKey) = createKey()
|
|
||||||
val privateKS = KeyStore.getInstance("BKS", "BC")
|
|
||||||
privateKS.load(null, passwordCharArray)
|
|
||||||
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
|
|
||||||
privateKS.store(FileOutputStream(out), passwordCharArray)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createKey(): Pair<X509Certificate, PrivateKey> {
|
|
||||||
val gen = KeyPairGenerator.getInstance("RSA")
|
|
||||||
gen.initialize(2048)
|
|
||||||
val pair = gen.generateKeyPair()
|
|
||||||
var serialNumber: BigInteger
|
|
||||||
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
|
||||||
val x500Name = X500Name("CN=${signingOptions.cn}")
|
|
||||||
val builder = X509v3CertificateBuilder(
|
|
||||||
x500Name,
|
|
||||||
serialNumber,
|
|
||||||
Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L),
|
|
||||||
Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 366L * 30L),
|
|
||||||
Locale.ENGLISH,
|
|
||||||
x500Name,
|
|
||||||
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
|
|
||||||
)
|
|
||||||
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
|
|
||||||
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signApk(input: File, output: File) {
|
|
||||||
Security.addProvider(BouncyCastleProvider())
|
|
||||||
|
|
||||||
// TODO: keystore should be saved securely
|
|
||||||
val ks = File(signingOptions.keyStoreFilePath)
|
|
||||||
if (!ks.exists()) newKeystore(ks) else {
|
|
||||||
logger.info("Found existing keystore: ${ks.name}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
|
||||||
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
|
|
||||||
val alias = keyStore.aliases().nextElement()
|
|
||||||
|
|
||||||
val config = ApkSigner.SignerConfig.Builder(
|
|
||||||
signingOptions.cn,
|
|
||||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
|
||||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
|
||||||
).build()
|
|
||||||
|
|
||||||
val signer = ApkSigner.Builder(listOf(config))
|
|
||||||
signer.setCreatedBy(signingOptions.cn)
|
|
||||||
signer.setInputApk(input)
|
|
||||||
signer.setOutputApk(output)
|
|
||||||
|
|
||||||
signer.build().sign()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package app.revanced.utils.signing.align
|
|
||||||
|
|
||||||
import app.revanced.utils.signing.align.zip.structures.ZipEntry
|
|
||||||
|
|
||||||
internal object ZipAligner {
|
|
||||||
private const val DEFAULT_ALIGNMENT = 4
|
|
||||||
private const val LIBRARY_ALIGNMENT = 4096
|
|
||||||
|
|
||||||
fun getEntryAlignment(entry: ZipEntry): Int? =
|
|
||||||
if (entry.compression.toUInt() != 0u) null else if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.utils.signing.align.zip
|
|
||||||
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.io.DataOutput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
fun UInt.toLittleEndian() =
|
|
||||||
(((this.toInt() and 0xff000000.toInt()) shr 24) or ((this.toInt() and 0x00ff0000) shr 8) or ((this.toInt() and 0x0000ff00) shl 8) or (this.toInt() shl 24)).toUInt()
|
|
||||||
|
|
||||||
fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
|
|
||||||
|
|
||||||
fun UInt.toBigEndian() = (((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
|
|
||||||
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)).toUInt()
|
|
||||||
|
|
||||||
fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
|
|
||||||
|
|
||||||
fun ByteBuffer.getUShort() = this.getShort().toUShort()
|
|
||||||
fun ByteBuffer.getUInt() = this.getInt().toUInt()
|
|
||||||
|
|
||||||
fun ByteBuffer.putUShort(ushort: UShort) = this.putShort(ushort.toShort())
|
|
||||||
fun ByteBuffer.putUInt(uint: UInt) = this.putInt(uint.toInt())
|
|
||||||
|
|
||||||
fun DataInput.readUShort() = this.readShort().toUShort()
|
|
||||||
fun DataInput.readUInt() = this.readInt().toUInt()
|
|
||||||
|
|
||||||
fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
|
|
||||||
fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
|
|
||||||
|
|
||||||
fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
|
|
||||||
fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
|
|
||||||
|
|
||||||
fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
|
|
||||||
fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
package app.revanced.utils.signing.align.zip
|
|
||||||
|
|
||||||
import app.revanced.utils.signing.align.zip.structures.ZipEndRecord
|
|
||||||
import app.revanced.utils.signing.align.zip.structures.ZipEntry
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.RandomAccessFile
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
import java.util.zip.CRC32
|
|
||||||
import java.util.zip.Deflater
|
|
||||||
|
|
||||||
class ZipFile(file: File) : Closeable {
|
|
||||||
var entries: MutableList<ZipEntry> = mutableListOf()
|
|
||||||
|
|
||||||
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
|
|
||||||
private var CDNeedsRewrite = false
|
|
||||||
|
|
||||||
private val compressionLevel = 5
|
|
||||||
|
|
||||||
init {
|
|
||||||
//if file isn't empty try to load entries
|
|
||||||
if (file.length() > 0) {
|
|
||||||
val endRecord = findEndRecord()
|
|
||||||
|
|
||||||
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries)
|
|
||||||
throw IllegalArgumentException("Multi-file archives are not supported")
|
|
||||||
|
|
||||||
entries = readEntries(endRecord).toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
//seek back to start for writing
|
|
||||||
filePointer.seek(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findEndRecord(): ZipEndRecord {
|
|
||||||
//look from end to start since end record is at the end
|
|
||||||
for (i in filePointer.length() - 1 downTo 0) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
//possible beginning of signature
|
|
||||||
if (filePointer.readByte() == 0x50.toByte()) {
|
|
||||||
//seek back to get the full int
|
|
||||||
filePointer.seek(i)
|
|
||||||
val possibleSignature = filePointer.readUIntLE()
|
|
||||||
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
return ZipEndRecord.fromECD(filePointer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Exception("Couldn't find end record")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readEntries(endRecord: ZipEndRecord): List<ZipEntry> {
|
|
||||||
filePointer.seek(endRecord.centralDirectoryStartOffset.toLong())
|
|
||||||
|
|
||||||
val numberOfEntries = endRecord.diskEntries.toInt()
|
|
||||||
|
|
||||||
return buildList(numberOfEntries) {
|
|
||||||
for (i in 1..numberOfEntries) {
|
|
||||||
add(
|
|
||||||
ZipEntry.fromCDE(filePointer).also
|
|
||||||
{
|
|
||||||
//for some reason the local extra field can be different from the central one
|
|
||||||
it.readLocalExtra(
|
|
||||||
filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
it.localHeaderOffset.toLong() + 28,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeCD() {
|
|
||||||
val CDStart = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
entries.forEach {
|
|
||||||
filePointer.channel.write(it.toCDE())
|
|
||||||
}
|
|
||||||
|
|
||||||
val entriesCount = entries.size.toUShort()
|
|
||||||
|
|
||||||
val endRecord = ZipEndRecord(
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
entriesCount,
|
|
||||||
entriesCount,
|
|
||||||
filePointer.channel.position().toUInt() - CDStart,
|
|
||||||
CDStart,
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
filePointer.channel.write(endRecord.toECD())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntry(entry: ZipEntry, data: ByteBuffer) {
|
|
||||||
CDNeedsRewrite = true
|
|
||||||
|
|
||||||
entry.localHeaderOffset = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
filePointer.channel.write(entry.toLFH())
|
|
||||||
filePointer.channel.write(data)
|
|
||||||
|
|
||||||
entries.add(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addEntryCompressData(entry: ZipEntry, data: ByteArray) {
|
|
||||||
val compressor = Deflater(compressionLevel, true)
|
|
||||||
compressor.setInput(data)
|
|
||||||
compressor.finish()
|
|
||||||
|
|
||||||
val uncompressedSize = data.size
|
|
||||||
val compressedData =
|
|
||||||
ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger
|
|
||||||
|
|
||||||
val compressedDataLength = compressor.deflate(compressedData)
|
|
||||||
val compressedBuffer =
|
|
||||||
ByteBuffer.wrap(compressedData.take(compressedDataLength).toByteArray())
|
|
||||||
|
|
||||||
compressor.end()
|
|
||||||
|
|
||||||
val crc = CRC32()
|
|
||||||
crc.update(data)
|
|
||||||
|
|
||||||
entry.compression = 8u //deflate compression
|
|
||||||
entry.uncompressedSize = uncompressedSize.toUInt()
|
|
||||||
entry.compressedSize = compressedDataLength.toUInt()
|
|
||||||
entry.crc32 = crc.value.toUInt()
|
|
||||||
|
|
||||||
addEntry(entry, compressedBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
|
|
||||||
alignment?.let {
|
|
||||||
//calculate where data would end up
|
|
||||||
val dataOffset = filePointer.filePointer + entry.LFHSize
|
|
||||||
|
|
||||||
val mod = dataOffset % alignment
|
|
||||||
|
|
||||||
//wrong alignment
|
|
||||||
if (mod != 0L) {
|
|
||||||
//add padding at end of extra field
|
|
||||||
entry.localExtraField =
|
|
||||||
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addEntry(entry, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDataForEntry(entry: ZipEntry): ByteBuffer {
|
|
||||||
return filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
entry.dataOffset.toLong(),
|
|
||||||
entry.compressedSize.toLong()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) {
|
|
||||||
for (entry in file.entries) {
|
|
||||||
if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates
|
|
||||||
|
|
||||||
val data = file.getDataForEntry(entry)
|
|
||||||
addEntryCopyData(entry, data, entryAlignment(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (CDNeedsRewrite) writeCD()
|
|
||||||
filePointer.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package app.revanced.utils.signing.align.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.utils.signing.align.zip.putUInt
|
|
||||||
import app.revanced.utils.signing.align.zip.putUShort
|
|
||||||
import app.revanced.utils.signing.align.zip.readUIntLE
|
|
||||||
import app.revanced.utils.signing.align.zip.readUShortLE
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
data class ZipEndRecord(
|
|
||||||
val diskNumber: UShort,
|
|
||||||
val startingDiskNumber: UShort,
|
|
||||||
val diskEntries: UShort,
|
|
||||||
val totalEntries: UShort,
|
|
||||||
val centralDirectorySize: UInt,
|
|
||||||
val centralDirectoryStartOffset: UInt,
|
|
||||||
val fileComment: String,
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ECD_HEADER_SIZE = 22
|
|
||||||
const val ECD_SIGNATURE = 0x06054b50u
|
|
||||||
|
|
||||||
fun fromECD(input: DataInput): ZipEndRecord {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != ECD_SIGNATURE)
|
|
||||||
throw IllegalArgumentException("Input doesn't start with end record signature")
|
|
||||||
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val startingDiskNumber = input.readUShortLE()
|
|
||||||
val diskEntries = input.readUShortLE()
|
|
||||||
val totalEntries = input.readUShortLE()
|
|
||||||
val centralDirectorySize = input.readUIntLE()
|
|
||||||
val centralDirectoryStartOffset = input.readUIntLE()
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
|
|
||||||
if (fileCommentLength > 0u) {
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ZipEndRecord(
|
|
||||||
diskNumber,
|
|
||||||
startingDiskNumber,
|
|
||||||
diskEntries,
|
|
||||||
totalEntries,
|
|
||||||
centralDirectorySize,
|
|
||||||
centralDirectoryStartOffset,
|
|
||||||
fileComment
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toECD(): ByteBuffer {
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(ECD_SIGNATURE)
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(startingDiskNumber)
|
|
||||||
buffer.putUShort(diskEntries)
|
|
||||||
buffer.putUShort(totalEntries)
|
|
||||||
buffer.putUInt(centralDirectorySize)
|
|
||||||
buffer.putUInt(centralDirectoryStartOffset)
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
package app.revanced.utils.signing.align.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.utils.signing.align.zip.*
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
data class ZipEntry(
|
|
||||||
val version: UShort,
|
|
||||||
val versionNeeded: UShort,
|
|
||||||
val flags: UShort,
|
|
||||||
var compression: UShort,
|
|
||||||
val modificationTime: UShort,
|
|
||||||
val modificationDate: UShort,
|
|
||||||
var crc32: UInt,
|
|
||||||
var compressedSize: UInt,
|
|
||||||
var uncompressedSize: UInt,
|
|
||||||
val diskNumber: UShort,
|
|
||||||
val internalAttributes: UShort,
|
|
||||||
val externalAttributes: UInt,
|
|
||||||
var localHeaderOffset: UInt,
|
|
||||||
val fileName: String,
|
|
||||||
val extraField: ByteArray,
|
|
||||||
val fileComment: String,
|
|
||||||
var localExtraField: ByteArray = ByteArray(0), //separate for alignment
|
|
||||||
) {
|
|
||||||
val LFHSize: Int
|
|
||||||
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
|
|
||||||
|
|
||||||
val dataOffset: UInt
|
|
||||||
get() = localHeaderOffset + LFHSize.toUInt()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val CDE_HEADER_SIZE = 46
|
|
||||||
const val CDE_SIGNATURE = 0x02014b50u
|
|
||||||
|
|
||||||
const val LFH_HEADER_SIZE = 30
|
|
||||||
const val LFH_SIGNATURE = 0x04034b50u
|
|
||||||
|
|
||||||
fun createWithName(fileName: String): ZipEntry {
|
|
||||||
return ZipEntry(
|
|
||||||
0x1403u, //made by unix, version 20
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0x0821u, //seems to be static time google uses, no idea
|
|
||||||
0x0221u, //same as above
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
fileName,
|
|
||||||
ByteArray(0),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromCDE(input: DataInput): ZipEntry {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != CDE_SIGNATURE)
|
|
||||||
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
|
|
||||||
|
|
||||||
val version = input.readUShortLE()
|
|
||||||
val versionNeeded = input.readUShortLE()
|
|
||||||
var flags = input.readUShortLE()
|
|
||||||
val compression = input.readUShortLE()
|
|
||||||
val modificationTime = input.readUShortLE()
|
|
||||||
val modificationDate = input.readUShortLE()
|
|
||||||
val crc32 = input.readUIntLE()
|
|
||||||
val compressedSize = input.readUIntLE()
|
|
||||||
val uncompressedSize = input.readUIntLE()
|
|
||||||
val fileNameLength = input.readUShortLE()
|
|
||||||
var fileName = ""
|
|
||||||
val extraFieldLength = input.readUShortLE()
|
|
||||||
val extraField = ByteArray(extraFieldLength.toInt())
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val internalAttributes = input.readUShortLE()
|
|
||||||
val externalAttributes = input.readUIntLE()
|
|
||||||
val localHeaderOffset = input.readUIntLE()
|
|
||||||
|
|
||||||
val variableFieldsLength =
|
|
||||||
fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
|
|
||||||
|
|
||||||
if (variableFieldsLength > 0) {
|
|
||||||
val fileNameBytes = ByteArray(fileNameLength.toInt())
|
|
||||||
input.readFully(fileNameBytes)
|
|
||||||
fileName = fileNameBytes.toString(Charsets.UTF_8)
|
|
||||||
|
|
||||||
input.readFully(extraField)
|
|
||||||
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = (flags and 0b1000u.inv()
|
|
||||||
.toUShort()) //disable data descriptor flag as they are not used
|
|
||||||
|
|
||||||
return ZipEntry(
|
|
||||||
version,
|
|
||||||
versionNeeded,
|
|
||||||
flags,
|
|
||||||
compression,
|
|
||||||
modificationTime,
|
|
||||||
modificationDate,
|
|
||||||
crc32,
|
|
||||||
compressedSize,
|
|
||||||
uncompressedSize,
|
|
||||||
diskNumber,
|
|
||||||
internalAttributes,
|
|
||||||
externalAttributes,
|
|
||||||
localHeaderOffset,
|
|
||||||
fileName,
|
|
||||||
extraField,
|
|
||||||
fileComment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readLocalExtra(buffer: ByteBuffer) {
|
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN)
|
|
||||||
localExtraField = ByteArray(buffer.getUShort().toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toLFH(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer = ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(LFH_SIGNATURE)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(localExtraField.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(localExtraField)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toCDE(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer =
|
|
||||||
ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(CDE_SIGNATURE)
|
|
||||||
buffer.putUShort(version)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(extraField.size.toUShort())
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(internalAttributes)
|
|
||||||
buffer.putUInt(externalAttributes)
|
|
||||||
buffer.putUInt(localHeaderOffset)
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(extraField)
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
src/main/resources/app/revanced/cli/version.properties
Normal file
1
src/main/resources/app/revanced/cli/version.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
version=${projectVersion}
|
||||||
Reference in New Issue
Block a user