mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-21 02:03:58 +00:00
Compare commits
170 Commits
arsclib-re
...
v18.0.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b52e4b0f9 | ||
|
|
098c2c1efa | ||
|
|
64343e5a7c | ||
|
|
0caf6caeb9 | ||
|
|
55f6c2a9fc | ||
|
|
c6095bc38a | ||
|
|
09cd6aa568 | ||
|
|
ebbaafb78e | ||
|
|
e6de90d300 | ||
|
|
c7922e90d0 | ||
|
|
c1f4c0445a | ||
|
|
caa634fac6 | ||
|
|
f28bfe0dbd | ||
|
|
155e787ff4 | ||
|
|
c38f0ef42a | ||
|
|
4456031459 | ||
|
|
5fb59a227f | ||
|
|
d9fb241d57 | ||
|
|
642c4ea97e | ||
|
|
8c8a251626 | ||
|
|
5953d6cfb5 | ||
|
|
a1962fe600 | ||
|
|
77dbee3d6a | ||
|
|
cb5e39d73e | ||
|
|
38ef2f470a | ||
|
|
129d84e108 | ||
|
|
affeba76b8 | ||
|
|
6059d3ca26 | ||
|
|
444dee5a16 | ||
|
|
d314466ce2 | ||
|
|
fdaf9c21c8 | ||
|
|
06c2b76f11 | ||
|
|
3896b30738 | ||
|
|
2c4b88e1a0 | ||
|
|
dfc7e1596b | ||
|
|
f590436399 | ||
|
|
cbfb9ba02f | ||
|
|
b4cfe80ad5 | ||
|
|
b37906fa35 | ||
|
|
356f1f1553 | ||
|
|
e882af74ee | ||
|
|
46875fb28e | ||
|
|
417c3e4234 | ||
|
|
6d2c28807b | ||
|
|
4d6e08a650 | ||
|
|
5cebc1fd30 | ||
|
|
ac61731dc6 | ||
|
|
9e4ffabd5c | ||
|
|
3f410bd39f | ||
|
|
d51bc32e37 | ||
|
|
b7f6aa94cc | ||
|
|
ff965e6953 | ||
|
|
468d5d7421 | ||
|
|
fc95b28c49 | ||
|
|
69184187d9 | ||
|
|
a802d0df46 | ||
|
|
8de30633ae | ||
|
|
a1fbb7990f | ||
|
|
aa71146b1b | ||
|
|
9fdb8f087f | ||
|
|
670f0153de | ||
|
|
1d7aeca696 | ||
|
|
4e7811ea07 | ||
|
|
e11283744a | ||
|
|
91cdfd53ef | ||
|
|
bc7d6b9941 | ||
|
|
6b1e0a1656 | ||
|
|
72c9eb2129 | ||
|
|
4bc4b0dc01 | ||
|
|
637d48746f | ||
|
|
9a109c129b | ||
|
|
d49e4ee5ea | ||
|
|
30f0ea29a3 | ||
|
|
49930f6565 | ||
|
|
909d89fa8d | ||
|
|
81d1d7f544 | ||
|
|
67b7dff67a | ||
|
|
4b76d19596 | ||
|
|
080fbe9feb | ||
|
|
d3721229bf | ||
|
|
86c1c9c772 | ||
|
|
c299817193 | ||
|
|
fcc1de45ed | ||
|
|
a29931f2ec | ||
|
|
3fc6a139ee | ||
|
|
4dd04975d9 | ||
|
|
3b4db3ddb7 | ||
|
|
c4a7117ee8 | ||
|
|
b4e900fde8 | ||
|
|
9818d730e4 | ||
|
|
11a3378659 | ||
|
|
1bb05f22d3 | ||
|
|
26b70554c4 | ||
|
|
93b29d2e83 | ||
|
|
072986374a | ||
|
|
2c590d212a | ||
|
|
6cc863efb3 | ||
|
|
b832812767 | ||
|
|
c44558cacd | ||
|
|
6d83a720cd | ||
|
|
8d0dd9c448 | ||
|
|
64020eec49 | ||
|
|
4dedfb85cb | ||
|
|
55d694579a | ||
|
|
86db64edff | ||
|
|
983563efb6 | ||
|
|
37abb2db99 | ||
|
|
5ba0b47e60 | ||
|
|
e8f2087a6f | ||
|
|
6ce99f5cdf | ||
|
|
13c0c9cdd3 | ||
|
|
58ffdb60d7 | ||
|
|
ba56a6a2ee | ||
|
|
ccccf5b1d2 | ||
|
|
b507ac0a54 | ||
|
|
e985676c2d | ||
|
|
f7f4ba6c55 | ||
|
|
4292f43814 | ||
|
|
30bd4fd9fe | ||
|
|
76de39369d | ||
|
|
88a703ce36 | ||
|
|
5938f6b7ea | ||
|
|
5c0c0d6c37 | ||
|
|
0f15077225 | ||
|
|
273dd8d388 | ||
|
|
1795f376ef | ||
|
|
e7360a7692 | ||
|
|
e1fc86934f | ||
|
|
6b8977f178 | ||
|
|
12c6c73de0 | ||
|
|
db62a1607b | ||
|
|
58bb879ef5 | ||
|
|
254912438a | ||
|
|
0e48918bcc | ||
|
|
783ccf8529 | ||
|
|
8fb2f2dc1d | ||
|
|
2a8cc283c7 | ||
|
|
433fe3af9f | ||
|
|
c2d89c622e | ||
|
|
02d6ff15fe | ||
|
|
f2cb7ee7df | ||
|
|
a2ac44dcc1 | ||
|
|
3cf9d74efa | ||
|
|
d5f89a903f | ||
|
|
496c2242bc | ||
|
|
98fbff87df | ||
|
|
ddb51a1c45 | ||
|
|
8df1155215 | ||
|
|
53f2a61409 | ||
|
|
746544f9d5 | ||
|
|
c65c3df11c | ||
|
|
b29b8f12b3 | ||
|
|
d6945677c4 | ||
|
|
aedf4aea08 | ||
|
|
dc28d414dc | ||
|
|
9755bab298 | ||
|
|
fae4029cfc | ||
|
|
1790f0d706 | ||
|
|
0ba2c51676 | ||
|
|
03cd97b49c | ||
|
|
16a162c1dd | ||
|
|
c9bbcf2bf2 | ||
|
|
86e1bf6078 | ||
|
|
1bca84ef0b | ||
|
|
e0f8e1b71a | ||
|
|
416d69142f | ||
|
|
426807aeaa | ||
|
|
90cb075a97 | ||
|
|
ac2ca8fbd3 | ||
|
|
69e4a49065 |
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:
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ 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
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew clean --no-daemon
|
run: ./gradlew build clean --no-daemon
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install
|
run: npm install
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
540
CHANGELOG.md
540
CHANGELOG.md
@@ -1,3 +1,543 @@
|
|||||||
|
# [18.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v18.0.0-dev.1...v18.0.0-dev.2) (2023-10-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Change `PatchOption` from abstract to open class ([09cd6aa](https://github.com/ReVanced/revanced-patcher/commit/09cd6aa568988dd5241bfa6a2e12b7926a7b0683))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add function to reset options to their default value ([ebbaafb](https://github.com/ReVanced/revanced-patcher/commit/ebbaafb78e88f34faeafe9ff8532afe29231bd79))
|
||||||
|
* Add function to reset options to their default value ([e6de90d](https://github.com/ReVanced/revanced-patcher/commit/e6de90d300bc9c82ca1696cb898db04c65a1cd5b))
|
||||||
|
* Add getter for default option value ([c7922e9](https://github.com/ReVanced/revanced-patcher/commit/c7922e90d0c6ae83f513611c706ebea33c1a2b63))
|
||||||
|
* Name patch option value validator property correctly ([caa634f](https://github.com/ReVanced/revanced-patcher/commit/caa634fac6d7a717f54e3b015827c8858fd637b9))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This gets rid of the existing basic implementations of the `PatchOptions` type and moves extension functions.
|
||||||
|
* This changes the getter name of the property.
|
||||||
|
|
||||||
|
# [18.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v17.0.1-dev.1...v18.0.0-dev.1) (2023-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Remove patch annotation processor ([4456031](https://github.com/ReVanced/revanced-patcher/commit/445603145979a6f67823a79f9d6cd140299cff37))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* Various patch constructor signatures have changed.
|
||||||
|
|
||||||
|
## [17.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v17.0.0...v17.0.1-dev.1) (2023-10-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Run the garbage collector after writing dex files ([d9fb241](https://github.com/ReVanced/revanced-patcher/commit/d9fb241d57b0c4340130c0e5900250e66730ea56))
|
||||||
|
|
||||||
|
# [17.0.0](https://github.com/ReVanced/revanced-patcher/compare/v16.0.2...v17.0.0) (2023-10-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add option to use single threaded writer for dex files ([77dbee3](https://github.com/ReVanced/revanced-patcher/commit/77dbee3d6ae7b8dc77543e036624daa68ae63504))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This commit gets rid of deprecated constructors.
|
||||||
|
|
||||||
|
# [17.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.2...v17.0.0-dev.1) (2023-10-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add option to use single threaded writer for dex files ([77dbee3](https://github.com/ReVanced/revanced-patcher/commit/77dbee3d6ae7b8dc77543e036624daa68ae63504))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This commit gets rid of deprecated constructors.
|
||||||
|
|
||||||
|
## [16.0.2](https://github.com/ReVanced/revanced-patcher/compare/v16.0.1...v16.0.2) (2023-10-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Use a map to merge integrations classes ([6059d3c](https://github.com/ReVanced/revanced-patcher/commit/6059d3ca2685cb659023b171b95d4b9d279c6e53))
|
||||||
|
|
||||||
|
## [16.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.1...v16.0.2-dev.1) (2023-10-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Use a map to merge integrations classes ([6059d3c](https://github.com/ReVanced/revanced-patcher/commit/6059d3ca2685cb659023b171b95d4b9d279c6e53))
|
||||||
|
|
||||||
|
## [16.0.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.0...v16.0.1) (2023-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Merge integrations when required ([06c2b76](https://github.com/ReVanced/revanced-patcher/commit/06c2b76f11ac1bfe43d51d54d425e7577ecefdf6))
|
||||||
|
|
||||||
|
## [16.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v16.0.0...v16.0.1-dev.1) (2023-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Merge integrations when required ([06c2b76](https://github.com/ReVanced/revanced-patcher/commit/06c2b76f11ac1bfe43d51d54d425e7577ecefdf6))
|
||||||
|
|
||||||
|
# [16.0.0](https://github.com/ReVanced/revanced-patcher/compare/v15.0.3...v16.0.0) (2023-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Use correct super class type ([f590436](https://github.com/ReVanced/revanced-patcher/commit/f590436399f6385c51cea54618251b5d823c31f9))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the super classes of some `PatchOptionException` classes
|
||||||
|
|
||||||
|
# [16.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.3...v16.0.0-dev.1) (2023-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Use correct super class type ([f590436](https://github.com/ReVanced/revanced-patcher/commit/f590436399f6385c51cea54618251b5d823c31f9))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the super classes of some `PatchOptionException` classes
|
||||||
|
|
||||||
|
## [15.0.3](https://github.com/ReVanced/revanced-patcher/compare/v15.0.2...v15.0.3) (2023-10-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fix SMALI compilation on devices with RTL language ([#242](https://github.com/ReVanced/revanced-patcher/issues/242)) ([356f1f1](https://github.com/ReVanced/revanced-patcher/commit/356f1f155348347a8f318a2e024716ebf4fec99b))
|
||||||
|
|
||||||
|
## [15.0.3-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.2...v15.0.3-dev.1) (2023-09-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fix SMALI compilation on devices with RTL language ([#242](https://github.com/ReVanced/revanced-patcher/issues/242)) ([356f1f1](https://github.com/ReVanced/revanced-patcher/commit/356f1f155348347a8f318a2e024716ebf4fec99b))
|
||||||
|
|
||||||
|
## [15.0.2](https://github.com/ReVanced/revanced-patcher/compare/v15.0.1...v15.0.2) (2023-09-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Do not unnecessary resolve fingeprints twice ([#241](https://github.com/ReVanced/revanced-patcher/issues/241)) ([4d6e08a](https://github.com/ReVanced/revanced-patcher/commit/4d6e08a650dde6ec2e18611c5db1ab92b9a61dd1))
|
||||||
|
|
||||||
|
## [15.0.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.1...v15.0.2-dev.1) (2023-09-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Do not unnecessary resolve fingeprints twice ([#241](https://github.com/ReVanced/revanced-patcher/issues/241)) ([4d6e08a](https://github.com/ReVanced/revanced-patcher/commit/4d6e08a650dde6ec2e18611c5db1ab92b9a61dd1))
|
||||||
|
|
||||||
|
## [15.0.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0...v15.0.1) (2023-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Remove log management ([d51bc32](https://github.com/ReVanced/revanced-patcher/commit/d51bc32e37a865194c3825471085b3ccf8c78421))
|
||||||
|
|
||||||
|
## [15.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0...v15.0.1-dev.1) (2023-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Remove log management ([d51bc32](https://github.com/ReVanced/revanced-patcher/commit/d51bc32e37a865194c3825471085b3ccf8c78421))
|
||||||
|
|
||||||
|
# [15.0.0](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0) (2023-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
||||||
|
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
||||||
|
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
||||||
|
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
||||||
|
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
||||||
|
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
||||||
|
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
||||||
|
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
||||||
|
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
||||||
|
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
||||||
|
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
||||||
|
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
||||||
|
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
||||||
|
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
||||||
|
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
||||||
|
|
||||||
|
|
||||||
|
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
||||||
|
* feat!: Add patch annotation processor ([3fc6a13](https://github.com/ReVanced/revanced-patcher/commit/3fc6a139eef67237c116fb4e3e29bf9542d3a981))
|
||||||
|
* feat!: Remove patch annotations ([3b4db3d](https://github.com/ReVanced/revanced-patcher/commit/3b4db3ddb72cdcee8af2f787eadf58eeb37543de))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add patch annotation processor ([#231](https://github.com/ReVanced/revanced-patcher/issues/231)) ([a29931f](https://github.com/ReVanced/revanced-patcher/commit/a29931f2ec0a666dba209b54855425d9dc2f4462))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This gets rid of the public constructor.
|
||||||
|
* `PatchBundleLoader` is not a map anymore
|
||||||
|
* This renames packages and the Maven package.
|
||||||
|
* The manifest for patches has been removed, and the properties have been added to patches. Patches are now `OptionsContainer`. The `@Patch` annotation has been removed in favour of the `@Patch` annotation from the annotation processor.
|
||||||
|
* Patch annotations have been removed. PatcherException is now thrown in various places. PatchBundleLoader is now a map of patches associated by their name. Patches are now instances.
|
||||||
|
|
||||||
|
# [15.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.3...v15.0.0-dev.4) (2023-09-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Account for source patch dependency for tests ([6918418](https://github.com/ReVanced/revanced-patcher/commit/69184187d90f126478d2f49415c1e3381217557f))
|
||||||
|
* Always make the generated patch depend on the source patch ([8de3063](https://github.com/ReVanced/revanced-patcher/commit/8de30633ae6eb7acf7f0a351e26d4a6c2fdbdfec))
|
||||||
|
* Catch correct exception ([637d487](https://github.com/ReVanced/revanced-patcher/commit/637d48746ff8694e01c5aead1c75a9a1efeb5ac8))
|
||||||
|
* Delegate `PatchBundleLoader` by mutable set of patches ([9a109c1](https://github.com/ReVanced/revanced-patcher/commit/9a109c129b135a634be1aad4130a06d9e8e96ecd))
|
||||||
|
* Do not resolve the proxied patch to the proxy in the dependency list ([e112837](https://github.com/ReVanced/revanced-patcher/commit/e11283744a21fe2d09435e99d6924462b6aac3b8))
|
||||||
|
* Do not set `CompatiblePackage.versions` if `@CompatiblePackage.versions` is empty ([6b1e0a1](https://github.com/ReVanced/revanced-patcher/commit/6b1e0a16568124e9f82fb5740353360fa8ec614a))
|
||||||
|
* Filter for patches correctly ([4bc4b0d](https://github.com/ReVanced/revanced-patcher/commit/4bc4b0dc0104073b62528d02a88383cecd7a50e7))
|
||||||
|
* Find dependency in `context.allPatches` ([670f015](https://github.com/ReVanced/revanced-patcher/commit/670f0153de10c6f0db25b08df1c01a2905037f84))
|
||||||
|
* Log the correct patch names ([9fdb8f0](https://github.com/ReVanced/revanced-patcher/commit/9fdb8f087f62babf6081879db65c80db639aa0a7))
|
||||||
|
* Print patch name instead of class name ([4e7811e](https://github.com/ReVanced/revanced-patcher/commit/4e7811ea07762667a1f22526dc176022038f60eb))
|
||||||
|
* Print stack trace of exception ([aa71146](https://github.com/ReVanced/revanced-patcher/commit/aa71146b1bf4ffebcc81a1663e15abae89e97ff0))
|
||||||
|
* Run code-block if `executablePatches` does not yet contain `patch` ([1d7aeca](https://github.com/ReVanced/revanced-patcher/commit/1d7aeca696be873dfaf88eaa6d312949a3b8572b))
|
||||||
|
* Suppress logger when loading patches in `PatchBundleLoader` ([72c9eb2](https://github.com/ReVanced/revanced-patcher/commit/72c9eb212985f99f3390cf1faa10ab547d2dbe7e))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* Internalize processor constructor ([a802d0d](https://github.com/ReVanced/revanced-patcher/commit/a802d0df463695976e85d8391762942eb977920b))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This gets rid of the public constructor.
|
||||||
|
|
||||||
|
# [15.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.2...v15.0.0-dev.3) (2023-09-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Make `CompatiblePackage.versions` a property ([67b7dff](https://github.com/ReVanced/revanced-patcher/commit/67b7dff67a212b4fc30eb4f0cbe58f0ba09fb09a))
|
||||||
|
* Use correct module name ([080fbe9](https://github.com/ReVanced/revanced-patcher/commit/080fbe9feb9d4ea9ec4e599ecef296eacd803b05))
|
||||||
|
|
||||||
|
|
||||||
|
* feat Use `Set` as super type for `PatchBundleLoader` ([4b76d19](https://github.com/ReVanced/revanced-patcher/commit/4b76d1959691babf8c99d3d5235df4a4388956f0))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* `PatchBundleLoader` is not a map anymore
|
||||||
|
* This renames packages and the Maven package.
|
||||||
|
|
||||||
|
# [15.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v15.0.0-dev.1...v15.0.0-dev.2) (2023-09-06)
|
||||||
|
|
||||||
|
# [15.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.2...v15.0.0-dev.1) (2023-09-04)
|
||||||
|
|
||||||
|
|
||||||
|
* feat!: Add patch annotation processor ([3fc6a13](https://github.com/ReVanced/revanced-patcher/commit/3fc6a139eef67237c116fb4e3e29bf9542d3a981))
|
||||||
|
* feat!: Remove patch annotations ([3b4db3d](https://github.com/ReVanced/revanced-patcher/commit/3b4db3ddb72cdcee8af2f787eadf58eeb37543de))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add patch annotation processor ([#231](https://github.com/ReVanced/revanced-patcher/issues/231)) ([a29931f](https://github.com/ReVanced/revanced-patcher/commit/a29931f2ec0a666dba209b54855425d9dc2f4462))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* The manifest for patches has been removed, and the properties have been added to patches. Patches are now `OptionsContainer`. The `@Patch` annotation has been removed in favour of the `@Patch` annotation from the annotation processor.
|
||||||
|
* Patch annotations have been removed. PatcherException is now thrown in various places. PatchBundleLoader is now a map of patches associated by their name. Patches are now instances.
|
||||||
|
|
||||||
|
## [14.2.2](https://github.com/ReVanced/revanced-patcher/compare/v14.2.1...v14.2.2) (2023-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow setting `DexClassLoader.optimizedDirectory` ([11a3378](https://github.com/ReVanced/revanced-patcher/commit/11a337865947a6ac74a63ebb3f3f9bc2610f7771))
|
||||||
|
|
||||||
|
## [14.2.2-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.1...v14.2.2-dev.1) (2023-08-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow setting `DexClassLoader.optimizedDirectory` ([11a3378](https://github.com/ReVanced/revanced-patcher/commit/11a337865947a6ac74a63ebb3f3f9bc2610f7771))
|
||||||
|
|
||||||
|
## [14.2.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0...v14.2.1) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not flag resource table as sparse when main package is not loaded ([b832812](https://github.com/ReVanced/revanced-patcher/commit/b832812767a06ec6ec232291e6d14c8c2f14118c))
|
||||||
|
|
||||||
|
## [14.2.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0...v14.2.1-dev.1) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not flag resource table as sparse when main package is not loaded ([b832812](https://github.com/ReVanced/revanced-patcher/commit/b832812767a06ec6ec232291e6d14c8c2f14118c))
|
||||||
|
|
||||||
|
# [14.2.0](https://github.com/ReVanced/revanced-patcher/compare/v14.1.0...v14.2.0) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* load patches in lexicographical order ([e8f2087](https://github.com/ReVanced/revanced-patcher/commit/e8f2087a6ffa6077fb3a6a69e29f3aec72e2fc1b))
|
||||||
|
* log when merging integrations ([983563e](https://github.com/ReVanced/revanced-patcher/commit/983563efb6d7c8d289464b8bf71a016b8a735630))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* compare types of classes ([55d6945](https://github.com/ReVanced/revanced-patcher/commit/55d694579ac2718b9e2c61ca5f38419c3775ef87))
|
||||||
|
|
||||||
|
# [14.2.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0-dev.2...v14.2.0-dev.3) (2023-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* compare types of classes ([55d6945](https://github.com/ReVanced/revanced-patcher/commit/55d694579ac2718b9e2c61ca5f38419c3775ef87))
|
||||||
|
|
||||||
|
# [14.2.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v14.2.0-dev.1...v14.2.0-dev.2) (2023-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* log when merging integrations ([983563e](https://github.com/ReVanced/revanced-patcher/commit/983563efb6d7c8d289464b8bf71a016b8a735630))
|
||||||
|
|
||||||
|
# [14.2.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.1.0...v14.2.0-dev.1) (2023-08-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* load patches in lexicographical order ([e8f2087](https://github.com/ReVanced/revanced-patcher/commit/e8f2087a6ffa6077fb3a6a69e29f3aec72e2fc1b))
|
||||||
|
|
||||||
|
# [14.1.0](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0...v14.1.0) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* move version properties file to correct package ([e985676](https://github.com/ReVanced/revanced-patcher/commit/e985676c2d8e5d6cb907d371de30428caaa6da43))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* properly make use of logging facade ([ba56a6a](https://github.com/ReVanced/revanced-patcher/commit/ba56a6a2eef503c0d6cdd846ddce2e1474d8ed1a))
|
||||||
|
|
||||||
|
# [14.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.0.1-dev.1...v14.1.0-dev.1) (2023-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* properly make use of logging facade ([ba56a6a](https://github.com/ReVanced/revanced-patcher/commit/ba56a6a2eef503c0d6cdd846ddce2e1474d8ed1a))
|
||||||
|
|
||||||
|
## [14.0.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0...v14.0.1-dev.1) (2023-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* move version properties file to correct package ([e985676](https://github.com/ReVanced/revanced-patcher/commit/e985676c2d8e5d6cb907d371de30428caaa6da43))
|
||||||
|
|
||||||
|
# [14.0.0](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0...v14.0.0) (2023-08-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* log decoding resources after logging deleting resource cache directory ([db62a16](https://github.com/ReVanced/revanced-patcher/commit/db62a1607b4a9d6256b5f5153decb088d9680553))
|
||||||
|
* only emit closed patches that did not throw an exception with the `@Patch` annotation ([5938f6b](https://github.com/ReVanced/revanced-patcher/commit/5938f6b7ea25103a0a1b56ceebe49139bc80c6f5))
|
||||||
|
* supply the parent classloader to `DexClassLoader` ([0f15077](https://github.com/ReVanced/revanced-patcher/commit/0f15077225600b65200022c1a318e504deb472b9))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* improve structure and public API ([6b8977f](https://github.com/ReVanced/revanced-patcher/commit/6b8977f17854ef0344d868e6391cb18134eceadc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* do not log instantiation of ReVanced Patcher ([273dd8d](https://github.com/ReVanced/revanced-patcher/commit/273dd8d388f8e9b7436c6d6145a94c12c1fabe55))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* Various public APIs have been changed. The `Version` annotation has been removed. Patches do not return anything anymore and instead throw `PatchException`. Multiple patch bundles can now be loaded in a single ClassLoader to bypass class loader isolation.
|
||||||
|
|
||||||
|
# [14.0.0-dev.4](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.3...v14.0.0-dev.4) (2023-08-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* only emit closed patches that did not throw an exception with the `@Patch` annotation ([5938f6b](https://github.com/ReVanced/revanced-patcher/commit/5938f6b7ea25103a0a1b56ceebe49139bc80c6f5))
|
||||||
|
|
||||||
|
# [14.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.2...v14.0.0-dev.3) (2023-08-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* supply the parent classloader to `DexClassLoader` ([0f15077](https://github.com/ReVanced/revanced-patcher/commit/0f15077225600b65200022c1a318e504deb472b9))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* do not log instantiation of ReVanced Patcher ([273dd8d](https://github.com/ReVanced/revanced-patcher/commit/273dd8d388f8e9b7436c6d6145a94c12c1fabe55))
|
||||||
|
|
||||||
|
# [14.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v14.0.0-dev.1...v14.0.0-dev.2) (2023-08-19)
|
||||||
|
|
||||||
|
# [14.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0...v14.0.0-dev.1) (2023-08-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* log decoding resources after logging deleting resource cache directory ([db62a16](https://github.com/ReVanced/revanced-patcher/commit/db62a1607b4a9d6256b5f5153decb088d9680553))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* improve structure and public API ([6b8977f](https://github.com/ReVanced/revanced-patcher/commit/6b8977f17854ef0344d868e6391cb18134eceadc))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* Various public APIs have been changed. The `Version` annotation has been removed. Patches do not return anything anymore and instead throw `PatchException`. Multiple patch bundles can now be loaded in a single ClassLoader to bypass class loader isolation.
|
||||||
|
|
||||||
|
# [13.0.0](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1...v13.0.0) (2023-08-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* decode in correct order ([8fb2f2d](https://github.com/ReVanced/revanced-patcher/commit/8fb2f2dc1d3b9b1e9fd13b39485985d2886d52ae))
|
||||||
|
* disable correct loggers ([c2d89c6](https://github.com/ReVanced/revanced-patcher/commit/c2d89c622e06e58e5042e1a00ef67cee8a246e53))
|
||||||
|
* get framework ids to compile resources ([f2cb7ee](https://github.com/ReVanced/revanced-patcher/commit/f2cb7ee7dffa573c31df497cf235a3f5d120f91f))
|
||||||
|
* only enable logging for ReVanced ([783ccf8](https://github.com/ReVanced/revanced-patcher/commit/783ccf8529f5d16aa463982da6977328306232bb))
|
||||||
|
* set package metadata correctly ([02d6ff1](https://github.com/ReVanced/revanced-patcher/commit/02d6ff15fe87c2352de29749610e9d72db8ba418))
|
||||||
|
|
||||||
|
|
||||||
|
* build(Needs bump)!: Bump dependencies ([d5f89a9](https://github.com/ReVanced/revanced-patcher/commit/d5f89a903f019c199bdb27a50287124fc4b4978e))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This bump updates smali, a crucial dependency
|
||||||
|
|
||||||
|
# [13.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.2...v13.0.0-dev.3) (2023-08-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* decode in correct order ([8fb2f2d](https://github.com/ReVanced/revanced-patcher/commit/8fb2f2dc1d3b9b1e9fd13b39485985d2886d52ae))
|
||||||
|
* only enable logging for ReVanced ([783ccf8](https://github.com/ReVanced/revanced-patcher/commit/783ccf8529f5d16aa463982da6977328306232bb))
|
||||||
|
|
||||||
|
# [13.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.1...v13.0.0-dev.2) (2023-08-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable correct loggers ([c2d89c6](https://github.com/ReVanced/revanced-patcher/commit/c2d89c622e06e58e5042e1a00ef67cee8a246e53))
|
||||||
|
* get framework ids to compile resources ([f2cb7ee](https://github.com/ReVanced/revanced-patcher/commit/f2cb7ee7dffa573c31df497cf235a3f5d120f91f))
|
||||||
|
* set package metadata correctly ([02d6ff1](https://github.com/ReVanced/revanced-patcher/commit/02d6ff15fe87c2352de29749610e9d72db8ba418))
|
||||||
|
|
||||||
|
# [13.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1...v13.0.0-dev.1) (2023-08-11)
|
||||||
|
|
||||||
|
|
||||||
|
* build(Needs bump)!: Bump dependencies ([d5f89a9](https://github.com/ReVanced/revanced-patcher/commit/d5f89a903f019c199bdb27a50287124fc4b4978e))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This bump updates smali, a crucial dependency
|
||||||
|
|
||||||
|
## [12.1.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0...v12.1.1) (2023-08-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear method lookup maps before initializing them ([#210](https://github.com/ReVanced/revanced-patcher/issues/210)) ([746544f](https://github.com/ReVanced/revanced-patcher/commit/746544f9d51d1013bb160075709cd26bffd425b3))
|
||||||
|
|
||||||
|
## [12.1.1-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1-dev.1...v12.1.1-dev.2) (2023-08-03)
|
||||||
|
|
||||||
|
## [12.1.1-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0...v12.1.1-dev.1) (2023-08-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear method lookup maps before initializing them ([#210](https://github.com/ReVanced/revanced-patcher/issues/210)) ([746544f](https://github.com/ReVanced/revanced-patcher/commit/746544f9d51d1013bb160075709cd26bffd425b3))
|
||||||
|
|
||||||
|
# [12.1.0](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0...v12.1.0) (2023-08-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `MutableMethod.getInstructions` extension function ([fae4029](https://github.com/ReVanced/revanced-patcher/commit/fae4029cfccfad7aa3dd8f7fbef1c63ee26b85b3))
|
||||||
|
|
||||||
|
# [12.1.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0-dev.1...v12.1.0-dev.2) (2023-08-03)
|
||||||
|
|
||||||
|
# [12.1.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0...v12.1.0-dev.1) (2023-08-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add `MutableMethod.getInstructions` extension function ([fae4029](https://github.com/ReVanced/revanced-patcher/commit/fae4029cfccfad7aa3dd8f7fbef1c63ee26b85b3))
|
||||||
|
|
||||||
|
# [12.0.0](https://github.com/ReVanced/revanced-patcher/compare/v11.0.4...v12.0.0) (2023-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* correct access flags of `PackageMetadata` ([416d691](https://github.com/ReVanced/revanced-patcher/commit/416d69142f50dab49c9ea3f027e9d53e4777f257))
|
||||||
|
* set resource table via resource decoder ([e0f8e1b](https://github.com/ReVanced/revanced-patcher/commit/e0f8e1b71a295948b610029c89a48f52762396b6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Deprecate `Version` annotation ([c9bbcf2](https://github.com/ReVanced/revanced-patcher/commit/c9bbcf2bf2b0f50ab9100380a3a66c6346ad42ac))
|
||||||
|
* remove `Path` option ([#202](https://github.com/ReVanced/revanced-patcher/issues/202)) ([69e4a49](https://github.com/ReVanced/revanced-patcher/commit/69e4a490659ebc4fb4bf46148634f4b064ef1713))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This removes the previously available `Path` option
|
||||||
|
|
||||||
|
# [12.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v12.0.0-dev.1...v12.0.0-dev.2) (2023-07-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Deprecate `Version` annotation ([400442f](https://github.com/ReVanced/revanced-patcher/commit/400442f70ee56cafd4493b2ce64a294db9836509))
|
||||||
|
|
||||||
|
# [12.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v11.0.4...v12.0.0-dev.1) (2023-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* correct access flags of `PackageMetadata` ([416d691](https://github.com/ReVanced/revanced-patcher/commit/416d69142f50dab49c9ea3f027e9d53e4777f257))
|
||||||
|
* set resource table via resource decoder ([e0f8e1b](https://github.com/ReVanced/revanced-patcher/commit/e0f8e1b71a295948b610029c89a48f52762396b6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove `Path` option ([#202](https://github.com/ReVanced/revanced-patcher/issues/202)) ([69e4a49](https://github.com/ReVanced/revanced-patcher/commit/69e4a490659ebc4fb4bf46148634f4b064ef1713))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This removes the previously available `Path` option
|
||||||
|
|
||||||
## [11.0.4](https://github.com/revanced/revanced-patcher/compare/v11.0.3...v11.0.4) (2023-07-01)
|
## [11.0.4](https://github.com/revanced/revanced-patcher/compare/v11.0.3...v11.0.4) (2023-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
842
api/revanced-patcher.api
Normal file
842
api/revanced-patcher.api
Normal file
@@ -0,0 +1,842 @@
|
|||||||
|
public abstract interface class app/revanced/patcher/IntegrationsConsumer {
|
||||||
|
public abstract fun acceptIntegrations (Ljava/util/List;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PackageMetadata {
|
||||||
|
public final fun getPackageName ()Ljava/lang/String;
|
||||||
|
public final fun getPackageVersion ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/PatchBundleLoader : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
|
||||||
|
public synthetic fun <init> (Ljava/lang/ClassLoader;[Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public fun add (Lapp/revanced/patcher/patch/Patch;)Z
|
||||||
|
public synthetic fun add (Ljava/lang/Object;)Z
|
||||||
|
public fun addAll (Ljava/util/Collection;)Z
|
||||||
|
public fun clear ()V
|
||||||
|
public fun contains (Lapp/revanced/patcher/patch/Patch;)Z
|
||||||
|
public final fun contains (Ljava/lang/Object;)Z
|
||||||
|
public fun containsAll (Ljava/util/Collection;)Z
|
||||||
|
public fun getSize ()I
|
||||||
|
public fun isEmpty ()Z
|
||||||
|
public fun iterator ()Ljava/util/Iterator;
|
||||||
|
public fun remove (Ljava/lang/Object;)Z
|
||||||
|
public fun removeAll (Ljava/util/Collection;)Z
|
||||||
|
public fun retainAll (Ljava/util/Collection;)Z
|
||||||
|
public final fun size ()I
|
||||||
|
public fun toArray ()[Ljava/lang/Object;
|
||||||
|
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatchBundleLoader$Dex : app/revanced/patcher/PatchBundleLoader {
|
||||||
|
public fun <init> ([Ljava/io/File;)V
|
||||||
|
public fun <init> ([Ljava/io/File;Ljava/io/File;)V
|
||||||
|
public synthetic fun <init> ([Ljava/io/File;Ljava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatchBundleLoader$Jar : app/revanced/patcher/PatchBundleLoader {
|
||||||
|
public fun <init> ([Ljava/io/File;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/PatchExecutorFunction : java/util/function/Function {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/Patcher : app/revanced/patcher/IntegrationsConsumer, app/revanced/patcher/PatchExecutorFunction, app/revanced/patcher/PatchesConsumer, java/io/Closeable, java/util/function/Supplier {
|
||||||
|
public fun <init> (Lapp/revanced/patcher/PatcherOptions;)V
|
||||||
|
public fun acceptIntegrations (Ljava/util/List;)V
|
||||||
|
public fun acceptPatches (Ljava/util/List;)V
|
||||||
|
public synthetic fun apply (Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun apply (Z)Lkotlinx/coroutines/flow/Flow;
|
||||||
|
public fun close ()V
|
||||||
|
public fun get ()Lapp/revanced/patcher/PatcherResult;
|
||||||
|
public synthetic fun get ()Ljava/lang/Object;
|
||||||
|
public final fun getContext ()Lapp/revanced/patcher/PatcherContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatcherContext {
|
||||||
|
public final fun getPackageMetadata ()Lapp/revanced/patcher/PackageMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/PatcherException : java/lang/Exception {
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatcherException$CircularDependencyException : app/revanced/patcher/PatcherException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatcherOptions {
|
||||||
|
public fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Z)V
|
||||||
|
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun copy (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Z)Lapp/revanced/patcher/PatcherOptions;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/PatcherOptions;Ljava/io/File;Ljava/io/File;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lapp/revanced/patcher/PatcherOptions;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public fun hashCode ()I
|
||||||
|
public final fun recreateResourceCacheDirectory ()Ljava/io/File;
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatcherResult {
|
||||||
|
public fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;)V
|
||||||
|
public synthetic fun <init> (Ljava/util/List;Ljava/io/File;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun component1 ()Ljava/util/List;
|
||||||
|
public final fun component2 ()Ljava/io/File;
|
||||||
|
public final fun component3 ()Ljava/util/List;
|
||||||
|
public final fun copy (Ljava/util/List;Ljava/io/File;Ljava/util/List;)Lapp/revanced/patcher/PatcherResult;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/PatcherResult;Ljava/util/List;Ljava/io/File;Ljava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/PatcherResult;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getDexFiles ()Ljava/util/List;
|
||||||
|
public final fun getDoNotCompress ()Ljava/util/List;
|
||||||
|
public final fun getResourceFile ()Ljava/io/File;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/PatcherResult$PatchedDexFile {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/io/InputStream;)V
|
||||||
|
public final fun getName ()Ljava/lang/String;
|
||||||
|
public final fun getStream ()Ljava/io/InputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/PatchesConsumer {
|
||||||
|
public abstract fun acceptPatches (Ljava/util/List;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/data/BytecodeContext : app/revanced/patcher/data/Context {
|
||||||
|
public final fun findClass (Ljava/lang/String;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
||||||
|
public final fun findClass (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
||||||
|
public synthetic fun get ()Ljava/lang/Object;
|
||||||
|
public fun get ()Ljava/util/List;
|
||||||
|
public final fun getClasses ()Lapp/revanced/patcher/util/ProxyClassList;
|
||||||
|
public final fun proxy (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/ClassProxy;
|
||||||
|
public final fun toMethodWalker (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/method/MethodWalker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/data/Context : java/util/function/Supplier {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/data/ResourceContext : app/revanced/patcher/data/Context, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
||||||
|
public fun get ()Ljava/io/File;
|
||||||
|
public synthetic fun get ()Ljava/lang/Object;
|
||||||
|
public final fun get (Ljava/lang/String;)Ljava/io/File;
|
||||||
|
public final fun getXmlEditor ()Lapp/revanced/patcher/data/ResourceContext$XmlFileHolder;
|
||||||
|
public fun iterator ()Ljava/util/Iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/data/ResourceContext$XmlFileHolder {
|
||||||
|
public fun <init> (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||||
|
public final fun get (Ljava/io/InputStream;)Lapp/revanced/patcher/util/DomFileEditor;
|
||||||
|
public final fun get (Ljava/lang/String;)Lapp/revanced/patcher/util/DomFileEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/extensions/ExtensionsKt {
|
||||||
|
public static final fun newLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/Label;
|
||||||
|
public static final fun or (ILcom/android/tools/smali/dexlib2/AccessFlags;)I
|
||||||
|
public static final fun or (Lcom/android/tools/smali/dexlib2/AccessFlags;I)I
|
||||||
|
public static final fun or (Lcom/android/tools/smali/dexlib2/AccessFlags;Lcom/android/tools/smali/dexlib2/AccessFlags;)I
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/extensions/InstructionExtensions {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patcher/extensions/InstructionExtensions;
|
||||||
|
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||||
|
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||||
|
public final fun addInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
|
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
||||||
|
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
|
public final fun addInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/util/List;)V
|
||||||
|
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
||||||
|
public final fun addInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;Ljava/util/List;)V
|
||||||
|
public final fun addInstructionsWithLabels (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
|
||||||
|
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||||
|
public final fun getInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)Ljava/lang/Object;
|
||||||
|
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||||
|
public final fun getInstruction (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)Ljava/lang/Object;
|
||||||
|
public final fun getInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
||||||
|
public final fun removeInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||||
|
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||||
|
public final fun removeInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;II)V
|
||||||
|
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)V
|
||||||
|
public final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;II)V
|
||||||
|
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V
|
||||||
|
public final fun replaceInstruction (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public final fun replaceInstructions (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/util/List;)V
|
||||||
|
public final fun replaceInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/extensions/MethodFingerprintExtensions {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patcher/extensions/MethodFingerprintExtensions;
|
||||||
|
public final fun getFuzzyPatternScanMethod (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/method/annotation/FuzzyPatternScanMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/fingerprint/Fingerprint {
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class app/revanced/patcher/fingerprint/method/annotation/FuzzyPatternScanMethod : java/lang/annotation/Annotation {
|
||||||
|
public abstract fun threshold ()I
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/fingerprint/method/impl/MethodFingerprint : app/revanced/patcher/fingerprint/Fingerprint {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint$Companion;
|
||||||
|
public fun <init> ()V
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)V
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun getResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
||||||
|
public final fun setResult (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprint$Companion {
|
||||||
|
public final fun resolve (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public final fun resolve (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public final fun resolve (Ljava/lang/Iterable;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/Iterable;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult {
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
public final fun component1 ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
||||||
|
public final fun component2 ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
||||||
|
public final fun component3 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
||||||
|
public final fun copy (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/data/BytecodeContext;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getClassDef ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
||||||
|
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
||||||
|
public final fun getMutableClass ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
||||||
|
public final fun getMutableMethod ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
||||||
|
public final fun getScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult {
|
||||||
|
public fun <init> (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;)V
|
||||||
|
public final fun component1 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
||||||
|
public final fun component2 ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||||
|
public final fun copy (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getPatternScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
||||||
|
public final fun getStringsScanResult ()Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult {
|
||||||
|
public fun <init> (IILjava/util/List;)V
|
||||||
|
public synthetic fun <init> (IILjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun component1 ()I
|
||||||
|
public final fun component2 ()I
|
||||||
|
public final fun component3 ()Ljava/util/List;
|
||||||
|
public final fun copy (IILjava/util/List;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;IILjava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getEndIndex ()I
|
||||||
|
public final fun getStartIndex ()I
|
||||||
|
public final fun getWarnings ()Ljava/util/List;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public final fun setWarnings (Ljava/util/List;)V
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning {
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;II)V
|
||||||
|
public final fun component1 ()Lcom/android/tools/smali/dexlib2/Opcode;
|
||||||
|
public final fun component2 ()Lcom/android/tools/smali/dexlib2/Opcode;
|
||||||
|
public final fun component3 ()I
|
||||||
|
public final fun component4 ()I
|
||||||
|
public final fun copy (Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;II)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;Lcom/android/tools/smali/dexlib2/Opcode;Lcom/android/tools/smali/dexlib2/Opcode;IIILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$PatternScanResult$Warning;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getCorrectOpcode ()Lcom/android/tools/smali/dexlib2/Opcode;
|
||||||
|
public final fun getInstructionIndex ()I
|
||||||
|
public final fun getPatternIndex ()I
|
||||||
|
public final fun getWrongOpcode ()Lcom/android/tools/smali/dexlib2/Opcode;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult {
|
||||||
|
public fun <init> (Ljava/util/List;)V
|
||||||
|
public final fun component1 ()Ljava/util/List;
|
||||||
|
public final fun copy (Ljava/util/List;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;Ljava/util/List;ILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getMatches ()Ljava/util/List;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch {
|
||||||
|
public fun <init> (Ljava/lang/String;I)V
|
||||||
|
public final fun component1 ()Ljava/lang/String;
|
||||||
|
public final fun component2 ()I
|
||||||
|
public final fun copy (Ljava/lang/String;I)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;Ljava/lang/String;IILjava/lang/Object;)Lapp/revanced/patcher/fingerprint/method/impl/MethodFingerprintResult$MethodFingerprintScanResult$StringsScanResult$StringMatch;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public final fun getIndex ()I
|
||||||
|
public final fun getString ()Ljava/lang/String;
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/logging/Logger {
|
||||||
|
public abstract fun error (Ljava/lang/String;)V
|
||||||
|
public abstract fun info (Ljava/lang/String;)V
|
||||||
|
public abstract fun trace (Ljava/lang/String;)V
|
||||||
|
public abstract fun warn (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/logging/Logger$DefaultImpls {
|
||||||
|
public static fun error (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||||
|
public static fun info (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||||
|
public static fun trace (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||||
|
public static fun warn (Lapp/revanced/patcher/logging/Logger;Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/logging/impl/NopLogger : app/revanced/patcher/logging/Logger {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patcher/logging/impl/NopLogger;
|
||||||
|
public fun error (Ljava/lang/String;)V
|
||||||
|
public fun info (Ljava/lang/String;)V
|
||||||
|
public fun trace (Ljava/lang/String;)V
|
||||||
|
public fun warn (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/BytecodePatch : app/revanced/patcher/patch/Patch {
|
||||||
|
public fun <init> ()V
|
||||||
|
public fun <init> (Ljava/util/Set;)V
|
||||||
|
public synthetic fun <init> (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/Patch {
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public abstract fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
public final fun getCompatiblePackages ()Ljava/util/Set;
|
||||||
|
public final fun getDependencies ()Ljava/util/Set;
|
||||||
|
public final fun getDescription ()Ljava/lang/String;
|
||||||
|
public final fun getName ()Ljava/lang/String;
|
||||||
|
public final fun getOptions ()Lapp/revanced/patcher/patch/options/PatchOptions;
|
||||||
|
public final fun getRequiresIntegrations ()Z
|
||||||
|
public final fun getUse ()Z
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/Patch$CompatiblePackage {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun getName ()Ljava/lang/String;
|
||||||
|
public final fun getVersions ()Ljava/util/Set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/PatchException : java/lang/Exception {
|
||||||
|
public fun <init> (Ljava/lang/String;)V
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
|
||||||
|
public fun <init> (Ljava/lang/Throwable;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/PatchResult {
|
||||||
|
public final fun getException ()Lapp/revanced/patcher/patch/PatchException;
|
||||||
|
public final fun getPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/ResourcePatch : app/revanced/patcher/patch/Patch {
|
||||||
|
public fun <init> ()V
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/CompatiblePackage : java/lang/annotation/Annotation {
|
||||||
|
public abstract fun name ()Ljava/lang/String;
|
||||||
|
public abstract fun versions ()[Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/Patch : java/lang/annotation/Annotation {
|
||||||
|
public abstract fun compatiblePackages ()[Lapp/revanced/patcher/patch/annotation/CompatiblePackage;
|
||||||
|
public abstract fun dependencies ()[Ljava/lang/Class;
|
||||||
|
public abstract fun description ()Ljava/lang/String;
|
||||||
|
public abstract fun name ()Ljava/lang/String;
|
||||||
|
public abstract fun requiresIntegrations ()Z
|
||||||
|
public abstract fun use ()Z
|
||||||
|
}
|
||||||
|
|
||||||
|
public class app/revanced/patcher/patch/options/PatchOption {
|
||||||
|
public static final field PatchExtensions Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)V
|
||||||
|
public final fun getDefault ()Ljava/lang/Object;
|
||||||
|
public final fun getDescription ()Ljava/lang/String;
|
||||||
|
public final fun getKey ()Ljava/lang/String;
|
||||||
|
public final fun getRequired ()Z
|
||||||
|
public final fun getTitle ()Ljava/lang/String;
|
||||||
|
public final fun getValidator ()Lkotlin/jvm/functions/Function2;
|
||||||
|
public final fun getValue ()Ljava/lang/Object;
|
||||||
|
public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
|
||||||
|
public final fun getValues ()Ljava/util/Set;
|
||||||
|
public fun reset ()V
|
||||||
|
public final fun setValue (Ljava/lang/Object;)V
|
||||||
|
public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOption$PatchExtensions {
|
||||||
|
public final fun booleanArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun booleanArrayPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun booleanPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun booleanPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun floatArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun floatArrayPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Float;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun floatPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun floatPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun intArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun intArrayPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Integer;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun intPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun intPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun longArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun longArrayPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/Long;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun longPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun longPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun stringArrayPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun stringArrayPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;[Ljava/lang/String;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun stringPatchOption (Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public static synthetic fun stringPatchOption$default (Lapp/revanced/patcher/patch/options/PatchOption$PatchExtensions;Lapp/revanced/patcher/patch/Patch;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class app/revanced/patcher/patch/options/PatchOptionException : java/lang/Exception {
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$InvalidValueTypeException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$PatchOptionNotFoundException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||||
|
public fun <init> (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueRequiredException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||||
|
public fun <init> (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptionException$ValueValidationException : app/revanced/patcher/patch/options/PatchOptionException {
|
||||||
|
public fun <init> (Ljava/lang/Object;Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/options/PatchOptions : java/util/Map, kotlin/jvm/internal/markers/KMutableMap {
|
||||||
|
public fun <init> ()V
|
||||||
|
public fun clear ()V
|
||||||
|
public final fun containsKey (Ljava/lang/Object;)Z
|
||||||
|
public fun containsKey (Ljava/lang/String;)Z
|
||||||
|
public fun containsValue (Lapp/revanced/patcher/patch/options/PatchOption;)Z
|
||||||
|
public final fun containsValue (Ljava/lang/Object;)Z
|
||||||
|
public final fun entrySet ()Ljava/util/Set;
|
||||||
|
public final fun get (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun get (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public fun getEntries ()Ljava/util/Set;
|
||||||
|
public fun getKeys ()Ljava/util/Set;
|
||||||
|
public fun getSize ()I
|
||||||
|
public fun getValues ()Ljava/util/Collection;
|
||||||
|
public fun isEmpty ()Z
|
||||||
|
public final fun keySet ()Ljava/util/Set;
|
||||||
|
public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun put (Ljava/lang/String;Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public fun putAll (Ljava/util/Map;)V
|
||||||
|
public final fun register (Lapp/revanced/patcher/patch/options/PatchOption;)V
|
||||||
|
public final fun remove (Ljava/lang/Object;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
public fun remove (Ljava/lang/String;)Lapp/revanced/patcher/patch/options/PatchOption;
|
||||||
|
public final fun set (Ljava/lang/String;Ljava/lang/Object;)V
|
||||||
|
public final fun size ()I
|
||||||
|
public final fun values ()Ljava/util/Collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/DomFileEditor : java/io/Closeable {
|
||||||
|
public fun <init> (Ljava/io/File;)V
|
||||||
|
public fun close ()V
|
||||||
|
public final fun getFile ()Lorg/w3c/dom/Document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/ProxyClassList : java/util/Set, kotlin/jvm/internal/markers/KMutableSet {
|
||||||
|
public final fun add (Lapp/revanced/patcher/util/proxy/ClassProxy;)Z
|
||||||
|
public fun add (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public synthetic fun add (Ljava/lang/Object;)Z
|
||||||
|
public fun addAll (Ljava/util/Collection;)Z
|
||||||
|
public fun clear ()V
|
||||||
|
public fun contains (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public final fun contains (Ljava/lang/Object;)Z
|
||||||
|
public fun containsAll (Ljava/util/Collection;)Z
|
||||||
|
public fun getSize ()I
|
||||||
|
public fun isEmpty ()Z
|
||||||
|
public fun iterator ()Ljava/util/Iterator;
|
||||||
|
public fun remove (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Z
|
||||||
|
public final fun remove (Ljava/lang/Object;)Z
|
||||||
|
public fun removeAll (Ljava/util/Collection;)Z
|
||||||
|
public fun retainAll (Ljava/util/Collection;)Z
|
||||||
|
public final fun size ()I
|
||||||
|
public fun toArray ()[Ljava/lang/Object;
|
||||||
|
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/method/MethodWalker {
|
||||||
|
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
||||||
|
public final fun nextMethod (IZ)Lapp/revanced/patcher/util/method/MethodWalker;
|
||||||
|
public static synthetic fun nextMethod$default (Lapp/revanced/patcher/util/method/MethodWalker;IZILjava/lang/Object;)Lapp/revanced/patcher/util/method/MethodWalker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/ClassProxy {
|
||||||
|
public final fun getImmutableClass ()Lcom/android/tools/smali/dexlib2/iface/ClassDef;
|
||||||
|
public final fun getMutableClass ()Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation : com/android/tools/smali/dexlib2/base/BaseAnnotation {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Annotation;)V
|
||||||
|
public fun getElements ()Ljava/util/Set;
|
||||||
|
public fun getType ()Ljava/lang/String;
|
||||||
|
public fun getVisibility ()I
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Annotation;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement : com/android/tools/smali/dexlib2/base/BaseAnnotationElement {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)V
|
||||||
|
public fun getName ()Ljava/lang/String;
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
||||||
|
public final fun setName (Ljava/lang/String;)V
|
||||||
|
public final fun setValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/AnnotationElement;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableAnnotationElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass : com/android/tools/smali/dexlib2/base/reference/BaseTypeReference, com/android/tools/smali/dexlib2/iface/ClassDef {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)V
|
||||||
|
public final fun charAt (I)C
|
||||||
|
public fun get (I)C
|
||||||
|
public fun getAccessFlags ()I
|
||||||
|
public fun getAnnotations ()Ljava/util/Set;
|
||||||
|
public synthetic fun getDirectMethods ()Ljava/lang/Iterable;
|
||||||
|
public fun getDirectMethods ()Ljava/util/Set;
|
||||||
|
public synthetic fun getFields ()Ljava/lang/Iterable;
|
||||||
|
public fun getFields ()Ljava/util/Set;
|
||||||
|
public synthetic fun getInstanceFields ()Ljava/lang/Iterable;
|
||||||
|
public fun getInstanceFields ()Ljava/util/Set;
|
||||||
|
public fun getInterfaces ()Ljava/util/List;
|
||||||
|
public fun getLength ()I
|
||||||
|
public synthetic fun getMethods ()Ljava/lang/Iterable;
|
||||||
|
public fun getMethods ()Ljava/util/Set;
|
||||||
|
public fun getSourceFile ()Ljava/lang/String;
|
||||||
|
public synthetic fun getStaticFields ()Ljava/lang/Iterable;
|
||||||
|
public fun getStaticFields ()Ljava/util/Set;
|
||||||
|
public fun getSuperclass ()Ljava/lang/String;
|
||||||
|
public fun getType ()Ljava/lang/String;
|
||||||
|
public synthetic fun getVirtualMethods ()Ljava/lang/Iterable;
|
||||||
|
public fun getVirtualMethods ()Ljava/util/Set;
|
||||||
|
public final fun length ()I
|
||||||
|
public final fun setAccessFlags (I)V
|
||||||
|
public final fun setSourceFile (Ljava/lang/String;)V
|
||||||
|
public final fun setSuperClass (Ljava/lang/String;)V
|
||||||
|
public final fun setType (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableClass$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField : com/android/tools/smali/dexlib2/base/reference/BaseFieldReference, com/android/tools/smali/dexlib2/iface/Field {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Field;)V
|
||||||
|
public fun getAccessFlags ()I
|
||||||
|
public fun getAnnotations ()Ljava/util/Set;
|
||||||
|
public fun getDefiningClass ()Ljava/lang/String;
|
||||||
|
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
||||||
|
public fun getInitialValue ()Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
||||||
|
public synthetic fun getInitialValue ()Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;
|
||||||
|
public fun getName ()Ljava/lang/String;
|
||||||
|
public fun getType ()Ljava/lang/String;
|
||||||
|
public final fun setAccessFlags (I)V
|
||||||
|
public final fun setDefiningClass (Ljava/lang/String;)V
|
||||||
|
public final fun setInitialValue (Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;)V
|
||||||
|
public final fun setName (Ljava/lang/String;)V
|
||||||
|
public final fun setType (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableField$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Field;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod : com/android/tools/smali/dexlib2/base/reference/BaseMethodReference, com/android/tools/smali/dexlib2/iface/Method {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/Method;)V
|
||||||
|
public fun getAccessFlags ()I
|
||||||
|
public fun getAnnotations ()Ljava/util/Set;
|
||||||
|
public fun getDefiningClass ()Ljava/lang/String;
|
||||||
|
public fun getHiddenApiRestrictions ()Ljava/util/Set;
|
||||||
|
public fun getImplementation ()Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;
|
||||||
|
public synthetic fun getImplementation ()Lcom/android/tools/smali/dexlib2/iface/MethodImplementation;
|
||||||
|
public fun getName ()Ljava/lang/String;
|
||||||
|
public fun getParameterTypes ()Ljava/util/List;
|
||||||
|
public fun getParameters ()Ljava/util/List;
|
||||||
|
public fun getReturnType ()Ljava/lang/String;
|
||||||
|
public final fun setAccessFlags (I)V
|
||||||
|
public final fun setDefiningClass (Ljava/lang/String;)V
|
||||||
|
public final fun setName (Ljava/lang/String;)V
|
||||||
|
public final fun setReturnType (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethod$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter : com/android/tools/smali/dexlib2/base/BaseMethodParameter, com/android/tools/smali/dexlib2/iface/MethodParameter {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)V
|
||||||
|
public final fun charAt (I)C
|
||||||
|
public fun get (I)C
|
||||||
|
public fun getAnnotations ()Ljava/util/Set;
|
||||||
|
public fun getLength ()I
|
||||||
|
public fun getName ()Ljava/lang/String;
|
||||||
|
public fun getSignature ()Ljava/lang/String;
|
||||||
|
public fun getType ()Ljava/lang/String;
|
||||||
|
public final fun length ()I
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/MethodParameter;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethodParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseAnnotationEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)V
|
||||||
|
public fun getElements ()Ljava/util/Set;
|
||||||
|
public fun getType ()Ljava/lang/String;
|
||||||
|
public final fun setType (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/AnnotationEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableAnnotationEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseArrayEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)V
|
||||||
|
public fun getValue ()Ljava/util/List;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ArrayEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableArrayEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseBooleanEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)V
|
||||||
|
public fun getValue ()Z
|
||||||
|
public final fun setValue (Z)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/BooleanEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableBooleanEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseByteEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)V
|
||||||
|
public fun getValue ()B
|
||||||
|
public final fun setValue (B)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseCharEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)V
|
||||||
|
public fun getValue ()C
|
||||||
|
public final fun setValue (C)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/CharEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableCharEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseDoubleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)V
|
||||||
|
public fun getValue ()D
|
||||||
|
public final fun setValue (D)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/DoubleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableDoubleEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue : com/android/tools/smali/dexlib2/iface/value/EncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseEnumEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)V
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
||||||
|
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/EnumEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEnumEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFieldEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)V
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;
|
||||||
|
public fun getValueType ()I
|
||||||
|
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/FieldReference;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FieldEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFieldEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseFloatEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)V
|
||||||
|
public fun getValue ()F
|
||||||
|
public final fun setValue (F)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/FloatEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableFloatEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseIntEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)V
|
||||||
|
public fun getValue ()I
|
||||||
|
public final fun setValue (I)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/IntEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableIntEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseLongEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)V
|
||||||
|
public fun getValue ()J
|
||||||
|
public final fun setValue (J)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/LongEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableLongEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)V
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;
|
||||||
|
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodReference;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodHandleEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)V
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;
|
||||||
|
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodHandleReference;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodHandleEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodHandleEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseMethodTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)V
|
||||||
|
public fun getValue ()Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;
|
||||||
|
public final fun setValue (Lcom/android/tools/smali/dexlib2/iface/reference/MethodProtoReference;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/MethodTypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableMethodTypeEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseNullEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion;
|
||||||
|
public fun <init> ()V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableNullEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseShortEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)V
|
||||||
|
public fun getValue ()S
|
||||||
|
public final fun setValue (S)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ShortEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableShortEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseStringEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/StringEncodedValue;)V
|
||||||
|
public fun getValue ()Ljava/lang/String;
|
||||||
|
public final fun setValue (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableStringEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/ByteEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableByteEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue : com/android/tools/smali/dexlib2/base/value/BaseTypeEncodedValue, app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableEncodedValue {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion;
|
||||||
|
public fun <init> (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)V
|
||||||
|
public fun getValue ()Ljava/lang/String;
|
||||||
|
public final fun setValue (Ljava/lang/String;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue$Companion {
|
||||||
|
public final fun toMutable (Lcom/android/tools/smali/dexlib2/iface/value/TypeEncodedValue;)Lapp/revanced/patcher/util/proxy/mutableTypes/encodedValue/MutableTypeEncodedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/smali/ExternalLabel {
|
||||||
|
public fun <init> (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)V
|
||||||
|
public final fun copy (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
||||||
|
public static synthetic fun copy$default (Lapp/revanced/patcher/util/smali/ExternalLabel;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;ILjava/lang/Object;)Lapp/revanced/patcher/util/smali/ExternalLabel;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
|
public fun hashCode ()I
|
||||||
|
public fun toString ()Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler {
|
||||||
|
public static final field Companion Lapp/revanced/patcher/util/smali/InlineSmaliCompiler$Companion;
|
||||||
|
public fun <init> ()V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/smali/InlineSmaliCompiler$Companion {
|
||||||
|
public final fun compile (Ljava/lang/String;Ljava/lang/String;IZ)Ljava/util/List;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/util/smali/InlineSmaliCompilerKt {
|
||||||
|
public static final fun toInstruction (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||||
|
public static synthetic fun toInstruction$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;
|
||||||
|
public static final fun toInstructions (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)Ljava/util/List;
|
||||||
|
public static synthetic fun toInstructions$default (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/Object;)Ljava/util/List;
|
||||||
|
}
|
||||||
|
|
||||||
42
arsclib-utils/.gitignore
vendored
42
arsclib-utils/.gitignore
vendored
@@ -1,42 +0,0 @@
|
|||||||
.gradle
|
|
||||||
build/
|
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
.idea/modules.xml
|
|
||||||
.idea/jarRepositories.xml
|
|
||||||
.idea/compiler.xml
|
|
||||||
.idea/libraries/
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
out/
|
|
||||||
!**/src/main/**/out/
|
|
||||||
!**/src/test/**/out/
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
bin/
|
|
||||||
!**/src/main/**/bin/
|
|
||||||
!**/src/test/**/bin/
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
### Mac OS ###
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
`maven-publish`
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("io.github.reandroid:ARSCLib:1.1.7")
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(11)
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package app.revanced.arsc
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when there is an error with APK resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
sealed class ApkResourceException(message: String, throwable: Throwable? = null) : Exception(message, throwable) {
|
|
||||||
/**
|
|
||||||
* An exception when locking resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Locked(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when writing resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Write(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when reading resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Read(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
/**
|
|
||||||
* An exception when decoding resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Decode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when encoding resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Encode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a reference could not be resolved.
|
|
||||||
*
|
|
||||||
* @param reference The invalid reference.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class InvalidReference(reference: String, throwable: Throwable? = null) :
|
|
||||||
ApkResourceException("Failed to resolve: $reference", throwable) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a reference could not be resolved.
|
|
||||||
*
|
|
||||||
* @param type The type of the reference.
|
|
||||||
* @param name The name of the reference.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
constructor(type: String, name: String, throwable: Throwable? = null) : this("@$type/$name", throwable)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when the Apk file not have a resource table, but was expected to have one.
|
|
||||||
*/
|
|
||||||
class MissingResourceTable : ApkResourceException("Apk does not have a resource table.")
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package app.revanced.arsc.archive
|
|
||||||
|
|
||||||
import app.revanced.arsc.resource.ResourceContainer
|
|
||||||
import com.reandroid.apk.ApkModule
|
|
||||||
import com.reandroid.apk.DexFileInputSource
|
|
||||||
import com.reandroid.archive.InputSource
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Flushable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class for reading/writing files in an [ApkModule].
|
|
||||||
*
|
|
||||||
* @param module The [ApkModule] to operate on.
|
|
||||||
*/
|
|
||||||
class Archive(internal val module: ApkModule) : Flushable {
|
|
||||||
val mainPackageResources = ResourceContainer(this, module.tableBlock)
|
|
||||||
|
|
||||||
fun save(output: File) {
|
|
||||||
flush()
|
|
||||||
module.writeApk(output)
|
|
||||||
}
|
|
||||||
fun readDexFiles(): MutableList<DexFileInputSource> = module.listDexFiles()
|
|
||||||
fun write(inputSource: InputSource) = module.apkArchive.add(inputSource) // Overwrites existing files.
|
|
||||||
fun read(name: String): InputSource? = module.apkArchive.getInputSource(name)
|
|
||||||
override fun flush() = mainPackageResources.flush()
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package app.revanced.arsc.logging
|
|
||||||
interface Logger {
|
|
||||||
fun error(msg: String)
|
|
||||||
fun warn(msg: String)
|
|
||||||
fun info(msg: String)
|
|
||||||
fun trace(msg: String)
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import com.reandroid.arsc.coder.EncodeResult
|
|
||||||
import com.reandroid.arsc.coder.ValueDecoder
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.arsc.value.ValueType
|
|
||||||
import com.reandroid.arsc.value.array.ArrayBag
|
|
||||||
import com.reandroid.arsc.value.array.ArrayBagItem
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsBag
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsBagItem
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsQuantity
|
|
||||||
import com.reandroid.arsc.value.style.StyleBag
|
|
||||||
import com.reandroid.arsc.value.style.StyleBagItem
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A resource value.
|
|
||||||
*/
|
|
||||||
sealed class Resource {
|
|
||||||
internal abstract fun write(entry: Entry, resources: ResourceContainer)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val Resource.isComplex get() = when (this) {
|
|
||||||
is Scalar -> false
|
|
||||||
is Complex -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple resource.
|
|
||||||
*/
|
|
||||||
open class Scalar internal constructor(private val valueType: ValueType, private val value: Int) : Resource() {
|
|
||||||
protected open fun data(resources: ResourceContainer) = value
|
|
||||||
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
entry.setValueAsRaw(valueType, data(resources))
|
|
||||||
}
|
|
||||||
|
|
||||||
internal open fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.create(valueType, data(resources))
|
|
||||||
internal open fun toStyleItem(resources: ResourceContainer) = StyleBagItem.create(valueType, data(resources))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A marker class for complex resources.
|
|
||||||
*/
|
|
||||||
sealed class Complex : Resource()
|
|
||||||
|
|
||||||
private fun encoded(encodeResult: EncodeResult?) = encodeResult?.let { Scalar(it.valueType, it.value) }
|
|
||||||
?: throw ApkResourceException.Encode("Failed to encode value")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a color.
|
|
||||||
*
|
|
||||||
* @param hex The hex value of the color.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun color(hex: String) = encoded(ValueDecoder.encodeColor(hex))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a dimension or fraction.
|
|
||||||
*
|
|
||||||
* @param value The dimension value such as 24dp.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun dimension(value: String) = encoded(ValueDecoder.encodeDimensionOrFraction(value))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a boolean resource.
|
|
||||||
*
|
|
||||||
* @param value The boolean.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun boolean(value: Boolean) = Scalar(ValueType.INT_BOOLEAN, if (value) -Int.MAX_VALUE else 0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a float.
|
|
||||||
*
|
|
||||||
* @param n The number to encode.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun float(n: Float) = Scalar(ValueType.FLOAT, n.toBits())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an integer [Resource].
|
|
||||||
*
|
|
||||||
* @param n The number to encode.
|
|
||||||
* @return The integer [Resource].
|
|
||||||
*/
|
|
||||||
fun integer(n: Int) = Scalar(ValueType.INT_DEC, n)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a reference [Resource].
|
|
||||||
*
|
|
||||||
* @param resourceId The target resource.
|
|
||||||
* @return The reference resource.
|
|
||||||
*/
|
|
||||||
fun reference(resourceId: Int) = Scalar(ValueType.REFERENCE, resourceId)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve and create a reference [Resource].
|
|
||||||
*
|
|
||||||
* @see reference
|
|
||||||
* @param ref The reference string to resolve.
|
|
||||||
* @param resourceTable The resource table to resolve the reference with.
|
|
||||||
* @return The reference resource.
|
|
||||||
*/
|
|
||||||
fun reference(resourceTable: ResourceTable, ref: String) = reference(resourceTable.resolve(ref))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array [Resource].
|
|
||||||
*
|
|
||||||
* @param elements The elements of the array.
|
|
||||||
*/
|
|
||||||
class Array(private val elements: Collection<Scalar>) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
ArrayBag.create(entry).addAll(elements.map { it.toArrayItem(resources) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A style resource.
|
|
||||||
*
|
|
||||||
* @param elements The attributes to override.
|
|
||||||
* @param parent A reference to the parent style.
|
|
||||||
*/
|
|
||||||
class Style(private val elements: Map<String, Scalar>, private val parent: String? = null) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
val resTable = resources.resourceTable
|
|
||||||
val style = StyleBag.create(entry)
|
|
||||||
parent?.let {
|
|
||||||
style.parentId = resTable.resolve(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
style.putAll(
|
|
||||||
elements.asIterable().associate {
|
|
||||||
StyleBag.resolve(resTable.encodeMaterials, it.key) to it.value.toStyleItem(resources)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A quantity string [Resource].
|
|
||||||
*
|
|
||||||
* @param elements A map of the quantity to the corresponding string.
|
|
||||||
*/
|
|
||||||
class Plurals(private val elements: Map<String, String>) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
val plurals = PluralsBag.create(entry)
|
|
||||||
|
|
||||||
plurals.putAll(elements.asIterable().associate { (k, v) ->
|
|
||||||
PluralsQuantity.value(k) to PluralsBagItem.string(resources.getOrCreateString(v))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string [Resource].
|
|
||||||
*
|
|
||||||
* @param value The string value.
|
|
||||||
*/
|
|
||||||
class StringResource(val value: String) : Scalar(ValueType.STRING, 0) {
|
|
||||||
private fun tableString(resources: ResourceContainer) = resources.getOrCreateString(value)
|
|
||||||
|
|
||||||
override fun data(resources: ResourceContainer) = tableString(resources).index
|
|
||||||
override fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.string(tableString(resources))
|
|
||||||
override fun toStyleItem(resources: ResourceContainer) = StyleBagItem.string(tableString(resources))
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.archive.Archive
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeUtil
|
|
||||||
import com.reandroid.arsc.chunk.TableBlock
|
|
||||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.arsc.value.ResConfig
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Flushable
|
|
||||||
|
|
||||||
class ResourceContainer(private val archive: Archive, internal val tableBlock: TableBlock) : Flushable {
|
|
||||||
private val packageBlock = tableBlock.pickOne() // Pick the main package block.
|
|
||||||
internal lateinit var resourceTable: ResourceTable // TODO: Set this.
|
|
||||||
|
|
||||||
private val lockedResourceFileNames = mutableSetOf<String>()
|
|
||||||
|
|
||||||
private fun lock(resourceFile: ResourceFile) {
|
|
||||||
if (resourceFile.name in lockedResourceFileNames) {
|
|
||||||
throw ApkResourceException.Locked("Resource file ${resourceFile.name} is already locked.")
|
|
||||||
}
|
|
||||||
|
|
||||||
lockedResourceFileNames.add(resourceFile.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unlock(resourceFile: ResourceFile) {
|
|
||||||
lockedResourceFileNames.remove(resourceFile.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun <T : ResourceFile> openResource(name: String): ResourceFileEditor<T> {
|
|
||||||
val inputSource = archive.read(name)
|
|
||||||
?: throw ApkResourceException.Read("Resource file $name not found.")
|
|
||||||
|
|
||||||
val resourceFile = when {
|
|
||||||
ResXmlDocument.isResXmlBlock(inputSource.openStream()) -> {
|
|
||||||
val xmlDocument = archive.module
|
|
||||||
.loadResXmlDocument(inputSource)
|
|
||||||
.decodeToXml(resourceTable.entryStore, packageBlock.id)
|
|
||||||
|
|
||||||
ResourceFile.XmlResourceFile(name, xmlDocument)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val bytes = inputSource.openStream().use { it.readAllBytes() }
|
|
||||||
|
|
||||||
ResourceFile.BinaryResourceFile(name, bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return ResourceFileEditor(resourceFile as T).also {
|
|
||||||
lockedResourceFileNames.add(name)
|
|
||||||
}
|
|
||||||
} catch (e: ClassCastException) {
|
|
||||||
throw ApkResourceException.Decode("Resource file $name is not ${resourceFile::class}.", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ResourceFileEditor<T : ResourceFile> internal constructor(
|
|
||||||
private val resourceFile: T,
|
|
||||||
) : Closeable {
|
|
||||||
fun use(block: (T) -> Unit) = block(resourceFile)
|
|
||||||
override fun close() {
|
|
||||||
lockedResourceFileNames.remove(resourceFile.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush() {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a resource file, creating it if the file does not exist.
|
|
||||||
*
|
|
||||||
* @param path The resource file path.
|
|
||||||
* @return The corresponding [ResourceFiles],
|
|
||||||
*/
|
|
||||||
fun openFile(path: String) = ResourceFiles(createHandle(path), archive)
|
|
||||||
|
|
||||||
private fun getPackageBlock() = packageBlock ?: throw ApkResourceException.MissingResourceTable
|
|
||||||
|
|
||||||
internal fun getOrCreateString(value: String) =
|
|
||||||
tableBlock?.stringPool?.getOrCreate(value) ?: throw ApkResourceException.MissingResourceTable
|
|
||||||
|
|
||||||
private fun Entry.set(resource: Resource) {
|
|
||||||
val existingEntryNameReference = specReference
|
|
||||||
|
|
||||||
// Sets this.specReference if the entry is not yet initialized.
|
|
||||||
// Sets this.specReference to 0 if the resource type of the existing entry changes.
|
|
||||||
ensureComplex(resource.isComplex)
|
|
||||||
|
|
||||||
if (existingEntryNameReference != 0) {
|
|
||||||
// Preserve the entry name by restoring the previous spec block reference (if present).
|
|
||||||
specReference = existingEntryNameReference
|
|
||||||
}
|
|
||||||
|
|
||||||
resource.write(this, this@ResourceContainer)
|
|
||||||
resourceTable.registerChanged(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an [Entry] from the resource table.
|
|
||||||
*
|
|
||||||
* @param type The resource type.
|
|
||||||
* @param name The resource name.
|
|
||||||
* @param qualifiers The variant to use.
|
|
||||||
*/
|
|
||||||
private fun getEntry(type: String, name: String, qualifiers: String?): Entry? {
|
|
||||||
val resourceId = try {
|
|
||||||
resourceTable.resolve("@$type/$name")
|
|
||||||
} catch (_: ApkResourceException.InvalidReference) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = ResConfig.parse(qualifiers)
|
|
||||||
return tableBlock?.resolveReference(resourceId)?.singleOrNull { it.resConfig == config }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a [ResourceFiles.Handle] that can be used to open a [ResourceFiles].
|
|
||||||
* This may involve looking it up in the resource table to find the actual location in the archive.
|
|
||||||
*
|
|
||||||
* @param path The path of the resource.
|
|
||||||
*/
|
|
||||||
private fun createHandle(path: String): ResourceFiles.Handle {
|
|
||||||
if (path.startsWith("res/values")) throw ApkResourceException.Decode("Decoding the resource table as a file is not supported")
|
|
||||||
|
|
||||||
var onClose = {}
|
|
||||||
var archivePath = path
|
|
||||||
|
|
||||||
if (tableBlock != null && path.startsWith("res/") && path.count { it == '/' } == 2) {
|
|
||||||
val file = File(path)
|
|
||||||
|
|
||||||
val qualifiers = EncodeUtil.getQualifiersFromResFile(file)
|
|
||||||
val type = EncodeUtil.getTypeNameFromResFile(file)
|
|
||||||
val name = file.nameWithoutExtension
|
|
||||||
|
|
||||||
// The resource file names that the app developers used may have been minified, so we have to resolve it with the resource table.
|
|
||||||
// Example: res/drawable-hdpi/icon.png -> res/4a.png
|
|
||||||
getEntry(type, name, qualifiers)?.resValue?.valueAsString?.let {
|
|
||||||
archivePath = it
|
|
||||||
} ?: run {
|
|
||||||
// An entry for this specific resource file was not found in the resource table, so we have to register it after we save.
|
|
||||||
onClose = { setResource(type, name, StringResource(archivePath), qualifiers) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResourceFiles.Handle(path, archivePath, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setResource(type: String, entryName: String, resource: Resource, qualifiers: String? = null) =
|
|
||||||
getPackageBlock().getOrCreate(qualifiers, type, entryName).also { it.set(resource) }.resourceId
|
|
||||||
|
|
||||||
fun setResources(type: String, resources: Map<String, Resource>, configuration: String? = null) {
|
|
||||||
getPackageBlock().getOrCreateSpecTypePair(type).getOrCreateTypeBlock(configuration).apply {
|
|
||||||
resources.forEach { (entryName, resource) -> getOrCreateEntry(entryName).set(resource) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush() {
|
|
||||||
packageBlock?.name = archive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.archive.Archive
|
|
||||||
import com.reandroid.archive.InputSource
|
|
||||||
import com.reandroid.xml.XMLDocument
|
|
||||||
import com.reandroid.xml.XMLException
|
|
||||||
import java.io.*
|
|
||||||
|
|
||||||
|
|
||||||
abstract class ResourceFile(val name: String) {
|
|
||||||
internal var realName: String? = null
|
|
||||||
|
|
||||||
class XmlResourceFile(name: String, val document: XMLDocument) : ResourceFile(name)
|
|
||||||
class BinaryResourceFile(name: String, var bytes: ByteArray) : ResourceFile(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceFiles private constructor(
|
|
||||||
) : Closeable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiate a [ResourceFiles].
|
|
||||||
*
|
|
||||||
* @param handle The [Handle] associated with this file.
|
|
||||||
* @param archive The [Archive] that the file resides in.
|
|
||||||
*/
|
|
||||||
internal constructor(handle: Handle, archive: Archive) : this(
|
|
||||||
handle,
|
|
||||||
archive,
|
|
||||||
try {
|
|
||||||
archive.read(handle.archivePath)
|
|
||||||
} catch (e: XMLException) {
|
|
||||||
throw ApkResourceException.Decode("Failed to decode XML while reading ${handle.virtualPath}", e)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw ApkResourceException.Decode("Could not read ${handle.virtualPath}", e)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val DEFAULT_BUFFER_SIZE = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
var contents = readResult?.data ?: ByteArray(0)
|
|
||||||
set(value) {
|
|
||||||
pendingWrite = true
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
val exists = readResult != null
|
|
||||||
|
|
||||||
override fun toString() = handle.virtualPath
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (pendingWrite) {
|
|
||||||
val path = handle.archivePath
|
|
||||||
|
|
||||||
if (isXmlResource) archive.writeXml(
|
|
||||||
path,
|
|
||||||
try {
|
|
||||||
XMLDocument.load(inputStream())
|
|
||||||
} catch (e: XMLException) {
|
|
||||||
throw ApkResourceException.Encode("Failed to parse XML while writing ${handle.virtualPath}", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
) else archive.writeRaw(path, contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.onClose()
|
|
||||||
|
|
||||||
|
|
||||||
archive.unlock(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inputStream(): InputStream = ByteArrayInputStream(contents)
|
|
||||||
|
|
||||||
fun outputStream(bufferSize: Int = DEFAULT_BUFFER_SIZE): OutputStream =
|
|
||||||
object : ByteArrayOutputStream(bufferSize) {
|
|
||||||
override fun close() {
|
|
||||||
this@ResourceFiles.contents = if (buf.size > count) buf.copyOf(count) else buf
|
|
||||||
super.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param virtualPath The resource file path. Example: /res/drawable-hdpi/icon.png.
|
|
||||||
* @param archivePath The actual file path in the archive. Example: res/4a.png.
|
|
||||||
* @param onClose An action to perform when the file associated with this handle is closed
|
|
||||||
*/
|
|
||||||
internal data class Handle(val virtualPath: String, val archivePath: String, val onClose: () -> Unit)
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeException
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeMaterials
|
|
||||||
import com.reandroid.arsc.util.FrameworkTable
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.common.TableEntryStore
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A high-level API for resolving resources in the resource table, which spans the entire ApkBundle.
|
|
||||||
*/
|
|
||||||
class ResourceTable(base: ResourceContainer, all: Sequence<ResourceContainer>) {
|
|
||||||
private val packageName = base.tableBlock!!.name
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [TableEntryStore] used to decode XML.
|
|
||||||
*/
|
|
||||||
internal val entryStore = TableEntryStore()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [EncodeMaterials] to use for resolving resources and encoding XML.
|
|
||||||
*/
|
|
||||||
internal val encodeMaterials: EncodeMaterials = object : EncodeMaterials() {
|
|
||||||
/*
|
|
||||||
Our implementation is more efficient because it does not have to loop through every single entry group
|
|
||||||
when the resource id cannot be found in the TableIdentifier, which does not update when you create a new resource.
|
|
||||||
It also looks at the entire table instead of just the current package.
|
|
||||||
*/
|
|
||||||
override fun resolveLocalResourceId(type: String, name: String) = resolveLocal(type, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource mappings which are generated when the ApkBundle is created.
|
|
||||||
*/
|
|
||||||
private val tableIdentifier = encodeMaterials.tableIdentifier
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A table of all the resources that have been changed or added.
|
|
||||||
*/
|
|
||||||
private val modifiedResources = HashMap<String, HashMap<String, Int>>()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a resource id for the specified resource.
|
|
||||||
* Cannot resolve resources from the android framework.
|
|
||||||
*
|
|
||||||
* @param type The type of the resource.
|
|
||||||
* @param name The name of the resource.
|
|
||||||
* @return The id of the resource.
|
|
||||||
*/
|
|
||||||
fun resolveLocal(type: String, name: String) =
|
|
||||||
modifiedResources[type]?.get(name)
|
|
||||||
?: tableIdentifier.get(packageName, type, name)?.resourceId
|
|
||||||
?: throw ApkResourceException.InvalidReference(
|
|
||||||
type,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a resource id for the specified resource.
|
|
||||||
*
|
|
||||||
* @param reference The resource reference string.
|
|
||||||
* @return The id of the resource.
|
|
||||||
*/
|
|
||||||
fun resolve(reference: String) = try {
|
|
||||||
encodeMaterials.resolveReference(reference)
|
|
||||||
} catch (e: EncodeException) {
|
|
||||||
throw ApkResourceException.InvalidReference(reference, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the [ResourceTable] that an [Entry] has been created or modified.
|
|
||||||
*/
|
|
||||||
internal fun registerChanged(entry: Entry) {
|
|
||||||
modifiedResources.getOrPut(entry.typeName, ::HashMap)[entry.name] = entry.resourceId
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
all.forEach {
|
|
||||||
it.tableBlock?.let { table ->
|
|
||||||
entryStore.add(table)
|
|
||||||
tableIdentifier.load(table)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.resourceTable = this
|
|
||||||
}
|
|
||||||
|
|
||||||
base.also {
|
|
||||||
encodeMaterials.currentPackage = it.tableBlock
|
|
||||||
|
|
||||||
it.tableBlock!!.frameWorks.forEach { fw ->
|
|
||||||
if (fw is FrameworkTable) {
|
|
||||||
entryStore.add(fw)
|
|
||||||
encodeMaterials.addFramework(fw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package app.revanced.arsc.xml
|
|
||||||
|
|
||||||
import app.revanced.arsc.resource.ResourceContainer
|
|
||||||
import app.revanced.arsc.resource.boolean
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeException
|
|
||||||
import com.reandroid.apk.xmlencoder.XMLEncodeSource
|
|
||||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
|
||||||
import com.reandroid.xml.XMLDocument
|
|
||||||
import com.reandroid.xml.XMLElement
|
|
||||||
import com.reandroid.xml.source.XMLDocumentSource
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Archive input source to lazily encode an [XMLDocument] after it has been modified.
|
|
||||||
*
|
|
||||||
* @param name The file name of this input source.
|
|
||||||
* @param document The [XMLDocument] to encode.
|
|
||||||
* @param resources The [ResourceContainer] to use for encoding.
|
|
||||||
*/
|
|
||||||
internal class LazyXMLEncodeSource(
|
|
||||||
name: String,
|
|
||||||
val document: XMLDocument,
|
|
||||||
private val resources: ResourceContainer
|
|
||||||
) : XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document)) {
|
|
||||||
private var encoded = false
|
|
||||||
|
|
||||||
override fun getResXmlBlock(): ResXmlDocument {
|
|
||||||
if (encoded) return super.getResXmlBlock()
|
|
||||||
|
|
||||||
XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document))
|
|
||||||
|
|
||||||
fun XMLElement.registerIds() {
|
|
||||||
listAttributes().forEach { attr ->
|
|
||||||
if (!attr.value.startsWith("@+id/")) return@forEach
|
|
||||||
|
|
||||||
val name = attr.value.split('/').last()
|
|
||||||
resources.setResource("id", name, boolean(false))
|
|
||||||
attr.value = "@id/$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
listChildElements().forEach { it.registerIds() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle all @+id/id_name references in the document.
|
|
||||||
document.documentElement.registerIds()
|
|
||||||
|
|
||||||
encoded = true
|
|
||||||
|
|
||||||
// This will call XMLEncodeSource.getResXmlBlock(),
|
|
||||||
// which will encode the document if it has not already been encoded.
|
|
||||||
try {
|
|
||||||
return super.getResXmlBlock()
|
|
||||||
} catch (e: EncodeException) {
|
|
||||||
throw EncodeException("Failed to encode $name", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,86 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.20" apply false
|
kotlin("jvm") version "1.9.0"
|
||||||
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
|
`maven-publish`
|
||||||
|
signing
|
||||||
|
java
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group = "app.revanced"
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
processResources {
|
||||||
|
expand("projectVersion" to project.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
maven { url = uri("https://jitpack.io") }
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
implementation(libs.xpp3)
|
||||||
|
implementation(libs.smali)
|
||||||
|
implementation(libs.multidexlib2)
|
||||||
|
implementation(libs.apktool.lib)
|
||||||
|
implementation(libs.kotlin.reflect)
|
||||||
|
|
||||||
|
compileOnly(libs.android)
|
||||||
|
|
||||||
|
testImplementation(libs.kotlin.test)
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("revanced-patcher-publication") {
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
version = project.version.toString()
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "ReVanced Patcher"
|
||||||
|
description = "Patcher used by ReVanced."
|
||||||
|
url = "https://revanced.app"
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = "GNU General Public License v3.0"
|
||||||
|
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "ReVanced"
|
||||||
|
name = "ReVanced"
|
||||||
|
email = "contact@revanced.app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
||||||
|
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
||||||
|
url = "https://github.com/revanced/revanced-patcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching=true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 11.0.4
|
version = 18.0.0-dev.2
|
||||||
|
|||||||
23
gradle/libs.versions.toml
Normal file
23
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[versions]
|
||||||
|
android = "4.1.1.4"
|
||||||
|
kotlin-reflect = "1.9.0"
|
||||||
|
apktool-lib = "2.9.1"
|
||||||
|
kotlin-test = "1.8.20-RC"
|
||||||
|
kotlinx-coroutines-core = "1.7.3"
|
||||||
|
multidexlib2 = "3.0.3.r3"
|
||||||
|
smali = "3.0.3"
|
||||||
|
xpp3 = "1.1.4c"
|
||||||
|
binary-compatibility-validator = "0.13.2"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
android = { module = "com.google.android:android", version.ref = "android" }
|
||||||
|
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" }
|
||||||
|
apktool-lib = { module = "app.revanced:apktool", version.ref = "apktool-lib" }
|
||||||
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
|
||||||
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" }
|
||||||
|
multidexlib2 = { module = "app.revanced:multidexlib2", version.ref = "multidexlib2" }
|
||||||
|
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||||
|
xpp3 = { module = "xpp3:xpp3", version.ref = "xpp3" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
`maven-publish`
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("xpp3:xpp3:1.1.4c")
|
|
||||||
implementation("app.revanced:smali:2.5.3-a3836654")
|
|
||||||
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
|
||||||
implementation("io.github.reandroid:ARSCLib:1.1.7")
|
|
||||||
implementation(project(":arsclib-utils"))
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
|
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
|
|
||||||
|
|
||||||
compileOnly("com.google.android:android:4.1.1.4")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processResources {
|
|
||||||
expand("projectVersion" to project.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(11)
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
if (System.getenv("GITHUB_ACTOR") != null)
|
|
||||||
maven {
|
|
||||||
name = "GitHubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
|
||||||
credentials {
|
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mavenLocal()
|
|
||||||
}
|
|
||||||
publications {
|
|
||||||
register<MavenPublication>("gpr") {
|
|
||||||
from(components["java"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
rootProject.name = "revanced-patcher"
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.arsc.resource.ResourceContainer
|
|
||||||
import app.revanced.patcher.apk.Apk
|
|
||||||
import app.revanced.patcher.apk.ApkBundle
|
|
||||||
import app.revanced.arsc.resource.ResourceFiles
|
|
||||||
import app.revanced.patcher.util.method.MethodWalker
|
|
||||||
import org.jf.dexlib2.iface.Method
|
|
||||||
import org.w3c.dom.Document
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.StringWriter
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
import javax.xml.transform.TransformerFactory
|
|
||||||
import javax.xml.transform.dom.DOMSource
|
|
||||||
import javax.xml.transform.stream.StreamResult
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A common class to constrain [Context] to [BytecodeContext] and [ResourceContext].
|
|
||||||
* @param apkBundle The [ApkBundle] for this context.
|
|
||||||
*/
|
|
||||||
sealed class Context(val apkBundle: ApkBundle)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context for the bytecode of an [Apk.Base] file.
|
|
||||||
*
|
|
||||||
* @param apkBundle The [ApkBundle] for this context.
|
|
||||||
*/
|
|
||||||
class BytecodeContext internal constructor(apkBundle: ApkBundle) : Context(apkBundle) {
|
|
||||||
/**
|
|
||||||
* The list of classes.
|
|
||||||
*/
|
|
||||||
val classes = apkBundle.base.bytecodeData.classes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a [MethodWalker] instance for the current [BytecodeContext].
|
|
||||||
*
|
|
||||||
* @param startMethod The method to start at.
|
|
||||||
* @return A [MethodWalker] instance.
|
|
||||||
*/
|
|
||||||
fun traceMethodCalls(startMethod: Method) = MethodWalker(this, startMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context for [Apk] file resources.
|
|
||||||
*
|
|
||||||
* @param apkBundle the [ApkBundle] for this context.
|
|
||||||
*/
|
|
||||||
class ResourceContext internal constructor(apkBundle: ApkBundle) : Context(apkBundle) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open an [DomFileEditor] for a given DOM file.
|
|
||||||
*
|
|
||||||
* @param inputStream The input stream to read the DOM file from.
|
|
||||||
* @return A [DomFileEditor] instance.
|
|
||||||
*/
|
|
||||||
fun openXmlFile(inputStream: InputStream) = DomFileEditor(inputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a [DomFileEditor] for a resource file in the archive.
|
|
||||||
*
|
|
||||||
* @see [ResourceContainer.openFile]
|
|
||||||
* @param path The resource file path.
|
|
||||||
* @return A [DomFileEditor].
|
|
||||||
*/
|
|
||||||
fun ResourceContainer.openXmlFile(path: String) = DomFileEditor(openFile(path))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for a file that can be edited as a dom document.
|
|
||||||
*
|
|
||||||
* @param inputStream the input stream to read the xml file from.
|
|
||||||
* @param onSave A callback that will be called when the editor is closed to save the file.
|
|
||||||
*/
|
|
||||||
class DomFileEditor internal constructor(
|
|
||||||
private val inputStream: InputStream,
|
|
||||||
private val onSave: ((String) -> Unit)? = null
|
|
||||||
) : Closeable {
|
|
||||||
private var closed: Boolean = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The document of the xml file.
|
|
||||||
*/
|
|
||||||
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
|
||||||
.also(Document::normalize)
|
|
||||||
|
|
||||||
internal constructor(file: ResourceFiles) : this(
|
|
||||||
file.inputStream(),
|
|
||||||
{
|
|
||||||
file.contents = it.toByteArray()
|
|
||||||
file.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the editor and writes back to the file.
|
|
||||||
*/
|
|
||||||
override fun close() {
|
|
||||||
if (closed) return
|
|
||||||
|
|
||||||
inputStream.close()
|
|
||||||
|
|
||||||
onSave?.let { callback ->
|
|
||||||
// Save the updated file.
|
|
||||||
val writer = StringWriter()
|
|
||||||
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), StreamResult(writer))
|
|
||||||
callback(writer.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
closed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.apk.Apk
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
|
|
||||||
import app.revanced.patcher.patch.*
|
|
||||||
import app.revanced.patcher.util.VersionReader
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import lanchon.multidexlib2.BasicDexFileNamer
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
typealias ExecutedPatchResults = Flow<Pair<String, PatchException?>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ReVanced Patcher.
|
|
||||||
* @param options The options for the patcher.
|
|
||||||
* @param patches The patches to use.
|
|
||||||
* @param integrations The integrations to merge if necessary. Must be dex files or dex file container such as ZIP, APK or DEX files.
|
|
||||||
*/
|
|
||||||
class Patcher(private val options: PatcherOptions, patches: Iterable<PatchClass>, integrations: Iterable<File>) :
|
|
||||||
Function<Boolean, ExecutedPatchResults> {
|
|
||||||
private val context = PatcherContext(options, patches.toList(), integrations)
|
|
||||||
private val logger = options.logger
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* The version of the ReVanced Patcher.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
val version = VersionReader.read()
|
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
|
||||||
internal val dexFileNamer = BasicDexFileNamer()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
/**
|
|
||||||
* Returns true if at least one patches or its dependencies matches the given predicate.
|
|
||||||
*/
|
|
||||||
fun PatchClass.anyRecursively(predicate: (PatchClass) -> Boolean): Boolean =
|
|
||||||
predicate(this) || dependencies?.any { it.java.anyRecursively(predicate) } == true
|
|
||||||
|
|
||||||
// Determine if merging integrations is required.
|
|
||||||
for (patch in context.patches) {
|
|
||||||
if (patch.anyRecursively { it.requiresIntegrations }) {
|
|
||||||
context.integrations.merge = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the patcher.
|
|
||||||
*
|
|
||||||
* @param stopOnError If true, the patches will stop on the first error.
|
|
||||||
* @return A pair of the name of the [Patch] and a [PatchException] if it failed.
|
|
||||||
*/
|
|
||||||
override fun apply(stopOnError: Boolean) = flow {
|
|
||||||
/**
|
|
||||||
* Execute a [Patch] and its dependencies recursively.
|
|
||||||
*
|
|
||||||
* @param patchClass The [Patch] to execute.
|
|
||||||
* @param executedPatches A map of [Patch]es paired to a boolean indicating their success, to prevent infinite recursion.
|
|
||||||
*/
|
|
||||||
suspend fun executePatch(
|
|
||||||
patchClass: PatchClass,
|
|
||||||
executedPatches: HashMap<String, ExecutedPatch>
|
|
||||||
) {
|
|
||||||
val patchName = patchClass.patchName
|
|
||||||
|
|
||||||
// If the patch has already executed silently skip it.
|
|
||||||
if (executedPatches.contains(patchName)) {
|
|
||||||
if (!executedPatches[patchName]!!.success)
|
|
||||||
throw PatchException("'$patchName' did not succeed previously")
|
|
||||||
|
|
||||||
logger.trace("Skipping '$patchName' because it has already been executed")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively execute all dependency patches.
|
|
||||||
patchClass.dependencies?.forEach { dependencyClass ->
|
|
||||||
val dependency = dependencyClass.java
|
|
||||||
|
|
||||||
try {
|
|
||||||
executePatch(dependency, executedPatches)
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
throw PatchException(
|
|
||||||
"'$patchName' depends on '${dependency.patchName}' " +
|
|
||||||
"but the following exception was raised: ${throwable.cause?.stackTraceToString() ?: throwable.message}",
|
|
||||||
throwable
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
|
|
||||||
val patchInstance = patchClass.getDeclaredConstructor().newInstance()
|
|
||||||
|
|
||||||
// TODO: implement this in a more polymorphic way.
|
|
||||||
val patchContext = if (isResourcePatch) {
|
|
||||||
context.resourceContext
|
|
||||||
} else {
|
|
||||||
context.bytecodeContext.apply {
|
|
||||||
val bytecodePatch = patchInstance as BytecodePatch
|
|
||||||
bytecodePatch.fingerprints?.resolveUsingLookupMap(context.bytecodeContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.trace("Executing '$patchName' of type: ${if (isResourcePatch) "resource" else "bytecode"}")
|
|
||||||
|
|
||||||
var success = false
|
|
||||||
try {
|
|
||||||
patchInstance.execute(patchContext)
|
|
||||||
|
|
||||||
success = true
|
|
||||||
} catch (patchException: PatchException) {
|
|
||||||
throw patchException
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
throw PatchException("Unhandled patch exception: ${throwable.message}", throwable)
|
|
||||||
} finally {
|
|
||||||
executedPatches[patchName] = ExecutedPatch(patchInstance, success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.integrations.merge) context.integrations.merge(logger)
|
|
||||||
|
|
||||||
logger.trace("Initialize lookup maps for method MethodFingerprint resolution")
|
|
||||||
|
|
||||||
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
|
|
||||||
|
|
||||||
logger.info("Executing patches")
|
|
||||||
|
|
||||||
// Key is patch name.
|
|
||||||
LinkedHashMap<String, ExecutedPatch>().apply {
|
|
||||||
context.patches.forEach { patch ->
|
|
||||||
var exception: PatchException? = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
executePatch(patch, this)
|
|
||||||
} catch (patchException: PatchException) {
|
|
||||||
exception = patchException
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: only emit if the patch is not a closeable.
|
|
||||||
// If it is a closeable, this should be done when closing the patch.
|
|
||||||
emit(patch.patchName to exception)
|
|
||||||
|
|
||||||
if (stopOnError && exception != null) return@flow
|
|
||||||
}
|
|
||||||
}.let {
|
|
||||||
it.values
|
|
||||||
.filter(ExecutedPatch::success)
|
|
||||||
.map(ExecutedPatch::patchInstance)
|
|
||||||
.filterIsInstance(Closeable::class.java)
|
|
||||||
.asReversed().forEach { patch ->
|
|
||||||
try {
|
|
||||||
patch.close()
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
val patchException =
|
|
||||||
if (throwable is PatchException) throwable
|
|
||||||
else PatchException(throwable)
|
|
||||||
|
|
||||||
val patchName = (patch as Patch<Context>).javaClass.patchName
|
|
||||||
|
|
||||||
logger.error("Failed to close '$patchName': ${patchException.stackTraceToString()}")
|
|
||||||
|
|
||||||
emit(patchName to patchException)
|
|
||||||
|
|
||||||
// This is not failsafe. If a patch throws an exception while closing,
|
|
||||||
// the other patches that depend on it may fail.
|
|
||||||
if (stopOnError) return@flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodFingerprint.clearFingerprintResolutionLookupMaps()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finish patching all [Apk]s.
|
|
||||||
*
|
|
||||||
* @return The [PatcherResult] of the [Patcher].
|
|
||||||
*/
|
|
||||||
fun finish(): PatcherResult {
|
|
||||||
val patchResults = buildList {
|
|
||||||
logger.info("Processing patched apks")
|
|
||||||
options.apkBundle.cleanup(options).forEach { result ->
|
|
||||||
if (result.exception != null) {
|
|
||||||
logger.error("Got exception while processing ${result.apk}: ${result.exception.stackTraceToString()}")
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
|
|
||||||
val patch = result.let {
|
|
||||||
when (it.apk) {
|
|
||||||
is Apk.Base -> PatcherResult.Patch.Base(it.apk)
|
|
||||||
is Apk.Split -> PatcherResult.Patch.Split(it.apk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add(patch)
|
|
||||||
|
|
||||||
logger.info("Patched ${result.apk}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return PatcherResult(patchResults)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A result of executing a [Patch].
|
|
||||||
*
|
|
||||||
* @param patchInstance The instance of the [Patch] that was executed.
|
|
||||||
* @param success The result of the [Patch].
|
|
||||||
*/
|
|
||||||
internal data class ExecutedPatch(val patchInstance: Patch<Context>, val success: Boolean)
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.logging.Logger
|
|
||||||
import app.revanced.patcher.patch.PatchClass
|
|
||||||
import app.revanced.patcher.util.ClassMerger.merge
|
|
||||||
import lanchon.multidexlib2.MultiDexIO
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class PatcherContext(
|
|
||||||
options: PatcherOptions,
|
|
||||||
internal val patches: List<PatchClass>,
|
|
||||||
integrations: Iterable<File>
|
|
||||||
) {
|
|
||||||
internal val integrations = Integrations(this, integrations)
|
|
||||||
internal val bytecodeContext = BytecodeContext(options.apkBundle)
|
|
||||||
internal val resourceContext = ResourceContext(options.apkBundle)
|
|
||||||
|
|
||||||
internal class Integrations(val context: PatcherContext, private val dexContainers: Iterable<File>) {
|
|
||||||
var merge = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge integrations.
|
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
|
||||||
fun merge(logger: Logger) {
|
|
||||||
context.bytecodeContext.classes.apply {
|
|
||||||
for (integrations in dexContainers) {
|
|
||||||
logger.info("Merging $integrations")
|
|
||||||
|
|
||||||
for (classDef in MultiDexIO.readDexFile(true, integrations, Patcher.dexFileNamer, null, null).classes) {
|
|
||||||
val type = classDef.type
|
|
||||||
|
|
||||||
val existingClassIndex = this.indexOfFirst { it.type == type }
|
|
||||||
if (existingClassIndex == -1) {
|
|
||||||
logger.trace("Merging type $type")
|
|
||||||
add(classDef)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
logger.trace("Type $type exists. Adding missing methods and fields.")
|
|
||||||
|
|
||||||
get(existingClassIndex).apply {
|
|
||||||
merge(classDef, context.bytecodeContext, logger).let { mergedClass ->
|
|
||||||
if (mergedClass !== this) // referential equality check
|
|
||||||
set(existingClassIndex, mergedClass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.apk.ApkBundle
|
|
||||||
import app.revanced.patcher.logging.Logger
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for the [Patcher].
|
|
||||||
* @param apkBundle The [ApkBundle].
|
|
||||||
* @param logger Custom logger implementation for the [Patcher].
|
|
||||||
*/
|
|
||||||
class PatcherOptions(
|
|
||||||
internal val apkBundle: ApkBundle,
|
|
||||||
internal val logger: Logger = Logger.Nop
|
|
||||||
)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.patcher
|
|
||||||
|
|
||||||
import app.revanced.patcher.apk.Apk
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patcher.
|
|
||||||
* @param apkFiles The patched [Apk] files.
|
|
||||||
*/
|
|
||||||
data class PatcherResult(val apkFiles: List<Patch>) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patch.
|
|
||||||
*
|
|
||||||
* @param apk The patched [Apk] file.
|
|
||||||
*/
|
|
||||||
sealed class Patch(val apk: Apk) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patch of an [Apk.Split] file.
|
|
||||||
*
|
|
||||||
* @param apk The patched [Apk.Split] file.
|
|
||||||
*/
|
|
||||||
class Split(apk: Apk.Split) : Patch(apk)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of a patch of an [Apk.Split] file.
|
|
||||||
*
|
|
||||||
* @param apk The patched [Apk.Base] file.
|
|
||||||
*/
|
|
||||||
class Base(apk: Apk.Base) : Patch(apk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to constrain a [Patch] to compatible packages.
|
|
||||||
* @param compatiblePackages A list of packages a [Patch] is compatible with.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class Compatibility(
|
|
||||||
val compatiblePackages: Array<Package>,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to represent packages a patch can be compatible with.
|
|
||||||
* @param name The package identifier name.
|
|
||||||
* @param versions The versions of the package the [Patch] is compatible with.
|
|
||||||
*/
|
|
||||||
@Target()
|
|
||||||
annotation class Package(
|
|
||||||
val name: String,
|
|
||||||
val versions: Array<String> = [],
|
|
||||||
)
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package app.revanced.patcher.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to name a [Patch].
|
|
||||||
* @param name A suggestive name for the [Patch].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class Name(
|
|
||||||
val name: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to describe a [Patch].
|
|
||||||
* @param description A description for the [Patch].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class Description(
|
|
||||||
val description: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to version a [Patch].
|
|
||||||
* @param version The version of a [Patch].
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
@Deprecated("This annotation is deprecated and will be removed in the future.")
|
|
||||||
annotation class Version(
|
|
||||||
val version: String,
|
|
||||||
)
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package app.revanced.patcher.apk
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.archive.Archive
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
import app.revanced.patcher.PatcherOptions
|
|
||||||
import app.revanced.patcher.logging.asArscLogger
|
|
||||||
import app.revanced.patcher.util.ProxyBackedClassList
|
|
||||||
import com.reandroid.apk.ApkModule
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeException
|
|
||||||
import com.reandroid.archive.InputSource
|
|
||||||
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock
|
|
||||||
import com.reandroid.arsc.value.ResConfig
|
|
||||||
import lanchon.multidexlib2.*
|
|
||||||
import org.jf.dexlib2.Opcodes
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile
|
|
||||||
import org.jf.dexlib2.iface.DexFile
|
|
||||||
import org.jf.dexlib2.iface.MultiDexContainer
|
|
||||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [Apk] file.
|
|
||||||
*/
|
|
||||||
sealed class Apk private constructor(module: ApkModule) {
|
|
||||||
/**
|
|
||||||
* A wrapper around the zip archive of this [Apk].
|
|
||||||
*
|
|
||||||
* @see Archive
|
|
||||||
*/
|
|
||||||
private val archive = Archive(module)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The metadata of the [Apk].
|
|
||||||
*/
|
|
||||||
val packageMetadata = PackageMetadata(module.androidManifestBlock)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh updated resources and close any open files.
|
|
||||||
*
|
|
||||||
* @param options The [PatcherOptions] of the [Patcher].
|
|
||||||
*/
|
|
||||||
internal open fun cleanup(options: PatcherOptions) {
|
|
||||||
try {
|
|
||||||
archive.cleanup(options.logger.asArscLogger())
|
|
||||||
} catch (e: EncodeException) {
|
|
||||||
throw ApkResourceException.Encode(e.message!!, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
archive.mainPackageResources.refreshPackageName()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the [Apk] to a file.
|
|
||||||
*
|
|
||||||
* @param output The target file.
|
|
||||||
*/
|
|
||||||
fun write(output: File) = archive.save(output)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val MANIFEST_FILE_NAME = "AndroidManifest.xml"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the [Module] and [Type] of an [ApkModule].
|
|
||||||
*
|
|
||||||
* @return A [Pair] containing the [Module] and [Type] of the [ApkModule].
|
|
||||||
*/
|
|
||||||
fun ApkModule.identify(): Pair<Module, Type> {
|
|
||||||
val manifestElement = androidManifestBlock.manifestElement
|
|
||||||
return when {
|
|
||||||
isBaseModule -> Module.Main to Type.Base
|
|
||||||
// The module is a base apk for a dynamic feature module if the "isFeatureModule" attribute is set to true.
|
|
||||||
manifestElement.searchAttributeByName("isFeatureModule")?.valueAsBoolean == true -> Module.DynamicFeature(
|
|
||||||
split
|
|
||||||
) to Type.Base
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val module = manifestElement.searchAttributeByName("configForSplit")
|
|
||||||
?.let { Module.DynamicFeature(it.valueAsString) } ?: Module.Main
|
|
||||||
|
|
||||||
// Examples:
|
|
||||||
// config.xhdpi
|
|
||||||
// df_my_feature.config.en
|
|
||||||
val config = this.split.split(".").last()
|
|
||||||
|
|
||||||
val type = when {
|
|
||||||
// Language splits have a two-letter country code.
|
|
||||||
config.length == 2 -> Type.Language(config)
|
|
||||||
// Library splits use the target CPU architecture.
|
|
||||||
Split.Library.architectures.contains(config) -> Type.Library(config)
|
|
||||||
// Asset splits use the density.
|
|
||||||
ResConfig.Density.valueOf(config) != null -> Type.Asset(config)
|
|
||||||
else -> throw IllegalArgumentException("Invalid split config: $config")
|
|
||||||
}
|
|
||||||
|
|
||||||
module to type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inner class BytecodeData {
|
|
||||||
private val opcodes: Opcodes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The classes and proxied classes of the [Base] apk file.
|
|
||||||
*/
|
|
||||||
val classes: ProxyBackedClassList
|
|
||||||
|
|
||||||
init {
|
|
||||||
MultiDexContainerBackedDexFile(object : MultiDexContainer<DexBackedDexFile> {
|
|
||||||
// Load all dex files from the apk module and create a dex entry for each of them.
|
|
||||||
private val entries = archive.readDexFiles().associateBy { it.name }
|
|
||||||
.mapValues { (name, inputSource) ->
|
|
||||||
BasicDexEntry(
|
|
||||||
this,
|
|
||||||
name,
|
|
||||||
RawDexIO.readRawDexFile(inputSource.openStream(), inputSource.length, null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getDexEntryNames() = entries.keys.toList()
|
|
||||||
override fun getEntry(entryName: String) = entries[entryName]
|
|
||||||
}).let {
|
|
||||||
opcodes = it.opcodes
|
|
||||||
classes = ProxyBackedClassList(it.classes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write [classes] to the archive.
|
|
||||||
*/
|
|
||||||
internal fun writeDexFiles() {
|
|
||||||
// Create patched dex files.
|
|
||||||
mutableMapOf<String, MemoryDataStore>().also {
|
|
||||||
val newDexFile = object : DexFile {
|
|
||||||
override fun getClasses() =
|
|
||||||
this@BytecodeData.classes.also(ProxyBackedClassList::applyProxies).toSet()
|
|
||||||
override fun getOpcodes() = this@BytecodeData.opcodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write modified dex files.
|
|
||||||
MultiDexIO.writeDexFile(
|
|
||||||
true, -1, // Core count.
|
|
||||||
it, Patcher.dexFileNamer, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
|
|
||||||
)
|
|
||||||
}.forEach { (name, store) ->
|
|
||||||
val dexFileInputSource = object : InputSource(name) {
|
|
||||||
override fun openStream() = store.readAt(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
archive.write(dexFileInputSource)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata about an [Apk] file.
|
|
||||||
*
|
|
||||||
* @param packageName The package name of the [Apk] file.
|
|
||||||
* @param packageVersion The package version of the [Apk] file.
|
|
||||||
*/
|
|
||||||
data class PackageMetadata(val packageName: String?, val packageVersion: String?) {
|
|
||||||
internal constructor(manifestBlock: AndroidManifestBlock) : this(
|
|
||||||
manifestBlock.packageName,
|
|
||||||
manifestBlock.versionName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [Apk] of type [Split].
|
|
||||||
*
|
|
||||||
* @param config The device configuration associated with this [Split], such as arm64_v8a, en or xhdpi.
|
|
||||||
* @see Apk
|
|
||||||
*/
|
|
||||||
sealed class Split(val config: String, module: ApkModule) : Apk(module) {
|
|
||||||
override fun toString() = "split_config.$config.apk"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The split apk file which contains libraries.
|
|
||||||
*
|
|
||||||
* @see Split
|
|
||||||
*/
|
|
||||||
class Library internal constructor(config: String, module: ApkModule) : Split(config, module) {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* A set of all architectures supported by android.
|
|
||||||
*/
|
|
||||||
val architectures = setOf("armeabi_v7a", "arm64_v8a", "x86", "x86_64")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The split apk file which contains language strings.
|
|
||||||
*
|
|
||||||
* @see Split
|
|
||||||
*/
|
|
||||||
class Language internal constructor(config: String, module: ApkModule) : Split(config, module)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The split apk file which contains assets.
|
|
||||||
*
|
|
||||||
* @see Split
|
|
||||||
*/
|
|
||||||
class Asset internal constructor(config: String, module: ApkModule) : Split(config, module)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base [Apk] file..
|
|
||||||
*
|
|
||||||
* @see Apk
|
|
||||||
*/
|
|
||||||
class Base internal constructor(module: ApkModule) : Apk(module) {
|
|
||||||
/**
|
|
||||||
* Data of the [Base] apk file.
|
|
||||||
*/
|
|
||||||
internal val bytecodeData = BytecodeData()
|
|
||||||
|
|
||||||
override fun toString() = "base.apk"
|
|
||||||
|
|
||||||
override fun cleanup(options: PatcherOptions) {
|
|
||||||
super.cleanup(options)
|
|
||||||
|
|
||||||
options.logger.info("Writing patched dex files")
|
|
||||||
bytecodeData.writeDexFiles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The module that the [ApkModule] belongs to.
|
|
||||||
*/
|
|
||||||
sealed class Module {
|
|
||||||
/**
|
|
||||||
* The default [Module] that is always installed by software repositories.
|
|
||||||
*/
|
|
||||||
object Main : Module()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Module] that can be installed later by software repositories when requested by the application.
|
|
||||||
*
|
|
||||||
* @param name The name of the feature.
|
|
||||||
*/
|
|
||||||
data class DynamicFeature(val name: String) : Module()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the [ApkModule].
|
|
||||||
*/
|
|
||||||
sealed class Type {
|
|
||||||
/**
|
|
||||||
* The main Apk of a [Module].
|
|
||||||
*/
|
|
||||||
object Base : Type()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A superclass for all split configuration types.
|
|
||||||
*
|
|
||||||
* @param target The target device configuration.
|
|
||||||
*/
|
|
||||||
sealed class SplitConfig(val target: String) : Type()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [Type] of an apk containing native libraries.
|
|
||||||
*
|
|
||||||
* @param architecture The target CPU architecture.
|
|
||||||
*/
|
|
||||||
data class Library(val architecture: String) : SplitConfig(architecture)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [Type] for an Apk containing language resources.
|
|
||||||
*
|
|
||||||
* @param language The target language code.
|
|
||||||
*/
|
|
||||||
data class Language(val language: String) : SplitConfig(language)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [Type] for an Apk containing assets.
|
|
||||||
*
|
|
||||||
* @param pixelDensity The target screen density.
|
|
||||||
*/
|
|
||||||
data class Asset(val pixelDensity: String) : SplitConfig(pixelDensity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package app.revanced.patcher.apk
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.resource.ResourceTable
|
|
||||||
import app.revanced.patcher.Patcher
|
|
||||||
import app.revanced.patcher.PatcherOptions
|
|
||||||
import app.revanced.patcher.apk.Apk.Companion.identify
|
|
||||||
import com.reandroid.apk.ApkModule
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [Apk] file of type [Apk.Split].
|
|
||||||
*
|
|
||||||
* @param files A list of apk files to load.
|
|
||||||
*/
|
|
||||||
class ApkBundle(files: List<File>) : Sequence<Apk> {
|
|
||||||
/**
|
|
||||||
* The [Apk.Base] of this [ApkBundle].
|
|
||||||
*/
|
|
||||||
val base: Apk.Base
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map containing all the [Apk.Split]s in this bundle associated by their configuration.
|
|
||||||
*/
|
|
||||||
val splits: Map<String, Apk.Split>?
|
|
||||||
|
|
||||||
init {
|
|
||||||
var baseApk: Apk.Base? = null
|
|
||||||
|
|
||||||
splits = buildMap {
|
|
||||||
files.forEach {
|
|
||||||
val apk = ApkModule.loadApkFile(it)
|
|
||||||
val (module, type) = apk.identify()
|
|
||||||
if (module is Apk.Module.DynamicFeature) {
|
|
||||||
return@forEach // Dynamic feature modules are not supported yet.
|
|
||||||
}
|
|
||||||
|
|
||||||
when (type) {
|
|
||||||
Apk.Type.Base -> {
|
|
||||||
if (baseApk != null) {
|
|
||||||
throw IllegalArgumentException("Cannot have more than one base apk")
|
|
||||||
}
|
|
||||||
baseApk = Apk.Base(apk)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Apk.Type.SplitConfig -> {
|
|
||||||
val target = type.target
|
|
||||||
if (this.contains(target)) {
|
|
||||||
throw IllegalArgumentException("Duplicate split: $target")
|
|
||||||
}
|
|
||||||
|
|
||||||
val constructor = when (type) {
|
|
||||||
is Apk.Type.Asset -> Apk.Split::Asset
|
|
||||||
is Apk.Type.Library -> Apk.Split::Library
|
|
||||||
is Apk.Type.Language -> Apk.Split::Language
|
|
||||||
}
|
|
||||||
|
|
||||||
this[target] = constructor(target, apk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.takeIf { it.isNotEmpty() }
|
|
||||||
|
|
||||||
base = baseApk ?: throw IllegalArgumentException("Base apk not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [ResourceTable] of this [ApkBundle].
|
|
||||||
*/
|
|
||||||
val resources = ResourceTable(base.resources, map { it.resources })
|
|
||||||
|
|
||||||
override fun iterator() = sequence {
|
|
||||||
yield(base)
|
|
||||||
splits?.values?.let {
|
|
||||||
yieldAll(it)
|
|
||||||
}
|
|
||||||
}.iterator()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh all updated resources in an [ApkBundle].
|
|
||||||
*
|
|
||||||
* @param options The [PatcherOptions] of the [Patcher].
|
|
||||||
* @return A sequence of the [Apk] files which are being refreshed.
|
|
||||||
*/
|
|
||||||
internal fun cleanup(options: PatcherOptions) = map {
|
|
||||||
var exception: ApkResourceException? = null
|
|
||||||
try {
|
|
||||||
it.cleanup(options)
|
|
||||||
} catch (e: ApkResourceException) {
|
|
||||||
exception = e
|
|
||||||
}
|
|
||||||
|
|
||||||
SplitApkResult(it, exception)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The result of writing an [Apk] file.
|
|
||||||
*
|
|
||||||
* @param apk The corresponding [Apk] file.
|
|
||||||
* @param exception The optional [ApkResourceException] when an exception occurred.
|
|
||||||
*/
|
|
||||||
data class SplitApkResult(val apk: Apk, val exception: ApkResourceException? = null)
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package app.revanced.patcher.extensions
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Compatibility
|
|
||||||
import app.revanced.patcher.annotation.Description
|
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
|
||||||
import app.revanced.patcher.patch.OptionsContainer
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchClass
|
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
|
||||||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
|
||||||
import kotlin.reflect.KVisibility
|
|
||||||
import kotlin.reflect.full.companionObject
|
|
||||||
import kotlin.reflect.full.companionObjectInstance
|
|
||||||
|
|
||||||
object PatchExtensions {
|
|
||||||
/**
|
|
||||||
* The name of a [Patch].
|
|
||||||
*/
|
|
||||||
val PatchClass.patchName: String
|
|
||||||
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The version of a [Patch].
|
|
||||||
*/
|
|
||||||
@Deprecated("This property is deprecated and will be removed in the future.")
|
|
||||||
val PatchClass.version
|
|
||||||
get() = findAnnotationRecursively(Version::class)?.version
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Weather or not a [Patch] should be included.
|
|
||||||
*/
|
|
||||||
val PatchClass.include
|
|
||||||
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The description of a [Patch].
|
|
||||||
*/
|
|
||||||
val PatchClass.description
|
|
||||||
get() = findAnnotationRecursively(Description::class)?.description
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The dependencies of a [Patch].
|
|
||||||
*/
|
|
||||||
val PatchClass.dependencies
|
|
||||||
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The packages a [Patch] is compatible with.
|
|
||||||
*/
|
|
||||||
val PatchClass.compatiblePackages
|
|
||||||
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Weather or not a [Patch] requires integrations.
|
|
||||||
*/
|
|
||||||
internal val PatchClass.requiresIntegrations
|
|
||||||
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The options of a [Patch].
|
|
||||||
*/
|
|
||||||
val PatchClass.options: PatchOptions?
|
|
||||||
get() = kotlin.companionObject?.let { cl ->
|
|
||||||
if (cl.visibility != KVisibility.PUBLIC) return null
|
|
||||||
kotlin.companionObjectInstance?.let {
|
|
||||||
(it as? OptionsContainer)?.options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package app.revanced.patcher.logging
|
|
||||||
|
|
||||||
interface Logger {
|
|
||||||
fun error(msg: String) {}
|
|
||||||
fun warn(msg: String) {}
|
|
||||||
fun info(msg: String) {}
|
|
||||||
fun trace(msg: String) {}
|
|
||||||
|
|
||||||
object Nop : Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn a Patcher [Logger] into an [app.revanced.arsc.logging.Logger].
|
|
||||||
*/
|
|
||||||
internal fun Logger.asArscLogger() = object : app.revanced.arsc.logging.Logger {
|
|
||||||
override fun error(msg: String) = this@asArscLogger.error(msg)
|
|
||||||
override fun warn(msg: String) = this@asArscLogger.warn(msg)
|
|
||||||
override fun info(msg: String) = this@asArscLogger.info(msg)
|
|
||||||
override fun trace(msg: String) = this@asArscLogger.error(msg)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container for patch options.
|
|
||||||
*/
|
|
||||||
abstract class OptionsContainer {
|
|
||||||
/**
|
|
||||||
* A list of [PatchOption]s.
|
|
||||||
* @see PatchOptions
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
val options = PatchOptions()
|
|
||||||
|
|
||||||
protected fun <T> option(opt: PatchOption<T>): PatchOption<T> {
|
|
||||||
options.register(opt)
|
|
||||||
return opt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
|
||||||
import app.revanced.patcher.Context
|
|
||||||
import app.revanced.patcher.ResourceContext
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import java.io.Closeable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ReVanced patch.
|
|
||||||
*
|
|
||||||
* If it implements [Closeable], it will be closed after all patches have been executed.
|
|
||||||
* Closing will be done in reverse execution order.
|
|
||||||
*/
|
|
||||||
sealed interface Patch<out T : Context> {
|
|
||||||
/**
|
|
||||||
* The main function of the [Patch] which the patcher will call.
|
|
||||||
*
|
|
||||||
* @param context The [Context] the patch will work on.
|
|
||||||
*/
|
|
||||||
suspend fun execute(context: @UnsafeVariance T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resource patch for the Patcher.
|
|
||||||
*/
|
|
||||||
interface ResourcePatch : Patch<ResourceContext>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bytecode patch for the Patcher.
|
|
||||||
*
|
|
||||||
* @param fingerprints A list of [MethodFingerprint] this patch relies on.
|
|
||||||
*/
|
|
||||||
abstract class BytecodePatch(
|
|
||||||
internal val fingerprints: Iterable<MethodFingerprint>? = null
|
|
||||||
) : Patch<BytecodeContext>
|
|
||||||
|
|
||||||
// TODO: populate this everywhere where the alias is not used yet
|
|
||||||
/**
|
|
||||||
* The class type of [Patch].
|
|
||||||
*/
|
|
||||||
typealias PatchClass = Class<out Patch<Context>>
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
@file:Suppress("CanBeParameter", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST")
|
|
||||||
|
|
||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
class NoSuchOptionException(val option: String) : Exception("No such option: $option")
|
|
||||||
class IllegalValueException(val value: Any?) : Exception("Illegal value: $value")
|
|
||||||
class InvalidTypeException(val got: String, val expected: String) :
|
|
||||||
Exception("Invalid option value type: $got, expected $expected")
|
|
||||||
|
|
||||||
object RequirementNotMetException : Exception("null was passed into an option that requires a value")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A registry for an array of [PatchOption]s.
|
|
||||||
* @param options An array of [PatchOption]s.
|
|
||||||
*/
|
|
||||||
class PatchOptions(vararg options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
|
||||||
private val register = mutableMapOf<String, PatchOption<*>>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
options.forEach { register(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun register(option: PatchOption<*>) {
|
|
||||||
if (register.containsKey(option.key)) {
|
|
||||||
throw IllegalStateException("Multiple options found with the same key")
|
|
||||||
}
|
|
||||||
register[option.key] = option
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a [PatchOption] by its key.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
@JvmName("getUntyped")
|
|
||||||
operator fun get(key: String) = register[key] ?: throw NoSuchOptionException(key)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a [PatchOption] by its key.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
inline operator fun <reified T> get(key: String): PatchOption<T> {
|
|
||||||
val opt = get(key)
|
|
||||||
if (opt.value !is T) throw InvalidTypeException(
|
|
||||||
opt.value?.let { it::class.java.canonicalName } ?: "null",
|
|
||||||
T::class.java.canonicalName
|
|
||||||
)
|
|
||||||
return opt as PatchOption<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of a [PatchOption].
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
* @param value The value you want it to be.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
inline operator fun <reified T> set(key: String, value: T) {
|
|
||||||
val opt = get<T>(key)
|
|
||||||
if (opt.value !is T) throw InvalidTypeException(
|
|
||||||
T::class.java.canonicalName,
|
|
||||||
opt.value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
opt.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of a [PatchOption] to `null`.
|
|
||||||
* @param key The key of the [PatchOption].
|
|
||||||
*/
|
|
||||||
fun nullify(key: String) {
|
|
||||||
get(key).value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator() = register.values.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [Patch] option.
|
|
||||||
* @param key Unique identifier of the option. Example: _`settings.microg.enabled`_
|
|
||||||
* @param default The default value of the option.
|
|
||||||
* @param title A human-readable title of the option. Example: _MicroG Settings_
|
|
||||||
* @param description A human-readable description of the option. Example: _Settings integration for MicroG._
|
|
||||||
* @param required Whether the option is required.
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
sealed class PatchOption<T>(
|
|
||||||
val key: String,
|
|
||||||
default: T?,
|
|
||||||
val title: String,
|
|
||||||
val description: String,
|
|
||||||
val required: Boolean,
|
|
||||||
val validator: (T?) -> Boolean
|
|
||||||
) {
|
|
||||||
var value: T? = default
|
|
||||||
get() {
|
|
||||||
if (field == null && required) {
|
|
||||||
throw RequirementNotMetException
|
|
||||||
}
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
if (value == null && required) {
|
|
||||||
throw RequirementNotMetException
|
|
||||||
}
|
|
||||||
if (!validator(value)) {
|
|
||||||
throw IllegalValueException(value)
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the option.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
@JvmName("getValueTyped")
|
|
||||||
inline operator fun <reified V> getValue(thisRef: Nothing?, property: KProperty<*>): V? {
|
|
||||||
if (value !is V?) throw InvalidTypeException(
|
|
||||||
V::class.java.canonicalName,
|
|
||||||
value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
return value as? V?
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the option.
|
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
|
||||||
*/
|
|
||||||
@JvmName("setValueTyped")
|
|
||||||
inline operator fun <reified V> setValue(thisRef: Nothing?, property: KProperty<*>, new: V) {
|
|
||||||
if (value !is V) throw InvalidTypeException(
|
|
||||||
V::class.java.canonicalName,
|
|
||||||
value?.let { it::class.java.canonicalName } ?: "null"
|
|
||||||
)
|
|
||||||
value = new as T
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, new: T?) {
|
|
||||||
value = new
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [String].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class StringOption(
|
|
||||||
key: String,
|
|
||||||
default: String?,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (String?) -> Boolean = { true }
|
|
||||||
) : PatchOption<String>(
|
|
||||||
key, default, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] representing a [Boolean].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
class BooleanOption(
|
|
||||||
key: String,
|
|
||||||
default: Boolean?,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Boolean?) -> Boolean = { true }
|
|
||||||
) : PatchOption<Boolean>(
|
|
||||||
key, default, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [PatchOption] with a list of allowed options.
|
|
||||||
* @param options A list of allowed options for the [ListOption].
|
|
||||||
* @see PatchOption
|
|
||||||
*/
|
|
||||||
sealed class ListOption<E>(
|
|
||||||
key: String,
|
|
||||||
default: E?,
|
|
||||||
val options: Iterable<E>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (E?) -> Boolean = { true }
|
|
||||||
) : PatchOption<E>(
|
|
||||||
key, default, title, description, required, {
|
|
||||||
(it?.let { it in options } ?: true) && validator(it)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
if (default != null && default !in options) {
|
|
||||||
throw IllegalStateException("Default option must be an allowed option")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [ListOption] of type [String].
|
|
||||||
* @see ListOption
|
|
||||||
*/
|
|
||||||
class StringListOption(
|
|
||||||
key: String,
|
|
||||||
default: String?,
|
|
||||||
options: Iterable<String>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (String?) -> Boolean = { true }
|
|
||||||
) : ListOption<String>(
|
|
||||||
key, default, options, title, description, required, validator
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [ListOption] of type [Int].
|
|
||||||
* @see ListOption
|
|
||||||
*/
|
|
||||||
class IntListOption(
|
|
||||||
key: String,
|
|
||||||
default: Int?,
|
|
||||||
options: Iterable<Int>,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
required: Boolean = false,
|
|
||||||
validator: (Int?) -> Boolean = { true }
|
|
||||||
) : ListOption<Int>(
|
|
||||||
key, default, options, title, description, required, validator
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package app.revanced.patcher.patch.annotations
|
|
||||||
|
|
||||||
import app.revanced.patcher.Context
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to mark a class as a patch.
|
|
||||||
* @param include If false, the patch should be treated as optional by default.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class Patch(val include: Boolean = true)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation for dependencies of [Patch]es.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class DependsOn(
|
|
||||||
val dependencies: Array<KClass<out Patch<Context>>> = [] // TODO: This should be a list of PatchClass instead
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to mark [Patch]es which depend on integrations.
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
annotation class RequiresIntegrations // TODO: Remove this annotation and replace it with a proper system
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
internal class ListBackedSet<E>(private val list: MutableList<E>) : MutableSet<E> {
|
|
||||||
override val size get() = list.size
|
|
||||||
override fun add(element: E) = list.add(element)
|
|
||||||
override fun addAll(elements: Collection<E>) = list.addAll(elements)
|
|
||||||
override fun clear() = list.clear()
|
|
||||||
override fun iterator() = list.listIterator()
|
|
||||||
override fun remove(element: E) = list.remove(element)
|
|
||||||
override fun removeAll(elements: Collection<E>) = list.removeAll(elements)
|
|
||||||
override fun retainAll(elements: Collection<E>) = list.retainAll(elements)
|
|
||||||
override fun contains(element: E) = list.contains(element)
|
|
||||||
override fun containsAll(elements: Collection<E>) = list.containsAll(elements)
|
|
||||||
override fun isEmpty() = list.isEmpty()
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that represents a list of classes and proxies.
|
|
||||||
*
|
|
||||||
* @param classes The classes to be backed by proxies.
|
|
||||||
*/
|
|
||||||
class ProxyBackedClassList(classes: Set<ClassDef>) : Iterable<ClassDef> {
|
|
||||||
// A list for pending proxied classes to be added to the current ProxyBackedClassList instance.
|
|
||||||
private val proxiedClasses = mutableListOf<ClassProxy>()
|
|
||||||
private val mutableClasses = classes.toMutableList()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace the [mutableClasses]es with their proxies.
|
|
||||||
*/
|
|
||||||
internal fun applyProxies() {
|
|
||||||
proxiedClasses.removeIf { proxy ->
|
|
||||||
// If the proxy is unused, keep it in the proxiedClasses list.
|
|
||||||
if (!proxy.resolved) return@removeIf false
|
|
||||||
|
|
||||||
with(mutableClasses) {
|
|
||||||
remove(proxy.immutableClass)
|
|
||||||
add(proxy.mutableClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@removeIf true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace a [ClassDef] at a given [index].
|
|
||||||
*
|
|
||||||
* @param index The index of the class to be replaced.
|
|
||||||
* @param classDef The new class to replace the old one.
|
|
||||||
*/
|
|
||||||
operator fun set(index: Int, classDef: ClassDef) {
|
|
||||||
mutableClasses[index] = classDef
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a [ClassDef] at a given [index].
|
|
||||||
*
|
|
||||||
* @param index The index of the class.
|
|
||||||
*/
|
|
||||||
operator fun get(index: Int) = mutableClasses[index]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator for the classes in [ProxyBackedClassList].
|
|
||||||
*
|
|
||||||
* @return The iterator for the classes.
|
|
||||||
*/
|
|
||||||
override fun iterator() = mutableClasses.iterator()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Proxy a [ClassDef].
|
|
||||||
*
|
|
||||||
* Note: This creates a [ClassProxy] of the [ClassDef], if not already present.
|
|
||||||
*
|
|
||||||
* @return A proxy for the given class.
|
|
||||||
*/
|
|
||||||
fun proxy(classDef: ClassDef) = proxiedClasses
|
|
||||||
.find { it.immutableClass.type == classDef.type } ?: ClassProxy(classDef).also(proxiedClasses::add)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a [ClassDef].
|
|
||||||
*/
|
|
||||||
fun add(classDef: ClassDef) = mutableClasses.add(classDef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given class name.
|
|
||||||
*
|
|
||||||
* @param className The name of the class.
|
|
||||||
* @return A proxy for the first class that matches the class name.
|
|
||||||
*/
|
|
||||||
fun findClassProxied(className: String) = findClassProxied { it.type.contains(className) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a class by a given predicate.
|
|
||||||
*
|
|
||||||
* @param predicate A predicate to match the class.
|
|
||||||
* @return A proxy for the first class that matches the predicate.
|
|
||||||
*/
|
|
||||||
fun findClassProxied(predicate: (ClassDef) -> Boolean) = this.find(predicate)?.let(::proxy)
|
|
||||||
|
|
||||||
val size get() = mutableClasses.size
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
|
||||||
|
|
||||||
object TypeUtil {
|
|
||||||
/**
|
|
||||||
* Traverse the class hierarchy starting from the given root class.
|
|
||||||
*
|
|
||||||
* @param targetClass The class to start traversing the class hierarchy from.
|
|
||||||
* @param callback The function that is called for every class in the hierarchy.
|
|
||||||
*/
|
|
||||||
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
|
||||||
callback(targetClass)
|
|
||||||
this.classes.findClassProxied(targetClass.superclass ?: return)?.mutableClass?.let {
|
|
||||||
traverseClassHierarchy(it, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patcher.util
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Deprecated("This class serves no purpose anymore")
|
|
||||||
internal object VersionReader {
|
|
||||||
@JvmStatic
|
|
||||||
private val properties = Properties().apply {
|
|
||||||
load(
|
|
||||||
VersionReader::class.java.getResourceAsStream("/app/revanced/patcher/version.properties")
|
|
||||||
?: throw IllegalStateException("Could not load version.properties")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun read(): String {
|
|
||||||
return properties.getProperty("version") ?: throw IllegalStateException("Version not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package app.revanced.patcher.util.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchClass
|
|
||||||
import dalvik.system.PathClassLoader
|
|
||||||
import org.jf.dexlib2.DexFileFactory
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.util.jar.JarFile
|
|
||||||
import kotlin.streams.toList
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A patch bundle.
|
|
||||||
*
|
|
||||||
* @param fromClasses The classes to get [Patch]es from.
|
|
||||||
*/
|
|
||||||
sealed class PatchBundle private constructor(fromClasses: Iterable<Class<*>>) : Iterable<PatchClass> {
|
|
||||||
private val patches = fromClasses.filter {
|
|
||||||
if (it.isAnnotation) return@filter false
|
|
||||||
|
|
||||||
it.findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class) != null
|
|
||||||
}.map {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
it as PatchClass
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator() = patches.iterator()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A patch bundle of type [Jar].
|
|
||||||
*
|
|
||||||
* @param patchBundlePath The path to a patch bundle.
|
|
||||||
*/
|
|
||||||
class Jar(private val patchBundlePath: File) : PatchBundle(
|
|
||||||
with(URLClassLoader(arrayOf(patchBundlePath.toURI().toURL()), PatchBundle::class.java.classLoader)) {
|
|
||||||
JarFile(patchBundlePath).stream().filter { it.name.endsWith(".class") }.map {
|
|
||||||
loadClass(
|
|
||||||
it.realName.replace('/', '.').replace(".class", "")
|
|
||||||
)
|
|
||||||
}.toList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A patch bundle of type [Dex] format.
|
|
||||||
*
|
|
||||||
* @param patchBundlePath The path to a patch bundle of dex format.
|
|
||||||
*/
|
|
||||||
class Dex(private val patchBundlePath: File) : PatchBundle(
|
|
||||||
with(PathClassLoader(patchBundlePath.absolutePath, null, PatchBundle::class.java.classLoader)) {
|
|
||||||
DexFileFactory.loadDexFile(patchBundlePath, null).classes.map { classDef ->
|
|
||||||
classDef.type.substring(1, classDef.length - 1).replace('/', '.')
|
|
||||||
}.map { loadClass(it) }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package app.revanced.patcher.issues
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.PatchOption
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
internal class Issue98 {
|
|
||||||
companion object {
|
|
||||||
var key1: String? by PatchOption.StringOption(
|
|
||||||
"key1", null, "title", "description"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should infer nullable type correctly`() {
|
|
||||||
assertNull(key1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package app.revanced.patcher.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.usage.bytecode.ExampleBytecodePatch
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.assertThrows
|
|
||||||
import kotlin.test.assertNotEquals
|
|
||||||
|
|
||||||
internal class PatchOptionsTest {
|
|
||||||
private val options = ExampleBytecodePatch.options
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should not throw an exception`() {
|
|
||||||
for (option in options) {
|
|
||||||
when (option) {
|
|
||||||
is PatchOption.StringOption -> {
|
|
||||||
option.value = "Hello World"
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.BooleanOption -> {
|
|
||||||
option.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.StringListOption -> {
|
|
||||||
option.value = option.options.first()
|
|
||||||
for (choice in option.options) {
|
|
||||||
println(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is PatchOption.IntListOption -> {
|
|
||||||
option.value = option.options.first()
|
|
||||||
for (choice in option.options) {
|
|
||||||
println(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val option = options.get<String>("key1")
|
|
||||||
// or: val option: String? by options["key1"]
|
|
||||||
// then you won't need `.value` every time
|
|
||||||
println(option.value)
|
|
||||||
options["key1"] = "Hello, world!"
|
|
||||||
println(option.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should return a different value when changed`() {
|
|
||||||
var value: String? by options["key1"]
|
|
||||||
val current = value + "" // force a copy
|
|
||||||
value = "Hello, world!"
|
|
||||||
assertNotEquals(current, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should be able to set value to null`() {
|
|
||||||
// Sadly, doing:
|
|
||||||
// > options["key2"] = null
|
|
||||||
// is not possible because Kotlin
|
|
||||||
// cannot reify the type "Nothing?".
|
|
||||||
// So we have to do this instead:
|
|
||||||
options["key2"] = null as Any?
|
|
||||||
// This is a cleaner replacement for the above:
|
|
||||||
options.nullify("key2")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because the option does not exist`() {
|
|
||||||
assertThrows<NoSuchOptionException> {
|
|
||||||
options["this option does not exist"] = 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of invalid value type when setting an option`() {
|
|
||||||
assertThrows<InvalidTypeException> {
|
|
||||||
options["key1"] = 123
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of invalid value type when getting an option`() {
|
|
||||||
assertThrows<InvalidTypeException> {
|
|
||||||
options.get<Int>("key1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because of an illegal value`() {
|
|
||||||
assertThrows<IllegalValueException> {
|
|
||||||
options["key3"] = "this value is not an allowed option"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because the requirement is not met`() {
|
|
||||||
assertThrows<RequirementNotMetException> {
|
|
||||||
options.nullify("key1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `should fail because getting a non-initialized option is illegal`() {
|
|
||||||
assertThrows<RequirementNotMetException> {
|
|
||||||
println(options["key5"].value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.patcher.usage.bytecode
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Compatibility
|
|
||||||
import app.revanced.patcher.annotation.Package
|
|
||||||
|
|
||||||
@Compatibility(
|
|
||||||
[Package(
|
|
||||||
"com.example.examplePackage", arrayOf("0.0.1", "0.0.2")
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
internal annotation class ExampleBytecodeCompatibility
|
|
||||||
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
package app.revanced.patcher.usage.bytecode
|
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
|
||||||
import app.revanced.patcher.annotation.Description
|
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.OptionsContainer
|
|
||||||
import app.revanced.patcher.patch.PatchOption
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibility
|
|
||||||
import app.revanced.patcher.usage.resource.patch.ExampleResourcePatch
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Format
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
|
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableField
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference
|
|
||||||
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
|
||||||
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
|
||||||
import org.jf.dexlib2.util.Preconditions
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
@Name("example-bytecode-patch")
|
|
||||||
@Description("Example demonstration of a bytecode patch.")
|
|
||||||
@ExampleResourceCompatibility
|
|
||||||
@Version("0.0.1")
|
|
||||||
@DependsOn([ExampleResourcePatch::class])
|
|
||||||
class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|
||||||
// This function will be executed by the patcher.
|
|
||||||
// You can treat it as a constructor
|
|
||||||
override suspend fun execute(context: BytecodeContext) {
|
|
||||||
// Get the resolved method by its fingerprint from the resolver cache
|
|
||||||
val result = ExampleFingerprint.result!!
|
|
||||||
|
|
||||||
// Patch options
|
|
||||||
println(key1)
|
|
||||||
key2 = false
|
|
||||||
|
|
||||||
// Get the implementation for the resolved method
|
|
||||||
val method = result.mutableMethod
|
|
||||||
val implementation = method.implementation!!
|
|
||||||
|
|
||||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
|
||||||
// Get the start index of our opcode pattern.
|
|
||||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
|
||||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
|
||||||
|
|
||||||
implementation.replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
|
||||||
|
|
||||||
// Get the class in which the method matching our fingerprint is defined in.
|
|
||||||
val mainClass = context.classes.findClassProxied {
|
|
||||||
it.type == result.classDef.type
|
|
||||||
}!!.mutableClass
|
|
||||||
|
|
||||||
// Add a new method returning a string
|
|
||||||
mainClass.methods.add(
|
|
||||||
ImmutableMethod(
|
|
||||||
result.classDef.type,
|
|
||||||
"returnHello",
|
|
||||||
null,
|
|
||||||
"Ljava/lang/String;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
1,
|
|
||||||
ImmutableList.of(
|
|
||||||
BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
0,
|
|
||||||
ImmutableStringReference("Hello, ReVanced! Adding bytecode.")
|
|
||||||
),
|
|
||||||
BuilderInstruction11x(Opcode.RETURN_OBJECT, 0)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add a field in the main class
|
|
||||||
// We will use this field in our method below to call println on
|
|
||||||
// The field holds the Ljava/io/PrintStream->out; field
|
|
||||||
mainClass.fields.add(
|
|
||||||
ImmutableField(
|
|
||||||
mainClass.type,
|
|
||||||
"dummyField",
|
|
||||||
"Ljava/io/PrintStream;",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
|
||||||
ImmutableFieldEncodedValue(
|
|
||||||
ImmutableFieldReference(
|
|
||||||
"Ljava/lang/System;",
|
|
||||||
"out",
|
|
||||||
"Ljava/io/PrintStream;"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
|
|
||||||
// store the fields initial value into the first virtual register
|
|
||||||
method.replaceInstruction(0, "sget-object v0, LTestClass;->dummyField:Ljava/io/PrintStream;")
|
|
||||||
|
|
||||||
// Now let's create a new call to our method and print the return value!
|
|
||||||
// You can also use the smali compiler to create instructions.
|
|
||||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
|
||||||
//
|
|
||||||
// Control flow instructions are not supported as of now.
|
|
||||||
method.addInstructionsWithLabels(
|
|
||||||
startIndex + 2,
|
|
||||||
"""
|
|
||||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
|
||||||
move-result-object v1
|
|
||||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace the string for an instruction at the given index with a new one.
|
|
||||||
* @param index The index of the instruction to replace the string for
|
|
||||||
* @param string The replacing string
|
|
||||||
*/
|
|
||||||
private fun MutableMethodImplementation.replaceStringAt(index: Int, string: String) {
|
|
||||||
val instruction = this.instructions[index]
|
|
||||||
|
|
||||||
// Utility method of dexlib2
|
|
||||||
Preconditions.checkFormat(instruction.opcode, Format.Format21c)
|
|
||||||
|
|
||||||
// Cast this to an instruction of the format 21c
|
|
||||||
// The instruction format can be found in the docs at
|
|
||||||
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode
|
|
||||||
val strInstruction = instruction as Instruction21c
|
|
||||||
|
|
||||||
// In our case we want an instruction with the opcode CONST_STRING
|
|
||||||
// The format is 21c, so we create a new BuilderInstruction21c
|
|
||||||
// This instruction will hold the string reference constant in the virtual register of the original instruction
|
|
||||||
// For that a reference to the string is needed. It can be created with an ImmutableStringReference.
|
|
||||||
// At last, use the method replaceInstruction to replace it at the given index startIndex.
|
|
||||||
this.replaceInstruction(
|
|
||||||
index,
|
|
||||||
BuilderInstruction21c(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
strInstruction.registerA,
|
|
||||||
ImmutableStringReference(string)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
companion object : OptionsContainer() {
|
|
||||||
private var key1 by option(
|
|
||||||
PatchOption.StringOption(
|
|
||||||
"key1", "default", "title", "description", true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private var key2 by option(
|
|
||||||
PatchOption.BooleanOption(
|
|
||||||
"key2", true, "title", "description" // required defaults to false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private var key3 by option(
|
|
||||||
PatchOption.StringListOption(
|
|
||||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private var key4 by option(
|
|
||||||
PatchOption.IntListOption(
|
|
||||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private var key5 by option(
|
|
||||||
PatchOption.StringOption(
|
|
||||||
"key5", null, "title", "description", true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.patcher.usage.resource.annotation
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Compatibility
|
|
||||||
import app.revanced.patcher.annotation.Package
|
|
||||||
|
|
||||||
@Compatibility(
|
|
||||||
[Package(
|
|
||||||
"com.example.examplePackage", arrayOf("0.0.1", "0.0.2")
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
|
||||||
internal annotation class ExampleResourceCompatibility
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.patcher.usage.resource.patch
|
|
||||||
|
|
||||||
import app.revanced.patcher.ResourceContext
|
|
||||||
import app.revanced.patcher.annotation.Description
|
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.apk.Apk
|
|
||||||
import app.revanced.patcher.openXmlFile
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibility
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
@Name("example-resource-patch")
|
|
||||||
@Description("Example demonstration of a resource patch.")
|
|
||||||
@ExampleResourceCompatibility
|
|
||||||
@Version("0.0.1")
|
|
||||||
class ExampleResourcePatch : ResourcePatch {
|
|
||||||
override suspend fun execute(context: ResourceContext) {
|
|
||||||
context.apkBundle.base.resources.openXmlFile(Apk.MANIFEST_FILE_NAME).use { editor ->
|
|
||||||
val element = editor // regular DomFileEditor
|
|
||||||
.file
|
|
||||||
.getElementsByTagName("application")
|
|
||||||
.item(0) as Element
|
|
||||||
element
|
|
||||||
.setAttribute(
|
|
||||||
"exampleAttribute",
|
|
||||||
"exampleValue"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +1 @@
|
|||||||
pluginManagement {
|
rootProject.name = "revanced-patcher"
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
|
|
||||||
credentials {
|
|
||||||
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
|
|
||||||
password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
|
|
||||||
credentials {
|
|
||||||
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
|
|
||||||
password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
include("revanced-patcher", "arsclib-utils")
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface IntegrationsConsumer {
|
||||||
|
fun acceptIntegrations(integrations: List<File>)
|
||||||
|
}
|
||||||
14
src/main/kotlin/app/revanced/patcher/PackageMetadata.kt
Normal file
14
src/main/kotlin/app/revanced/patcher/PackageMetadata.kt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import brut.androlib.apk.ApkInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a package.
|
||||||
|
*/
|
||||||
|
class PackageMetadata internal constructor(internal val apkInfo: ApkInfo) {
|
||||||
|
lateinit var packageName: String
|
||||||
|
internal set
|
||||||
|
|
||||||
|
lateinit var packageVersion: String
|
||||||
|
internal set
|
||||||
|
}
|
||||||
127
src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt
Normal file
127
src/main/kotlin/app/revanced/patcher/PatchBundleLoader.kt
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import dalvik.system.DexClassLoader
|
||||||
|
import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import java.util.logging.Logger
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of [Patch]es.
|
||||||
|
*/
|
||||||
|
typealias PatchSet = Set<Patch<*>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Patch] class.
|
||||||
|
*/
|
||||||
|
typealias PatchClass = KClass<out Patch<*>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A loader of [Patch]es from patch bundles.
|
||||||
|
* This will load all [Patch]es from the given patch bundles that have a name.
|
||||||
|
*
|
||||||
|
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
||||||
|
* @param classLoader The [ClassLoader] to use for loading the classes.
|
||||||
|
* @param patchBundles A set of patches to initialize this instance with.
|
||||||
|
*/
|
||||||
|
sealed class PatchBundleLoader private constructor(
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
patchBundles: Array<out File>,
|
||||||
|
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
||||||
|
// This constructor parameter is unfortunately necessary,
|
||||||
|
// so that a reference to the mutable set is present in the constructor to be able to add patches to it.
|
||||||
|
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
||||||
|
private val patchSet: MutableSet<Patch<*>> = mutableSetOf()
|
||||||
|
) : PatchSet by patchSet {
|
||||||
|
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||||
|
|
||||||
|
init {
|
||||||
|
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
||||||
|
classLoader.loadClass(it)
|
||||||
|
}.filter {
|
||||||
|
Patch::class.java.isAssignableFrom(it)
|
||||||
|
}.mapNotNull { patchClass ->
|
||||||
|
patchClass.getInstance(logger, silent = true)
|
||||||
|
}.filter {
|
||||||
|
it.name != null
|
||||||
|
}.let { patches ->
|
||||||
|
patchSet.addAll(patches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal companion object Utils {
|
||||||
|
/**
|
||||||
|
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
||||||
|
*
|
||||||
|
* @param logger The [Logger] to use for logging.
|
||||||
|
* @param silent Whether to suppress logging.
|
||||||
|
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
||||||
|
*/
|
||||||
|
internal fun Class<*>.getInstance(logger: Logger, silent: Boolean = false): Patch<*>? {
|
||||||
|
return try {
|
||||||
|
getField("INSTANCE").get(null)
|
||||||
|
} catch (exception: NoSuchFieldException) {
|
||||||
|
if (!silent) logger.fine(
|
||||||
|
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
||||||
|
"Will try to instantiate it."
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
getDeclaredConstructor().newInstance()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
if (!silent) logger.severe(
|
||||||
|
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
||||||
|
"therefor cannot be instantiated and will be ignored."
|
||||||
|
)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
} as Patch<*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchBundleLoader] for JAR files.
|
||||||
|
*
|
||||||
|
* @param patchBundles The path to patch bundles of JAR format.
|
||||||
|
*/
|
||||||
|
class Jar(vararg patchBundles: File) : PatchBundleLoader(
|
||||||
|
URLClassLoader(patchBundles.map { it.toURI().toURL() }.toTypedArray()),
|
||||||
|
patchBundles,
|
||||||
|
{ patchBundle ->
|
||||||
|
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
||||||
|
.map { it.name.replace('/', '.').replace(".class", "") }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchBundleLoader] for [Dex] files.
|
||||||
|
*
|
||||||
|
* @param patchBundles The path to patch bundles of DEX format.
|
||||||
|
* @param optimizedDexDirectory The directory to store optimized DEX files in.
|
||||||
|
* This parameter is deprecated and has no effect since API level 26.
|
||||||
|
*/
|
||||||
|
class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader(
|
||||||
|
DexClassLoader(
|
||||||
|
patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, optimizedDexDirectory?.absolutePath,
|
||||||
|
null,
|
||||||
|
PatchBundleLoader::class.java.classLoader
|
||||||
|
),
|
||||||
|
patchBundles,
|
||||||
|
{ patchBundle ->
|
||||||
|
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
||||||
|
.map { classDef ->
|
||||||
|
classDef.type.substring(1, classDef.length - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
||||||
|
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface PatchExecutorFunction : Function<Boolean, Flow<PatchResult>>
|
||||||
265
src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
265
src/main/kotlin/app/revanced/patcher/Patcher.kt
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchBundleLoader.Utils.getInstance
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
|
||||||
|
import app.revanced.patcher.patch.*
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReVanced Patcher.
|
||||||
|
*
|
||||||
|
* @param options The options for the patcher.
|
||||||
|
*/
|
||||||
|
class Patcher(
|
||||||
|
private val options: PatcherOptions
|
||||||
|
) : PatchExecutorFunction, PatchesConsumer, IntegrationsConsumer, Supplier<PatcherResult>, Closeable {
|
||||||
|
private val logger = Logger.getLogger(Patcher::class.java.name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context of ReVanced [Patcher].
|
||||||
|
* This holds the current state of the patcher.
|
||||||
|
*/
|
||||||
|
val context = PatcherContext(options)
|
||||||
|
|
||||||
|
init {
|
||||||
|
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.MANIFEST_ONLY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fix circular dependency detection.
|
||||||
|
// /**
|
||||||
|
// * Add [Patch]es to ReVanced [Patcher].
|
||||||
|
// * It is not guaranteed that all supplied [Patch]es will be accepted, if an exception is thrown.
|
||||||
|
// *
|
||||||
|
// * @param patches The [Patch]es to add.
|
||||||
|
// * @throws PatcherException.CircularDependencyException If a circular dependency is detected.
|
||||||
|
// */
|
||||||
|
/**
|
||||||
|
* Add [Patch]es to ReVanced [Patcher].
|
||||||
|
*
|
||||||
|
* @param patches The [Patch]es to add.
|
||||||
|
*/
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
override fun acceptPatches(patches: List<Patch<*>>) {
|
||||||
|
/**
|
||||||
|
* Add dependencies of a [Patch] recursively to [PatcherContext.allPatches].
|
||||||
|
* If a [Patch] is already in [PatcherContext.allPatches], it will not be added again.
|
||||||
|
*/
|
||||||
|
fun PatchClass.putDependenciesRecursively() {
|
||||||
|
if (context.allPatches.contains(this)) return
|
||||||
|
|
||||||
|
val dependency = this.java.getInstance(logger)!!
|
||||||
|
context.allPatches[this] = dependency
|
||||||
|
|
||||||
|
dependency.dependencies?.forEach { it.putDependenciesRecursively() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all patches and their dependencies to the context.
|
||||||
|
for (patch in patches) context.executablePatches.putIfAbsent(patch::class, patch) ?: run {
|
||||||
|
context.allPatches[patch::class] = patch
|
||||||
|
|
||||||
|
patch.dependencies?.forEach { it.putDependenciesRecursively() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Fix circular dependency detection.
|
||||||
|
val graph = mutableMapOf<PatchClass, MutableList<PatchClass>>()
|
||||||
|
fun PatchClass.visit() {
|
||||||
|
if (this in graph) return
|
||||||
|
|
||||||
|
val group = graph.getOrPut(this) { mutableListOf(this) }
|
||||||
|
|
||||||
|
val dependencies = context.allPatches[this]!!.manifest.dependencies ?: return
|
||||||
|
dependencies.forEach { dependency ->
|
||||||
|
if (group == graph[dependency])
|
||||||
|
throw PatcherException.CircularDependencyException(context.allPatches[this]!!.manifest.name)
|
||||||
|
|
||||||
|
graph[dependency] = group.apply { add(dependency) }
|
||||||
|
dependency.visit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if at least one patch or its dependencies matches the given predicate.
|
||||||
|
*
|
||||||
|
* @param predicate The predicate to match.
|
||||||
|
*/
|
||||||
|
fun Patch<*>.anyRecursively(predicate: (Patch<*>) -> Boolean): Boolean =
|
||||||
|
predicate(this) || dependencies?.any { dependency ->
|
||||||
|
context.allPatches[dependency]!!.anyRecursively(predicate)
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
context.allPatches.values.let { patches ->
|
||||||
|
// Determine, if resource patching is required.
|
||||||
|
for (patch in patches)
|
||||||
|
if (patch.anyRecursively { patch is ResourcePatch }) {
|
||||||
|
options.resourceDecodingMode = ResourceContext.ResourceDecodingMode.FULL
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine, if merging integrations is required.
|
||||||
|
for (patch in patches)
|
||||||
|
if (patch.anyRecursively { it.requiresIntegrations }) {
|
||||||
|
context.bytecodeContext.integrations.merge = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add integrations to the [Patcher].
|
||||||
|
*
|
||||||
|
* @param integrations The integrations to add. Must be a DEX file or container of DEX files.
|
||||||
|
*/
|
||||||
|
override fun acceptIntegrations(integrations: List<File>) {
|
||||||
|
context.bytecodeContext.integrations.addAll(integrations)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute [Patch]es that were added to ReVanced [Patcher].
|
||||||
|
*
|
||||||
|
* @param returnOnError If true, ReVanced [Patcher] will return immediately if a [Patch] fails.
|
||||||
|
* @return A pair of the name of the [Patch] and its [PatchResult].
|
||||||
|
*/
|
||||||
|
override fun apply(returnOnError: Boolean) = flow {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a [Patch] and its dependencies recursively.
|
||||||
|
*
|
||||||
|
* @param patch The [Patch] to execute.
|
||||||
|
* @param executedPatches A map to prevent [Patch]es from being executed twice due to dependencies.
|
||||||
|
* @return The result of executing the [Patch].
|
||||||
|
*/
|
||||||
|
fun executePatch(
|
||||||
|
patch: Patch<*>,
|
||||||
|
executedPatches: LinkedHashMap<Patch<*>, PatchResult>
|
||||||
|
): PatchResult {
|
||||||
|
val patchName = patch.name ?: patch.toString()
|
||||||
|
|
||||||
|
executedPatches[patch]?.let { patchResult ->
|
||||||
|
patchResult.exception ?: return patchResult
|
||||||
|
|
||||||
|
// Return a new result with an exception indicating that the patch was not executed previously,
|
||||||
|
// because it is a dependency of another patch that failed.
|
||||||
|
return PatchResult(patch, PatchException("'$patchName' did not succeed previously"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively execute all dependency patches.
|
||||||
|
patch.dependencies?.forEach { dependencyClass ->
|
||||||
|
val dependency = context.allPatches[dependencyClass]!!
|
||||||
|
val result = executePatch(dependency, executedPatches)
|
||||||
|
|
||||||
|
result.exception?.let {
|
||||||
|
return PatchResult(
|
||||||
|
patch,
|
||||||
|
PatchException(
|
||||||
|
"'$patchName' depends on '${dependency.name ?: dependency}' " +
|
||||||
|
"that raised an exception:\n${it.stackTraceToString()}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
// TODO: Implement this in a more polymorphic way.
|
||||||
|
when (patch) {
|
||||||
|
is BytecodePatch -> {
|
||||||
|
patch.fingerprints.resolveUsingLookupMap(context.bytecodeContext)
|
||||||
|
patch.execute(context.bytecodeContext)
|
||||||
|
}
|
||||||
|
is ResourcePatch -> {
|
||||||
|
patch.execute(context.resourceContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchResult(patch)
|
||||||
|
} catch (exception: PatchException) {
|
||||||
|
PatchResult(patch, exception)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
PatchResult(patch, PatchException(exception))
|
||||||
|
}.also { executedPatches[patch] = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.bytecodeContext.integrations.merge) context.bytecodeContext.integrations.flush()
|
||||||
|
|
||||||
|
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
|
||||||
|
|
||||||
|
// Prevent from decoding the app manifest twice if it is not needed.
|
||||||
|
if (options.resourceDecodingMode == ResourceContext.ResourceDecodingMode.FULL)
|
||||||
|
context.resourceContext.decodeResources(ResourceContext.ResourceDecodingMode.FULL)
|
||||||
|
|
||||||
|
logger.info("Executing patches")
|
||||||
|
|
||||||
|
val executedPatches = LinkedHashMap<Patch<*>, PatchResult>() // Key is name.
|
||||||
|
|
||||||
|
context.executablePatches.values.sortedBy { it.name }.forEach { patch ->
|
||||||
|
val patchResult = executePatch(patch, executedPatches)
|
||||||
|
|
||||||
|
// If the patch failed, emit the result, even if it is closeable.
|
||||||
|
// Results of executed patches that are closeable will be emitted later.
|
||||||
|
patchResult.exception?.let {
|
||||||
|
// Propagate exception to caller instead of wrapping it in a new exception.
|
||||||
|
emit(patchResult)
|
||||||
|
|
||||||
|
if (returnOnError) return@flow
|
||||||
|
} ?: run {
|
||||||
|
if (patch is Closeable) return@run
|
||||||
|
|
||||||
|
emit(patchResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executedPatches.values
|
||||||
|
.filter { it.exception == null }
|
||||||
|
.filter { it.patch is Closeable }.asReversed().forEach { executedPatch ->
|
||||||
|
val patch = executedPatch.patch
|
||||||
|
|
||||||
|
val result = try {
|
||||||
|
(patch as Closeable).close()
|
||||||
|
|
||||||
|
executedPatch
|
||||||
|
} catch (exception: PatchException) {
|
||||||
|
PatchResult(patch, exception)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
PatchResult(patch, PatchException(exception))
|
||||||
|
}
|
||||||
|
|
||||||
|
result.exception?.let {
|
||||||
|
emit(
|
||||||
|
PatchResult(
|
||||||
|
patch,
|
||||||
|
PatchException(
|
||||||
|
"'${patch.name}' raised an exception while being closed: ${it.stackTraceToString()}",
|
||||||
|
result.exception
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (returnOnError) return@flow
|
||||||
|
} ?: run {
|
||||||
|
patch.name ?: return@run
|
||||||
|
|
||||||
|
emit(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() = MethodFingerprint.clearFingerprintResolutionLookupMaps()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile and save the patched APK file.
|
||||||
|
*
|
||||||
|
* @return The [PatcherResult] containing the patched input files.
|
||||||
|
*/
|
||||||
|
override fun get() = PatcherResult(
|
||||||
|
context.bytecodeContext.get(),
|
||||||
|
context.resourceContext.get(),
|
||||||
|
context.packageMetadata.apkInfo.doNotCompress?.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
41
src/main/kotlin/app/revanced/patcher/PatcherContext.kt
Normal file
41
src/main/kotlin/app/revanced/patcher/PatcherContext.kt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import brut.androlib.apk.ApkInfo
|
||||||
|
import brut.directory.ExtFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for ReVanced [Patcher].
|
||||||
|
*
|
||||||
|
* @param options The [PatcherOptions] used to create this context.
|
||||||
|
*/
|
||||||
|
class PatcherContext internal constructor(options: PatcherOptions) {
|
||||||
|
/**
|
||||||
|
* [PackageMetadata] of the supplied [PatcherOptions.inputFile].
|
||||||
|
*/
|
||||||
|
val packageMetadata = PackageMetadata(ApkInfo(ExtFile(options.inputFile)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of [Patch]es associated by their [PatchClass].
|
||||||
|
*/
|
||||||
|
internal val executablePatches = mutableMapOf<PatchClass, Patch<*>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of all [Patch]es and their dependencies associated by their [PatchClass].
|
||||||
|
*/
|
||||||
|
internal val allPatches = mutableMapOf<PatchClass, Patch<*>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [ResourceContext] of this [PatcherContext].
|
||||||
|
* This holds the current state of the resources.
|
||||||
|
*/
|
||||||
|
internal val resourceContext = ResourceContext(this, options)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [BytecodeContext] of this [PatcherContext].
|
||||||
|
* This holds the current state of the bytecode.
|
||||||
|
*/
|
||||||
|
internal val bytecodeContext = BytecodeContext(options)
|
||||||
|
}
|
||||||
16
src/main/kotlin/app/revanced/patcher/PatcherException.kt
Normal file
16
src/main/kotlin/app/revanced/patcher/PatcherException.kt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown by ReVanced [Patcher].
|
||||||
|
*
|
||||||
|
* @param errorMessage The exception message.
|
||||||
|
* @param cause The corresponding [Throwable].
|
||||||
|
*/
|
||||||
|
sealed class PatcherException(errorMessage: String?, cause: Throwable?) : Exception(errorMessage, cause) {
|
||||||
|
constructor(errorMessage: String) : this(errorMessage, null)
|
||||||
|
|
||||||
|
|
||||||
|
class CircularDependencyException internal constructor(dependant: String) : PatcherException(
|
||||||
|
"Patch '$dependant' causes a circular dependency"
|
||||||
|
)
|
||||||
|
}
|
||||||
72
src/main/kotlin/app/revanced/patcher/PatcherOptions.kt
Normal file
72
src/main/kotlin/app/revanced/patcher/PatcherOptions.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import brut.androlib.Config
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for ReVanced [Patcher].
|
||||||
|
* @param inputFile The input file to patch.
|
||||||
|
* @param resourceCachePath The path to the directory to use for caching resources.
|
||||||
|
* @param aaptBinaryPath The path to a custom aapt binary.
|
||||||
|
* @param frameworkFileDirectory The path to the directory to cache the framework file in.
|
||||||
|
* @param multithreadingDexFileWriter Whether to use multiple threads for writing dex files.
|
||||||
|
* This can impact memory usage.
|
||||||
|
*/
|
||||||
|
data class PatcherOptions(
|
||||||
|
internal val inputFile: File,
|
||||||
|
internal val resourceCachePath: File = File("revanced-resource-cache"),
|
||||||
|
internal val aaptBinaryPath: String? = null,
|
||||||
|
internal val frameworkFileDirectory: String? = null,
|
||||||
|
internal val multithreadingDexFileWriter: Boolean = false,
|
||||||
|
) {
|
||||||
|
private val logger = Logger.getLogger(PatcherOptions::class.java.name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mode to use for resource decoding.
|
||||||
|
* @see ResourceContext.ResourceDecodingMode
|
||||||
|
*/
|
||||||
|
internal var resourceDecodingMode = ResourceContext.ResourceDecodingMode.MANIFEST_ONLY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration to use for resource decoding and compiling.
|
||||||
|
*/
|
||||||
|
internal val resourceConfig = Config.getDefaultConfig().apply {
|
||||||
|
useAapt2 = true
|
||||||
|
aaptPath = aaptBinaryPath ?: ""
|
||||||
|
frameworkDirectory = frameworkFileDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for ReVanced [Patcher].
|
||||||
|
* @param inputFile The input file to patch.
|
||||||
|
* @param resourceCachePath The path to the directory to use for caching resources.
|
||||||
|
* @param aaptBinaryPath The path to a custom aapt binary.
|
||||||
|
* @param frameworkFileDirectory The path to the directory to cache the framework file in.
|
||||||
|
*/
|
||||||
|
@Deprecated("Use the constructor with the multithreadingDexFileWriter parameter instead")
|
||||||
|
constructor(
|
||||||
|
inputFile: File,
|
||||||
|
resourceCachePath: File = File("revanced-resource-cache"),
|
||||||
|
aaptBinaryPath: String? = null,
|
||||||
|
frameworkFileDirectory: String? = null,
|
||||||
|
) : this(
|
||||||
|
inputFile,
|
||||||
|
resourceCachePath,
|
||||||
|
aaptBinaryPath,
|
||||||
|
frameworkFileDirectory,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun recreateResourceCacheDirectory() = resourceCachePath.also {
|
||||||
|
if (it.exists()) {
|
||||||
|
logger.info("Deleting existing resource cache directory")
|
||||||
|
|
||||||
|
if (!it.deleteRecursively())
|
||||||
|
logger.severe("Failed to delete existing resource cache directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
it.mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/kotlin/app/revanced/patcher/PatcherResult.kt
Normal file
23
src/main/kotlin/app/revanced/patcher/PatcherResult.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a patcher.
|
||||||
|
* @param dexFiles The patched dex files.
|
||||||
|
* @param resourceFile File containing resources that need to be extracted into the APK.
|
||||||
|
* @param doNotCompress List of relative paths of files to exclude from compressing.
|
||||||
|
*/
|
||||||
|
data class PatcherResult(
|
||||||
|
val dexFiles: List<PatchedDexFile>,
|
||||||
|
val resourceFile: File?,
|
||||||
|
val doNotCompress: List<String>? = null
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Wrapper for dex files.
|
||||||
|
* @param name The original name of the dex file.
|
||||||
|
* @param stream The dex file as [InputStream].
|
||||||
|
*/
|
||||||
|
class PatchedDexFile(val name: String, val stream: InputStream)
|
||||||
|
}
|
||||||
8
src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt
Normal file
8
src/main/kotlin/app/revanced/patcher/PatchesConsumer.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface PatchesConsumer {
|
||||||
|
fun acceptPatches(patches: List<Patch<*>>)
|
||||||
|
}
|
||||||
168
src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt
Normal file
168
src/main/kotlin/app/revanced/patcher/data/BytecodeContext.kt
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package app.revanced.patcher.data
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatcherContext
|
||||||
|
import app.revanced.patcher.PatcherOptions
|
||||||
|
import app.revanced.patcher.PatcherResult
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.util.ClassMerger.merge
|
||||||
|
import app.revanced.patcher.util.ProxyClassList
|
||||||
|
import app.revanced.patcher.util.method.MethodWalker
|
||||||
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
|
import com.android.tools.smali.dexlib2.Opcodes
|
||||||
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
|
import com.android.tools.smali.dexlib2.iface.DexFile
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
|
import lanchon.multidexlib2.DexIO
|
||||||
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileFilter
|
||||||
|
import java.io.Flushable
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for bytecode.
|
||||||
|
* This holds the current state of the bytecode.
|
||||||
|
*
|
||||||
|
* @param options The [PatcherOptions] used to create this context.
|
||||||
|
*/
|
||||||
|
class BytecodeContext internal constructor(private val options: PatcherOptions) :
|
||||||
|
Context<List<PatcherResult.PatchedDexFile>> {
|
||||||
|
private val logger = Logger.getLogger(BytecodeContext::class.java.name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Opcodes] of the supplied [PatcherOptions.inputFile].
|
||||||
|
*/
|
||||||
|
internal lateinit var opcodes: Opcodes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of classes.
|
||||||
|
*/
|
||||||
|
val classes by lazy {
|
||||||
|
ProxyClassList(
|
||||||
|
MultiDexIO.readDexFile(
|
||||||
|
true, options.inputFile, BasicDexFileNamer(), null, null
|
||||||
|
).also { opcodes = it.opcodes }.classes.toMutableSet()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [Integrations] of this [PatcherContext].
|
||||||
|
*/
|
||||||
|
internal val integrations = Integrations()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a class by a given class name.
|
||||||
|
*
|
||||||
|
* @param className The name of the class.
|
||||||
|
* @return A proxy for the first class that matches the class name.
|
||||||
|
*/
|
||||||
|
fun findClass(className: String) = findClass { it.type.contains(className) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a class by a given predicate.
|
||||||
|
*
|
||||||
|
* @param predicate A predicate to match the class.
|
||||||
|
* @return A proxy for the first class that matches the predicate.
|
||||||
|
*/
|
||||||
|
fun findClass(predicate: (ClassDef) -> Boolean) =
|
||||||
|
// if we already proxied the class matching the predicate...
|
||||||
|
classes.proxies.firstOrNull { predicate(it.immutableClass) } ?:
|
||||||
|
// else resolve the class to a proxy and return it, if the predicate is matching a class
|
||||||
|
classes.find(predicate)?.let { proxy(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy a class.
|
||||||
|
* This will allow the class to be modified.
|
||||||
|
*
|
||||||
|
* @param classDef The class to proxy.
|
||||||
|
* @return A proxy for the class.
|
||||||
|
*/
|
||||||
|
fun proxy(classDef: ClassDef) = this.classes.proxies.find { it.immutableClass.type == classDef.type } ?: let {
|
||||||
|
ClassProxy(classDef).also { this.classes.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a [MethodWalker] instance for the current [BytecodeContext].
|
||||||
|
*
|
||||||
|
* @param startMethod The method to start at.
|
||||||
|
* @return A [MethodWalker] instance.
|
||||||
|
*/
|
||||||
|
fun toMethodWalker(startMethod: Method) = MethodWalker(this, startMethod)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile bytecode from the [BytecodeContext].
|
||||||
|
*
|
||||||
|
* @return The compiled bytecode.
|
||||||
|
*/
|
||||||
|
override fun get(): List<PatcherResult.PatchedDexFile> {
|
||||||
|
logger.info("Compiling patched dex files")
|
||||||
|
|
||||||
|
val patchedDexFileResults = options.resourceCachePath.resolve("dex").also {
|
||||||
|
it.deleteRecursively() // Make sure the directory is empty.
|
||||||
|
it.mkdirs()
|
||||||
|
}.apply {
|
||||||
|
MultiDexIO.writeDexFile(
|
||||||
|
true,
|
||||||
|
if (options.multithreadingDexFileWriter) -1 else 1,
|
||||||
|
this,
|
||||||
|
BasicDexFileNamer(),
|
||||||
|
object : DexFile {
|
||||||
|
override fun getClasses() = this@BytecodeContext.classes.also(ProxyClassList::replaceClasses)
|
||||||
|
override fun getOpcodes() = this@BytecodeContext.opcodes
|
||||||
|
},
|
||||||
|
DexIO.DEFAULT_MAX_DEX_POOL_SIZE
|
||||||
|
) { _, entryName, _ -> logger.info("Compiled $entryName") }
|
||||||
|
}.listFiles(FileFilter { it.isFile })!!.map { PatcherResult.PatchedDexFile(it.name, it.inputStream()) }
|
||||||
|
|
||||||
|
System.gc()
|
||||||
|
|
||||||
|
return patchedDexFileResults
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The integrations of a [PatcherContext].
|
||||||
|
*/
|
||||||
|
internal inner class Integrations : MutableList<File> by mutableListOf(), Flushable {
|
||||||
|
/**
|
||||||
|
* Whether to merge integrations.
|
||||||
|
* Set to true, if the field requiresIntegrations of any supplied [Patch] is true.
|
||||||
|
*/
|
||||||
|
var merge = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge integrations into the [BytecodeContext] and flush all [Integrations].
|
||||||
|
*/
|
||||||
|
override fun flush() {
|
||||||
|
if (!merge) return
|
||||||
|
|
||||||
|
logger.info("Merging integrations")
|
||||||
|
|
||||||
|
val classMap = classes.associateBy { it.type }
|
||||||
|
|
||||||
|
this@Integrations.forEach { integrations ->
|
||||||
|
MultiDexIO.readDexFile(
|
||||||
|
true,
|
||||||
|
integrations, BasicDexFileNamer(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).classes.forEach classDef@{ classDef ->
|
||||||
|
val existingClass = classMap[classDef.type] ?: run {
|
||||||
|
logger.fine("Adding $classDef")
|
||||||
|
classes.add(classDef)
|
||||||
|
return@classDef
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.fine("$classDef exists. Adding missing methods and fields.")
|
||||||
|
|
||||||
|
existingClass.merge(classDef, this@BytecodeContext).let { mergedClass ->
|
||||||
|
// If the class was merged, replace the original class with the merged class.
|
||||||
|
if (mergedClass === existingClass) return@let
|
||||||
|
classes.apply { remove(existingClass); add(mergedClass) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/kotlin/app/revanced/patcher/data/Context.kt
Normal file
9
src/main/kotlin/app/revanced/patcher/data/Context.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patcher.data
|
||||||
|
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common interface for contexts such as [ResourceContext] and [BytecodeContext].
|
||||||
|
*/
|
||||||
|
|
||||||
|
sealed interface Context<T> : Supplier<T>
|
||||||
170
src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt
Normal file
170
src/main/kotlin/app/revanced/patcher/data/ResourceContext.kt
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
package app.revanced.patcher.data
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatcherContext
|
||||||
|
import app.revanced.patcher.PatcherOptions
|
||||||
|
import app.revanced.patcher.util.DomFileEditor
|
||||||
|
import brut.androlib.AaptInvoker
|
||||||
|
import brut.androlib.ApkDecoder
|
||||||
|
import brut.androlib.apk.UsesFramework
|
||||||
|
import brut.androlib.res.Framework
|
||||||
|
import brut.androlib.res.ResourcesDecoder
|
||||||
|
import brut.androlib.res.decoder.AndroidManifestResourceParser
|
||||||
|
import brut.androlib.res.decoder.XmlPullStreamDecoder
|
||||||
|
import brut.androlib.res.xml.ResXmlPatcher
|
||||||
|
import brut.directory.ExtFile
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context for resources.
|
||||||
|
* This holds the current state of the resources.
|
||||||
|
*
|
||||||
|
* @param context The [PatcherContext] to create the context for.
|
||||||
|
*/
|
||||||
|
class ResourceContext internal constructor(
|
||||||
|
private val context: PatcherContext,
|
||||||
|
private val options: PatcherOptions
|
||||||
|
) : Context<File?>, Iterable<File> {
|
||||||
|
private val logger = Logger.getLogger(ResourceContext::class.java.name)
|
||||||
|
|
||||||
|
val xmlEditor = XmlFileHolder()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode resources for the patcher.
|
||||||
|
*
|
||||||
|
* @param mode The [ResourceDecodingMode] to use when decoding.
|
||||||
|
*/
|
||||||
|
internal fun decodeResources(mode: ResourceDecodingMode) = with(context.packageMetadata.apkInfo) {
|
||||||
|
// Needed to decode resources.
|
||||||
|
val resourcesDecoder = ResourcesDecoder(options.resourceConfig, this)
|
||||||
|
|
||||||
|
when (mode) {
|
||||||
|
ResourceDecodingMode.FULL -> {
|
||||||
|
val outDir = options.recreateResourceCacheDirectory()
|
||||||
|
|
||||||
|
logger.info("Decoding resources")
|
||||||
|
|
||||||
|
resourcesDecoder.decodeResources(outDir)
|
||||||
|
resourcesDecoder.decodeManifest(outDir)
|
||||||
|
|
||||||
|
// Needed to record uncompressed files.
|
||||||
|
val apkDecoder = ApkDecoder(options.resourceConfig, this)
|
||||||
|
apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping)
|
||||||
|
|
||||||
|
usesFramework = UsesFramework().apply {
|
||||||
|
ids = resourcesDecoder.resTable.listFramePackages().map { it.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceDecodingMode.MANIFEST_ONLY -> {
|
||||||
|
logger.info("Decoding app manifest")
|
||||||
|
|
||||||
|
// Decode manually instead of using resourceDecoder.decodeManifest
|
||||||
|
// because it does not support decoding to an OutputStream.
|
||||||
|
XmlPullStreamDecoder(
|
||||||
|
AndroidManifestResourceParser(resourcesDecoder.resTable),
|
||||||
|
resourcesDecoder.resXmlSerializer
|
||||||
|
).decodeManifest(
|
||||||
|
apkFile.directory.getFileInput("AndroidManifest.xml"),
|
||||||
|
// Older Android versions do not support OutputStream.nullOutputStream()
|
||||||
|
object : OutputStream() {
|
||||||
|
override fun write(b: Int) { /* do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get the package name and version from the manifest using the XmlPullStreamDecoder.
|
||||||
|
// XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo.
|
||||||
|
context.packageMetadata.let { metadata ->
|
||||||
|
metadata.packageName = resourcesDecoder.resTable.packageRenamed
|
||||||
|
versionInfo.let {
|
||||||
|
metadata.packageVersion = it.versionName ?: it.versionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ResTable if flagged as sparse if the main package is not loaded, which is the case here,
|
||||||
|
because ResourcesDecoder.decodeResources loads the main package
|
||||||
|
and not XmlPullStreamDecoder.decodeManifest.
|
||||||
|
See ARSCDecoder.readTableType for more info.
|
||||||
|
|
||||||
|
Set this to false again to prevent the ResTable from being flagged as sparse falsely.
|
||||||
|
*/
|
||||||
|
metadata.apkInfo.sparseResources = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(path: String) = options.resourceCachePath.resolve(path)
|
||||||
|
|
||||||
|
override fun iterator() = options.resourceCachePath.walkTopDown().iterator()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile resources from the [ResourceContext].
|
||||||
|
*
|
||||||
|
* @return The compiled resources.
|
||||||
|
*/
|
||||||
|
override fun get(): File? {
|
||||||
|
var resourceFile: File? = null
|
||||||
|
|
||||||
|
if (options.resourceDecodingMode == ResourceDecodingMode.FULL) {
|
||||||
|
logger.info("Compiling modified resources")
|
||||||
|
|
||||||
|
val cacheDirectory = ExtFile(options.resourceCachePath)
|
||||||
|
val aaptFile = cacheDirectory.resolve("aapt_temp_file").also {
|
||||||
|
Files.deleteIfExists(it.toPath())
|
||||||
|
}.also { resourceFile = it }
|
||||||
|
|
||||||
|
try {
|
||||||
|
AaptInvoker(
|
||||||
|
options.resourceConfig, context.packageMetadata.apkInfo
|
||||||
|
).invokeAapt(aaptFile,
|
||||||
|
cacheDirectory.resolve("AndroidManifest.xml").also {
|
||||||
|
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
|
||||||
|
},
|
||||||
|
cacheDirectory.resolve("res"),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
context.packageMetadata.apkInfo.usesFramework.let { usesFramework ->
|
||||||
|
usesFramework.ids.map { id ->
|
||||||
|
Framework(options.resourceConfig).getFrameworkApk(id, usesFramework.tag)
|
||||||
|
}.toTypedArray()
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
cacheDirectory.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of decoding the resources.
|
||||||
|
*/
|
||||||
|
internal enum class ResourceDecodingMode {
|
||||||
|
/**
|
||||||
|
* Decode all resources.
|
||||||
|
*/
|
||||||
|
FULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the manifest file only.
|
||||||
|
*/
|
||||||
|
MANIFEST_ONLY,
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class XmlFileHolder {
|
||||||
|
operator fun get(inputStream: InputStream) =
|
||||||
|
DomFileEditor(inputStream)
|
||||||
|
|
||||||
|
operator fun get(path: String): DomFileEditor {
|
||||||
|
return DomFileEditor(this@ResourceContext[path])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import org.jf.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a label for the instruction at given index.
|
* Create a label for the instruction at given index.
|
||||||
@@ -4,12 +4,12 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
import app.revanced.patcher.util.smali.toInstructions
|
||||||
import org.jf.dexlib2.builder.BuilderInstruction
|
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||||
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction
|
||||||
import org.jf.dexlib2.builder.Label
|
import com.android.tools.smali.dexlib2.builder.Label
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
import org.jf.dexlib2.builder.instruction.*
|
import com.android.tools.smali.dexlib2.builder.instruction.*
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
object InstructionExtensions {
|
object InstructionExtensions {
|
||||||
|
|
||||||
@@ -22,8 +22,7 @@ object InstructionExtensions {
|
|||||||
fun MutableMethodImplementation.addInstructions(
|
fun MutableMethodImplementation.addInstructions(
|
||||||
index: Int,
|
index: Int,
|
||||||
instructions: List<BuilderInstruction>
|
instructions: List<BuilderInstruction>
|
||||||
) =
|
) = instructions.asReversed().forEach { addInstruction(index, it) }
|
||||||
instructions.asReversed().forEach { addInstruction(index, it) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instructions to a method.
|
* Add instructions to a method.
|
||||||
@@ -321,6 +320,11 @@ object InstructionExtensions {
|
|||||||
* @param T The type of instruction to return.
|
* @param T The type of instruction to return.
|
||||||
* @return The instruction.
|
* @return The instruction.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
|
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instructions of a method.
|
||||||
|
* @return The instructions.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.getInstructions(): MutableList<BuilderInstruction> = implementation!!.instructions
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,7 @@ import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
object MethodFingerprintExtensions {
|
object MethodFingerprintExtensions {
|
||||||
|
// TODO: Make this a property.
|
||||||
/**
|
|
||||||
* The name of a [MethodFingerprint].
|
|
||||||
*/
|
|
||||||
val MethodFingerprint.name: String
|
|
||||||
get() = this.javaClass.simpleName
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
||||||
*/
|
*/
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
package app.revanced.patcher.fingerprint.method.impl
|
package app.revanced.patcher.fingerprint.method.impl
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.fingerprint.Fingerprint
|
import app.revanced.patcher.fingerprint.Fingerprint
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
import org.jf.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import org.jf.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
import org.jf.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch
|
private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch
|
||||||
@@ -99,7 +99,7 @@ abstract class MethodFingerprint(
|
|||||||
methodClassPairs!!.add(methodClassPair)
|
methodClassPairs!!.add(methodClassPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methods.isNotEmpty()) throw PatchException("Map already initialized")
|
if (methods.isNotEmpty()) clearFingerprintResolutionLookupMaps()
|
||||||
|
|
||||||
context.classes.forEach { classDef ->
|
context.classes.forEach { classDef ->
|
||||||
classDef.methods.forEach { method ->
|
classDef.methods.forEach { method ->
|
||||||
@@ -159,10 +159,10 @@ abstract class MethodFingerprint(
|
|||||||
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
* - Faster: Specify [accessFlags], [returnType] and [parameters].
|
||||||
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
|
||||||
*/
|
*/
|
||||||
internal fun Iterable<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
internal fun Set<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
|
||||||
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
if (methods.isEmpty()) throw PatchException("lookup map not initialized")
|
||||||
|
|
||||||
for (fingerprint in this) {
|
forEach { fingerprint ->
|
||||||
fingerprint.resolveUsingLookupMap(context)
|
fingerprint.resolveUsingLookupMap(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,18 +362,18 @@ abstract class MethodFingerprint(
|
|||||||
val patternOpcode = pattern.elementAt(patternIndex)
|
val patternOpcode = pattern.elementAt(patternIndex)
|
||||||
|
|
||||||
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
|
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
|
||||||
// Reaching maximum threshold (0) means,
|
// reaching maximum threshold (0) means,
|
||||||
// the pattern does not match to the current instructions.
|
// the pattern does not match to the current instructions
|
||||||
if (threshold-- == 0) break
|
if (threshold-- == 0) break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patternIndex < patternLength - 1) {
|
if (patternIndex < patternLength - 1) {
|
||||||
// If the entire pattern has not been scanned yet
|
// if the entire pattern has not been scanned yet
|
||||||
// continue the scan.
|
// continue the scan
|
||||||
patternIndex++
|
patternIndex++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// The pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
|
// the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
|
||||||
val result =
|
val result =
|
||||||
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
|
||||||
index,
|
index,
|
||||||
@@ -448,7 +448,7 @@ data class MethodFingerprintResult(
|
|||||||
* Use [classDef] where possible.
|
* Use [classDef] where possible.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
val mutableClass by lazy { context.classes.proxy(classDef).mutableClass }
|
val mutableClass by lazy { context.proxy(classDef).mutableClass }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a mutable clone of [method]
|
* Returns a mutable clone of [method]
|
||||||
@@ -510,4 +510,4 @@ data class MethodFingerprintResult(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
9
src/main/kotlin/app/revanced/patcher/logging/Logger.kt
Normal file
9
src/main/kotlin/app/revanced/patcher/logging/Logger.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patcher.logging
|
||||||
|
|
||||||
|
@Deprecated("This will be removed in a future release")
|
||||||
|
interface Logger {
|
||||||
|
fun error(msg: String) {}
|
||||||
|
fun warn(msg: String) {}
|
||||||
|
fun info(msg: String) {}
|
||||||
|
fun trace(msg: String) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package app.revanced.patcher.logging.impl
|
||||||
|
|
||||||
|
import app.revanced.patcher.logging.Logger
|
||||||
|
|
||||||
|
@Deprecated("This will be removed in a future release")
|
||||||
|
object NopLogger : Logger
|
||||||
13
src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt
Normal file
13
src/main/kotlin/app/revanced/patcher/patch/BytecodePatch.kt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced [Patch] that works on [BytecodeContext].
|
||||||
|
*
|
||||||
|
* @param fingerprints A list of [MethodFingerprint]s which will be resolved before the patch is executed.
|
||||||
|
*/
|
||||||
|
abstract class BytecodePatch(
|
||||||
|
internal val fingerprints : Set<MethodFingerprint> = emptySet(),
|
||||||
|
) : Patch<BytecodeContext>()
|
||||||
106
src/main/kotlin/app/revanced/patcher/patch/Patch.kt
Normal file
106
src/main/kotlin/app/revanced/patcher/patch/Patch.kt
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchClass
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
import app.revanced.patcher.data.Context
|
||||||
|
import app.revanced.patcher.patch.options.PatchOptions
|
||||||
|
import java.io.Closeable
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced patch.
|
||||||
|
* If an implementation of [Patch] also implements [Closeable]
|
||||||
|
* it will be closed in reverse execution order of patches executed by ReVanced [Patcher].
|
||||||
|
*
|
||||||
|
* @param T The [Context] type this patch will work on.
|
||||||
|
*/
|
||||||
|
sealed class Patch<out T : Context<*>> {
|
||||||
|
/**
|
||||||
|
* The name of the patch.
|
||||||
|
*/
|
||||||
|
var name: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of the patch.
|
||||||
|
*/
|
||||||
|
var description: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packages the patch is compatible with.
|
||||||
|
*/
|
||||||
|
var compatiblePackages: Set<CompatiblePackage>? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other patches this patch depends on.
|
||||||
|
*/
|
||||||
|
var dependencies: Set<PatchClass>? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not the patch should be used.
|
||||||
|
*/
|
||||||
|
var use = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
|
/**
|
||||||
|
* Weather or not the patch requires integrations.
|
||||||
|
*/
|
||||||
|
var requiresIntegrations = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options of the patch associated by the options key.
|
||||||
|
*/
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
|
init {
|
||||||
|
this::class.findAnnotation<app.revanced.patcher.patch.annotation.Patch>()?.let { annotation ->
|
||||||
|
name = annotation.name.ifEmpty { null }
|
||||||
|
description = annotation.description.ifEmpty { null }
|
||||||
|
compatiblePackages = annotation.compatiblePackages
|
||||||
|
.map { CompatiblePackage(it.name, it.versions.toSet()) }.toSet()
|
||||||
|
dependencies = annotation.dependencies.toSet().ifEmpty { null }
|
||||||
|
use = annotation.use
|
||||||
|
requiresIntegrations = annotation.requiresIntegrations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The execution function of the patch.
|
||||||
|
*
|
||||||
|
* @param context The [Context] the patch will work on.
|
||||||
|
* @return The result of executing the patch.
|
||||||
|
*/
|
||||||
|
abstract fun execute(context: @UnsafeVariance T)
|
||||||
|
|
||||||
|
override fun hashCode() = name.hashCode()
|
||||||
|
|
||||||
|
override fun toString() = name ?: this::class.simpleName ?: "Unnamed patch"
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Patch<*>
|
||||||
|
|
||||||
|
return name == other.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A package a [Patch] is compatible with.
|
||||||
|
*
|
||||||
|
* @param name The name of the package.
|
||||||
|
* @param versions The versions of the package.
|
||||||
|
*/
|
||||||
|
class CompatiblePackage(
|
||||||
|
val name: String,
|
||||||
|
val versions: Set<String>? = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A result of executing a [Patch].
|
||||||
|
*
|
||||||
|
* @param patch The [Patch] that was executed.
|
||||||
|
* @param exception The [PatchException] thrown, if any.
|
||||||
|
*/
|
||||||
|
class PatchResult internal constructor(val patch: Patch<*>, val exception: PatchException? = null)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package app.revanced.patcher.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ReVanced [Patch] that works on [ResourceContext].
|
||||||
|
*/
|
||||||
|
abstract class ResourcePatch : Patch<ResourceContext>()
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation
|
||||||
|
|
||||||
|
import java.lang.annotation.Inherited
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for [app.revanced.patcher.patch.Patch] classes.
|
||||||
|
*
|
||||||
|
* @param name The name of the patch. If empty, the patch will be unnamed.
|
||||||
|
* @param description The description of the patch. If empty, no description will be used.
|
||||||
|
* @param dependencies The patches this patch depends on.
|
||||||
|
* @param compatiblePackages The packages this patch is compatible with.
|
||||||
|
* @param use Whether this patch should be used.
|
||||||
|
* @param requiresIntegrations Whether this patch requires integrations.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Inherited
|
||||||
|
annotation class Patch(
|
||||||
|
val name: String = "",
|
||||||
|
val description: String = "",
|
||||||
|
val dependencies: Array<KClass<out app.revanced.patcher.patch.Patch<*>>> = [],
|
||||||
|
val compatiblePackages: Array<CompatiblePackage> = [],
|
||||||
|
val use: Boolean = true,
|
||||||
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
|
val requiresIntegrations: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A package that a [app.revanced.patcher.patch.Patch] is compatible with.
|
||||||
|
*
|
||||||
|
* @param name The name of the package.
|
||||||
|
* @param versions The versions of the package.
|
||||||
|
*/
|
||||||
|
annotation class CompatiblePackage(
|
||||||
|
val name: String,
|
||||||
|
val versions: Array<String> = [],
|
||||||
|
)
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Patch] option.
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
* @param T The value type of the option.
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
open class PatchOption<T>(
|
||||||
|
val key: String,
|
||||||
|
val default: T?,
|
||||||
|
val values: Set<T>,
|
||||||
|
val title: String?,
|
||||||
|
val description: String?,
|
||||||
|
val required: Boolean,
|
||||||
|
val validator: PatchOption<T>.(T?) -> Boolean
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The value of the [PatchOption].
|
||||||
|
*/
|
||||||
|
var value: T?
|
||||||
|
/**
|
||||||
|
* Set the value of the [PatchOption].
|
||||||
|
*
|
||||||
|
* @param value The value to set.
|
||||||
|
*
|
||||||
|
* @throws PatchOptionException.ValueRequiredException If the value is required but null.
|
||||||
|
* @throws PatchOptionException.ValueValidationException If the value is invalid.
|
||||||
|
*/
|
||||||
|
set(value) {
|
||||||
|
assertRequiredButNotNull(value)
|
||||||
|
assertValid(value)
|
||||||
|
|
||||||
|
uncheckedValue = value
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the value of the [PatchOption].
|
||||||
|
*
|
||||||
|
* @return The value.
|
||||||
|
*
|
||||||
|
* @throws PatchOptionException.ValueRequiredException If the value is required but null.
|
||||||
|
* @throws PatchOptionException.ValueValidationException If the value is invalid.
|
||||||
|
*/
|
||||||
|
get() {
|
||||||
|
assertRequiredButNotNull(uncheckedValue)
|
||||||
|
assertValid(uncheckedValue)
|
||||||
|
|
||||||
|
return uncheckedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The unchecked value is used to allow setting the value without validation.
|
||||||
|
private var uncheckedValue = default
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the [PatchOption] to its default value.
|
||||||
|
* Override this method if you need to mutate the value instead of replacing it.
|
||||||
|
*/
|
||||||
|
open fun reset() {
|
||||||
|
uncheckedValue = default
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertRequiredButNotNull(value: T?) {
|
||||||
|
if (required && value == null) throw PatchOptionException.ValueRequiredException(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertValid(value: T?) {
|
||||||
|
if (!validator(value)) throw PatchOptionException.ValueValidationException(value, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = value.toString()
|
||||||
|
|
||||||
|
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
|
||||||
|
|
||||||
|
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
companion object PatchExtensions {
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a string value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.stringPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: String? = null,
|
||||||
|
values: Set<String> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<String>.(String?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with an integer value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.intPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Int? = null,
|
||||||
|
values: Set<Int> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Int?>.(Int?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a boolean value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.booleanPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Boolean? = null,
|
||||||
|
values: Set<Boolean> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Boolean?>.(Boolean?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a float value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.floatPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Float? = null,
|
||||||
|
values: Set<Float> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Float?>.(Float?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a long value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.longPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Long? = null,
|
||||||
|
values: Set<Long> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Long?>.(Long?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a string array value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.stringArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<String>? = null,
|
||||||
|
values: Set<Array<String>> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Array<String>?>.(Array<String>?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with an integer array value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.intArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Int>? = null,
|
||||||
|
values: Set<Array<Int>> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Array<Int>?>.(Array<Int>?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a boolean array value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.booleanArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Boolean>? = null,
|
||||||
|
values: Set<Array<Boolean>> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Array<Boolean>?>.(Array<Boolean>?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a float array value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.floatArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Float>? = null,
|
||||||
|
values: Set<Array<Float>> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Array<Float>?>.(Array<Float>?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new [PatchOption] with a long array value and add it to the current [Patch].
|
||||||
|
*
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param default The default value.
|
||||||
|
* @param values The set of guaranteed valid values.
|
||||||
|
* @param title The title.
|
||||||
|
* @param description A description.
|
||||||
|
* @param required Whether the option is required.
|
||||||
|
* @param validator The function to validate the option value.
|
||||||
|
*
|
||||||
|
* @return The created [PatchOption].
|
||||||
|
*
|
||||||
|
* @see PatchOption
|
||||||
|
*/
|
||||||
|
fun <P : Patch<*>> P.longArrayPatchOption(
|
||||||
|
key: String,
|
||||||
|
default: Array<Long>? = null,
|
||||||
|
values: Set<Array<Long>> = emptySet(),
|
||||||
|
title: String? = null,
|
||||||
|
description: String? = null,
|
||||||
|
required: Boolean = false,
|
||||||
|
validator: PatchOption<Array<Long>?>.(Array<Long>?) -> Boolean = { true }
|
||||||
|
) = PatchOption(key, default, values, title, description, required, validator).also { registerOption(it) }
|
||||||
|
|
||||||
|
private fun <P : Patch<*>> P.registerOption(option: PatchOption<*>) = option.also { options.register(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when using [PatchOption]s.
|
||||||
|
*
|
||||||
|
* @param errorMessage The exception message.
|
||||||
|
*/
|
||||||
|
sealed class PatchOptionException(errorMessage: String) : Exception(errorMessage, null) {
|
||||||
|
/**
|
||||||
|
* An exception thrown when a [PatchOption] is set to an invalid value.
|
||||||
|
*
|
||||||
|
* @param invalidType The type of the value that was passed.
|
||||||
|
* @param expectedType The type of the value that was expected.
|
||||||
|
*/
|
||||||
|
class InvalidValueTypeException(invalidType: String, expectedType: String) :
|
||||||
|
PatchOptionException("Type $expectedType was expected but received type $invalidType")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a value did not satisfy the value conditions specified by the [PatchOption].
|
||||||
|
*
|
||||||
|
* @param value The value that failed validation.
|
||||||
|
*/
|
||||||
|
class ValueValidationException(value: Any?, option: PatchOption<*>) :
|
||||||
|
PatchOptionException("The option value \"$value\" failed validation for ${option.key}")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a value is required but null was passed.
|
||||||
|
*
|
||||||
|
* @param option The [PatchOption] that requires a value.
|
||||||
|
*/
|
||||||
|
class ValueRequiredException(option: PatchOption<*>) :
|
||||||
|
PatchOptionException("The option ${option.key} requires a value, but null was passed")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception thrown when a [PatchOption] is not found.
|
||||||
|
*
|
||||||
|
* @param key The key of the [PatchOption].
|
||||||
|
*/
|
||||||
|
class PatchOptionNotFoundException(key: String)
|
||||||
|
: PatchOptionException("No option with key $key")
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package app.revanced.patcher.patch.options
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of [PatchOption]s associated by their keys.
|
||||||
|
*
|
||||||
|
* @param options The [PatchOption]s to initialize with.
|
||||||
|
*/
|
||||||
|
class PatchOptions internal constructor(
|
||||||
|
private val options: MutableMap<String, PatchOption<*>> = mutableMapOf()
|
||||||
|
) : MutableMap<String, PatchOption<*>> by options {
|
||||||
|
/**
|
||||||
|
* Register a [PatchOption]. Acts like [MutableMap.put].
|
||||||
|
* @param value The [PatchOption] to register.
|
||||||
|
*/
|
||||||
|
fun register(value: PatchOption<*>) {
|
||||||
|
options[value.key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an option's value.
|
||||||
|
* @param key The identifier.
|
||||||
|
* @param value The value.
|
||||||
|
* @throws PatchOptionException.PatchOptionNotFoundException If the option does not exist.
|
||||||
|
*/
|
||||||
|
operator fun <T : Any> set(key: String, value: T?) {
|
||||||
|
val option = this[key]
|
||||||
|
|
||||||
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(option as PatchOption<T>).value = value
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
throw PatchOptionException.InvalidValueTypeException(
|
||||||
|
value?.let { it::class.java.name } ?: "null",
|
||||||
|
option.value?.let { it::class.java.name } ?: "null",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an option.
|
||||||
|
*/
|
||||||
|
override operator fun get(key: String) =
|
||||||
|
options[key] ?: throw PatchOptionException.PatchOptionNotFoundException(key)
|
||||||
|
}
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
package app.revanced.patcher.util
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.logging.Logger
|
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.isPublic
|
||||||
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
import app.revanced.patcher.util.ClassMerger.Utils.toPublic
|
||||||
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
|
import app.revanced.patcher.util.ClassMerger.Utils.traverseClassHierarchy
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import org.jf.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import org.jf.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
import java.util.logging.Logger
|
||||||
import kotlin.reflect.KFunction2
|
import kotlin.reflect.KFunction2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,27 +25,28 @@ import kotlin.reflect.KFunction2
|
|||||||
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
||||||
*/
|
*/
|
||||||
internal object ClassMerger {
|
internal object ClassMerger {
|
||||||
|
private val logger = Logger.getLogger(ClassMerger::class.java.name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge a class with [otherClass].
|
* Merge a class with [otherClass].
|
||||||
*
|
*
|
||||||
* @param otherClass The class to merge with
|
* @param otherClass The class to merge with
|
||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
* @param logger A logger.
|
* @return The merged class or the original class if no merge was needed.
|
||||||
*/
|
*/
|
||||||
fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext, logger: Logger? = null) = this
|
fun ClassDef.merge(otherClass: ClassDef, context: BytecodeContext) = this
|
||||||
//.fixFieldAccess(otherClass, logger)
|
//.fixFieldAccess(otherClass)
|
||||||
//.fixMethodAccess(otherClass, logger)
|
//.fixMethodAccess(otherClass)
|
||||||
.addMissingFields(otherClass, logger)
|
.addMissingFields(otherClass)
|
||||||
.addMissingMethods(otherClass, logger)
|
.addMissingMethods(otherClass)
|
||||||
.publicize(otherClass, context, logger)
|
.publicize(otherClass, context)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add methods which are missing but existing in [fromClass].
|
* Add methods which are missing but existing in [fromClass].
|
||||||
*
|
*
|
||||||
* @param fromClass The class to add missing methods from.
|
* @param fromClass The class to add missing methods from.
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingMethods(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
private fun ClassDef.addMissingMethods(fromClass: ClassDef): ClassDef {
|
||||||
val missingMethods = fromClass.methods.let { fromMethods ->
|
val missingMethods = fromClass.methods.let { fromMethods ->
|
||||||
methods.filterNot { method ->
|
methods.filterNot { method ->
|
||||||
fromMethods.any { fromMethod ->
|
fromMethods.any { fromMethod ->
|
||||||
@@ -56,7 +57,7 @@ internal object ClassMerger {
|
|||||||
|
|
||||||
if (missingMethods.isEmpty()) return this
|
if (missingMethods.isEmpty()) return this
|
||||||
|
|
||||||
logger?.trace("Found ${missingMethods.size} missing methods")
|
logger.fine("Found ${missingMethods.size} missing methods")
|
||||||
|
|
||||||
return asMutableClass().apply {
|
return asMutableClass().apply {
|
||||||
methods.addAll(missingMethods.map { it.toMutable() })
|
methods.addAll(missingMethods.map { it.toMutable() })
|
||||||
@@ -67,16 +68,15 @@ internal object ClassMerger {
|
|||||||
* Add fields which are missing but existing in [fromClass].
|
* Add fields which are missing but existing in [fromClass].
|
||||||
*
|
*
|
||||||
* @param fromClass The class to add missing fields from.
|
* @param fromClass The class to add missing fields from.
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.addMissingFields(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
private fun ClassDef.addMissingFields(fromClass: ClassDef): ClassDef {
|
||||||
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
||||||
fromField.name == field.name
|
fromField.name == field.name
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingFields.isEmpty()) return this
|
if (missingFields.isEmpty()) return this
|
||||||
|
|
||||||
logger?.trace("Found ${missingFields.size} missing fields")
|
logger.fine("Found ${missingFields.size} missing fields")
|
||||||
|
|
||||||
return asMutableClass().apply {
|
return asMutableClass().apply {
|
||||||
fields.addAll(missingFields.map { it.toMutable() })
|
fields.addAll(missingFields.map { it.toMutable() })
|
||||||
@@ -87,15 +87,14 @@ internal object ClassMerger {
|
|||||||
* Make a class and its super class public recursively.
|
* Make a class and its super class public recursively.
|
||||||
* @param reference The class to check the [AccessFlags] of.
|
* @param reference The class to check the [AccessFlags] of.
|
||||||
* @param context The context to traverse the class hierarchy in.
|
* @param context The context to traverse the class hierarchy in.
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext, logger: Logger? = null) =
|
private fun ClassDef.publicize(reference: ClassDef, context: BytecodeContext) =
|
||||||
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
||||||
this.asMutableClass().apply {
|
this.asMutableClass().apply {
|
||||||
context.traverseClassHierarchy(this) {
|
context.traverseClassHierarchy(this) {
|
||||||
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||||
|
|
||||||
logger?.trace("Publicizing ${this.type}")
|
logger.fine("Publicizing ${this.type}")
|
||||||
|
|
||||||
accessFlags = accessFlags.toPublic()
|
accessFlags = accessFlags.toPublic()
|
||||||
}
|
}
|
||||||
@@ -106,9 +105,8 @@ internal object ClassMerger {
|
|||||||
* Publicize fields if they are public in [reference].
|
* Publicize fields if they are public in [reference].
|
||||||
*
|
*
|
||||||
* @param reference The class to check the [AccessFlags] of the fields in.
|
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixFieldAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
private fun ClassDef.fixFieldAccess(reference: ClassDef): ClassDef {
|
||||||
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
||||||
if (field.name != referenceField.name) return@filterAny false
|
if (field.name != referenceField.name) return@filterAny false
|
||||||
|
|
||||||
@@ -117,7 +115,7 @@ internal object ClassMerger {
|
|||||||
|
|
||||||
if (brokenFields.isEmpty()) return this
|
if (brokenFields.isEmpty()) return this
|
||||||
|
|
||||||
logger?.trace("Found ${brokenFields.size} broken fields")
|
logger.fine("Found ${brokenFields.size} broken fields")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a field public.
|
* Make a field public.
|
||||||
@@ -135,9 +133,8 @@ internal object ClassMerger {
|
|||||||
* Publicize methods if they are public in [reference].
|
* Publicize methods if they are public in [reference].
|
||||||
*
|
*
|
||||||
* @param reference The class to check the [AccessFlags] of the methods in.
|
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||||
* @param logger A logger.
|
|
||||||
*/
|
*/
|
||||||
private fun ClassDef.fixMethodAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
private fun ClassDef.fixMethodAccess(reference: ClassDef): ClassDef {
|
||||||
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||||
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||||
|
|
||||||
@@ -146,7 +143,7 @@ internal object ClassMerger {
|
|||||||
|
|
||||||
if (brokenMethods.isEmpty()) return this
|
if (brokenMethods.isEmpty()) return this
|
||||||
|
|
||||||
logger?.trace("Found ${brokenMethods.size} methods")
|
logger.fine("Found ${brokenMethods.size} methods")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a method public.
|
* Make a method public.
|
||||||
@@ -161,6 +158,19 @@ internal object ClassMerger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object Utils {
|
private object Utils {
|
||||||
|
/**
|
||||||
|
* traverse the class hierarchy starting from the given root class
|
||||||
|
*
|
||||||
|
* @param targetClass the class to start traversing the class hierarchy from
|
||||||
|
* @param callback function that is called for every class in the hierarchy
|
||||||
|
*/
|
||||||
|
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||||
|
callback(targetClass)
|
||||||
|
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||||
|
traverseClassHierarchy(it, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,7 +190,6 @@ internal object ClassMerger {
|
|||||||
/**
|
/**
|
||||||
* Filter [this] on [needles] matching the given [predicate].
|
* Filter [this] on [needles] matching the given [predicate].
|
||||||
*
|
*
|
||||||
* @param this The hay to filter for [needles].
|
|
||||||
* @param needles The needles to filter [this] with.
|
* @param needles The needles to filter [this] with.
|
||||||
* @param predicate The filter.
|
* @param predicate The filter.
|
||||||
* @return The [this] filtered on [needles] matching the given [predicate].
|
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||||
@@ -192,7 +201,6 @@ internal object ClassMerger {
|
|||||||
/**
|
/**
|
||||||
* Filter [this] on [needles] not matching the given [predicate].
|
* Filter [this] on [needles] not matching the given [predicate].
|
||||||
*
|
*
|
||||||
* @param this The hay to filter for [needles].
|
|
||||||
* @param needles The needles to filter [this] with.
|
* @param needles The needles to filter [this] with.
|
||||||
* @param predicate The filter.
|
* @param predicate The filter.
|
||||||
* @return The [this] filtered on [needles] not matching the given [predicate].
|
* @return The [this] filtered on [needles] not matching the given [predicate].
|
||||||
87
src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt
Normal file
87
src/main/kotlin/app/revanced/patcher/util/DomFileEditor.kt
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import org.w3c.dom.Document
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.transform.TransformerFactory
|
||||||
|
import javax.xml.transform.dom.DOMSource
|
||||||
|
import javax.xml.transform.stream.StreamResult
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a file that can be edited as a dom document.
|
||||||
|
*
|
||||||
|
* This constructor does not check for locks to the file when writing.
|
||||||
|
* Use the secondary constructor.
|
||||||
|
*
|
||||||
|
* @param inputStream the input stream to read the xml file from.
|
||||||
|
* @param outputStream the output stream to write the xml file to. If null, the file will be read only.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DomFileEditor internal constructor(
|
||||||
|
private val inputStream: InputStream,
|
||||||
|
private val outputStream: Lazy<OutputStream>? = null,
|
||||||
|
) : Closeable {
|
||||||
|
// path to the xml file to unlock the resource when closing the editor
|
||||||
|
private var filePath: String? = null
|
||||||
|
private var closed: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The document of the xml file
|
||||||
|
*/
|
||||||
|
val file: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
|
||||||
|
.also(Document::normalize)
|
||||||
|
|
||||||
|
|
||||||
|
// lazily open an output stream
|
||||||
|
// this is required because when constructing a DomFileEditor the output stream is created along with the input stream, which is not allowed
|
||||||
|
// the workaround is to lazily create the output stream. This way it would be used after the input stream is closed, which happens in the constructor
|
||||||
|
constructor(file: File) : this(file.inputStream(), lazy { file.outputStream() }) {
|
||||||
|
// increase the lock
|
||||||
|
locks.merge(file.path, 1, Integer::sum)
|
||||||
|
filePath = file.path
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the editor. Write backs and decreases the lock count.
|
||||||
|
*
|
||||||
|
* Will not write back to the file if the file is still locked.
|
||||||
|
*/
|
||||||
|
override fun close() {
|
||||||
|
if (closed) return
|
||||||
|
|
||||||
|
inputStream.close()
|
||||||
|
|
||||||
|
// if the output stream is not null, do not close it
|
||||||
|
outputStream?.let {
|
||||||
|
// prevent writing to same file, if it is being locked
|
||||||
|
// isLocked will be false if the editor was created through a stream
|
||||||
|
val isLocked = filePath?.let { path ->
|
||||||
|
val isLocked = locks[path]!! > 1
|
||||||
|
// decrease the lock count if the editor was opened for a file
|
||||||
|
locks.merge(path, -1, Integer::sum)
|
||||||
|
isLocked
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
// if unlocked, write back to the file
|
||||||
|
if (!isLocked) {
|
||||||
|
it.value.use { stream ->
|
||||||
|
val result = StreamResult(stream)
|
||||||
|
TransformerFactory.newInstance().newTransformer().transform(DOMSource(file), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.value.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
// map of concurrent open files
|
||||||
|
val locks = mutableMapOf<String, Int>()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt
Normal file
33
src/main/kotlin/app/revanced/patcher/util/ProxyClassList.kt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents a set of classes and proxies.
|
||||||
|
*
|
||||||
|
* @param classes The classes to be backed by proxies.
|
||||||
|
*/
|
||||||
|
class ProxyClassList internal constructor(classes: MutableSet<ClassDef>) : MutableSet<ClassDef> by classes {
|
||||||
|
internal val proxies = mutableListOf<ClassProxy>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a [ClassProxy].
|
||||||
|
*/
|
||||||
|
fun add(classProxy: ClassProxy) = proxies.add(classProxy)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace all classes with their mutated versions.
|
||||||
|
*/
|
||||||
|
internal fun replaceClasses() = proxies.removeIf { proxy ->
|
||||||
|
// If the proxy is unused, return false to keep it in the proxies list.
|
||||||
|
if (!proxy.resolved) return@removeIf false
|
||||||
|
|
||||||
|
// If it has been used, replace the original class with the mutable class.
|
||||||
|
remove(proxy.immutableClass)
|
||||||
|
add(proxy.mutableClass)
|
||||||
|
|
||||||
|
// Return true to remove the proxy from the proxies list.
|
||||||
|
return@removeIf true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package app.revanced.patcher.util.method
|
package app.revanced.patcher.util.method
|
||||||
|
|
||||||
import app.revanced.patcher.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import org.jf.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import org.jf.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a method from another method via instruction offsets.
|
* Find a method from another method via instruction offsets.
|
||||||
@@ -41,7 +41,7 @@ class MethodWalker internal constructor(
|
|||||||
val instruction = instructions.elementAt(offset)
|
val instruction = instructions.elementAt(offset)
|
||||||
|
|
||||||
val newMethod = (instruction as ReferenceInstruction).reference as MethodReference
|
val newMethod = (instruction as ReferenceInstruction).reference as MethodReference
|
||||||
val proxy = bytecodeContext.classes.findClassProxied(newMethod.definingClass)!!
|
val proxy = bytecodeContext.findClass(newMethod.definingClass)!!
|
||||||
|
|
||||||
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
val methods = if (walkMutable) proxy.mutableClass.methods else proxy.immutableClass.methods
|
||||||
currentMethod = methods.first {
|
currentMethod = methods.first {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy
|
package app.revanced.patcher.util.proxy
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A proxy class for a [ClassDef].
|
* A proxy class for a [ClassDef].
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes
|
package app.revanced.patcher.util.proxy.mutableTypes
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
||||||
import org.jf.dexlib2.base.BaseAnnotation
|
import com.android.tools.smali.dexlib2.base.BaseAnnotation
|
||||||
import org.jf.dexlib2.iface.Annotation
|
import com.android.tools.smali.dexlib2.iface.Annotation
|
||||||
|
|
||||||
class MutableAnnotation(annotation: Annotation) : BaseAnnotation() {
|
class MutableAnnotation(annotation: Annotation) : BaseAnnotation() {
|
||||||
private val visibility = annotation.visibility
|
private val visibility = annotation.visibility
|
||||||
@@ -2,9 +2,9 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import org.jf.dexlib2.base.BaseAnnotationElement
|
import com.android.tools.smali.dexlib2.base.BaseAnnotationElement
|
||||||
import org.jf.dexlib2.iface.AnnotationElement
|
import com.android.tools.smali.dexlib2.iface.AnnotationElement
|
||||||
import org.jf.dexlib2.iface.value.EncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.EncodedValue
|
||||||
|
|
||||||
class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnotationElement() {
|
class MutableAnnotationElement(annotationElement: AnnotationElement) : BaseAnnotationElement() {
|
||||||
private var name = annotationElement.name
|
private var name = annotationElement.name
|
||||||
@@ -4,10 +4,10 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import com.google.common.collect.Iterables
|
import com.google.common.collect.Iterables
|
||||||
import org.jf.dexlib2.base.reference.BaseTypeReference
|
import com.android.tools.smali.dexlib2.base.reference.BaseTypeReference
|
||||||
import org.jf.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import org.jf.dexlib2.util.FieldUtil
|
import com.android.tools.smali.dexlib2.util.FieldUtil
|
||||||
import org.jf.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() {
|
class MutableClass(classDef: ClassDef) : ClassDef, BaseTypeReference() {
|
||||||
// Class
|
// Class
|
||||||
@@ -3,9 +3,9 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import org.jf.dexlib2.HiddenApiRestriction
|
import com.android.tools.smali.dexlib2.HiddenApiRestriction
|
||||||
import org.jf.dexlib2.base.reference.BaseFieldReference
|
import com.android.tools.smali.dexlib2.base.reference.BaseFieldReference
|
||||||
import org.jf.dexlib2.iface.Field
|
import com.android.tools.smali.dexlib2.iface.Field
|
||||||
|
|
||||||
class MutableField(field: Field) : Field, BaseFieldReference() {
|
class MutableField(field: Field) : Field, BaseFieldReference() {
|
||||||
private var definingClass = field.definingClass
|
private var definingClass = field.definingClass
|
||||||
@@ -2,10 +2,10 @@ package app.revanced.patcher.util.proxy.mutableTypes
|
|||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethodParameter.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethodParameter.Companion.toMutable
|
||||||
import org.jf.dexlib2.HiddenApiRestriction
|
import com.android.tools.smali.dexlib2.HiddenApiRestriction
|
||||||
import org.jf.dexlib2.base.reference.BaseMethodReference
|
import com.android.tools.smali.dexlib2.base.reference.BaseMethodReference
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
import org.jf.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
|
||||||
class MutableMethod(method: Method) : Method, BaseMethodReference() {
|
class MutableMethod(method: Method) : Method, BaseMethodReference() {
|
||||||
private var definingClass = method.definingClass
|
private var definingClass = method.definingClass
|
||||||
@@ -13,7 +13,7 @@ class MutableMethod(method: Method) : Method, BaseMethodReference() {
|
|||||||
private var accessFlags = method.accessFlags
|
private var accessFlags = method.accessFlags
|
||||||
private var returnType = method.returnType
|
private var returnType = method.returnType
|
||||||
|
|
||||||
// TODO: Create own mutable MethodImplementation (due to not being able to change members like register count).
|
// Create own mutable MethodImplementation (due to not being able to change members like register count)
|
||||||
private val _implementation by lazy { method.implementation?.let { MutableMethodImplementation(it) } }
|
private val _implementation by lazy { method.implementation?.let { MutableMethodImplementation(it) } }
|
||||||
private val _annotations by lazy { method.annotations.map { annotation -> annotation.toMutable() }.toMutableSet() }
|
private val _annotations by lazy { method.annotations.map { annotation -> annotation.toMutable() }.toMutableSet() }
|
||||||
private val _parameters by lazy { method.parameters.map { parameter -> parameter.toMutable() }.toMutableList() }
|
private val _parameters by lazy { method.parameters.map { parameter -> parameter.toMutable() }.toMutableList() }
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes
|
package app.revanced.patcher.util.proxy.mutableTypes
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotation.Companion.toMutable
|
||||||
import org.jf.dexlib2.base.BaseMethodParameter
|
import com.android.tools.smali.dexlib2.base.BaseMethodParameter
|
||||||
import org.jf.dexlib2.iface.MethodParameter
|
import com.android.tools.smali.dexlib2.iface.MethodParameter
|
||||||
|
|
||||||
// TODO: Finish overriding all members if necessary.
|
// TODO: finish overriding all members if necessary
|
||||||
class MutableMethodParameter(parameter: MethodParameter) : MethodParameter, BaseMethodParameter() {
|
class MutableMethodParameter(parameter: MethodParameter) : MethodParameter, BaseMethodParameter() {
|
||||||
private var type = parameter.type
|
private var type = parameter.type
|
||||||
private var name = parameter.name
|
private var name = parameter.name
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableAnnotationElement.Companion.toMutable
|
||||||
import org.jf.dexlib2.base.value.BaseAnnotationEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseAnnotationEncodedValue
|
||||||
import org.jf.dexlib2.iface.AnnotationElement
|
import com.android.tools.smali.dexlib2.iface.AnnotationElement
|
||||||
import org.jf.dexlib2.iface.value.AnnotationEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.AnnotationEncodedValue
|
||||||
|
|
||||||
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : BaseAnnotationEncodedValue(),
|
class MutableAnnotationEncodedValue(annotationEncodedValue: AnnotationEncodedValue) : BaseAnnotationEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue.Companion.toMutable
|
||||||
import org.jf.dexlib2.base.value.BaseArrayEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseArrayEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.ArrayEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.ArrayEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.EncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.EncodedValue
|
||||||
|
|
||||||
class MutableArrayEncodedValue(arrayEncodedValue: ArrayEncodedValue) : BaseArrayEncodedValue(), MutableEncodedValue {
|
class MutableArrayEncodedValue(arrayEncodedValue: ArrayEncodedValue) : BaseArrayEncodedValue(), MutableEncodedValue {
|
||||||
private val _value by lazy {
|
private val _value by lazy {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseBooleanEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseBooleanEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.BooleanEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.BooleanEncodedValue
|
||||||
|
|
||||||
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : BaseBooleanEncodedValue(),
|
class MutableBooleanEncodedValue(booleanEncodedValue: BooleanEncodedValue) : BaseBooleanEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseByteEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseByteEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.ByteEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.ByteEncodedValue
|
||||||
|
|
||||||
class MutableByteEncodedValue(byteEncodedValue: ByteEncodedValue) : BaseByteEncodedValue(), MutableEncodedValue {
|
class MutableByteEncodedValue(byteEncodedValue: ByteEncodedValue) : BaseByteEncodedValue(), MutableEncodedValue {
|
||||||
private var value = byteEncodedValue.value
|
private var value = byteEncodedValue.value
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseCharEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseCharEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.CharEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.CharEncodedValue
|
||||||
|
|
||||||
class MutableCharEncodedValue(charEncodedValue: CharEncodedValue) : BaseCharEncodedValue(), MutableEncodedValue {
|
class MutableCharEncodedValue(charEncodedValue: CharEncodedValue) : BaseCharEncodedValue(), MutableEncodedValue {
|
||||||
private var value = charEncodedValue.value
|
private var value = charEncodedValue.value
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseDoubleEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseDoubleEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.DoubleEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.DoubleEncodedValue
|
||||||
|
|
||||||
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDoubleEncodedValue(),
|
class MutableDoubleEncodedValue(doubleEncodedValue: DoubleEncodedValue) : BaseDoubleEncodedValue(),
|
||||||
MutableEncodedValue {
|
MutableEncodedValue {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.ValueType
|
import com.android.tools.smali.dexlib2.ValueType
|
||||||
import org.jf.dexlib2.iface.value.*
|
import com.android.tools.smali.dexlib2.iface.value.*
|
||||||
|
|
||||||
interface MutableEncodedValue : EncodedValue {
|
interface MutableEncodedValue : EncodedValue {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseEnumEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseEnumEncodedValue
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import org.jf.dexlib2.iface.value.EnumEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.EnumEncodedValue
|
||||||
|
|
||||||
class MutableEnumEncodedValue(enumEncodedValue: EnumEncodedValue) : BaseEnumEncodedValue(), MutableEncodedValue {
|
class MutableEnumEncodedValue(enumEncodedValue: EnumEncodedValue) : BaseEnumEncodedValue(), MutableEncodedValue {
|
||||||
private var value = enumEncodedValue.value
|
private var value = enumEncodedValue.value
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.ValueType
|
import com.android.tools.smali.dexlib2.ValueType
|
||||||
import org.jf.dexlib2.base.value.BaseFieldEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseFieldEncodedValue
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import org.jf.dexlib2.iface.value.FieldEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.FieldEncodedValue
|
||||||
|
|
||||||
class MutableFieldEncodedValue(fieldEncodedValue: FieldEncodedValue) : BaseFieldEncodedValue(), MutableEncodedValue {
|
class MutableFieldEncodedValue(fieldEncodedValue: FieldEncodedValue) : BaseFieldEncodedValue(), MutableEncodedValue {
|
||||||
private var value = fieldEncodedValue.value
|
private var value = fieldEncodedValue.value
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseFloatEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseFloatEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.FloatEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.FloatEncodedValue
|
||||||
|
|
||||||
class MutableFloatEncodedValue(floatEncodedValue: FloatEncodedValue) : BaseFloatEncodedValue(), MutableEncodedValue {
|
class MutableFloatEncodedValue(floatEncodedValue: FloatEncodedValue) : BaseFloatEncodedValue(), MutableEncodedValue {
|
||||||
private var value = floatEncodedValue.value
|
private var value = floatEncodedValue.value
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
package app.revanced.patcher.util.proxy.mutableTypes.encodedValue
|
||||||
|
|
||||||
import org.jf.dexlib2.base.value.BaseIntEncodedValue
|
import com.android.tools.smali.dexlib2.base.value.BaseIntEncodedValue
|
||||||
import org.jf.dexlib2.iface.value.IntEncodedValue
|
import com.android.tools.smali.dexlib2.iface.value.IntEncodedValue
|
||||||
|
|
||||||
class MutableIntEncodedValue(intEncodedValue: IntEncodedValue) : BaseIntEncodedValue(), MutableEncodedValue {
|
class MutableIntEncodedValue(intEncodedValue: IntEncodedValue) : BaseIntEncodedValue(), MutableEncodedValue {
|
||||||
private var value = intEncodedValue.value
|
private var value = intEncodedValue.value
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user