Compare commits

..

7 Commits

Author SHA1 Message Date
semantic-release-bot
543464f159 chore(release): 2.58.0 [skip ci]
# [2.58.0](https://github.com/oSumAtrIX/revanced-patches/compare/v2.57.0...v2.58.0) (2022-09-17)

### Features

* migrate to `osumatrix/revanced-patcher` dependency ([d20a2d9](331107288d))
2022-09-17 02:31:44 +00:00
oSumAtrIX
de2f9c9d83 build: bump patcher dependency version
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-17 04:28:44 +02:00
oSumAtrIX
b2bca0167a refactor(custom-branding): use getValue operator for options
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-17 04:26:29 +02:00
oSumAtrIX
2f1fef0449 Merge remote-tracking branch 'revanced/main'
# Conflicts:
#	src/main/kotlin/app/revanced/patches/youtube/layout/amoled/patch/AmoledPatch.kt
#	src/main/kotlin/app/revanced/patches/youtube/layout/customthemes/patch/CustomThemePatch.kt
2022-09-17 04:20:49 +02:00
oSumAtrIX
5a058ebcf1 docs: add missing section for patches [skip ci]
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-13 21:36:56 +02:00
oSumAtrIX
011f413088 docs: add differences section in README
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-13 21:36:56 +02:00
oSumAtrIX
331107288d feat: migrate to osumatrix/revanced-patcher dependency
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-13 21:36:44 +02:00
573 changed files with 4876 additions and 18300 deletions

View File

@@ -59,15 +59,3 @@ body:
description: Add additional context here.
validations:
required: false
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I filled out all of the requested information in this issue properly.
required: true

View File

@@ -27,15 +27,3 @@ body:
description: Why should your patch request should be considered? What makes it valuable to the community?
validations:
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I filled out all of the requested information in this issue properly.
required: true

View File

@@ -1,25 +0,0 @@
name: PR to main
on:
push:
branches:
- dev
workflow_dispatch:
env:
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
jobs:
pull-request:
name: Open pull request
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Open pull request
uses: repo-sync/pull-request@v2
with:
destination_branch: 'main'
pr_title: 'chore: ${{ env.MESSAGE }}'
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
pr_draft: true

View File

@@ -1,5 +1,4 @@
name: Release
on:
workflow_dispatch:
push:
@@ -10,43 +9,32 @@ on:
branches:
- main
- dev
jobs:
release:
name: Release
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Setup JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'zulu'
distribution: 'adopt'
cache: gradle
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 'latest'
cache: 'npm'
- name: Setup Android SDK
uses: amyu/setup-android@v2
with:
cache-disabled: false
sdk-version: '33'
build-tools-version: '33.0.1'
uses: android-actions/setup-android@v2
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew generateMeta clean --no-daemon
run: ./gradlew build clean generateMeta
- name: Install Android build-tools
run: sdkmanager "build-tools;32.0.0"
- name: Setup semantic-release
run: npm install semantic-release@19.0.5 @saithodev/semantic-release-backmerge @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin@1.7.4 -D
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release

3
.gitignore vendored
View File

@@ -118,6 +118,3 @@ gradle-app.setting
# Ignore vscode config
.vscode/
# Dependency directories
node_modules/

View File

