mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-18 00:43:56 +00:00
Compare commits
23 Commits
v1.5.1-dev
...
v2.1.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1670ceff9f | ||
|
|
41805fcb0b | ||
|
|
0e9939f70d | ||
|
|
f28a7ddeec | ||
|
|
3e574e723c | ||
|
|
a73ca721f3 | ||
|
|
393d74b7d9 | ||
|
|
51d6e9976c | ||
|
|
24cecb1e57 | ||
|
|
d4938c57e8 | ||
|
|
1526f98f53 | ||
|
|
244ebc2186 | ||
|
|
486a7acff2 | ||
|
|
f0b8bc5438 | ||
|
|
806f45c77f | ||
|
|
259bcd04c9 | ||
|
|
0d040dcfd0 | ||
|
|
13af960de1 | ||
|
|
d21fd5798c | ||
|
|
1c348f3621 | ||
|
|
0c02680b70 | ||
|
|
b86b8aacea | ||
|
|
3aa6dc223a |
3
.editorconfig
Normal file
3
.editorconfig
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[*.{kt,kts}]
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_standard_no-wildcard-imports = disabled
|
||||||
25
.github/workflows/build_pull_request.yml
vendored
Normal file
25
.github/workflows/build_pull_request.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Build pull request
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: burrunan/gradle-cache-action@v1
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: ./gradlew build --no-daemon
|
||||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -6,10 +6,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -41,6 +37,13 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
|
- name: Import GPG key
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v6
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
fingerprint: ${{ env.GPG_FINGERPRINT }}
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||||
|
|||||||
@@ -32,6 +32,12 @@
|
|||||||
backmergeBranches: [{"from": "main", "to": "dev"}],
|
backmergeBranches: [{"from": "main", "to": "dev"}],
|
||||||
clearWorkspace: true
|
clearWorkspace: true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/github",
|
||||||
|
{
|
||||||
|
successComment: false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,3 +1,47 @@
|
|||||||
|
# [2.1.0-dev.2](https://github.com/ReVanced/revanced-library/compare/v2.1.0-dev.1...v2.1.0-dev.2) (2024-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Use `BKS` instead of default signing provider to fix backwards compatibility ([41805fc](https://github.com/ReVanced/revanced-library/commit/41805fcb0bdc778fe0870427a0a1caa6d4369cee))
|
||||||
|
|
||||||
|
# [2.1.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.0.0...v2.1.0-dev.1) (2024-02-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Mention APK file name when logging aligning ([244ebc2](https://github.com/ReVanced/revanced-library/commit/244ebc21868c07d1852857f6858c1a53a5561155))
|
||||||
|
|
||||||
|
# [2.0.0](https://github.com/ReVanced/revanced-library/compare/v1.5.0...v2.0.0) (2024-02-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Map dependencies from `KClass` into `String` to fix serialization ([57e36ab](https://github.com/ReVanced/revanced-library/commit/57e36ab5c15a5fa7c50fb689ee43ad4eb9a4a515))
|
||||||
|
* Use the JVM name instead of the value from `KClass#toString` ([d18e436](https://github.com/ReVanced/revanced-library/commit/d18e436de1df14452ecaa7d827be5e6596ba8a2d))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Use `apkzlib` instead of own implementations and bump ReVanced Patcher ([3aa6dc2](https://github.com/ReVanced/revanced-library/commit/3aa6dc223a9a1a2f735eda407917548ecbd366aa))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This commit removes deprecated APIs and bumps ReVanced Patcher. Because of it's changes, `apkzlib` is now used instead of own implementations of `ZipFile`
|
||||||
|
|
||||||
|
# [2.0.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v1.5.1-dev.2...v2.0.0-dev.1) (2024-02-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Use `apkzlib` instead of own implementations and bump ReVanced Patcher ([3aa6dc2](https://github.com/ReVanced/revanced-library/commit/3aa6dc223a9a1a2f735eda407917548ecbd366aa))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This commit removes deprecated APIs and bumps ReVanced Patcher. Because of it's changes, `apkzlib` is now used instead of own implementations of `ZipFile`
|
||||||
|
|
||||||
## [1.5.1-dev.2](https://github.com/ReVanced/revanced-library/compare/v1.5.1-dev.1...v1.5.1-dev.2) (2024-02-08)
|
## [1.5.1-dev.2](https://github.com/ReVanced/revanced-library/compare/v1.5.1-dev.1...v1.5.1-dev.2) (2024-02-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,13 +65,14 @@ This document describes how to contribute to ReVanced Library.
|
|||||||
## 📖 Resources to help you get started
|
## 📖 Resources to help you get started
|
||||||
|
|
||||||
<!-- * The [documentation](/docs) explains how to use ReVanced Library -->
|
<!-- * The [documentation](/docs) explains how to use ReVanced Library -->
|
||||||
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
|
||||||
* [Issues](https://github.com/ReVanced/revanced-cli/issues) are where we keep track of bugs and feature requests
|
- [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
|
||||||
|
- [Issues](https://github.com/ReVanced/revanced-cli/issues) are where we keep track of bugs and feature requests
|
||||||
|
|
||||||
## 🙏 Submitting a feature request
|
## 🙏 Submitting a feature request
|
||||||
|
|
||||||
Features can be requested by opening an issue using the
|
Features can be requested by opening an issue using the
|
||||||
[Feature request issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Feature+request&projects=&template=feature-request.yml&title=feat%3A+).
|
[Feature request issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Library.
|
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Library.
|
||||||
@@ -80,7 +81,7 @@ Features can be requested by opening an issue using the
|
|||||||
## 🐞 Submitting a bug report
|
## 🐞 Submitting a bug report
|
||||||
|
|
||||||
If you encounter a bug while using ReVanced Library, open an issue using the
|
If you encounter a bug while using ReVanced Library, open an issue using the
|
||||||
[Bug report issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Bug+report&projects=&template=bug-report.yml&title=bug%3A+).
|
[Bug report issue template](https://github.com/ReVanced/revanced-cli/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
|
||||||
|
|
||||||
## 📝 How to contribute
|
## 📝 How to contribute
|
||||||
|
|
||||||
@@ -88,11 +89,11 @@ If you encounter a bug while using ReVanced Library, open an issue using the
|
|||||||
with the maintainers of ReVanced Library. This will help you determine whether your change is acceptable
|
with the maintainers of ReVanced Library. This will help you determine whether your change is acceptable
|
||||||
and whether it is worth your time to implement it
|
and whether it is worth your time to implement it
|
||||||
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
|
||||||
3. Commit your changes.
|
3. Commit your changes
|
||||||
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
4. Submit a pull request to the `dev` branch of the repository and reference issues
|
||||||
that your pull request closes in the description of your pull request
|
that your pull request closes in the description of your pull request
|
||||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
||||||
it will be merged into the `dev` branch and will be included in the next release of ReVanced Library
|
it will be merged into the `dev` branch and will be included in the next release of ReVanced Library
|
||||||
|
|
||||||
❤️ Thank you for considering contributing to ReVanced Library,
|
❤️ Thank you for considering contributing to ReVanced Library,
|
||||||
ReVanced
|
ReVanced
|
||||||
|
|||||||
117
README.md
Normal file
117
README.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<p align="center">
|
||||||
|
<picture>
|
||||||
|
<source
|
||||||
|
width="256px"
|
||||||
|
media="(prefers-color-scheme: dark)"
|
||||||
|
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
width="256px"
|
||||||
|
src="assets/revanced-headline/revanced-headline-vertical-light.svg"
|
||||||
|
>
|
||||||
|
</picture>
|
||||||
|
<br>
|
||||||
|
<a href="https://revanced.app/">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
|
||||||
|
<img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/ReVanced">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
|
||||||
|
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="http://revanced.app/discord">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="https://reddit.com/r/revancedapp">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="https://t.me/app_revanced">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="https://x.com/revancedapp">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.youtube.com/@ReVanced">
|
||||||
|
<picture>
|
||||||
|
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||||
|
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Continuing the legacy of Vanced
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# 📚 ReVanced Library
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
Library containing common utilities for ReVanced.
|
||||||
|
|
||||||
|
## ❓ About
|
||||||
|
|
||||||
|
ReVanced Library powers projects such as [ReVanced Manager](https://github.com/ReVanced/revanced-manager),
|
||||||
|
[ReVanced CLI](https://github.com/ReVanced/revanced-cli) with common utilities and functionalities
|
||||||
|
by providing shared code.
|
||||||
|
|
||||||
|
## 💪 Features
|
||||||
|
|
||||||
|
Some of the features the ReVanced Library provides are:
|
||||||
|
|
||||||
|
- 📝 **Signing APKs**: Read and write keystores, and sign APK files
|
||||||
|
- 🧩 **Common utility functions**: Various APIs for ReVanced patches such as JSON serialization,
|
||||||
|
reading and setting patch options, calculating the most common compatible version for a set of patches and more
|
||||||
|
- 💾 **Install and uninstall APKs**: Install and uninstall APK files via ADB or locally,
|
||||||
|
the Android package manager, or by mounting using root permissions
|
||||||
|
- 📦 **Repackage patched files to an APK**: Apply patched files from
|
||||||
|
[ReVanced Patcher](https://github.com/revanced/revanced-patcher) to an APK file, and align & sign the APK file automatically
|
||||||
|
|
||||||
|
## 🚀 How to get started
|
||||||
|
|
||||||
|
To use ReVanced Library in your project, follow these steps:
|
||||||
|
|
||||||
|
1. [Add the repository](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#using-a-published-package)
|
||||||
|
to your project
|
||||||
|
2. Add the dependency to your project:
|
||||||
|
|
||||||
|
```kt
|
||||||
|
dependencies {
|
||||||
|
implementation("app.revanced:revanced-library:{$version}")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Everything else
|
||||||
|
|
||||||
|
### 📙 Contributing
|
||||||
|
|
||||||
|
Thank you for considering contributing to ReVanced Library.
|
||||||
|
You can find the contribution guidelines [here](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### 🛠️ Building
|
||||||
|
|
||||||
|
To build ReVanced Library,
|
||||||
|
you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
|
||||||
|
|
||||||
|
## 📜 Licence
|
||||||
|
|
||||||
|
ReVanced Library is licensed under the GPLv3 license. Please see the [licence file](LICENSE) for more information.
|
||||||
|
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Library as long as you track changes/dates in source files.
|
||||||
|
Any modifications to ReVanced Library must also be made available under the GPL,
|
||||||
|
along with build & install instructions.
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
public final class app/revanced/library/ApkSigner {
|
public final class app/revanced/library/ApkSigner {
|
||||||
public static final field INSTANCE Lapp/revanced/library/ApkSigner;
|
public static final field INSTANCE Lapp/revanced/library/ApkSigner;
|
||||||
public final fun newApkSignerBuilder (Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
public final fun newApkSigner (Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
|
||||||
public final fun newApkSignerBuilder (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
public final fun newApkSigner (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer;
|
||||||
public final fun newKeyStore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/List;)V
|
public final fun newKeyStore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/Set;)V
|
||||||
public final fun newKeyStore (Ljava/util/List;)Ljava/security/KeyStore;
|
public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore;
|
||||||
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
||||||
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/library/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/library/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
||||||
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
|
||||||
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
|
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
|
||||||
public final fun signApk (Lcom/android/apksig/ApkSigner$Builder;Ljava/io/File;Ljava/io/File;)V
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/ApkSigner$KeyStoreEntry {
|
public final class app/revanced/library/ApkSigner$KeyStoreEntry {
|
||||||
@@ -25,10 +24,16 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair {
|
|||||||
public final fun getPrivateKey ()Ljava/security/PrivateKey;
|
public final fun getPrivateKey ()Ljava/security/PrivateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/ApkSigner$Signer {
|
||||||
|
public final fun getSigningExtension ()Lcom/android/tools/build/apkzlib/sign/SigningExtension;
|
||||||
|
public final fun signApk (Lcom/android/tools/build/apkzlib/zip/ZFile;)V
|
||||||
|
public final fun signApk (Ljava/io/File;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/ApkUtils {
|
public final class app/revanced/library/ApkUtils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/ApkUtils;
|
public static final field INSTANCE Lapp/revanced/library/ApkUtils;
|
||||||
public final fun copyAligned (Ljava/io/File;Ljava/io/File;Lapp/revanced/patcher/PatcherResult;)V
|
public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V
|
||||||
public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
|
public final fun sign (Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/ApkUtils$SigningOptions {
|
public final class app/revanced/library/ApkUtils$SigningOptions {
|
||||||
@@ -62,7 +67,6 @@ public final class app/revanced/library/Options$Patch$Option {
|
|||||||
|
|
||||||
public final class app/revanced/library/PatchUtils {
|
public final class app/revanced/library/PatchUtils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
||||||
public final fun getMostCommonCompatibleVersion (Ljava/util/Set;Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
||||||
public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
|
public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
|
||||||
}
|
}
|
||||||
@@ -162,23 +166,3 @@ public final class app/revanced/library/logging/Logger {
|
|||||||
public static synthetic fun setFormat$default (Lapp/revanced/library/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
|
public static synthetic fun setFormat$default (Lapp/revanced/library/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/zip/ZipFile : java/io/Closeable {
|
|
||||||
public static final field ApkZipFile Lapp/revanced/library/zip/ZipFile$ApkZipFile;
|
|
||||||
public fun <init> (Ljava/io/File;)V
|
|
||||||
public final fun addEntryCompressData (Lapp/revanced/library/zip/structures/ZipEntry;[B)V
|
|
||||||
public fun close ()V
|
|
||||||
public final fun copyEntriesFromFileAligned (Lapp/revanced/library/zip/ZipFile;Lkotlin/jvm/functions/Function1;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/library/zip/ZipFile$ApkZipFile {
|
|
||||||
public final fun getApkZipEntryAlignment ()Lkotlin/jvm/functions/Function1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/library/zip/structures/ZipEntry {
|
|
||||||
public static final field Companion Lapp/revanced/library/zip/structures/ZipEntry$Companion;
|
|
||||||
public fun <init> (Ljava/lang/String;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/library/zip/structures/ZipEntry$Companion {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.10"
|
alias(libs.plugins.kotlin)
|
||||||
alias(libs.plugins.binary.compatibility.validator)
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
|
signing
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
@@ -9,17 +10,25 @@ group = "app.revanced"
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven { url = uri("https://jitpack.io") }
|
|
||||||
google()
|
google()
|
||||||
|
maven {
|
||||||
|
// A repository must be speficied for some reason. "registry" is a dummy.
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||||
|
credentials {
|
||||||
|
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
|
||||||
|
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.revanced.patcher)
|
implementation(libs.revanced.patcher)
|
||||||
implementation(libs.kotlin.reflect)
|
implementation(libs.kotlin.reflect)
|
||||||
implementation(libs.jadb) // Updated fork
|
implementation(libs.jadb) // Fork with Shell v2 support.
|
||||||
implementation(libs.apksig)
|
|
||||||
implementation(libs.bcpkix.jdk18on)
|
|
||||||
implementation(libs.jackson.module.kotlin)
|
implementation(libs.jackson.module.kotlin)
|
||||||
|
implementation(libs.apkzlib)
|
||||||
|
implementation(libs.bcpkix.jdk15on)
|
||||||
|
implementation(libs.guava)
|
||||||
|
|
||||||
testImplementation(libs.revanced.patcher)
|
testImplementation(libs.revanced.patcher)
|
||||||
testImplementation(libs.kotlin.test)
|
testImplementation(libs.kotlin.test)
|
||||||
@@ -41,8 +50,19 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/revanced-library")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
publications {
|
publications {
|
||||||
create<MavenPublication>("gpr") {
|
create<MavenPublication>("revanced-library-publication") {
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
|
|
||||||
version = project.version.toString()
|
version = project.version.toString()
|
||||||
@@ -76,3 +96,8 @@ publishing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(publishing.publications["revanced-library-publication"])
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 = 1.5.1-dev.2
|
version = 2.1.0-dev.2
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
[versions]
|
[versions]
|
||||||
apksig = "8.1.4"
|
jackson-module-kotlin = "2.15.0"
|
||||||
bcpkix-jdk18on = "1.76"
|
|
||||||
jackson-module-kotlin = "2.14.3"
|
|
||||||
jadb = "1.2.1"
|
jadb = "1.2.1"
|
||||||
kotlin-reflect = "1.9.20"
|
kotlin = "1.9.22"
|
||||||
kotlin-test = "1.9.20"
|
revanced-patcher = "19.3.1"
|
||||||
revanced-patcher = "19.2.0"
|
binary-compatibility-validator = "0.14.0"
|
||||||
binary-compatibility-validator = "0.13.2"
|
apkzlib = "8.2.2"
|
||||||
|
bcpkix-jdk15on = "1.70"
|
||||||
|
guava = "33.0.0-jre"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }
|
|
||||||
bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bcpkix-jdk18on" }
|
|
||||||
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
|
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
|
||||||
jadb = { module = "app.revanced:jadb", version.ref = "jadb" }
|
jadb = { module = "app.revanced:jadb", version.ref = "jadb" }
|
||||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" }
|
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||||
|
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "apkzlib" }
|
||||||
|
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
|
||||||
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||||
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
|||||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dist
|
zipStorePath=wrapper/dist
|
||||||
282
package-lock.json
generated
282
package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "revanced-patches",
|
"name": "revanced-library",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.9.1",
|
"gradle-semantic-release-plugin": "^1.9.1",
|
||||||
"semantic-release": "^23.0.0"
|
"semantic-release": "^23.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -326,9 +326,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/request": {
|
"node_modules/@octokit/request": {
|
||||||
"version": "8.1.6",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz",
|
||||||
"integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==",
|
"integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/endpoint": "^9.0.0",
|
"@octokit/endpoint": "^9.0.0",
|
||||||
@@ -564,6 +564,26 @@
|
|||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@saithodev/semantic-release-backmerge/node_modules/marked-terminal": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-escapes": "^6.2.0",
|
||||||
|
"cardinal": "^2.1.1",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"cli-table3": "^0.6.3",
|
||||||
|
"node-emoji": "^2.1.3",
|
||||||
|
"supports-hyperlinks": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"marked": ">=1 <12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@saithodev/semantic-release-backmerge/node_modules/mimic-fn": {
|
"node_modules/@saithodev/semantic-release-backmerge/node_modules/mimic-fn": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||||
@@ -1195,9 +1215,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sindresorhus/merge-streams": {
|
"node_modules/@sindresorhus/merge-streams": {
|
||||||
"version": "1.0.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.2.0.tgz",
|
||||||
"integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==",
|
"integrity": "sha512-UTce8mUwUW0RikMb/eseJ7ys0BRkZVFB86orHzrfW12ZmFtym5zua8joZ4L7okH2dDFHkcFjqnZ5GocWBXOFtA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -1282,6 +1302,12 @@
|
|||||||
"integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
|
"integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/any-promise": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@@ -1376,6 +1402,81 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cli-highlight": {
|
||||||
|
"version": "2.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
|
||||||
|
"integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"highlight.js": "^10.7.1",
|
||||||
|
"mz": "^2.4.0",
|
||||||
|
"parse5": "^5.1.1",
|
||||||
|
"parse5-htmlparser2-tree-adapter": "^6.0.0",
|
||||||
|
"yargs": "^16.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"highlight": "bin/highlight"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0",
|
||||||
|
"npm": ">=5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-highlight/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-highlight/node_modules/cliui": {
|
||||||
|
"version": "7.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||||
|
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-highlight/node_modules/yargs": {
|
||||||
|
"version": "16.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||||
|
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^7.0.2",
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"y18n": "^5.0.5",
|
||||||
|
"yargs-parser": "^20.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-highlight/node_modules/yargs-parser": {
|
||||||
|
"version": "20.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||||
|
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cli-table3": {
|
"node_modules/cli-table3": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
|
||||||
@@ -1818,9 +1919,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -1888,9 +1989,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.16.0",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
"integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
|
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
@@ -2052,12 +2153,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globby": {
|
"node_modules/globby": {
|
||||||
"version": "14.0.0",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
|
||||||
"integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==",
|
"integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/merge-streams": "^1.0.0",
|
"@sindresorhus/merge-streams": "^2.1.0",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"ignore": "^5.2.4",
|
"ignore": "^5.2.4",
|
||||||
"path-type": "^5.0.0",
|
"path-type": "^5.0.0",
|
||||||
@@ -2142,9 +2243,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
|
||||||
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -2153,6 +2254,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/highlight.js": {
|
||||||
|
"version": "10.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||||
|
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hook-std": {
|
"node_modules/hook-std": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
|
||||||
@@ -2178,9 +2288,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/http-proxy-agent": {
|
"node_modules/http-proxy-agent": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz",
|
||||||
"integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
|
"integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "^7.1.0",
|
"agent-base": "^7.1.0",
|
||||||
@@ -2191,9 +2301,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
"node_modules/https-proxy-agent": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz",
|
||||||
"integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
|
"integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "^7.0.2",
|
"agent-base": "^7.0.2",
|
||||||
@@ -2213,9 +2323,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||||
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
|
"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
@@ -2629,9 +2739,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked": {
|
"node_modules/marked": {
|
||||||
"version": "11.1.1",
|
"version": "12.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-11.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-12.0.0.tgz",
|
||||||
"integrity": "sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==",
|
"integrity": "sha512-Vkwtq9rLqXryZnWaQc86+FHLC6tr/fycMfYAhiOIXkrNmeGAyhSxjqu0Rs1i0bBqw5u0S7+lV9fdH2ZSVaoa0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"marked": "bin/marked.js"
|
"marked": "bin/marked.js"
|
||||||
@@ -2641,14 +2751,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked-terminal": {
|
"node_modules/marked-terminal": {
|
||||||
"version": "6.2.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.0.0.tgz",
|
||||||
"integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
|
"integrity": "sha512-sNEx8nn9Ktcm6pL0TnRz8tnXq/mSS0Q1FRSwJOAqw4lAB4l49UeDf85Gm1n9RPFm5qurCPjwi1StAQT2XExhZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-escapes": "^6.2.0",
|
"ansi-escapes": "^6.2.0",
|
||||||
"cardinal": "^2.1.1",
|
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
|
"cli-highlight": "^2.1.11",
|
||||||
"cli-table3": "^0.6.3",
|
"cli-table3": "^0.6.3",
|
||||||
"node-emoji": "^2.1.3",
|
"node-emoji": "^2.1.3",
|
||||||
"supports-hyperlinks": "^3.0.0"
|
"supports-hyperlinks": "^3.0.0"
|
||||||
@@ -2657,7 +2767,7 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"marked": ">=1 <12"
|
"marked": ">=1 <13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/meow": {
|
"node_modules/meow": {
|
||||||
@@ -2739,6 +2849,17 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/mz": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"any-promise": "^1.0.0",
|
||||||
|
"object-assign": "^4.0.1",
|
||||||
|
"thenify-all": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/neo-async": {
|
"node_modules/neo-async": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
@@ -5551,6 +5672,15 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -5695,6 +5825,27 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse5": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/parse5-htmlparser2-tree-adapter": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"parse5": "^6.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/parsimmon": {
|
"node_modules/parsimmon": {
|
||||||
"version": "1.18.1",
|
"version": "1.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz",
|
||||||
@@ -5860,9 +6011,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/read-pkg-up/node_modules/type-fest": {
|
"node_modules/read-pkg-up/node_modules/type-fest": {
|
||||||
"version": "4.10.1",
|
"version": "4.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
|
||||||
"integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==",
|
"integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
@@ -5889,9 +6040,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/read-pkg/node_modules/type-fest": {
|
"node_modules/read-pkg/node_modules/type-fest": {
|
||||||
"version": "4.10.1",
|
"version": "4.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
|
||||||
"integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==",
|
"integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
@@ -5994,9 +6145,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/semantic-release": {
|
"node_modules/semantic-release": {
|
||||||
"version": "23.0.0",
|
"version": "23.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-23.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-23.0.2.tgz",
|
||||||
"integrity": "sha512-Jz7jEWO2igTtske112gC4PPE2whCMVrsgxUPG3/SZI7VE357suIUZFlJd1Yu0g2I6RPc2HxNEfUg7KhmDTjwqg==",
|
"integrity": "sha512-OnVYJ6Xgzwe1x8MKswba7RU9+5djS1MWRTrTn5qsq3xZYpslroZkV9Pt0dA2YcIuieeuSZWJhn+yUWoBUHO5Fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@semantic-release/commit-analyzer": "^11.0.0",
|
"@semantic-release/commit-analyzer": "^11.0.0",
|
||||||
@@ -6017,8 +6168,8 @@
|
|||||||
"hosted-git-info": "^7.0.0",
|
"hosted-git-info": "^7.0.0",
|
||||||
"import-from-esm": "^1.3.1",
|
"import-from-esm": "^1.3.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"marked": "^11.0.0",
|
"marked": "^12.0.0",
|
||||||
"marked-terminal": "^6.0.0",
|
"marked-terminal": "^7.0.0",
|
||||||
"micromatch": "^4.0.2",
|
"micromatch": "^4.0.2",
|
||||||
"p-each-series": "^3.0.0",
|
"p-each-series": "^3.0.0",
|
||||||
"p-reduce": "^3.0.0",
|
"p-reduce": "^3.0.0",
|
||||||
@@ -6247,9 +6398,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.5.4",
|
"version": "7.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^6.0.0"
|
"lru-cache": "^6.0.0"
|
||||||
@@ -6481,9 +6632,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/spdx-license-ids": {
|
"node_modules/spdx-license-ids": {
|
||||||
"version": "3.0.16",
|
"version": "3.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
|
||||||
"integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
|
"integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/split2": {
|
"node_modules/split2": {
|
||||||
@@ -6655,6 +6806,27 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/thenify": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"any-promise": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/thenify-all": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"thenify": ">= 3.1.0 < 4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/through": {
|
"node_modules/through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.9.1",
|
"gradle-semantic-release-plugin": "^1.9.1",
|
||||||
"semantic-release": "^23.0.0"
|
"semantic-release": "^23.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package app.revanced.library
|
package app.revanced.library
|
||||||
|
|
||||||
import com.android.apksig.ApkSigner
|
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||||
|
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||||
|
import com.android.tools.build.apkzlib.zip.ZFile
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
import org.bouncycastle.cert.X509v3CertificateBuilder
|
||||||
@@ -19,11 +21,11 @@ import java.util.logging.Logger
|
|||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for writing or reading keystore files and entries as well as signing APK files.
|
* Utility class for reading or writing keystore files and entries as well as signing APK files.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
object ApkSigner {
|
object ApkSigner {
|
||||||
private val logger = Logger.getLogger(app.revanced.library.ApkSigner::class.java.name)
|
private val logger = Logger.getLogger(ApkSigner::class.java.name)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
|
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
|
||||||
@@ -36,6 +38,7 @@ object ApkSigner {
|
|||||||
*
|
*
|
||||||
* @param commonName The common name of the certificate.
|
* @param commonName The common name of the certificate.
|
||||||
* @param validUntil The date until the certificate is valid.
|
* @param validUntil The date until the certificate is valid.
|
||||||
|
*
|
||||||
* @return The created [PrivateKeyCertificatePair].
|
* @return The created [PrivateKeyCertificatePair].
|
||||||
*/
|
*/
|
||||||
fun newPrivateKeyCertificatePair(
|
fun newPrivateKeyCertificatePair(
|
||||||
@@ -79,7 +82,9 @@ object ApkSigner {
|
|||||||
* @param keyStore The keystore to read the entry from.
|
* @param keyStore The keystore to read the entry from.
|
||||||
* @param keyStoreEntryAlias The alias of the key store entry to read.
|
* @param keyStoreEntryAlias The alias of the key store entry to read.
|
||||||
* @param keyStoreEntryPassword The password for recovering the signing key.
|
* @param keyStoreEntryPassword The password for recovering the signing key.
|
||||||
|
*
|
||||||
* @return The read [PrivateKeyCertificatePair].
|
* @return The read [PrivateKeyCertificatePair].
|
||||||
|
*
|
||||||
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
|
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
|
||||||
*/
|
*/
|
||||||
fun readKeyCertificatePair(
|
fun readKeyCertificatePair(
|
||||||
@@ -111,13 +116,15 @@ object ApkSigner {
|
|||||||
* Create a new keystore with a new keypair.
|
* Create a new keystore with a new keypair.
|
||||||
*
|
*
|
||||||
* @param entries The entries to add to the keystore.
|
* @param entries The entries to add to the keystore.
|
||||||
|
*
|
||||||
* @return The created keystore.
|
* @return The created keystore.
|
||||||
|
*
|
||||||
* @see KeyStoreEntry
|
* @see KeyStoreEntry
|
||||||
*/
|
*/
|
||||||
fun newKeyStore(entries: List<KeyStoreEntry>): KeyStore {
|
fun newKeyStore(entries: Set<KeyStoreEntry>): KeyStore {
|
||||||
logger.fine("Creating keystore")
|
logger.fine("Creating keystore")
|
||||||
|
|
||||||
return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply {
|
return newKeyStoreInstance().apply {
|
||||||
load(null)
|
load(null)
|
||||||
|
|
||||||
entries.forEach { entry ->
|
entries.forEach { entry ->
|
||||||
@@ -132,6 +139,8 @@ object ApkSigner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun newKeyStoreInstance() = KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new keystore with a new keypair and saves it to the given [keyStoreOutputStream].
|
* Create a new keystore with a new keypair and saves it to the given [keyStoreOutputStream].
|
||||||
*
|
*
|
||||||
@@ -142,18 +151,20 @@ object ApkSigner {
|
|||||||
fun newKeyStore(
|
fun newKeyStore(
|
||||||
keyStoreOutputStream: OutputStream,
|
keyStoreOutputStream: OutputStream,
|
||||||
keyStorePassword: String,
|
keyStorePassword: String,
|
||||||
entries: List<KeyStoreEntry>,
|
entries: Set<KeyStoreEntry>,
|
||||||
) = newKeyStore(entries).store(
|
) = newKeyStore(entries).store(
|
||||||
keyStoreOutputStream,
|
keyStoreOutputStream,
|
||||||
keyStorePassword.toCharArray(),
|
keyStorePassword.toCharArray(),
|
||||||
) // Save the keystore.
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a keystore from the given [keyStoreInputStream].
|
* Read a keystore from the given [keyStoreInputStream].
|
||||||
*
|
*
|
||||||
* @param keyStoreInputStream The stream to read the keystore from.
|
* @param keyStoreInputStream The stream to read the keystore from.
|
||||||
* @param keyStorePassword The password for the keystore.
|
* @param keyStorePassword The password for the keystore.
|
||||||
|
*
|
||||||
* @return The keystore.
|
* @return The keystore.
|
||||||
|
*
|
||||||
* @throws IllegalArgumentException If the keystore password is invalid.
|
* @throws IllegalArgumentException If the keystore password is invalid.
|
||||||
*/
|
*/
|
||||||
fun readKeyStore(
|
fun readKeyStore(
|
||||||
@@ -162,7 +173,7 @@ object ApkSigner {
|
|||||||
): KeyStore {
|
): KeyStore {
|
||||||
logger.fine("Reading keystore")
|
logger.fine("Reading keystore")
|
||||||
|
|
||||||
return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply {
|
return newKeyStoreInstance().apply {
|
||||||
try {
|
try {
|
||||||
load(keyStoreInputStream, keyStorePassword?.toCharArray())
|
load(keyStoreInputStream, keyStorePassword?.toCharArray())
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
@@ -176,75 +187,45 @@ object ApkSigner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new [ApkSigner.Builder].
|
* Create a new [Signer].
|
||||||
*
|
*
|
||||||
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
|
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
|
||||||
* @param signer The name of the signer.
|
*
|
||||||
* @param createdBy The value for the `Created-By` attribute in the APK's manifest.
|
* @return The new [Signer].
|
||||||
* @return The created [ApkSigner.Builder] instance.
|
*
|
||||||
|
* @see PrivateKeyCertificatePair
|
||||||
|
* @see Signer
|
||||||
*/
|
*/
|
||||||
fun newApkSignerBuilder(
|
fun newApkSigner(privateKeyCertificatePair: PrivateKeyCertificatePair) =
|
||||||
privateKeyCertificatePair: PrivateKeyCertificatePair,
|
Signer(
|
||||||
signer: String,
|
SigningExtension(
|
||||||
createdBy: String,
|
SigningOptions.builder()
|
||||||
): ApkSigner.Builder {
|
.setMinSdkVersion(21) // TODO: Extracting from the target APK would be ideal.
|
||||||
logger.fine(
|
.setV1SigningEnabled(true)
|
||||||
"Creating new ApkSigner " +
|
.setV2SigningEnabled(true)
|
||||||
"with $signer as signer and " +
|
.setCertificates(privateKeyCertificatePair.certificate)
|
||||||
"$createdBy as Created-By attribute in the APK's manifest",
|
.setKey(privateKeyCertificatePair.privateKey)
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the signer config.
|
|
||||||
val signerConfig =
|
|
||||||
ApkSigner.SignerConfig.Builder(
|
|
||||||
signer,
|
|
||||||
privateKeyCertificatePair.privateKey,
|
|
||||||
listOf(privateKeyCertificatePair.certificate),
|
|
||||||
).build()
|
|
||||||
|
|
||||||
// Create the signer.
|
|
||||||
return ApkSigner.Builder(listOf(signerConfig)).apply {
|
|
||||||
setCreatedBy(createdBy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new [ApkSigner.Builder].
|
* Create a new [Signer].
|
||||||
*
|
*
|
||||||
* @param keyStore The keystore to use for signing.
|
* @param keyStore The keystore to use for signing.
|
||||||
* @param keyStoreEntryAlias The alias of the key store entry to use for signing.
|
* @param keyStoreEntryAlias The alias of the key store entry to use for signing.
|
||||||
* @param keyStoreEntryPassword The password for recovering the signing key.
|
* @param keyStoreEntryPassword The password for recovering the signing key.
|
||||||
* @param signer The name of the signer.
|
*
|
||||||
* @param createdBy The value for the `Created-By` attribute in the APK's manifest.
|
* @return The new [Signer].
|
||||||
* @return The created [ApkSigner.Builder] instance.
|
*
|
||||||
* @see KeyStoreEntry
|
* @see KeyStore
|
||||||
* @see PrivateKeyCertificatePair
|
* @see Signer
|
||||||
* @see ApkSigner.Builder.setCreatedBy
|
|
||||||
* @see ApkSigner.Builder.signApk
|
|
||||||
*/
|
*/
|
||||||
fun newApkSignerBuilder(
|
fun newApkSigner(
|
||||||
keyStore: KeyStore,
|
keyStore: KeyStore,
|
||||||
keyStoreEntryAlias: String,
|
keyStoreEntryAlias: String,
|
||||||
keyStoreEntryPassword: String,
|
keyStoreEntryPassword: String,
|
||||||
signer: String,
|
) = newApkSigner(readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
|
||||||
createdBy: String,
|
|
||||||
) = newApkSignerBuilder(
|
|
||||||
readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword),
|
|
||||||
signer,
|
|
||||||
createdBy,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun ApkSigner.Builder.signApk(
|
|
||||||
input: File,
|
|
||||||
output: File,
|
|
||||||
) {
|
|
||||||
logger.info("Signing ${input.name}")
|
|
||||||
|
|
||||||
setInputApk(input)
|
|
||||||
setOutputApk(output)
|
|
||||||
|
|
||||||
build().sign()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entry in a keystore.
|
* An entry in a keystore.
|
||||||
@@ -252,6 +233,7 @@ object ApkSigner {
|
|||||||
* @param alias The alias of the entry.
|
* @param alias The alias of the entry.
|
||||||
* @param password The password for recovering the signing key.
|
* @param password The password for recovering the signing key.
|
||||||
* @param privateKeyCertificatePair The private key and certificate pair.
|
* @param privateKeyCertificatePair The private key and certificate pair.
|
||||||
|
*
|
||||||
* @see PrivateKeyCertificatePair
|
* @see PrivateKeyCertificatePair
|
||||||
*/
|
*/
|
||||||
class KeyStoreEntry(
|
class KeyStoreEntry(
|
||||||
@@ -270,4 +252,24 @@ object ApkSigner {
|
|||||||
val privateKey: PrivateKey,
|
val privateKey: PrivateKey,
|
||||||
val certificate: X509Certificate,
|
val certificate: X509Certificate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Signer internal constructor(val signingExtension: SigningExtension) {
|
||||||
|
/**
|
||||||
|
* Sign an APK file.
|
||||||
|
*
|
||||||
|
* @param apkFile The APK file to sign.
|
||||||
|
*/
|
||||||
|
fun signApk(apkFile: File) = ZFile.openReadWrite(apkFile).use { signApk(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign an APK file.
|
||||||
|
*
|
||||||
|
* @param apkZFile The APK [ZFile] to sign.
|
||||||
|
*/
|
||||||
|
fun signApk(apkZFile: ZFile) {
|
||||||
|
logger.info("Signing ${apkZFile.file.name}")
|
||||||
|
|
||||||
|
signingExtension.register(apkZFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,124 @@
|
|||||||
package app.revanced.library
|
package app.revanced.library
|
||||||
|
|
||||||
import app.revanced.library.ApkSigner.signApk
|
|
||||||
import app.revanced.library.zip.ZipFile
|
|
||||||
import app.revanced.library.zip.structures.ZipEntry
|
|
||||||
import app.revanced.patcher.PatcherResult
|
import app.revanced.patcher.PatcherResult
|
||||||
|
import com.android.tools.build.apkzlib.zip.AlignmentRules
|
||||||
|
import com.android.tools.build.apkzlib.zip.StoredEntry
|
||||||
|
import com.android.tools.build.apkzlib.zip.ZFile
|
||||||
|
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
import kotlin.io.path.deleteIfExists
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions for working with apks.
|
* Utility functions to work with APK files.
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
object ApkUtils {
|
object ApkUtils {
|
||||||
private val logger = Logger.getLogger(ApkUtils::class.java.name)
|
private val logger = Logger.getLogger(ApkUtils::class.java.name)
|
||||||
|
|
||||||
|
private const val LIBRARY_EXTENSION = ".so"
|
||||||
|
|
||||||
|
// Alignment for native libraries.
|
||||||
|
private const val LIBRARY_ALIGNMENT = 1024 * 4
|
||||||
|
|
||||||
|
// Alignment for all other files.
|
||||||
|
private const val DEFAULT_ALIGNMENT = 4
|
||||||
|
|
||||||
|
// Prefix for resources.
|
||||||
|
private const val RES_PREFIX = "res/"
|
||||||
|
|
||||||
|
private val zFileOptions =
|
||||||
|
ZFileOptions().setAlignmentRule(
|
||||||
|
AlignmentRules.compose(
|
||||||
|
AlignmentRules.constantForSuffix(LIBRARY_EXTENSION, LIBRARY_ALIGNMENT),
|
||||||
|
AlignmentRules.constant(DEFAULT_ALIGNMENT),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new apk from [apkFile] and [patchedEntriesSource] and writes it to [outputFile].
|
* Applies the [PatcherResult] to the given [apkFile].
|
||||||
*
|
*
|
||||||
* @param apkFile The apk to copy entries from.
|
* The order of operation is as follows:
|
||||||
* @param outputFile The apk to write the new entries to.
|
* 1. Write patched dex files.
|
||||||
* @param patchedEntriesSource The result of the patcher to add the patched dex files and resources.
|
* 2. Delete all resources in the target APK
|
||||||
|
* 3. Merge resources.apk compiled by AAPT.
|
||||||
|
* 4. Write raw resources.
|
||||||
|
* 5. Delete resources staged for deletion.
|
||||||
|
* 6. Realign the APK.
|
||||||
|
*
|
||||||
|
* @param apkFile The file to apply the patched files to.
|
||||||
*/
|
*/
|
||||||
fun copyAligned(
|
fun PatcherResult.applyTo(apkFile: File) {
|
||||||
apkFile: File,
|
ZFile.openReadWrite(apkFile, zFileOptions).use { targetApkZFile ->
|
||||||
outputFile: File,
|
dexFiles.forEach { dexFile ->
|
||||||
patchedEntriesSource: PatcherResult,
|
targetApkZFile.add(dexFile.name, dexFile.stream)
|
||||||
) {
|
|
||||||
logger.info("Aligning ${apkFile.name}")
|
|
||||||
|
|
||||||
outputFile.toPath().deleteIfExists()
|
|
||||||
|
|
||||||
ZipFile(outputFile).use { file ->
|
|
||||||
patchedEntriesSource.dexFiles.forEach {
|
|
||||||
file.addEntryCompressData(
|
|
||||||
ZipEntry(it.name),
|
|
||||||
it.stream.readBytes(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patchedEntriesSource.resourceFile?.let {
|
resources?.let { resources ->
|
||||||
file.copyEntriesFromFileAligned(
|
// Add resources compiled by AAPT.
|
||||||
ZipFile(it),
|
resources.resourcesApk?.let { resourcesApk ->
|
||||||
ZipFile.apkZipEntryAlignment,
|
ZFile.openReadOnly(resourcesApk).use { resourcesApkZFile ->
|
||||||
)
|
// Delete all resources in the target APK before merging the new ones.
|
||||||
|
// This is necessary because the resources.apk renames resources.
|
||||||
|
// So unless, the old resources are deleted, there will be orphaned resources in the APK.
|
||||||
|
// It is not necessary, but for the sake of cleanliness, it is done.
|
||||||
|
targetApkZFile.entries().filter { entry ->
|
||||||
|
entry.centralDirectoryHeader.name.startsWith(RES_PREFIX)
|
||||||
|
}.forEach(StoredEntry::delete)
|
||||||
|
|
||||||
|
targetApkZFile.mergeFrom(resourcesApkZFile) { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add resources not compiled by AAPT.
|
||||||
|
resources.otherResources?.let { otherResources ->
|
||||||
|
targetApkZFile.addAllRecursively(otherResources) { file ->
|
||||||
|
file.relativeTo(otherResources).invariantSeparatorsPath !in resources.doNotCompress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete resources that were staged for deletion.
|
||||||
|
if (resources.deleteResources.isNotEmpty()) {
|
||||||
|
targetApkZFile.entries().filter { entry ->
|
||||||
|
resources.deleteResources.any { shouldDelete -> shouldDelete(entry.centralDirectoryHeader.name) }
|
||||||
|
}.forEach(StoredEntry::delete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do not compress result.doNotCompress
|
logger.info("Aligning ${apkFile.name}")
|
||||||
|
|
||||||
// TODO: Fix copying resources that are not needed anymore.
|
targetApkZFile.realign()
|
||||||
file.copyEntriesFromFileAligned(
|
|
||||||
ZipFile(apkFile),
|
logger.fine("Writing changes")
|
||||||
ZipFile.apkZipEntryAlignment,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the [apk] file and writes it to [output].
|
* Signs the apk file with the given options.
|
||||||
*
|
*
|
||||||
* @param apk The apk to sign.
|
|
||||||
* @param output The apk to write the signed apk to.
|
|
||||||
* @param signingOptions The options to use for signing.
|
* @param signingOptions The options to use for signing.
|
||||||
*/
|
*/
|
||||||
fun sign(
|
fun File.sign(signingOptions: SigningOptions) {
|
||||||
apk: File,
|
|
||||||
output: File,
|
|
||||||
signingOptions: SigningOptions,
|
|
||||||
) {
|
|
||||||
// Get the keystore from the file or create a new one.
|
// Get the keystore from the file or create a new one.
|
||||||
val keyStore =
|
val keyStore =
|
||||||
if (signingOptions.keyStore.exists()) {
|
if (signingOptions.keyStore.exists()) {
|
||||||
ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword)
|
ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword ?: "")
|
||||||
} else {
|
} else {
|
||||||
val entry = ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password)
|
val entries = setOf(ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password))
|
||||||
|
|
||||||
// Create a new keystore with a new keypair and saves it.
|
// Create a new keystore with a new keypair and saves it.
|
||||||
ApkSigner.newKeyStore(listOf(entry)).also { keyStore ->
|
ApkSigner.newKeyStore(entries).apply {
|
||||||
keyStore.store(
|
store(
|
||||||
signingOptions.keyStore.outputStream(),
|
signingOptions.keyStore.outputStream(),
|
||||||
signingOptions.keyStorePassword?.toCharArray(),
|
signingOptions.keyStorePassword?.toCharArray(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApkSigner.newApkSignerBuilder(
|
ApkSigner.newApkSigner(
|
||||||
keyStore,
|
keyStore,
|
||||||
signingOptions.alias,
|
signingOptions.alias,
|
||||||
signingOptions.password,
|
signingOptions.password,
|
||||||
signingOptions.signer,
|
).signApk(this)
|
||||||
signingOptions.signer,
|
|
||||||
).signApk(apk, output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,35 +20,6 @@ typealias PackageNameMap = Map<PackageName, VersionMap>
|
|||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
object PatchUtils {
|
object PatchUtils {
|
||||||
/**
|
|
||||||
* Get the version that is most common for [packageName] in the supplied set of [patches].
|
|
||||||
*
|
|
||||||
* @param patches The set of patches to check.
|
|
||||||
* @param packageName The name of the compatible package.
|
|
||||||
* @return The most common version of.
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"Use getMostCommonCompatibleVersions instead.",
|
|
||||||
ReplaceWith(
|
|
||||||
"getMostCommonCompatibleVersions(patches, setOf(packageName))" +
|
|
||||||
".entries.firstOrNull()?.value?.keys?.firstOrNull()",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
fun getMostCommonCompatibleVersion(
|
|
||||||
patches: PatchSet,
|
|
||||||
packageName: String,
|
|
||||||
) = patches
|
|
||||||
.mapNotNull {
|
|
||||||
// Map all patches to their compatible packages with version constraints.
|
|
||||||
it.compatiblePackages?.firstOrNull { compatiblePackage ->
|
|
||||||
compatiblePackage.name == packageName && compatiblePackage.versions?.isNotEmpty() == true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.flatMap { it.versions!! }
|
|
||||||
.groupingBy { it }
|
|
||||||
.eachCount()
|
|
||||||
.maxByOrNull { it.value }?.key
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
|
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package app.revanced.library.zip
|
|
||||||
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.io.DataOutput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
internal fun UInt.toLittleEndian() =
|
|
||||||
(((this.toInt() and 0xff000000.toInt()) shr 24) or ((this.toInt() and 0x00ff0000) shr 8) or ((this.toInt() and 0x0000ff00) shl 8) or (this.toInt() shl 24)).toUInt()
|
|
||||||
|
|
||||||
internal fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
|
|
||||||
|
|
||||||
internal fun UInt.toBigEndian() =
|
|
||||||
(
|
|
||||||
((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
|
|
||||||
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)
|
|
||||||
).toUInt()
|
|
||||||
|
|
||||||
internal fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
|
|
||||||
|
|
||||||
internal fun ByteBuffer.getUShort() = this.getShort().toUShort()
|
|
||||||
|
|
||||||
internal fun ByteBuffer.getUInt() = this.getInt().toUInt()
|
|
||||||
|
|
||||||
internal fun ByteBuffer.putUShort(ushort: UShort) = this.putShort(ushort.toShort())
|
|
||||||
|
|
||||||
internal fun ByteBuffer.putUInt(uint: UInt) = this.putInt(uint.toInt())
|
|
||||||
|
|
||||||
internal fun DataInput.readUShort() = this.readShort().toUShort()
|
|
||||||
|
|
||||||
internal fun DataInput.readUInt() = this.readInt().toUInt()
|
|
||||||
|
|
||||||
internal fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
|
|
||||||
|
|
||||||
internal fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
|
|
||||||
|
|
||||||
internal fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
|
|
||||||
|
|
||||||
internal fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
|
|
||||||
|
|
||||||
internal fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
|
|
||||||
|
|
||||||
internal fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
package app.revanced.library.zip
|
|
||||||
|
|
||||||
import app.revanced.library.zip.structures.ZipEndRecord
|
|
||||||
import app.revanced.library.zip.structures.ZipEntry
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.RandomAccessFile
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
import java.util.zip.CRC32
|
|
||||||
import java.util.zip.Deflater
|
|
||||||
|
|
||||||
class ZipFile(file: File) : Closeable {
|
|
||||||
private var entries: MutableList<ZipEntry> = mutableListOf()
|
|
||||||
|
|
||||||
// Open file for writing if it doesn't exist (because the intention is to write) or is writable.
|
|
||||||
private val filePointer: RandomAccessFile =
|
|
||||||
RandomAccessFile(
|
|
||||||
file,
|
|
||||||
if (!file.exists() || file.canWrite()) "rw" else "r",
|
|
||||||
)
|
|
||||||
|
|
||||||
private var centralDirectoryNeedsRewrite = false
|
|
||||||
|
|
||||||
private val compressionLevel = 5
|
|
||||||
|
|
||||||
init {
|
|
||||||
// If file isn't empty try to load entries.
|
|
||||||
if (file.length() > 0) {
|
|
||||||
val endRecord = findEndRecord()
|
|
||||||
|
|
||||||
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries) {
|
|
||||||
throw IllegalArgumentException("Multi-file archives are not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
entries = readEntries(endRecord).toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek back to start for writing.
|
|
||||||
filePointer.seek(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findEndRecord(): ZipEndRecord {
|
|
||||||
// Look from end to start since end record is at the end.
|
|
||||||
for (i in filePointer.length() - 1 downTo 0) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
// Possible beginning of signature.
|
|
||||||
if (filePointer.readByte() == 0x50.toByte()) {
|
|
||||||
// Seek back to get the full int.
|
|
||||||
filePointer.seek(i)
|
|
||||||
val possibleSignature = filePointer.readUIntLE()
|
|
||||||
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
return ZipEndRecord.fromECD(filePointer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Exception("Couldn't find end record")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readEntries(endRecord: ZipEndRecord): List<ZipEntry> {
|
|
||||||
filePointer.seek(endRecord.centralDirectoryStartOffset.toLong())
|
|
||||||
|
|
||||||
val numberOfEntries = endRecord.diskEntries.toInt()
|
|
||||||
|
|
||||||
return buildList(numberOfEntries) {
|
|
||||||
for (i in 1..numberOfEntries) {
|
|
||||||
add(
|
|
||||||
ZipEntry.fromCDE(filePointer).also
|
|
||||||
{
|
|
||||||
// for some reason the local extra field can be different from the central one
|
|
||||||
it.readLocalExtra(
|
|
||||||
filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
it.localHeaderOffset.toLong() + 28,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeCD() {
|
|
||||||
val centralDirectoryStartOffset = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
entries.forEach {
|
|
||||||
filePointer.channel.write(it.toCDE())
|
|
||||||
}
|
|
||||||
|
|
||||||
val entriesCount = entries.size.toUShort()
|
|
||||||
|
|
||||||
val endRecord =
|
|
||||||
ZipEndRecord(
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
entriesCount,
|
|
||||||
entriesCount,
|
|
||||||
filePointer.channel.position().toUInt() - centralDirectoryStartOffset,
|
|
||||||
centralDirectoryStartOffset,
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
|
|
||||||
filePointer.channel.write(endRecord.toECD())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntry(
|
|
||||||
entry: ZipEntry,
|
|
||||||
data: ByteBuffer,
|
|
||||||
) {
|
|
||||||
centralDirectoryNeedsRewrite = true
|
|
||||||
|
|
||||||
entry.localHeaderOffset = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
filePointer.channel.write(entry.toLFH())
|
|
||||||
filePointer.channel.write(data)
|
|
||||||
|
|
||||||
entries.add(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addEntryCompressData(
|
|
||||||
entry: ZipEntry,
|
|
||||||
data: ByteArray,
|
|
||||||
) {
|
|
||||||
val compressor = Deflater(compressionLevel, true)
|
|
||||||
compressor.setInput(data)
|
|
||||||
compressor.finish()
|
|
||||||
|
|
||||||
val uncompressedSize = data.size
|
|
||||||
val compressedData = ByteArray(uncompressedSize) // I'm guessing compression won't make the data bigger.
|
|
||||||
|
|
||||||
val compressedDataLength = compressor.deflate(compressedData)
|
|
||||||
val compressedBuffer =
|
|
||||||
ByteBuffer.wrap(compressedData.take(compressedDataLength).toByteArray())
|
|
||||||
|
|
||||||
compressor.end()
|
|
||||||
|
|
||||||
val crc = CRC32()
|
|
||||||
crc.update(data)
|
|
||||||
|
|
||||||
entry.compression = 8u // Deflate compression.
|
|
||||||
entry.uncompressedSize = uncompressedSize.toUInt()
|
|
||||||
entry.compressedSize = compressedDataLength.toUInt()
|
|
||||||
entry.crc32 = crc.value.toUInt()
|
|
||||||
|
|
||||||
addEntry(entry, compressedBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntryCopyData(
|
|
||||||
entry: ZipEntry,
|
|
||||||
data: ByteBuffer,
|
|
||||||
alignment: Int? = null,
|
|
||||||
) {
|
|
||||||
alignment?.let {
|
|
||||||
// Calculate where data would end up.
|
|
||||||
val dataOffset = filePointer.filePointer + entry.LFHSize
|
|
||||||
|
|
||||||
val mod = dataOffset % alignment
|
|
||||||
|
|
||||||
// Wrong alignment.
|
|
||||||
if (mod != 0L) {
|
|
||||||
// Add padding at end of extra field.
|
|
||||||
entry.localExtraField =
|
|
||||||
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addEntry(entry, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getDataForEntry(entry: ZipEntry): ByteBuffer {
|
|
||||||
return filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
entry.dataOffset.toLong(),
|
|
||||||
entry.compressedSize.toLong(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies all entries from [file] to this file but skip already existing entries.
|
|
||||||
*
|
|
||||||
* @param file The file to copy entries from.
|
|
||||||
* @param entryAlignment A function that returns the alignment for a given entry.
|
|
||||||
*/
|
|
||||||
fun copyEntriesFromFileAligned(
|
|
||||||
file: ZipFile,
|
|
||||||
entryAlignment: (entry: ZipEntry) -> Int?,
|
|
||||||
) {
|
|
||||||
for (entry in file.entries) {
|
|
||||||
if (entries.any { it.fileName == entry.fileName }) continue // Skip duplicates
|
|
||||||
|
|
||||||
val data = file.getDataForEntry(entry)
|
|
||||||
addEntryCopyData(entry, data, entryAlignment(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (centralDirectoryNeedsRewrite) writeCD()
|
|
||||||
filePointer.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object ApkZipFile {
|
|
||||||
private const val DEFAULT_ALIGNMENT = 4
|
|
||||||
private const val LIBRARY_ALIGNMENT = 4096
|
|
||||||
|
|
||||||
val apkZipEntryAlignment = { entry: ZipEntry ->
|
|
||||||
if (entry.compression.toUInt() != 0u) {
|
|
||||||
null
|
|
||||||
} else if (entry.fileName.endsWith(".so")) {
|
|
||||||
LIBRARY_ALIGNMENT
|
|
||||||
} else {
|
|
||||||
DEFAULT_ALIGNMENT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package app.revanced.library.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.library.zip.putUInt
|
|
||||||
import app.revanced.library.zip.putUShort
|
|
||||||
import app.revanced.library.zip.readUIntLE
|
|
||||||
import app.revanced.library.zip.readUShortLE
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
internal class ZipEndRecord(
|
|
||||||
val diskNumber: UShort,
|
|
||||||
val startingDiskNumber: UShort,
|
|
||||||
val diskEntries: UShort,
|
|
||||||
val totalEntries: UShort,
|
|
||||||
val centralDirectorySize: UInt,
|
|
||||||
val centralDirectoryStartOffset: UInt,
|
|
||||||
val fileComment: String,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
const val ECD_HEADER_SIZE = 22
|
|
||||||
const val ECD_SIGNATURE = 0x06054b50u
|
|
||||||
|
|
||||||
fun fromECD(input: DataInput): ZipEndRecord {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != ECD_SIGNATURE) {
|
|
||||||
throw IllegalArgumentException("Input doesn't start with end record signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val startingDiskNumber = input.readUShortLE()
|
|
||||||
val diskEntries = input.readUShortLE()
|
|
||||||
val totalEntries = input.readUShortLE()
|
|
||||||
val centralDirectorySize = input.readUIntLE()
|
|
||||||
val centralDirectoryStartOffset = input.readUIntLE()
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
|
|
||||||
if (fileCommentLength > 0u) {
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ZipEndRecord(
|
|
||||||
diskNumber,
|
|
||||||
startingDiskNumber,
|
|
||||||
diskEntries,
|
|
||||||
totalEntries,
|
|
||||||
centralDirectorySize,
|
|
||||||
centralDirectoryStartOffset,
|
|
||||||
fileComment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toECD(): ByteBuffer {
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(ECD_SIGNATURE)
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(startingDiskNumber)
|
|
||||||
buffer.putUShort(diskEntries)
|
|
||||||
buffer.putUShort(totalEntries)
|
|
||||||
buffer.putUInt(centralDirectorySize)
|
|
||||||
buffer.putUInt(centralDirectoryStartOffset)
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
package app.revanced.library.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.library.zip.*
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
class ZipEntry private constructor(
|
|
||||||
internal val version: UShort,
|
|
||||||
internal val versionNeeded: UShort,
|
|
||||||
internal val flags: UShort,
|
|
||||||
internal var compression: UShort,
|
|
||||||
internal val modificationTime: UShort,
|
|
||||||
internal val modificationDate: UShort,
|
|
||||||
internal var crc32: UInt,
|
|
||||||
internal var compressedSize: UInt,
|
|
||||||
internal var uncompressedSize: UInt,
|
|
||||||
internal val diskNumber: UShort,
|
|
||||||
internal val internalAttributes: UShort,
|
|
||||||
internal val externalAttributes: UInt,
|
|
||||||
internal var localHeaderOffset: UInt,
|
|
||||||
internal val fileName: String,
|
|
||||||
internal val extraField: ByteArray,
|
|
||||||
internal val fileComment: String,
|
|
||||||
internal var localExtraField: ByteArray = ByteArray(0), // separate for alignment
|
|
||||||
) {
|
|
||||||
internal val LFHSize: Int
|
|
||||||
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
|
|
||||||
|
|
||||||
internal val dataOffset: UInt
|
|
||||||
get() = localHeaderOffset + LFHSize.toUInt()
|
|
||||||
|
|
||||||
constructor(fileName: String) : this(
|
|
||||||
0x1403u, // made by unix, version 20
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0x0821u, // seems to be static time google uses, no idea
|
|
||||||
0x0221u, // same as above
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
fileName,
|
|
||||||
ByteArray(0),
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
internal const val CDE_HEADER_SIZE = 46
|
|
||||||
internal const val CDE_SIGNATURE = 0x02014b50u
|
|
||||||
|
|
||||||
internal const val LFH_HEADER_SIZE = 30
|
|
||||||
internal const val LFH_SIGNATURE = 0x04034b50u
|
|
||||||
|
|
||||||
internal fun fromCDE(input: DataInput): ZipEntry {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != CDE_SIGNATURE) {
|
|
||||||
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
val version = input.readUShortLE()
|
|
||||||
val versionNeeded = input.readUShortLE()
|
|
||||||
var flags = input.readUShortLE()
|
|
||||||
val compression = input.readUShortLE()
|
|
||||||
val modificationTime = input.readUShortLE()
|
|
||||||
val modificationDate = input.readUShortLE()
|
|
||||||
val crc32 = input.readUIntLE()
|
|
||||||
val compressedSize = input.readUIntLE()
|
|
||||||
val uncompressedSize = input.readUIntLE()
|
|
||||||
val fileNameLength = input.readUShortLE()
|
|
||||||
var fileName = ""
|
|
||||||
val extraFieldLength = input.readUShortLE()
|
|
||||||
val extraField = ByteArray(extraFieldLength.toInt())
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val internalAttributes = input.readUShortLE()
|
|
||||||
val externalAttributes = input.readUIntLE()
|
|
||||||
val localHeaderOffset = input.readUIntLE()
|
|
||||||
|
|
||||||
val variableFieldsLength =
|
|
||||||
fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
|
|
||||||
|
|
||||||
if (variableFieldsLength > 0) {
|
|
||||||
val fileNameBytes = ByteArray(fileNameLength.toInt())
|
|
||||||
input.readFully(fileNameBytes)
|
|
||||||
fileName = fileNameBytes.toString(Charsets.UTF_8)
|
|
||||||
|
|
||||||
input.readFully(extraField)
|
|
||||||
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = (
|
|
||||||
flags and
|
|
||||||
0b1000u.inv()
|
|
||||||
.toUShort()
|
|
||||||
) // disable data descriptor flag as they are not used
|
|
||||||
|
|
||||||
return ZipEntry(
|
|
||||||
version,
|
|
||||||
versionNeeded,
|
|
||||||
flags,
|
|
||||||
compression,
|
|
||||||
modificationTime,
|
|
||||||
modificationDate,
|
|
||||||
crc32,
|
|
||||||
compressedSize,
|
|
||||||
uncompressedSize,
|
|
||||||
diskNumber,
|
|
||||||
internalAttributes,
|
|
||||||
externalAttributes,
|
|
||||||
localHeaderOffset,
|
|
||||||
fileName,
|
|
||||||
extraField,
|
|
||||||
fileComment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun readLocalExtra(buffer: ByteBuffer) {
|
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN)
|
|
||||||
localExtraField = ByteArray(buffer.getUShort().toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun toLFH(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer =
|
|
||||||
ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(LFH_SIGNATURE)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(localExtraField.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(localExtraField)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun toCDE(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer =
|
|
||||||
ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(CDE_SIGNATURE)
|
|
||||||
buffer.putUShort(version)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(extraField.size.toUShort())
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(internalAttributes)
|
|
||||||
buffer.putUInt(externalAttributes)
|
|
||||||
buffer.putUInt(localHeaderOffset)
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(extraField)
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ internal object PatchOptionsTest {
|
|||||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":false}]}]"
|
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":false}]}]"
|
||||||
|
|
||||||
@Patch("PatchOptionsTestPatch")
|
@Patch("PatchOptionsTestPatch")
|
||||||
object PatchOptionsTestPatch : BytecodePatch() {
|
object PatchOptionsTestPatch : BytecodePatch(emptySet()) {
|
||||||
var option1 by stringPatchOption("key1", null, null, "title1", "description1")
|
var option1 by stringPatchOption("key1", null, null, "title1", "description1")
|
||||||
var option2 by booleanPatchOption("key2", true, null, "title2", "description2")
|
var option2 by booleanPatchOption("key2", true, null, "title2", "description2")
|
||||||
|
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ internal object PatchUtilsTest {
|
|||||||
fun assertEqualsExpected(compatiblePackageNames: Set<String>?) =
|
fun assertEqualsExpected(compatiblePackageNames: Set<String>?) =
|
||||||
assertEqualsVersions(
|
assertEqualsVersions(
|
||||||
expected =
|
expected =
|
||||||
mapOf(
|
mapOf(
|
||||||
"some.package" to linkedMapOf("a" to 3, "b" to 2, "c" to 1),
|
"some.package" to linkedMapOf("a" to 3, "b" to 2, "c" to 1),
|
||||||
"some.other.package" to linkedMapOf("b" to 3, "c" to 2, "d" to 1),
|
"some.other.package" to linkedMapOf("b" to 3, "c" to 2, "d" to 1),
|
||||||
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
|
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
|
||||||
"some.other.other.other.package" to linkedMapOf(),
|
"some.other.other.other.package" to linkedMapOf(),
|
||||||
),
|
),
|
||||||
patches,
|
patches,
|
||||||
compatiblePackageNames,
|
compatiblePackageNames,
|
||||||
countUnusedPatches = true,
|
countUnusedPatches = true,
|
||||||
@@ -74,12 +74,12 @@ internal object PatchUtilsTest {
|
|||||||
|
|
||||||
assertEqualsExpected(
|
assertEqualsExpected(
|
||||||
compatiblePackageNames =
|
compatiblePackageNames =
|
||||||
setOf(
|
setOf(
|
||||||
"some.package",
|
"some.package",
|
||||||
"some.other.package",
|
"some.other.package",
|
||||||
"some.other.other.package",
|
"some.other.other.package",
|
||||||
"some.other.other.other.package",
|
"some.other.other.other.package",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEqualsExpected(
|
assertEqualsExpected(
|
||||||
@@ -91,19 +91,19 @@ internal object PatchUtilsTest {
|
|||||||
fun `common versions correctly ordered for each package without counting unused patches`() {
|
fun `common versions correctly ordered for each package without counting unused patches`() {
|
||||||
assertEqualsVersions(
|
assertEqualsVersions(
|
||||||
expected =
|
expected =
|
||||||
mapOf(
|
mapOf(
|
||||||
"some.package" to linkedMapOf("a" to 1),
|
"some.package" to linkedMapOf("a" to 1),
|
||||||
"some.other.package" to linkedMapOf("b" to 2, "c" to 2, "d" to 1),
|
"some.other.package" to linkedMapOf("b" to 2, "c" to 2, "d" to 1),
|
||||||
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
|
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
|
||||||
),
|
),
|
||||||
patches,
|
patches,
|
||||||
compatiblePackageNames =
|
compatiblePackageNames =
|
||||||
setOf(
|
setOf(
|
||||||
"some.package",
|
"some.package",
|
||||||
"some.other.package",
|
"some.other.package",
|
||||||
"some.other.other.package",
|
"some.other.other.package",
|
||||||
"some.other.other.other.package",
|
"some.other.other.other.package",
|
||||||
),
|
),
|
||||||
countUnusedPatches = false,
|
countUnusedPatches = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -170,14 +170,6 @@ internal object PatchUtilsTest {
|
|||||||
patches: PatchSet,
|
patches: PatchSet,
|
||||||
compatiblePackageName: String,
|
compatiblePackageName: String,
|
||||||
) {
|
) {
|
||||||
// Test both the deprecated and the new method.
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
assertEquals(
|
|
||||||
expected,
|
|
||||||
PatchUtils.getMostCommonCompatibleVersion(patches, compatiblePackageName),
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected,
|
expected,
|
||||||
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName))
|
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName))
|
||||||
|
|||||||
Reference in New Issue
Block a user