@@ -7,7 +7,11 @@
}
],
"plugins": [
"@semantic-release/commit-analyzer",
["@semantic-release/commit-analyzer", {
"releaseRules": [
{"type": "build", "release": "patch"}
]
}],
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"gradle-semantic-release-plugin",
@@ -32,15 +36,7 @@
{
"path": "patches.json"
}
],
successComment: false
}
],
[
"@saithodev/semantic-release-backmerge",
{
branches: [{from: "main", to: "dev"}],
clearWorkspace: true
]
}
]
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,18 @@
## 🧩 Patches
The official Patch bundle provided by ReVanced and the community.
Patch bundle provided by ReVanced and the community.
> Looking for the JSON variant of this? [Click here](patches.json).
### ⛏ Differences to [revanced/revanced-patches](https://github.com/revanced/revanced-patches)
- Based on [osumatrix/revanced-patcher](https://github.com/osumatrix/revanced-patcher)
- Minor QoL refactoring
- Several additional patches
### 💊 Patches
{{ table }}
## 📝 JSON Format
### 📝 JSON Format
This section explains the JSON format for the [patches.json](patches.json) file.
@@ -18,6 +24,7 @@ The file contains an array of objects, each object representing a patch. The obj
| `description` | The description of the patch. |
| `version` | The version of the patch. |
| `excluded` | Whether the patch is excluded by default. If `true`, the patch must never be included by default. |
| `deprecated` | Whether the patch is deprecated. |
| `options` | An array of options for this patch. |
| `options.key` | The key of the option. |
| `options.title` | The title of the option. |
@@ -38,6 +45,7 @@ Example:
"description": "Adds the ability to remember the video quality you chose in the video quality flyout.",
"version": "0.0.1",
"excluded": false,
"deprecated": false,
"options": [],
"dependencies": [
"integrations",

274
README.md
View File

@@ -1,200 +1,58 @@
## 🧩 Patches
The official Patch bundle provided by ReVanced and the community.
Patch bundle provided by ReVanced and the community.
> Looking for the JSON variant of this? [Click here](patches.json).
### ⛏ Differences to [revanced/revanced-patches](https://github.com/revanced/revanced-patches)
### [📦 `com.google.android.youtube`](https://play.google.com/store/apps/details?id=com.google.android.youtube)
- Based on [osumatrix/revanced-patcher](https://github.com/osumatrix/revanced-patcher)
- Minor QoL refactoring
- Several additional patches
### 💊 Patches
### 📦 `com.twitter.android`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `always-autorepeat` | Always repeats the playing video again. | 17.49.37 |
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
| `comments` | Hides components related to comments. | 17.49.37 |
| `copy-video-url` | Adds buttons in player to copy video links. | 17.49.37 |
| `custom-branding` | Changes the YouTube launcher icon and name to your choice (defaults to ReVanced). | all |
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.49.37 |
| `custom-video-speed` | Adds more video speed options. | 17.49.37 |
| `debugging` | Adds debugging options. | all |
| `disable-auto-captions` | Disable forced captions from being automatically enabled. | 17.49.37 |
| `disable-auto-player-popup-panels` | Disable automatic popup panels (playlist or live chat) on video player. | 17.49.37 |
| `disable-fullscreen-panels` | Disables video description and comments panel in fullscreen view. | 17.49.37 |
| `disable-startup-shorts-player` | Disables playing YouTube Shorts when launching YouTube. | 17.49.37 |
| `disable-zoom-haptics` | Disables haptics when zooming. | all |
| `downloads` | Enables downloading music and videos from YouTube. | 17.49.37 |
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.49.37 |
| `general-ads` | Removes general ads. | 17.49.37 |
| `hdr-auto-brightness` | Makes the brightness of HDR videos follow the system default. | 17.49.37 |
| `hide-album-cards` | Hides the album cards below the artist description. | 17.49.37 |
| `hide-artist-card` | Hides the artist card below the searchbar. | 17.49.37 |
| `hide-autoplay-button` | Hides the autoplay button in the video player. | 17.49.37 |
| `hide-breaking-news-shelf` | Hides the breaking news shelf on the homepage tab. | 17.49.37 |
| `hide-captions-button` | Hides the captions button on video player. | 17.49.37 |
| `hide-cast-button` | Hides the cast button in the video player. | all |
| `hide-create-button` | Hides the create button in the navigation bar. | 17.49.37 |
| `hide-crowdfunding-box` | Hides the crowdfunding box between the player and video description. | 17.49.37 |
| `hide-email-address` | Hides the email address in the account switcher. | 17.49.37 |
| `hide-endscreen-cards` | Hides the suggested video cards at the end of a video in fullscreen. | 17.49.37 |
| `hide-info-cards` | Hides info-cards in videos. | 17.49.37 |
| `hide-my-mix` | Hides mix playlists. | 17.49.37 |
| `hide-shorts-button` | Hides the shorts button on the navigation bar. | 17.49.37 |
| `hide-time-and-seekbar` | Hides progress bar and time counter on videos. | 17.49.37 |
| `hide-video-buttons` | Adds options to hide action buttons under a video. | 17.49.37 |
| `hide-watch-in-vr` | Hides the Watch in VR option in the player settings flyout panel. | 17.49.37 |
| `hide-watermark` | Hides creator's watermarks on videos. | 17.49.37 |
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.49.37 |
| `minimized-playback` | Enables minimized and background playback. | 17.49.37 |
| `old-quality-layout` | Enables the original video quality flyout in the video player settings | 17.49.37 |
| `open-links-directly` | Bypasses https://youtube.com/redirect URLs. | 17.49.37 |
| `premium-heading` | Shows premium branding on the home screen. | all |
| `remember-playback-rate` | Adds the ability to remember the playback rate you chose in the video playback rate flyout. | 17.49.37 |
| `remember-video-quality` | Adds the ability to remember the video quality you chose in the video quality flyout. | 17.49.37 |
| `remove-player-button-background` | Removes the background from the video player buttons. | 17.49.37 |
| `return-youtube-dislike` | Shows the dislike count of videos using the Return YouTube Dislike API. | 17.49.37 |
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.49.37 |
| `settings` | Adds settings for ReVanced to YouTube. | all |
| `sponsorblock` | Integrate SponsorBlock. | 17.49.37 |
| `spoof-app-version` | Tricks YouTube into thinking, you are running an older version of the app. One of the side effects also includes restoring the old UI. | 17.49.37 |
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.49.37 |
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.49.37 |
| `theme` | Applies a custom theme. | all |
| `video-ads` | Removes ads in the video player. | 17.49.37 |
| `timeline-ads` | Removes ads from the Twitter timeline. | all |
</details>
### [📦 `com.google.android.apps.youtube.music`](https://play.google.com/store/apps/details?id=com.google.android.apps.youtube.music)
### 📦 `com.reddit.frontpage`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `background-play` | Enables playing music in the background. | 5.39.52 |
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.39.52 |
| `compact-header` | Hides the music category bar at the top of the homepage. | 5.39.52 |
| `exclusive-audio-playback` | Enables the option to play music without video. | 5.39.52 |
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.39.52 |
| `minimized-playback-music` | Enables minimized playback on Kids music. | 5.39.52 |
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.39.52 |
| `music-video-ads` | Removes ads in the music player. | 5.39.52 |
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 5.39.52 |
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.39.52 |
| `premium-icon-reddit` | Unlocking Premium Icons in reddit app. | all |
| `general-reddit-ads` | Removes general ads from the Reddit frontpage and subreddits. | all |
</details>
### [📦 `com.ss.android.ugc.trill`](https://play.google.com/store/apps/details?id=com.ss.android.ugc.trill)
### 📦 `com.garzotto.pflotsh.ecmwf_a`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `disable-login-requirement` | Do not force login. | all |
| `downloads` | Removes download restrictions and changes the default path to download to. | 27.8.3 |
| `feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | 27.8.3 |
| `fix-google-login` | Allows logging in with a Google account. | all |
| `hide-ads` | Removes ads from TikTok. | all |
| `playback-speed` | Enables the playback speed option for all videos. | all |
| `settings` | Adds ReVanced settings to TikTok. | 27.8.3 |
| `show-seekbar` | Shows progress bar for all video. | all |
| `sim-spoof` | Spoofs the information which is retrieved from the sim-card. | 27.8.3 |
| `pflotsh-ecmwf-subscription-unlock` | Unlocks all subscription features. | 3.5.4 |
</details>
### [📦 `com.zhiliaoapp.musically`](https://play.google.com/store/apps/details?id=com.zhiliaoapp.musically)
### 📦 `com.google.android.apps.youtube.music`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `disable-login-requirement` | Do not force login. | all |
| `downloads` | Removes download restrictions and changes the default path to download to. | 27.8.3 |
| `feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | 27.8.3 |
| `fix-google-login` | Allows logging in with a Google account. | all |
| `hide-ads` | Removes ads from TikTok. | all |
| `playback-speed` | Enables the playback speed option for all videos. | all |
| `settings` | Adds ReVanced settings to TikTok. | 27.8.3 |
| `show-seekbar` | Shows progress bar for all video. | all |
| `sim-spoof` | Spoofs the information which is retrieved from the sim-card. | 27.8.3 |
| `minimized-playback-music` | Enables minimized playback on Kids music. | 5.23.50 |
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 5.23.50 |
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.23.50 |
| `compact-header` | Hides the music category bar at the top of the homepage. | 5.23.50 |
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.23.50 |
| `background-play` | Enables playing music in the background. | 5.23.50 |
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.23.50 |
| `music-video-ads` | Removes ads in the music player. | 5.23.50 |
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.23.50 |
| `exclusive-audio-playback` | Enables the option to play music without video. | 5.23.50 |
</details>
### [📦 `tv.twitch.android.app`](https://play.google.com/store/apps/details?id=tv.twitch.android.app)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `block-audio-ads` | Blocks audio ads in streams and VODs. | all |
| `block-embedded-ads` | Blocks embedded steam ads using services like TTV.lol or PurpleAdBlocker. | all |
| `block-video-ads` | Blocks video ads in streams and VODs. | all |
| `debug-mode` | Enables Twitch's internal debugging mode. | all |
| `settings` | Adds settings menu to Twitch. | all |
| `show-deleted-messages` | Shows deleted chat messages behind a clickable spoiler. | all |
</details>
### [📦 `com.twitter.android`](https://play.google.com/store/apps/details?id=com.twitter.android)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `dynamic-color` | Replaces the default Twitter Blue with the users Material You palette. | all |
| `hide-views-stats` | Hides the view stats under tweets. | all |
| `monochrome-icon` | Adds a monochrome icon. | all |
| `timeline-ads` | Removes ads from the Twitter timeline. Might require clearing app data to remove already cached ads. | all |
</details>
### [📦 `com.spotify.music`](https://play.google.com/store/apps/details?id=com.spotify.music)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `disable-capture-restriction` | Allows capturing Spotify's audio output while screen sharing or screen recording. | all |
| `hide-premium-navbar` | Removes the premium tab from the navbar. | all |
| `spotify-theme` | Applies a custom theme. | all |
</details>
### [📦 `com.reddit.frontpage`](https://play.google.com/store/apps/details?id=com.reddit.frontpage)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `general-reddit-ads` | Removes general ads from the Reddit frontpage and subreddits. | 2022.43.0 |
| `premium-icon-reddit` | Unlocks premium Reddit app icons. | all |
</details>
### [📦 `com.vanced.android.youtube`](https://play.google.com/store/apps/details?id=com.vanced.android.youtube)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
</details>
### [📦 `com.myprog.hexedit`](https://play.google.com/store/apps/details?id=com.myprog.hexedit)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `disable-ads` | Disables ads in HexEditor. | all |
</details>
### [📦 `org.citra.citra_emu`](https://play.google.com/store/apps/details?id=org.citra.citra_emu)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `premium-unlock` | Unlocks premium functions. | all |
</details>
### [📦 `org.citra.citra_emu.canary`](https://play.google.com/store/apps/details?id=org.citra.citra_emu.canary)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `premium-unlock` | Unlocks premium functions. | all |
</details>
### [📦 `com.backdrops.wallpapers`](https://play.google.com/store/apps/details?id=com.backdrops.wallpapers)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `pro-unlock` | Unlocks pro-only functions. | all |
</details>
### [📦 `de.dwd.warnapp`](https://play.google.com/store/apps/details?id=de.dwd.warnapp)
### 📦 `de.dwd.warnapp`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
@@ -202,65 +60,75 @@ The official Patch bundle provided by ReVanced and the community.
| `promo-code-unlock` | Disables the validation of promo code. Any code will work to unlock all features. | all |
</details>
### [📦 `com.awedea.nyx`](https://play.google.com/store/apps/details?id=com.awedea.nyx)
### 📦 `com.ss.android.ugc.trill`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-pro` | Unlocks all pro features. | all |
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
| `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
</details>
### [📦 `co.windyapp.android`](https://play.google.com/store/apps/details?id=co.windyapp.android)
### 📦 `com.zhiliaoapp.musically`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-pro` | Unlocks all pro features. | all |
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
| `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
</details>
### [📦 `ginlemon.iconpackstudio`](https://play.google.com/store/apps/details?id=ginlemon.iconpackstudio)
### 📦 `com.google.android.youtube`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-pro` | Unlocks all pro features. | all |
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.33.42 |
| `downloads` | Enables downloading music and videos from YouTube. | 17.33.42 |
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.33.42 |
| `disable-create-button` | Hides the create button in the navigation bar. | 17.33.42 |
| `hide-cast-button` | Hides the cast button in the video player. | all |
| `return-youtube-dislike` | Shows the dislike count of videos using the Return YouTube Dislike API. | 17.33.42 |
| `hide-autoplay-button` | Hides the autoplay button in the video player. | 17.33.42 |
| `premium-heading` | Shows premium branding on the home screen. | all |
| `custom-branding` | Changes the YouTube launcher icon and name to your choice (defaults to ReVanced). | all |
| `disable-fullscreen-panels` | Disables video description and comments panel in fullscreen view. | 17.33.42 |
| `old-quality-layout` | Enables the original quality flyout menu. | 17.33.42 |
| `hide-shorts-button` | Hides the shorts button on the navigation bar. | 17.33.42 |
| `hide-watermark` | Hides creator's watermarks on videos. | 17.33.42 |
| `sponsorblock` | Integrate SponsorBlock. | 17.33.42 |
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.33.42 |
| `custom-theme` | Applies a custom theme. | all |
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.33.42 |
| `disable-auto-captions` | Disable forced captions from being automatically enabled. | 17.33.42 |
| `minimized-playback` | Enables minimized and background playback. | 17.33.42 |
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.33.42 |
| `always-autorepeat` | Always repeats the playing video again. | 17.33.42 |
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.33.42 |
| `settings` | Adds settings for ReVanced to YouTube. | all |
| `enable-debugging` | Enables app debugging by patching the manifest file. | all |
| `custom-playback-speed` | Adds more video playback speed options. | 17.33.42 |
| `hdr-auto-brightness` | Makes the brightness of HDR videos follow the system default. | 17.33.42 |
| `remember-video-quality` | Adds the ability to remember the video quality you chose in the video quality flyout. | 17.33.42 |
| `video-ads` | Removes ads in the video player. | 17.33.42 |
| `general-ads` | Removes general ads. | 17.33.42 |
| `hide-infocard-suggestions` | Hides infocards in videos. | 17.33.42 |
</details>
### [📦 `org.totschnig.myexpenses`](https://play.google.com/store/apps/details?id=org.totschnig.myexpenses)
### 📦 `com.vanced.android.youtube`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-pro` | Unlocks all professional features. | all |
</details>
### [📦 `com.ithebk.expensemanager`](https://play.google.com/store/apps/details?id=com.ithebk.expensemanager)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-pro` | Unlocks pro features. | all |
</details>
### [📦 `com.ticktick.task`](https://play.google.com/store/apps/details?id=com.ticktick.task)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-themes` | Unlocks all themes. | all |
</details>
### [📦 `net.dinglisch.android.taskerm`](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm)
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `unlock-trial` | Unlocks the trial version. | all |
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
</details>
## 📝 JSON Format
### 📝 JSON Format
This section explains the JSON format for the [patches.json](patches.json) file.
@@ -272,6 +140,7 @@ The file contains an array of objects, each object representing a patch. The obj
| `description` | The description of the patch. |
| `version` | The version of the patch. |
| `excluded` | Whether the patch is excluded by default. If `true`, the patch must never be included by default. |
| `deprecated` | Whether the patch is deprecated. |
| `options` | An array of options for this patch. |
| `options.key` | The key of the option. |
| `options.title` | The title of the option. |
@@ -292,6 +161,7 @@ Example:
"description": "Adds the ability to remember the video quality you chose in the video quality flyout.",
"version": "0.0.1",
"excluded": false,
"deprecated": false,
"options": [],
"dependencies": [
"integrations",

View File

@@ -11,7 +11,14 @@ repositories {
mavenCentral()
mavenLocal()
maven {
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
url = uri("https://maven.pkg.github.com/osumatrix/revanced-patcher")
credentials {
username = githubUsername
password = githubPassword
}
}
maven {
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
credentials {
username = githubUsername
password = githubPassword
@@ -20,10 +27,11 @@ repositories {
}
dependencies {
implementation("app.revanced:revanced-patcher:6.4.1")
implementation("app.revanced:revanced-patcher:4.5.0")
implementation("app.revanced:multidexlib2:2.5.2.r2")
// Required for meta
implementation("com.google.code.gson:gson:2.10")
implementation("com.google.code.gson:gson:2.9.1")
}
tasks {
@@ -33,7 +41,7 @@ tasks {
doLast {
val androidHome = System.getenv("ANDROID_HOME") ?: throw GradleException("ANDROID_HOME not found")
val d8 = "${androidHome}/build-tools/33.0.1/d8"
val d8 = "${androidHome}/build-tools/32.0.0/d8"
val input = configurations.archives.get().allArtifacts.files.files.first().absolutePath
val work = File("${buildDir}/libs")

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 2.155.1-dev.1
version = 2.58.0

6107
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
{
"devDependencies": {
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.7.4",
"semantic-release": "^19.0.5"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,30 +1,45 @@
package app.revanced.extensions
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.util.MethodUtil
import app.revanced.patcher.util.smali.toInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.BuilderInstruction11n
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
import org.w3c.dom.Node
import java.nio.file.Files
// TODO: populate this to all patches
/**
* Convert a [MethodFingerprint] to a [PatchResultError].
*
* @return A [PatchResultError] for the [MethodFingerprint].
*/
fun MethodFingerprint.toErrorResult() = PatchResultError("Failed to resolve $name")
// TODO: this method does not make sense here
internal fun MutableMethodImplementation.injectHideCall(
index: Int,
register: Int
) {
this.addInstruction(
index,
"invoke-static { v$register }, Lapp/revanced/integrations/patches/HideHomeAdsPatch;->HideHomeAds(Landroid/view/View;)V".toInstruction()
)
}
/**
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
* traverse the class hierarchy starting from the given root class
*
* @param method The [Method] to find.
* @return The [MutableMethod].
* @param targetClass the class to start traversing the class hierarchy from
* @param callback function that is called for every class in the hierarchy
*/
fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first {
MethodUtil.methodSignaturesMatch(it, method)
fun BytecodeData.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
callback(targetClass)
this.findClass(targetClass.superclass ?: return)?.resolve()?.let {
traverseClassHierarchy(it, callback)
}
}
/**
@@ -38,7 +53,160 @@ fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod)
methods.addAll(transformedMethods)
}
/**
* Insert an event hook at the top of the method. If the hook returns true, the event is consumed and the method will return with true
*
* the hook method MUST return a boolean and MUST take two parameters, like so:
* fun hook(thisRef: Object, eventData: Object): Boolean {}
*
* The final injected code will resemble the following logic:
* if( YouHook(this, event) ) { return true; }
* ...
*
* @param hookRef reference to the hook method
*/
internal fun MutableMethod.injectConsumableEventHook(hookRef: ImmutableMethodReference) {
val isStaticMethod = AccessFlags.STATIC.isSet(this.accessFlags)
this.implementation?.let { impl ->
// create label to index 0 to continue to the normal program flow
val lblContinueNormalFlow = impl.newLabelForIndex(0)
// define registers
/** V0 */
val regV0 = 0
/** this */
val regP0 = impl.registerCount - this.parameters.size - (if (isStaticMethod) 0 else 1)
/** motionEvent */
val regP1 = regP0 + 1
// insert instructions at the start of the method:
// if( Hook(this, event) ) { return true; }
impl.addInstructions(
0, listOf(
// invoke-static { p0, p1 } <hook>
BuilderInstruction35c(
Opcode.INVOKE_STATIC,
2,
regP0,
regP1,
0, 0, 0,
hookRef
),
// move-result v0
BuilderInstruction11x(
Opcode.MOVE_RESULT,
regV0
),
// if-eqz v0, :continue_normal_flow
BuilderInstruction21t(
Opcode.IF_EQZ,
regV0,
lblContinueNormalFlow
),
// const/4 v0, 0x1
BuilderInstruction11n(
Opcode.CONST_4,
regV0,
0x1
),
// return v0
BuilderInstruction11x(
Opcode.RETURN,
regV0
)
// :continue_normal_flow
)
)
}
}
/**
* inject resources into the patched app
*
* @param classLoader classloader to use for loading the resources
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
* @param resourceType the resource type, for example 'drawable'. this has to match both the source and the target
* @param resourceFileNames names of all resources of this type to inject
*/
fun ResourceData.injectResources(
classLoader: ClassLoader,
patchDirectoryPath: String,
resourceType: String,
resourceFileNames: List<String>
) {
resourceFileNames.forEach { name ->
val relativePath = "$resourceType/$name"
val sourceRes = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
?: throw PatchResultError("could not open resource '$patchDirectoryPath/$relativePath'")
Files.copy(
sourceRes,
this["res"].resolve(relativePath).toPath()
)
}
}
/**
* inject strings into the patched app
*
* @param classLoader classloader to use for loading the resources
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
* @param languageIdentifier ISO 639-2 two- letter language code identifier (aka the one android uses for values directory)
*/
fun ResourceData.injectStrings(
classLoader: ClassLoader,
patchDirectoryPath: String,
languageIdentifier: String? = null,
) {
val relativePath =
if (languageIdentifier.isNullOrBlank()) "values/strings.xml" else "values/strings-$languageIdentifier.xml"
// open source strings.xml
val sourceInputStream = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
?: throw PatchResultError("failed to open '$patchDirectoryPath/$relativePath'")
xmlEditor[sourceInputStream].use { sourceStringsXml ->
val strings = sourceStringsXml.file.getElementsByTagName("resources").item(0).childNodes
// open target strings.xml
xmlEditor["res/$relativePath"].use { targetStringsXml ->
val targetFile = targetStringsXml.file
val targetRootNode = targetFile.getElementsByTagName("resources").item(0)
// process all children strings in the source
for (i in 0 until strings.length) {
// clone the node from source to target
val node = strings.item(i).cloneNode(true)
targetFile.adoptNode(node)
targetRootNode.appendChild(node)
}
}
}
}
internal fun Node.doRecursively(action: (Node) -> Unit) {
action(this)
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
}
}
internal fun String.startsWithAny(vararg prefixes: String): Boolean {
for (prefix in prefixes)
if (this.startsWith(prefix))
return true
return false
}
internal fun String.equalsAny(vararg other: String): Boolean {
for (_other in other)
if (this == _other)
return true
return false
}

View File

@@ -2,12 +2,12 @@ package app.revanced.meta
import app.revanced.meta.json.generateJson
import app.revanced.meta.readme.generateText
import app.revanced.patcher.data.Context
import app.revanced.patcher.data.Data
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.PatchBundle
import app.revanced.patcher.util.patch.impl.JarPatchBundle
import java.io.File
typealias Bundle = List<Class<out Patch<Context>>>
typealias Bundle = List<Class<out Patch<Data>>>
object Meta {
@JvmStatic
@@ -20,7 +20,7 @@ object Meta {
}
}
fun accumulatePatches() = PatchBundle.Jar(
fun accumulatePatches() = JarPatchBundle(
File("build/libs/").listFiles()!!.first {
it.name.startsWith("revanced-patches-") && it.name.endsWith(".jar")
}.absolutePath

View File

@@ -3,6 +3,7 @@ package app.revanced.meta.json
import app.revanced.meta.Bundle
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.dependencies
import app.revanced.patcher.extensions.PatchExtensions.deprecated
import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.include
import app.revanced.patcher.extensions.PatchExtensions.options
@@ -21,6 +22,7 @@ fun generateJson(bundle: Bundle) {
it.description ?: "This patch has no description.",
it.version ?: "0.0.0",
!it.include,
it.deprecated != null,
it.options?.map { option ->
Option(
option.key,

View File

@@ -7,6 +7,7 @@ data class JsonPatch(
val description: String,
val version: String,
val excluded: Boolean,
val deprecated: Boolean,
val options: Array<Option>,
val dependencies: Array<String>,
val compatiblePackages: Array<CompatiblePackage>,

View File

@@ -1,10 +1,10 @@
package app.revanced.meta.readme
import app.revanced.patcher.data.Context
import app.revanced.patcher.data.Data
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.patch.Patch
internal fun Class<out Patch<Context>>.getLatestVersion() =
internal fun Class<out Patch<Data>>.getLatestVersion() =
this.compatiblePackages?.first()?.versions?.map {
SemanticVersion.fromString(it)
}?.maxWithOrNull(SemanticVersionComparator)

View File

@@ -1,7 +1,7 @@
package app.revanced.meta.readme
import app.revanced.meta.Bundle
import app.revanced.patcher.data.Context
import app.revanced.patcher.data.Data
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.patchName
@@ -15,7 +15,7 @@ private val TABLE_REGEX = Regex("\\{\\{\\s?table\\s?}}")
fun generateText(bundle: Bundle) {
val output = StringBuilder()
val packages = mutableMapOf<String, MutableList<Class<out Patch<Context>>>>()
val packages = mutableMapOf<String, MutableList<Class<out Patch<Data>>>>()
for (patch in bundle) {
patch.compatiblePackages?.forEach { pkg ->
@@ -24,8 +24,8 @@ fun generateText(bundle: Bundle) {
}
}
for (pkg in packages.entries.sortedByDescending { it.value.size }) {
output.appendLine("### [\uD83D\uDCE6 `${pkg.key}`](https://play.google.com/store/apps/details?id=${pkg.key})")
for (pkg in packages) {
output.appendLine("### \uD83D\uDCE6 `${pkg.key}`")
output.appendLine("<details>\n")
output.appendLine(TABLE_HEADER)

View File

@@ -1,37 +0,0 @@
package app.revanced.patches.all.interaction.gestures.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.Patch
@Patch(false)
@Name("predictive-back-gesture")
@Description("Enables the predictive back gesture introduced on Android 13.")
@Version("0.0.1")
class PredictiveBackGesturePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
with(document.getElementsByTagName("application").item(0)) {
if (attributes.getNamedItem(FLAG) != null) return@with
document.createAttribute(FLAG)
.apply { value = "true" }
.let(attributes::setNamedItem)
}
}
return PatchResultSuccess()
}
private companion object {
const val FLAG = "android:enableOnBackInvokedCallback"
}
}

View File

@@ -1,106 +0,0 @@
package app.revanced.patches.all.screenshot.removerestriction.patch
import app.revanced.extensions.findMutableMethodOf
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
import org.jf.dexlib2.iface.reference.MethodReference
@Patch(false)
@Name("remove-screenshot-restriction")
@Description("Removes the restriction of making screenshots.")
@Version("0.0.1")
class RemoveScreenshotRestrictionPatch : BytecodePatch() {
private companion object {
const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch;"
}
// Information about method calls we want to replace
private enum class MethodCall(
val definedClassName: String,
val methodName: String,
val replacementMethodDefinition: String
) {
SetFlags(
"Landroid/view/Window;",
"setFlags",
"setFlags(Landroid/view/Window;II)V",
);
fun replaceInstruction(method: MutableMethod, instruction: Instruction35c, instructionIndex: Int) {
when (this) {
SetFlags -> {
method.replaceInstruction(
instructionIndex,
"invoke-static { v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE} }, ${INTEGRATIONS_CLASS_DESCRIPTOR}->${replacementMethodDefinition}"
)
}
}
}
companion object {
fun fromMethodReference(methodReference: MethodReference) = values().firstOrNull { search ->
search.definedClassName == methodReference.definingClass && search.methodName == methodReference.name
}
}
}
override fun execute(context: BytecodeContext): PatchResult {
// Find all instructions where one of the methods is called
buildMap {
context.classes.forEach { classDef ->
if (classDef.type == INTEGRATIONS_CLASS_DESCRIPTOR) {
// avoid infinite recursion
return@forEach
}
classDef.methods.let { methods ->
buildMap methodList@{
methods.forEach methods@{ method ->
with(method.implementation?.instructions ?: return@methods) {
ArrayDeque<Triple<MethodCall, Instruction35c, Int>>().also { patchIndices ->
this.forEachIndexed { index, instruction ->
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@forEachIndexed
val invokeInstruction = instruction as Instruction35c
val methodRef = invokeInstruction.reference as MethodReference
val methodCall = MethodCall.fromMethodReference(methodRef) ?: return@forEachIndexed
patchIndices.add(Triple(methodCall, invokeInstruction, index))
}
}.also { if (it.isEmpty()) return@methods }.let { patches ->
put(method, patches)
}
}
}
}
}.also { if (it.isEmpty()) return@forEach }.let { methodPatches ->
put(classDef, methodPatches)
}
}
}.forEach { (classDef, methods) ->
// And finally replace the instructions...
with(context.proxy(classDef).mutableClass) {
methods.forEach { (method, patches) ->
val mutableMethod = findMutableMethodOf(method)
while (!patches.isEmpty()) {
val (methodType, instruction, instructionIndex) = patches.removeLast()
methodType.replaceInstruction(mutableMethod, instruction, instructionIndex)
}
}
}
}
return PatchResultSuccess()
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.backdrops.wallpapers")])
internal annotation class ProUnlockCompatibility

View File

@@ -1,15 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object ProUnlockFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ
),
customFingerprint = { it.definingClass == "Lcom/backdrops/wallpapers/data/local/DatabaseHandlerIAB;" && it.name == "lambda\$existPurchase\$0" }
)

View File

@@ -1,42 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.backdrops.misc.pro.annotations.ProUnlockCompatibility
import app.revanced.patches.backdrops.misc.pro.fingerprints.ProUnlockFingerprint
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch
@Name("pro-unlock")
@Description("Unlocks pro-only functions.")
@ProUnlockCompatibility
@Version("0.0.1")
class ProUnlockPatch : BytecodePatch(
listOf(ProUnlockFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = ProUnlockFingerprint.result ?: return PatchResultError("${ProUnlockFingerprint.name} not found")
val moveRegisterInstruction = result.mutableMethod.instruction(result.scanResult.patternScanResult!!.endIndex - 1)
val register = (moveRegisterInstruction as OneRegisterInstruction).registerA
result.mutableMethod.addInstructions(
result.scanResult.patternScanResult!!.endIndex,
"""
const/4 v$register, 0x1
"""
)
return PatchResultSuccess()
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.citra.misc.premium.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("org.citra.citra_emu"), Package("org.citra.citra_emu.canary")])
internal annotation class PremiumUnlockCompatbility

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.citra.misc.premium.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PremiumUnlockFingerprint : MethodFingerprint(
customFingerprint = { it.definingClass == "Lorg/citra/citra_emu/ui/main/MainActivity;" && it.name == "isPremiumActive" }
)

View File

@@ -1,37 +0,0 @@
package app.revanced.patches.citra.misc.premium.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.citra.misc.premium.annotations.PremiumUnlockCompatbility
import app.revanced.patches.citra.misc.premium.fingerprints.PremiumUnlockFingerprint
@Patch
@Name("premium-unlock")
@Description("Unlocks premium functions.")
@PremiumUnlockCompatbility
@Version("0.0.1")
class PremiumUnlockPatch : BytecodePatch(
listOf(PremiumUnlockFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = PremiumUnlockFingerprint.result ?: return PatchResultError("${PremiumUnlockFingerprint.name} not found")
result.mutableMethod.addInstructions(
0,
"""
const v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -1,13 +0,0 @@
package app.revanced.patches.hexeditor.ad.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[
Package("com.myprog.hexedit")
]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class HexEditorAdsCompatibility

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.hexeditor.ad.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PrimaryAdsFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("PreferencesHelper;") && methodDef.name == "isAdsDisabled"
}
)

View File

@@ -1,39 +0,0 @@
package app.revanced.patches.hexeditor.ad.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.hexeditor.ad.annotations.HexEditorAdsCompatibility
import app.revanced.patches.hexeditor.ad.fingerprints.PrimaryAdsFingerprint
@Patch
@Name("disable-ads")
@Description("Disables ads in HexEditor.")
@HexEditorAdsCompatibility
@Version("0.0.1")
class HexEditorAdsPatch : BytecodePatch(
listOf(
PrimaryAdsFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = PrimaryAdsFingerprint.result!!.mutableMethod
method.replaceInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("ginlemon.iconpackstudio")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@@ -1,8 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object CheckProFingerprint : MethodFingerprint(
"Z",
customFingerprint = { it.definingClass.endsWith("IPSPurchaseRepository;")}
)

View File

@@ -1,38 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.iconpackstudio.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.iconpackstudio.misc.pro.fingerprints.CheckProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
CheckProFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = CheckProFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.moneymanager.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.ithebk.expensemanager")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@@ -1,19 +0,0 @@
package app.revanced.patches.moneymanager.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object UnlockProFingerprint : MethodFingerprint(
"Z",
AccessFlags.STATIC or AccessFlags.SYNTHETIC,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IGET_BOOLEAN,
Opcode.RETURN
),
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("MainActivity;")
}
)

View File

@@ -1,34 +0,0 @@
package app.revanced.patches.moneymanager.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.moneymanager.annotations.UnlockProCompatibility
import app.revanced.patches.moneymanager.fingerprints.UnlockProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(UnlockProFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,11 +1,23 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("show-video-ads-constructor-fingerprint")
@MatchingMethod(
"Laghd;",
"<init>",
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@MusicVideoAdsCompatibility
@Version("0.0.1")
object ShowMusicVideoAdsConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,

View File

@@ -1,10 +1,22 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("show-video-ads-method-fingerprint")
@MatchingMethod(
"Laghd;", "d"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@MusicVideoAdsCompatibility
@Version("0.0.1")
object ShowMusicVideoAdsFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), listOf(
Opcode.IPUT_BOOLEAN,

View File

@@ -3,13 +3,13 @@ package app.revanced.patches.music.ad.video.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsConstructorFingerprint
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsFingerprint
@@ -24,13 +24,13 @@ class MusicVideoAdsPatch : BytecodePatch(
ShowMusicVideoAdsConstructorFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
ShowMusicVideoAdsFingerprint.resolve(context, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
override fun execute(data: BytecodeData): PatchResult {
ShowMusicVideoAdsFingerprint.resolve(data, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
val result = ShowMusicVideoAdsFingerprint.result!!
result.mutableMethod.addInstructions(
result.scanResult.patternScanResult!!.startIndex, """
result.patternScanResult!!.startIndex, """
const/4 p1, 0x0
"""
)

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,13 +1,23 @@
package app.revanced.patches.music.audio.codecs.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher. fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("all-codecs-reference-fingerprint")
@MatchingMethod(
"Laari;",
"b",
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@CodecsUnlockCompatibility
@Version("0.0.1")
object AllCodecsReferenceFingerprint : MethodFingerprint(
"J", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
Opcode.INVOKE_STATIC,

View File

@@ -1,13 +1,23 @@
package app.revanced.patches.music.audio.codecs.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("codec-lock-fingerprint")
@MatchingMethod(
"Lacfr;",
"a",
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@CodecsUnlockCompatibility
@Version("0.0.1")
object CodecsLockFingerprint : MethodFingerprint(
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, opcodes = listOf(
Opcode.INVOKE_DIRECT,
@@ -22,7 +32,7 @@ object CodecsLockFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_VIRTUAL,
Opcode.INVOKE_DIRECT,
Opcode.RETURN_OBJECT
),
strings = listOf("eac3_supported")

View File

@@ -3,12 +3,12 @@ package app.revanced.patches.music.audio.codecs.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.toMethodWalker
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.toMethodWalker
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
import app.revanced.patches.music.audio.codecs.fingerprints.AllCodecsReferenceFingerprint
@@ -25,12 +25,12 @@ class CodecsUnlockPatch : BytecodePatch(
CodecsLockFingerprint, AllCodecsReferenceFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val codecsLockResult = CodecsLockFingerprint.result!!
val implementation = codecsLockResult.mutableMethod.implementation!!
val scanResultStartIndex = codecsLockResult.scanResult.patternScanResult!!.startIndex
val scanResultStartIndex = codecsLockResult.patternScanResult!!.startIndex
val instructionIndex = scanResultStartIndex +
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
// for 5.16.xx and lower
@@ -42,8 +42,8 @@ class CodecsUnlockPatch : BytecodePatch(
val allCodecsResult = AllCodecsReferenceFingerprint.result!!
val allCodecsMethod =
context.toMethodWalker(allCodecsResult.method)
.nextMethod(allCodecsResult.scanResult.patternScanResult!!.startIndex)
data.toMethodWalker(allCodecsResult.method)
.nextMethod(allCodecsResult.patternScanResult!!.startIndex)
.getMethod()
implementation.replaceInstruction(

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,10 +1,25 @@
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("audio-only-enabler-fingerprint")
@MatchingMethod(
"Lgmd;",
"d"
)
@DirectPatternScanMethod
@ExclusiveAudioCompatibility
@Version(
"0.0.1"
)
object AudioOnlyEnablerFingerprint: MethodFingerprint(
"Z", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IGET_OBJECT,

View File

@@ -1,13 +1,22 @@
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("exclusive-audio-fingerprints")
@MatchingMethod(
"Lgmd;", "c"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@ExclusiveAudioCompatibility
@Version("0.0.1")
object ExclusiveAudioFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,

View File

@@ -3,15 +3,17 @@ package app.revanced.patches.music.audio.exclusiveaudio.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.AudioOnlyEnablerFingerprint
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.ExclusiveAudioFingerprint
@Patch
@Name("exclusive-audio-playback")
@@ -23,7 +25,7 @@ class ExclusiveAudioPatch : BytecodePatch(
AudioOnlyEnablerFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val method = AudioOnlyEnablerFingerprint.result!!.mutableMethod
method.replaceInstruction(method.implementation!!.instructions.count() - 1, "const/4 v0, 0x1")
method.addInstruction("return v0")

View File

@@ -5,25 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,10 +1,20 @@
package app.revanced.patches.music.layout.compactheader.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.compactheader.annotations.CompactHeaderCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("compact-header-constructor-fingerprint")
@MatchingMethod(
"Llcz;", "<init>"
)
@CompactHeaderCompatibility
@Version("0.0.1")
object CompactHeaderConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,

View File

@@ -3,12 +3,12 @@ package app.revanced.patches.music.layout.compactheader.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.compactheader.annotations.CompactHeaderCompatibility
import app.revanced.patches.music.layout.compactheader.fingerprints.CompactHeaderConstructorFingerprint
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
@@ -23,11 +23,11 @@ class CompactHeaderPatch : BytecodePatch(
CompactHeaderConstructorFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val result = CompactHeaderConstructorFingerprint.result!!
val method = result.mutableMethod
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val insertIndex = result.patternScanResult!!.endIndex
val register = (method.implementation!!.instructions[insertIndex - 1] as BuilderInstruction11x).registerA
method.addInstructions(
insertIndex, """

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,10 +1,20 @@
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("minimized-playback-manager-fingerprint")
@MatchingMethod(
"Llxw;", "c"
)
@MinimizedPlaybackCompatibility
@Version("0.0.1")
object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,

View File

@@ -3,14 +3,18 @@ package app.revanced.patches.music.layout.minimizedplayback.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.music.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@Name("minimized-playback-music")
@@ -22,7 +26,7 @@ class MinimizedPlaybackPatch : BytecodePatch(
MinimizedPlaybackManagerFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
MinimizedPlaybackManagerFingerprint.result!!.mutableMethod.addInstructions(
0, """
return-void

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,10 +1,20 @@
package app.revanced.patches.music.layout.premium.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("hide-get-premium-fingerprint")
@MatchingMethod(
"Lktn;", "k"
)
@HideGetPremiumCompatibility
@Version("0.0.1")
object HideGetPremiumFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IF_NEZ,

View File

@@ -1,10 +1,20 @@
package app.revanced.patches.music.layout.premium.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("hide-get-premium-parent-fingerprint")
@MatchingMethod(
"Lktn;", "k"
)
@HideGetPremiumCompatibility
@Version("0.0.1")
object HideGetPremiumParentFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IGET_BOOLEAN,

View File

@@ -3,14 +3,14 @@ package app.revanced.patches.music.layout.premium.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumFingerprint
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumParentFingerprint
@@ -25,15 +25,13 @@ class HideGetPremiumPatch : BytecodePatch(
HideGetPremiumParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val parentResult = HideGetPremiumParentFingerprint.result!!
HideGetPremiumFingerprint.resolve(context, parentResult.classDef)
val startIndex = parentResult.scanResult.patternScanResult!!.startIndex
HideGetPremiumFingerprint.resolve(data, parentResult.classDef)
val parentMethod = parentResult.mutableMethod
parentMethod.replaceInstruction(
startIndex, """
parentResult.patternScanResult!!.startIndex, """
const/4 v1, 0x0
"""
)
@@ -41,7 +39,7 @@ class HideGetPremiumPatch : BytecodePatch(
val result = HideGetPremiumFingerprint.result!!
val method = result.mutableMethod
method.addInstructions(
startIndex, """
result.patternScanResult!!.startIndex, """
const/16 v0, 0x8
"""
)

View File

@@ -10,23 +10,7 @@ import app.revanced.patcher.annotation.Package
*/
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,13 +1,22 @@
package app.revanced.patches.music.layout.tastebuilder.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("taste-builder-constructor-fingerprint")
@MatchingMethod(
"Lkyu;", "<init>"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@RemoveTasteBuilderCompatibility
@Version("0.0.1")
object TasteBuilderConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,

View File

@@ -3,12 +3,12 @@ package app.revanced.patches.music.layout.tastebuilder.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
import app.revanced.patches.music.layout.tastebuilder.fingerprints.TasteBuilderConstructorFingerprint
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
@@ -23,11 +23,11 @@ class RemoveTasteBuilderPatch : BytecodePatch(
TasteBuilderConstructorFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val result = TasteBuilderConstructorFingerprint.result!!
val method = result.mutableMethod
val insertIndex = result.scanResult.patternScanResult!!.endIndex - 8
val insertIndex = result.patternScanResult!!.endIndex - 8
val register = (method.implementation!!.instructions[insertIndex] as Instruction22c).registerA
method.addInstructions(
insertIndex, """

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,13 +1,22 @@
package app.revanced.patches.music.layout.upgradebutton.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("pivot-bar-constructor-fingerprint")
@MatchingMethod(
"Lhfu;", "<init2>"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@RemoveUpgradeButtonCompatibility
@Version("0.0.1")
object PivotBarConstructorFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,

View File

@@ -3,12 +3,12 @@ package app.revanced.patches.music.layout.upgradebutton.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
import app.revanced.patches.music.layout.upgradebutton.fingerprints.PivotBarConstructorFingerprint
@@ -28,12 +28,12 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
PivotBarConstructorFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val result = PivotBarConstructorFingerprint.result!!
val implementation = result.mutableMethod.implementation!!
val pivotBarElementFieldRef =
(implementation.instructions[result.scanResult.patternScanResult!!.endIndex - 1] as Instruction22c).reference
(implementation.instructions[result.patternScanResult!!.endIndex - 1] as Instruction22c).reference
val register = (implementation.instructions.first() as Instruction35c).registerC
// first compile all the needed instructions
@@ -46,18 +46,16 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
""".toInstructions().toMutableList()
val endIndex = result.scanResult.patternScanResult!!.endIndex
// replace the instruction to retain the label at given index
implementation.replaceInstruction(
endIndex - 1, instructionList[0] // invoke-interface
result.patternScanResult!!.endIndex - 1, instructionList[0] // invoke-interface
)
// do not forget to remove this instruction since we added it already
instructionList.removeFirst()
val exitInstruction = instructionList.last() // iput-object
implementation.addInstruction(
endIndex, exitInstruction
result.patternScanResult!!.endIndex, exitInstruction
)
// do not forget to remove this instruction since we added it already
instructionList.removeLast()
@@ -66,12 +64,12 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
instructionList.add(
2, // if-le
BuilderInstruction22t(
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(endIndex)
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(result.patternScanResult!!.endIndex)
)
)
implementation.addInstructions(
endIndex, instructionList
result.patternScanResult!!.endIndex, instructionList
)
return PatchResultSuccess()
}

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,8 +1,19 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
@Name("cast-context-fetch-fingerprint")
@MatchingMethod(
"Lvvz;", "a"
)
@DirectPatternScanMethod
@MicroGPatchCompatibility
@Version("0.0.1")
object CastContextFetchFingerprint : MethodFingerprint(
strings = listOf("Error fetching CastContext.")
)

View File

@@ -1,8 +1,19 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
@Name("cast-module-fingerprint")
@MatchingMethod(
"Llqh;", "c"
)
@DirectPatternScanMethod
@MicroGPatchCompatibility
@Version("0.0.1")
object CastDynamiteModuleFingerprint : MethodFingerprint(
strings = listOf("com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl")
)

View File

@@ -1,8 +1,19 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
@Name("cast-context-fetch-fingerprint")
@MatchingMethod(
"Lmcf;", "c"
)
@DirectPatternScanMethod
@MicroGPatchCompatibility
@Version("0.0.1")
object CastDynamiteModuleV2Fingerprint : MethodFingerprint(
strings = listOf("Failed to load module via V2: ")
)

View File

@@ -1,9 +1,21 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import org.jf.dexlib2.AccessFlags
@Name("google-play-utility-fingerprint")
@MatchingMethod(
"Lnuv;", "b"
)
@DirectPatternScanMethod
@MusicMicroGPatchCompatibility
@Version("0.0.1")
object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,

View File

@@ -1,8 +1,19 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
@Name("google-play-prime-fingerprint")
@MatchingMethod(
"Lrwi;", "a"
)
@DirectPatternScanMethod
@MusicMicroGPatchCompatibility
@Version("0.0.1")
object PrimeFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
)

View File

@@ -1,12 +1,21 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import org.jf.dexlib2.AccessFlags
@Name("google-play-service-checker-fingerprint")
@MatchingMethod(
"Lnuv;", "d"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@MusicMicroGPatchCompatibility
@Version("0.0.1")
object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,

View File

@@ -1,27 +1,37 @@
package app.revanced.patches.music.misc.microg.patch.bytecode
import app.revanced.extensions.equalsAny
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.fingerprints.*
import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.patch.resource.enum.StringReplaceMode
import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants
import app.revanced.util.microg.MicroGBytecodeHelper
import app.revanced.patches.music.misc.microg.fingerprints.*
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
@Patch
@DependsOn([MusicMicroGResourcePatch::class])
@Name("music-microg-support")
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
@Version("0.0.2")
@Version("0.0.1")
class MusicMicroGBytecodePatch : BytecodePatch(
listOf(
ServiceCheckFingerprint,
@@ -32,34 +42,130 @@ class MusicMicroGBytecodePatch : BytecodePatch(
PrimeFingerprint,
)
) {
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
// - "com.google.android.gms.chimera.GmsIntentOperationService",
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
// - "com.google.android.gms.phenotype.UPDATE",
// - "com.google.android.gms.phenotype",
override fun execute(context: BytecodeContext) =
// apply common microG patch
MicroGBytecodeHelper.patchBytecode(
context,
arrayOf(
MicroGBytecodeHelper.packageNameTransform(
Constants.PACKAGE_NAME,
Constants.REVANCED_PACKAGE_NAME
)
),
MicroGBytecodeHelper.PrimeMethodTransformationData(
PrimeFingerprint,
MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_PACKAGE_NAME
),
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
override fun execute(data: BytecodeData): PatchResult {
disablePlayServiceChecks()
data.classes.forEach { classDef ->
var proxiedClass: MutableClass? = null
classDef.methods.forEach methodLoop@{ method ->
val implementation = method.implementation ?: return@methodLoop
var proxiedImplementation: MutableMethodImplementation? = null
implementation.instructions.forEachIndexed { i, instruction ->
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
val stringValue = ((instruction as Instruction21c).reference as StringReference).string
val replaceMode = if (stringValue.equalsAny(
"com.google.android.gms",
"com.google.android.gms.chimera",
"com.google.android.c2dm.intent.REGISTER",
"com.google.android.c2dm.permission.SEND",
"com.google.iid.TOKEN_REQUEST",
"com.google",
"com.google.android.gms.chimera.GmsIntentOperationService",
"com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
"com.google.android.gms.phenotype.internal.IPhenotypeService",
"com.google.android.gms.phenotype.service.START",
"com.google.android.gms.phenotype.PACKAGE_NAME",
"com.google.android.gms.phenotype.UPDATE",
"com.google.android.gms.phenotype",
"com.google.android.gms.auth.accounts",
"com.google.android.c2dm.intent.REGISTRATION",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.gsf.login",
"content://com.google.settings/partner",
"content://com.google.android.gms.phenotype/",
"content://com.google.android.gsf.gservices",
"content://com.google.android.gsf.gservices/prefix",
"com.google.android.c2dm.intent.RECEIVE"
)
) {
StringReplaceMode.REPLACE_WITH_MICROG
} else if (stringValue.equalsAny(
"com.google.android.apps.youtube.music.SuggestionsProvider", "com.google.android.apps.youtube.music.fileprovider"
)
) {
StringReplaceMode.REPLACE_WITH_REVANCED
} else {
StringReplaceMode.DO_NOT_REPLACE
}
if (replaceMode != StringReplaceMode.DO_NOT_REPLACE) {
if (proxiedClass == null) {
proxiedClass = data.proxy(classDef).resolve()
}
if (proxiedImplementation == null) {
proxiedImplementation = proxiedClass!!.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
}.implementation!!
}
val newString = if (replaceMode == StringReplaceMode.REPLACE_WITH_REVANCED) stringValue.replace(
"com.google.android.apps.youtube.music", REVANCED_MUSIC_PACKAGE_NAME
)
else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME)
proxiedImplementation!!.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString)
)
)
}
}
}
}
return PatchResultSuccess()
}
private fun disablePlayServiceChecks() {
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
).forEach { fingerprint ->
val result = fingerprint.result!!
val stringInstructions = when (result.method.returnType.first()) {
'L' -> """
const/4 v0, 0x0
return-object v0
"""
'V' -> "return-void"
'I' -> """
const/4 v0, 0x0
return v0
"""
else -> throw Exception("This case should never happen.")
}
result.mutableMethod.addInstructions(
0, stringInstructions
)
).let { PatchResultSuccess() }
}
val primeMethod = PrimeFingerprint.result!!.mutableMethod
val implementation = primeMethod.implementation!!
var register = 2
val index = implementation.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false
val instructionString = ((it as Instruction21c).reference as StringReference).string
if (instructionString != "com.google.android.apps.youtube.music") return@indexOfFirst false
register = it.registerA
return@indexOfFirst true
}
primeMethod.replaceInstruction(
index, "const-string v$register, \"$REVANCED_MUSIC_PACKAGE_NAME\""
)
}
}

View File

@@ -1,41 +1,44 @@
package app.revanced.patches.music.misc.microg.patch.resource
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.util.microg.MicroGManifestHelper
import app.revanced.util.microg.MicroGResourceHelper
@Name("music-microg-resource-patch")
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
@Version("0.0.2")
class MusicMicroGResourcePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
// update manifest
MicroGResourceHelper.patchManifest(
context,
MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_APP_NAME
)
// add metadata to the manifest
MicroGManifestHelper.addSpoofingMetadata(
context,
SPOOFED_PACKAGE_NAME,
SPOOFED_PACKAGE_SIGNATURE
)
return PatchResultSuccess()
}
package app.revanced.patches.music.misc.microg.patch.resource
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
@Name("music-microg-resource-patch")
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
@Version("0.0.1")
class MusicMicroGResourcePatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
val manifest = data["AndroidManifest.xml"].readText()
data["AndroidManifest.xml"].writeText(
manifest.replace(
"package=\"com.google.android.apps.youtube.music", "package=\"$REVANCED_MUSIC_PACKAGE_NAME"
).replace(
"android:label=\"@string/app_name", "android:label=\"$REVANCED_MUSIC_APP_NAME"
).replace(
"android:label=\"@string/app_launcher_name", "android:label=\"$REVANCED_MUSIC_APP_NAME"
).replace(
"android:authorities=\"com.google.android.apps.youtube.music", "android:authorities=\"$REVANCED_MUSIC_PACKAGE_NAME"
).replace(
"com.google.android.apps.youtube.music.permission.C2D_MESSAGE", "$REVANCED_MUSIC_PACKAGE_NAME.permission.C2D_MESSAGE"
).replace(
"com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm"
).replace(
"</queries>", "<package android:name=\"$BASE_MICROG_PACKAGE_NAME.android.gms\"/></queries>"
)
)
return PatchResultSuccess()
}
}

View File

@@ -1,9 +1,7 @@
package app.revanced.patches.music.misc.microg.shared
object Constants {
internal const val REVANCED_MUSIC_APP_NAME = "YT Music ReVanced"
internal const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle"
internal const val REVANCED_MUSIC_APP_NAME = "YouTube Music ReVanced"
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
}
}

View File

@@ -5,26 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51",
"5.38.53",
"5.39.52"
)
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50")
)]
)
@Target(AnnotationTarget.CLASS)

View File

@@ -1,13 +1,22 @@
package app.revanced.patches.music.premium.backgroundplay.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("background-playback-disabler-fingerprint")
@MatchingMethod(
"Lafgf;", "e"
)
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@BackgroundPlayCompatibility
@Version("0.0.1")
object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
Opcode.CONST_4,

View File

@@ -3,12 +3,12 @@ package app.revanced.patches.music.premium.backgroundplay.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
import app.revanced.patches.music.premium.backgroundplay.fingerprints.BackgroundPlaybackDisableFingerprint
@@ -22,7 +22,7 @@ class BackgroundPlayPatch : BytecodePatch(
BackgroundPlaybackDisableFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
BackgroundPlaybackDisableFingerprint.result!!.mutableMethod.addInstructions(
0,
"""

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("org.totschnig.myexpenses")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@@ -1,8 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object IsEnabledFingerprint : MethodFingerprint(
"Z",
strings = listOf("feature", "feature.licenceStatus")
)

View File

@@ -1,38 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.myexpenses.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.myexpenses.misc.pro.fingerprints.IsEnabledFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all professional features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
IsEnabledFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = IsEnabledFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.nyx.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.awedea.nyx")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.nyx.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object CheckProFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("BillingManager;") && methodDef.name == "isProVersion"
}
)

View File

@@ -1,38 +0,0 @@
package app.revanced.patches.nyx.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.nyx.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.nyx.misc.pro.fingerprints.CheckProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
CheckProFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = CheckProFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@@ -3,11 +3,7 @@ package app.revanced.patches.reddit.ad.general.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.reddit.frontpage", arrayOf("2022.43.0")
)]
)
@Compatibility([Package("com.reddit.frontpage")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class GeneralAdsCompatibility

View File

@@ -3,11 +3,12 @@ package app.revanced.patches.reddit.ad.general.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.reddit.ad.general.annotations.GeneralAdsCompatibility
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
@@ -21,8 +22,8 @@ import org.jf.dexlib2.immutable.reference.ImmutableStringReference
@GeneralAdsCompatibility
@Version("0.0.1")
class GeneralAdsPatch : BytecodePatch() {
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
override fun execute(data: BytecodeData): PatchResult {
data.classes.forEach { classDef ->
classDef.methods.forEach methodLoop@{ method ->
val implementation = method.implementation ?: return@methodLoop
@@ -30,7 +31,7 @@ class GeneralAdsPatch : BytecodePatch() {
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed
val proxiedClass = context.proxy(classDef).mutableClass
val proxiedClass = data.proxy(classDef).resolve()
val proxiedImplementation = proxiedClass.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)

View File

@@ -1,7 +1,17 @@
package app.revanced.patches.reddit.layout.premiumicon.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.reddit.layout.premiumicon.annotations.PremiumIconCompatibility
@Name("premium-icon-fingerprint")
@MatchingMethod(
"Lcom/reddit/domain/model/MyAccount;", "isPremiumSubscriber"
)
@PremiumIconCompatibility
@Version("0.0.1")
object PremiumIconFingerprint : MethodFingerprint(
"Z",
customFingerprint = { methodDef ->

View File

@@ -3,18 +3,18 @@ package app.revanced.patches.reddit.layout.premiumicon.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.reddit.layout.premiumicon.annotations.PremiumIconCompatibility
import app.revanced.patches.reddit.layout.premiumicon.fingerprints.PremiumIconFingerprint
@Patch
@Name("premium-icon-reddit")
@Description("Unlocks premium Reddit app icons.")
@Description("Unlocking Premium Icons in reddit app.")
@PremiumIconCompatibility
@Version("0.0.1")
class PremiumIconPatch : BytecodePatch(
@@ -22,7 +22,7 @@ class PremiumIconPatch : BytecodePatch(
PremiumIconFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
override fun execute(data: BytecodeData): PatchResult {
val method = PremiumIconFingerprint.result!!.mutableMethod
method.addInstructions(
0,

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SeekbarFingerprint : MethodFingerprint(
"V",
strings = listOf("timed_markers_width")
)

View File

@@ -1,8 +0,0 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SeekbarOnDrawFingerprint : MethodFingerprint(
customFingerprint = { it.name == "onDraw" }
)

View File

@@ -1,10 +0,0 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object WatchWhileActivityFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "<init>"
}
)

View File

@@ -1,66 +0,0 @@
package app.revanced.patches.shared.integrations.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import org.jf.dexlib2.iface.Method
@Description("Applies mandatory patches to implement the ReVanced integrations into the application.")
@Version("0.0.1")
abstract class AbstractIntegrationsPatch(
private val integrationsDescriptor: String,
private val hooks: Iterable<IntegrationsFingerprint>
) : BytecodePatch(hooks) {
/**
* [MethodFingerprint] for integrations.
*
* @param contextRegisterResolver A [RegisterResolver] to get the register.
* @see MethodFingerprint
*/
abstract class IntegrationsFingerprint(
strings: Iterable<String>? = null,
customFingerprint: ((methodDef: Method) -> Boolean)? = null,
private val contextRegisterResolver: (Method) -> Int = object : RegisterResolver {}
) : MethodFingerprint(strings = strings, customFingerprint = customFingerprint) {
fun invoke(integrationsDescriptor: String): PatchResult {
result?.mutableMethod?.let { method ->
val contextRegister = contextRegisterResolver(method)
method.addInstruction(
0,
"sput-object v$contextRegister, " +
"$integrationsDescriptor->context:Landroid/content/Context;"
)
} ?: return PatchResultError("Could not find hook target fingerprint.")
return PatchResultSuccess()
}
interface RegisterResolver : (Method) -> Int {
override operator fun invoke(method: Method) = method.implementation!!.registerCount - 1
}
}
override fun execute(context: BytecodeContext): PatchResult {
if (context.findClass(integrationsDescriptor) == null) return MISSING_INTEGRATIONS
for (hook in hooks) hook.invoke(integrationsDescriptor).let {
if (it is PatchResultError) return it
}
return PatchResultSuccess()
}
private companion object {
val MISSING_INTEGRATIONS = PatchResultError(
"Integrations have not been merged yet. " +
"This patch can not succeed without merging the integrations."
)
}
}

View File

@@ -1,73 +0,0 @@
package app.revanced.patches.shared.mapping.misc.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import org.w3c.dom.Element
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@Name("resource-mapping")
@Description("Creates a map of public resources.")
@Version("0.0.1")
class ResourceMappingPatch : ResourcePatch {
companion object {
internal lateinit var resourceMappings: List<ResourceElement>
private set
private val THREAD_COUNT = Runtime.getRuntime().availableProcessors()
private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
}
override fun execute(context: ResourceContext): PatchResult {
// save the file in memory to concurrently read from
val resourceXmlFile = context["res/values/public.xml"].readBytes()
// create a synchronized list to store the resource mappings
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
for (threadIndex in 0 until THREAD_COUNT) {
threadPoolExecutor.execute thread@{
context.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
val resources = editor.file.documentElement.childNodes
val resourcesLength = resources.length
val jobSize = resourcesLength / THREAD_COUNT
val batchStart = jobSize * threadIndex
val batchEnd = jobSize * (threadIndex + 1)
element@ for (i in batchStart until batchEnd) {
// make sure to not to go out of bounds when rounding errors occur at calculating the jobSize
if (i >= resourcesLength) return@thread
val node = resources.item(i)
if (node !is Element) continue
val nameAttribute = node.getAttribute("name")
val typeAttribute = node.getAttribute("type")
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) continue
val id = node.getAttribute("id").substring(2).toLong(16)
mappings.add(ResourceElement(typeAttribute, nameAttribute, id))
}
}
}
}
threadPoolExecutor
.also { it.shutdown() }
.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
resourceMappings = mappings
return PatchResultSuccess()
}
}
data class ResourceElement(val type: String, val name: String, val id: Long)

View File

@@ -1,31 +0,0 @@
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Base preference class for all preferences.
*
* @param key The key of the preference.
* @param title The title of the preference.
*/
internal abstract class BasePreference(
override val key: String,
override val title: StringResource,
) : IPreference {
/**
* Serialize preference element to XML.
* Overriding methods should invoke super and operate on its return value.
* @param ownerDocument Target document to create elements from.
* @param resourceCallback Called when a resource has been processed.
*/
open fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)? = null): Element {
return ownerDocument.createElement(tag).apply {
if(key.isNotEmpty())
setAttribute("android:key", key)
setAttribute("android:title", "@string/${title.also { resourceCallback?.invoke(it) }.name}")
}
}
}

View File

@@ -1,26 +0,0 @@
package app.revanced.patches.shared.settings.preference
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Base resource class for all resources.
*
* @param name The name of the resource.
*/
internal abstract class BaseResource(
override val name: String
) : IResource {
/**
* Serialize resource element to XML.
* Overriding methods should invoke super and operate on its return value.
* @param ownerDocument Target document to create elements from.
* @param resourceCallback Called when a resource has been processed.
*/
open fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)? = null): Element {
return ownerDocument.createElement(tag).apply {
setAttribute("name", name)
}
}
}

View File

@@ -1,42 +0,0 @@
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Element
import org.w3c.dom.Node
/**
* Add a resource node child
*
* @param resource The resource to add.
* @param resourceCallback Called when a resource has been processed.
*/
internal fun Node.addResource(resource: BaseResource, resourceCallback: ((IResource) -> Unit)? = null) {
appendChild(resource.serialize(ownerDocument, resourceCallback))
}
/**
* Add a preference node child to the settings.
*
* @param preference The preference to add.
* @param resourceCallback Called when a resource has been processed.
*/
internal fun Node.addPreference(preference: BasePreference, resourceCallback: ((IResource) -> Unit)? = null) {
appendChild(preference.serialize(ownerDocument, resourceCallback))
}
internal fun Element.addSummary(summaryResource: StringResource?, summaryType: SummaryType = SummaryType.DEFAULT) =
summaryResource?.let { summary ->
setAttribute("android:${summaryType.type}", "@string/${summary.name}")
}
internal fun <T> Element.addDefault(default: T) {
default?.let {
setAttribute(
"android:defaultValue", when (it) {
is Boolean -> if (it) "true" else "false"
is String -> it
else -> throw IllegalArgumentException("Unsupported default value type: ${it::class.java.name}")
}
)
}
}

View File

@@ -1,16 +0,0 @@
package app.revanced.patches.shared.settings.preference
/**
* Resource
*/
internal interface IResource {
/**
* Name of the resource.
*/
val name: String
/**
* Tag name of the resource.
*/
val tag: String
}

View File

@@ -1,5 +0,0 @@
package app.revanced.patches.shared.settings.preference
enum class SummaryType(val type: String) {
DEFAULT("summary"), ON("summaryOn"), OFF("summaryOff")
}

View File

@@ -1,33 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.preference.BaseResource
import app.revanced.patches.shared.settings.preference.IResource
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Represents an array resource.
*
* @param name The name of the array resource.
* @param items The items of the array resource.
*/
internal data class ArrayResource(
override val name: String,
val items: List<StringResource>
) : BaseResource(name) {
override val tag = "string-array"
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("name", name)
items.forEach { item ->
resourceCallback?.invoke(item)
this.appendChild(ownerDocument.createElement("item").also { itemNode ->
itemNode.textContent = "@string/${item.name}"
})
}
}
}
}

View File

@@ -1,38 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addDefault
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* List preference.
*
* @param key The key of the list preference.
* @param title The title of the list preference.
* @param entries The human-readable entries of the list preference.
* @param entryValues The entry values of the list preference.
* @param default The default entry value of the list preference.
* @param summary The summary of the list preference.
*/
internal class ListPreference(
key: String,
title: StringResource,
val entries: ArrayResource,
val entryValues: ArrayResource,
val default: String? = null,
val summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "ListPreference"
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("android:entries", "@array/${entries.also { resourceCallback?.invoke(it) }.name}")
setAttribute("android:entryValues", "@array/${entryValues.also { resourceCallback?.invoke(it) }.name}")
addDefault(default)
addSummary(summary)
}
}
}

Some files were not shown because too many files have changed in this diff Show More