mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 18:03:55 +01:00
Compare commits
16 Commits
v1.0.0-dev
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c97feb1cd8 | ||
|
|
aa365d684a | ||
|
|
d1b1e5a6cd | ||
|
|
0e465eb7f8 | ||
|
|
5b2d834a3e | ||
|
|
0aabe37152 | ||
|
|
89bb26b09c | ||
|
|
4e85cc4cbb | ||
|
|
771f237990 | ||
|
|
4d17ff4fb4 | ||
|
|
7a0ae2d56a | ||
|
|
71a08ca5eb | ||
|
|
4ba5b324a6 | ||
|
|
05ee34c756 | ||
|
|
73adc6f98c | ||
|
|
eb97257c41 |
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -28,12 +28,25 @@ jobs:
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew build clean
|
||||
run: ./gradlew build
|
||||
- name: Install Android build-tools
|
||||
run: sdkmanager "build-tools;32.0.0"
|
||||
- name: Generate Patches DEX
|
||||
run: |
|
||||
mkdir -p bin
|
||||
fn=$(find build/libs -maxdepth 1 | grep -P "[\d]+\.jar")
|
||||
out=bin/$(basename "$fn" | sed 's/jar/dex/g')
|
||||
$ANDROID_HOME/build-tools/32.0.0/d8 $fn
|
||||
mv *.dex $out
|
||||
- name: Clean build
|
||||
run: ./gradlew clean
|
||||
- name: Setup semantic-release
|
||||
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||
- name: Release
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
"assets": [
|
||||
{
|
||||
"path": "build/libs/*.jar"
|
||||
},
|
||||
{
|
||||
"path": "bin/*.dex"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
130
CHANGELOG.md
130
CHANGELOG.md
@@ -1,3 +1,133 @@
|
||||
# [1.1.0](https://github.com/revanced/revanced-patches/compare/v1.0.0...v1.1.0) (2022-06-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* generate & upload dex files ([#18](https://github.com/revanced/revanced-patches/issues/18)) ([e6f5355](https://github.com/revanced/revanced-patches/commit/e6f53553a98c164c4eb926fb273358ed506e00a4))
|
||||
|
||||
# 1.0.0 (2022-06-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `create-button-signature` ([a173f6e](https://github.com/revanced/revanced-patches/commit/a173f6e5a7e65943657e2072e8a72a4a680e5277))
|
||||
* `enable-seekbar-tapping` patch ([52fd726](https://github.com/revanced/revanced-patches/commit/52fd726d9b0d2efbd0f9742fc84ad01ccdcff168))
|
||||
* `Index` in wrong package ([2f9360f](https://github.com/revanced/revanced-patches/commit/2f9360f57cc8415564534fbbd8bd5e2a83a1b629))
|
||||
* `minimized-playback` & `old-quality-layout` wrong opcodes ([b45d175](https://github.com/revanced/revanced-patches/commit/b45d175c6f1ece6da894ab16128c2644d262c9c7))
|
||||
* `minimized-playback` patch ([55677a4](https://github.com/revanced/revanced-patches/commit/55677a44ff965c0b92c3f1d771bd68c12c142ad4))
|
||||
* `SignatureChecker` not handling nullable field `methodMetadata` ([17bcf78](https://github.com/revanced/revanced-patches/commit/17bcf786a85ccf1f7d9f5a66a044a3c26def09bb))
|
||||
* accidentally removed code in refactor ([0077e26](https://github.com/revanced/revanced-patches/commit/0077e26d23cc112b671a41614a55348fac2c88ca))
|
||||
* add missing `trimIndent()` to string literals ([76d3c71](https://github.com/revanced/revanced-patches/commit/76d3c71b67edebd79f2cdb1bb28e4d2969d72223))
|
||||
* add missing opcode for `create-button-method` ([0a398ef](https://github.com/revanced/revanced-patches/commit/0a398ef364f91a0dd9608df1a036a2515476ccf2))
|
||||
* attempt on all patches ([3395d69](https://github.com/revanced/revanced-patches/commit/3395d69747103a4bdf314297aa0bfa6ef6a0fc36))
|
||||
* breaking changes by `revanced-patcher` dependency ([e12e484](https://github.com/revanced/revanced-patches/commit/e12e484e3796c5c9c8505b677838cdf8432f2e79))
|
||||
* breaking changes by `revanced-patcher` dependency ([7e485b4](https://github.com/revanced/revanced-patches/commit/7e485b4ffe204d724809aeb9bd9f693a35ded94d))
|
||||
* breaking changes of the patcher ([1a49bbd](https://github.com/revanced/revanced-patches/commit/1a49bbdbc4ff6f427934259536218e161908b449))
|
||||
* breaking patcher changes ([50f9cc5](https://github.com/revanced/revanced-patches/commit/50f9cc52acfd5bc23330ecd23d8d85678a9d3eee))
|
||||
* breaking patcher changes ([cbb9e2c](https://github.com/revanced/revanced-patches/commit/cbb9e2cd1fa829e1d1dd92dbd40131b11ae6a05b))
|
||||
* breaking patcher changes ([581d1b0](https://github.com/revanced/revanced-patches/commit/581d1b0ca7d15adcdb1ab6116ef035acfe701757))
|
||||
* bugfixes in `microg` ([a43b33b](https://github.com/revanced/revanced-patches/commit/a43b33bdbb2b36e0a8f991fa11dfeeec34de01f9))
|
||||
* clean after building ([a2df3fb](https://github.com/revanced/revanced-patches/commit/a2df3fbc9761b07f3010542fa8684ade00e4dc91))
|
||||
* disable `hide-suggestions-patch` patch until fixed ([99099ea](https://github.com/revanced/revanced-patches/commit/99099ea0bc12f5f25896967db642442df69d0c4f))
|
||||
* incorrect endIndex (fixed in Patcher) ([424788e](https://github.com/revanced/revanced-patches/commit/424788edd777110cdaff97500556d18628f33385))
|
||||
* loop in `amoled` patch ([c4c86b6](https://github.com/revanced/revanced-patches/commit/c4c86b65fd8b2463c1d86ad2e46ec9f08e60d47c))
|
||||
* migrate patches to latest patcher api changes ([8a0ee03](https://github.com/revanced/revanced-patches/commit/8a0ee03a71cf4a000c9a7246d0e64ed8291a5127))
|
||||
* missing extension method `doRecursively` ([e9c9460](https://github.com/revanced/revanced-patches/commit/e9c946008ee912652d288e515b83b52ae2d239d8))
|
||||
* modified opcode for `show-video-ads-constructor` ([a0dcea3](https://github.com/revanced/revanced-patches/commit/a0dcea3a13f68cae449dfaf445b542e339c83ff0))
|
||||
* multiple bugs in patches ([e37201d](https://github.com/revanced/revanced-patches/commit/e37201d0ceef474696857a0d8845950c888194d0))
|
||||
* name for `IntegrationsPatch` ([e46ef02](https://github.com/revanced/revanced-patches/commit/e46ef02302825d62b57912b2747a25f858036bb7))
|
||||
* print instruction index of warning ([9e29aee](https://github.com/revanced/revanced-patches/commit/9e29aeeeff222412f6c45cf7e4879f8ec53ca6ee))
|
||||
* publish releases ([83916f9](https://github.com/revanced/revanced-patches/commit/83916f96d27989dcbb35c0ba6ef326a16b470501))
|
||||
* remove `HideSuggestionsPatch` from `Index` ([f32e474](https://github.com/revanced/revanced-patches/commit/f32e4747b512c675b807ff5eebfd0b8e66173fba))
|
||||
* remove unused patches ([d12e92a](https://github.com/revanced/revanced-patches/commit/d12e92aead677fefa9dcb48748d783225b65fab1))
|
||||
* signature checker with changes of patcher ([e82459d](https://github.com/revanced/revanced-patches/commit/e82459d37759e1a5a860d3e7fcdf69d95b06858e))
|
||||
* spelling mistake ([52f9147](https://github.com/revanced/revanced-patches/commit/52f9147ee8d591f786397d174dc02a141d9250a9))
|
||||
* tests failing ([102793f](https://github.com/revanced/revanced-patches/commit/102793f24f8bf7c7fd254968b29d65da7b2b962f))
|
||||
* update `HomeAdsPatch` ([62f1801](https://github.com/revanced/revanced-patches/commit/62f1801e9cbee53c0be3413c245161bd941e4aec))
|
||||
* update patches ([91b8ec8](https://github.com/revanced/revanced-patches/commit/91b8ec81f33417798546c32db708fe09ada3930c))
|
||||
* use the latest version of patcher dependency ([fe4a439](https://github.com/revanced/revanced-patches/commit/fe4a439cb2bc5e385ae13e8e155f25bb15e74633))
|
||||
* version in package metadata of music ([b299205](https://github.com/revanced/revanced-patches/commit/b299205aa7cde82f1f55fc598de3ff8d80b8bcb0))
|
||||
* **VideoAds:** remove `istore1` opcode ([dc4ec57](https://github.com/revanced/revanced-patches/commit/dc4ec574414c5df959efa0ca8f1cd39a812fedf8))
|
||||
* write while reading resources and remove checking for "." in resource extensions ([7bc6094](https://github.com/revanced/revanced-patches/commit/7bc60943cb2350e89dac091ec9c98c5effd0b8a9))
|
||||
* wrong access flag in signature for `Create button patch` ([9fbb89d](https://github.com/revanced/revanced-patches/commit/9fbb89d05336a256a0759eea6095e073946c45e5))
|
||||
* wrong annotation and signature in patches ([a0fdee8](https://github.com/revanced/revanced-patches/commit/a0fdee81a6d6773603520e7c3040ae8637642d58))
|
||||
* wrong opcode for `create-button-method` ([3214650](https://github.com/revanced/revanced-patches/commit/32146506f139aebc44cd5faffb7706b8b9c21c3d))
|
||||
* wrong opcode pattern for `create-button-method` ([f4d8a85](https://github.com/revanced/revanced-patches/commit/f4d8a8525bc64b90748b21979d463977a21dcd85))
|
||||
* wrong opcode pattern for `enable-seekbar-tapping-signature` ([1d83395](https://github.com/revanced/revanced-patches/commit/1d833957ed3e01188770c85e3d84e483419bd797))
|
||||
* wrong opcode pattern for signature in `Hide suggestions patch` ([535aee0](https://github.com/revanced/revanced-patches/commit/535aee08408b990c80f5966c13fa84666a8b35d0))
|
||||
* wrong patches in `upgrade-tab-remover` ([5182290](https://github.com/revanced/revanced-patches/commit/518229031ceca049ad790f7b77b19405d39f0ce1))
|
||||
* wrong signature for `hide-reels-signature` ([2d9ff2a](https://github.com/revanced/revanced-patches/commit/2d9ff2af0a991d7721f3741187716a3b08bb4029))
|
||||
* wrong signatures for patch `Old Quality Layout Patch` ([823e503](https://github.com/revanced/revanced-patches/commit/823e503d84037bdf27b09f17e63383f963c76854))
|
||||
* wrong versions of patches ([a112b22](https://github.com/revanced/revanced-patches/commit/a112b22ce6e685204caab6f95f511e26ef95806b))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* Rename `net.revanced` to `app.revanced` ([68ea89f](https://github.com/revanced/revanced-patches/commit/68ea89f15e9ea077df0d0ac20a40b735bb5ae26c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `Dependencies` annotation ([85806bb](https://github.com/revanced/revanced-patches/commit/85806bb355e342ecb33d4ee1e76f9edf89b2c2ee))
|
||||
* `GeneralBytecodeAds` and `GeneralResourceAds` patch ([f99bbef](https://github.com/revanced/revanced-patches/commit/f99bbef4c911ac2492166c7a3792ea11ffceffab))
|
||||
* `hide-shorts-button` patch ([88352ee](https://github.com/revanced/revanced-patches/commit/88352ee6ecd23faa4a7fd9f7495e67fa1d3e33bd))
|
||||
* `tastebuilder-remover` for music ([a6aeca3](https://github.com/revanced/revanced-patches/commit/a6aeca31bd80b8c4a8acd071e22faca6e136bdb0))
|
||||
* Add (WIP) Signature Checker ([ae4c7b2](https://github.com/revanced/revanced-patches/commit/ae4c7b29f211c461de460f97f3a8656e795adafb))
|
||||
* add `amoled` patch ([d61bac4](https://github.com/revanced/revanced-patches/commit/d61bac4f8243d0ef72ca91c7c1d5facd858d515e))
|
||||
* add home ads patch ([36cddd1](https://github.com/revanced/revanced-patches/commit/36cddd1488683e19e2b927e34c80a4f0f3cace35))
|
||||
* add patches compatibility to the newest version ([799401d](https://github.com/revanced/revanced-patches/commit/799401ddf99da0aaa5f52c39d3d3d4061370fd75))
|
||||
* add publishing to package registry ([b475e09](https://github.com/revanced/revanced-patches/commit/b475e09577db4dda7bbb45dbf170d78772834a6d))
|
||||
* add semantic-release ([d60f1d0](https://github.com/revanced/revanced-patches/commit/d60f1d06798d312b158b71691ecc87e828dccbc1))
|
||||
* added `codecs-unlock` patch ([e5fd7ce](https://github.com/revanced/revanced-patches/commit/e5fd7cece94b1ff5342178f59b29576db806e0f6))
|
||||
* begin `MicroG Patch` ([91474ba](https://github.com/revanced/revanced-patches/commit/91474ba07376c13e7a71685dfd8b6e6913ed5ee9))
|
||||
* display metadata for each signature in `SignatureChecker` ([736a71f](https://github.com/revanced/revanced-patches/commit/736a71fac21a32dbb1eef9c3a9f0d3005e7d9ca0))
|
||||
* get required register dynamically ([0924ca2](https://github.com/revanced/revanced-patches/commit/0924ca2ad30ae865dcc0fd484cb0da517e827352))
|
||||
* Initial commit ([bee5f2f](https://github.com/revanced/revanced-patches/commit/bee5f2faed882271ed059b0435e6e1aa91f93dbd))
|
||||
* integrations patch ([19c0b0d](https://github.com/revanced/revanced-patches/commit/19c0b0d194bb97c7248ea7a9b081176961653b9d))
|
||||
* migrate to dalvik patches ([e088c67](https://github.com/revanced/revanced-patches/commit/e088c671081bcf75586ccc1c4bdbed9366e93874))
|
||||
* MinimizedPlayback, CreateButtonRemover ([cc08c6c](https://github.com/revanced/revanced-patches/commit/cc08c6c3d38879dd4672ec671631b34aa2e3cc77))
|
||||
* OldQualityLayout, HideSuggestions, HideReels, EnableSeekbarTapping ([04a7cff](https://github.com/revanced/revanced-patches/commit/04a7cfff20d2734b1c92713de4e7e08a3b93ee85))
|
||||
* Patches for YouTube Music ([b60c9d3](https://github.com/revanced/revanced-patches/commit/b60c9d33b611bb4d5b55bb419652bc14b0309792))
|
||||
* remove obsolete patch `Hide suggestions patch` ([e65c6f2](https://github.com/revanced/revanced-patches/commit/e65c6f240ed23a54271d20a90fc57ec65cafc02d))
|
||||
* update MicroG patch to latest version ([c24f806](https://github.com/revanced/revanced-patches/commit/c24f8063a04f89aea2d2f7087a435738de7dfeae))
|
||||
* update patches to latest version ([bad25de](https://github.com/revanced/revanced-patches/commit/bad25dec1d73137f8b7a1bf4daaceb2279b4d48c))
|
||||
* use supplier instead of KClass ([08af6e5](https://github.com/revanced/revanced-patches/commit/08af6e54af79ef9ef4fb3372a348ce9b6fba4d20))
|
||||
* use supplier instead of KClass ([91aa019](https://github.com/revanced/revanced-patches/commit/91aa019f8d3d87fbf7affeb7abc2b02ba87af5c3))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* signature of patches was changed
|
||||
* signature of patches was changed
|
||||
* Package name was changed from "net.revanced" to "app.revanced"
|
||||
|
||||
# [1.0.0-dev.16](https://github.com/revanced/revanced-patches/compare/v1.0.0-dev.15...v1.0.0-dev.16) (2022-06-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add patches compatibility to the newest version ([799401d](https://github.com/revanced/revanced-patches/commit/799401ddf99da0aaa5f52c39d3d3d4061370fd75))
|
||||
|
||||
# [1.0.0-dev.15](https://github.com/revanced/revanced-patches/compare/v1.0.0-dev.14...v1.0.0-dev.15) (2022-06-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove unused patches ([d12e92a](https://github.com/revanced/revanced-patches/commit/d12e92aead677fefa9dcb48748d783225b65fab1))
|
||||
|
||||
# [1.0.0-dev.14](https://github.com/revanced/revanced-patches/compare/v1.0.0-dev.13...v1.0.0-dev.14) (2022-06-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* spelling mistake ([52f9147](https://github.com/revanced/revanced-patches/commit/52f9147ee8d591f786397d174dc02a141d9250a9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `Dependencies` annotation ([85806bb](https://github.com/revanced/revanced-patches/commit/85806bb355e342ecb33d4ee1e76f9edf89b2c2ee))
|
||||
* `GeneralBytecodeAds` and `GeneralResourceAds` patch ([f99bbef](https://github.com/revanced/revanced-patches/commit/f99bbef4c911ac2492166c7a3792ea11ffceffab))
|
||||
|
||||
# [1.0.0-dev.13](https://github.com/revanced/revanced-patches/compare/v1.0.0-dev.12...v1.0.0-dev.13) (2022-05-31)
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ repositories {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
|
||||
|
||||
implementation("app.revanced:revanced-patcher:1.0.0-dev.17")
|
||||
implementation("app.revanced:revanced-patcher:1.0.0")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 1.0.0-dev.13
|
||||
version = 1.1.0
|
||||
|
||||
@@ -19,9 +19,9 @@ internal fun Node.doRecursively(action: (Node) -> Unit) {
|
||||
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
||||
}
|
||||
|
||||
internal fun String.startsWithAny(vararg prefix: String): Boolean {
|
||||
for (_prefix in prefix)
|
||||
if (this.startsWith(_prefix))
|
||||
internal fun String.startsWithAny(vararg prefixes: String): Boolean {
|
||||
for (prefix in prefixes)
|
||||
if (this.startsWith(prefix))
|
||||
return true
|
||||
|
||||
return false
|
||||
@@ -33,4 +33,4 @@ internal fun String.containsAny(vararg others: String): Boolean {
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
@Patch
|
||||
@Name("upgrade-button-remover")
|
||||
@Description("Remove the upgrade tab from t he pivot bar in YouTube music.")
|
||||
@Description("Remove the upgrade tab from the pivot bar in YouTube music.")
|
||||
@RemoveUpgradeButtonCompatibility
|
||||
@Version("0.0.1")
|
||||
class RemoveUpgradeButtonPatch : BytecodePatch(
|
||||
@@ -73,4 +73,4 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
|
||||
)
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package app.revanced.patches.youtube.ad.home.annotation
|
||||
package app.revanced.patches.youtube.ad.general.annotation
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.03.38", "17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.19.36", "17.20.37", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class PromotionsCompatibility
|
||||
internal annotation class GeneralAdsCompatibility
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package app.revanced.patches.youtube.ad.general.bytecode.extensions
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.softCompareTo
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import org.jf.dexlib2.builder.BuilderInstruction
|
||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||
import org.jf.dexlib2.iface.Method
|
||||
import org.jf.dexlib2.iface.instruction.Instruction
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import org.jf.dexlib2.iface.reference.FieldReference
|
||||
import org.jf.dexlib2.iface.reference.MethodReference
|
||||
import org.jf.dexlib2.iface.reference.Reference
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
|
||||
internal object MethodExtensions {
|
||||
internal fun MutableMethodImplementation.insertBlocks(
|
||||
startIndex: Int,
|
||||
vararg blocks: List<BuilderInstruction>,
|
||||
) {
|
||||
blocks.reversed().forEach {
|
||||
this.addInstructions(
|
||||
startIndex, it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun MutableClass.addMethod(mutableMethod: MutableMethod) {
|
||||
this.methods.add(mutableMethod)
|
||||
}
|
||||
|
||||
internal fun MutableClass.findMutableMethodOf(
|
||||
method: Method
|
||||
) = this.methods.first {
|
||||
it.softCompareTo(
|
||||
ImmutableMethodReference(
|
||||
method.definingClass, method.name, method.parameters, method.returnType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal inline fun <reified T : Reference> Instruction.toDescriptor(): String {
|
||||
val reference = (this as ReferenceInstruction).reference
|
||||
return when (T::class) {
|
||||
MethodReference::class -> {
|
||||
val methodReference = reference as MethodReference
|
||||
"${methodReference.definingClass}->${methodReference.name}(${
|
||||
methodReference.parameterTypes.joinToString(
|
||||
""
|
||||
) { it }
|
||||
})${methodReference.returnType}"
|
||||
}
|
||||
|
||||
FieldReference::class -> {
|
||||
val fieldReference = reference as FieldReference
|
||||
"${fieldReference.definingClass}->${fieldReference.name}:${fieldReference.type}"
|
||||
}
|
||||
|
||||
else -> throw PatchResultError("Unsupported reference type")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
package app.revanced.patches.youtube.ad.general.bytecode.patch
|
||||
|
||||
import app.revanced.extensions.injectHideCall
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.implementation.proxy
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.toInstructions
|
||||
import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility
|
||||
import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.addMethod
|
||||
import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.findMutableMethodOf
|
||||
import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.insertBlocks
|
||||
import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.toDescriptor
|
||||
import app.revanced.patches.youtube.ad.general.bytecode.utils.MethodUtils.createMutableMethod
|
||||
import app.revanced.patches.youtube.ad.general.resource.patch.GeneralResourceAdsPatch
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||
import org.jf.dexlib2.builder.instruction.*
|
||||
import org.jf.dexlib2.iface.MethodImplementation
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import org.jf.dexlib2.iface.reference.FieldReference
|
||||
import org.jf.dexlib2.iface.reference.MethodReference
|
||||
import org.jf.dexlib2.iface.reference.StringReference
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
|
||||
@Patch
|
||||
@Dependencies(
|
||||
dependencies = [ResourceIdMappingProviderResourcePatch::class, IntegrationsPatch::class, GeneralResourceAdsPatch::class]
|
||||
)
|
||||
@Name("general-bytecode-ads")
|
||||
@Description("Patch to remove general ads in bytecode.")
|
||||
@GeneralAdsCompatibility
|
||||
@Version("0.0.1")
|
||||
class GeneralBytecodeAdsPatch : BytecodePatch(
|
||||
listOf()
|
||||
) {
|
||||
// a constant used by litho
|
||||
private val lithoConstant = 0xaed2868
|
||||
|
||||
// list of resource names to get the id of
|
||||
private val resourceIds = arrayOf(
|
||||
"ad_attribution",
|
||||
"reel_multiple_items_shelf",
|
||||
"info_cards_drawer_header",
|
||||
"endscreen_element_layout_video",
|
||||
"endscreen_element_layout_circle",
|
||||
"endscreen_element_layout_icon",
|
||||
"promoted_video_item_land",
|
||||
"promoted_video_item_full_bleed",
|
||||
).map {
|
||||
ResourceIdMappingProviderResourcePatch.resourceMappings[it]!!
|
||||
}
|
||||
|
||||
private val stringReferences = arrayOf(
|
||||
"Claiming to use more elements than provided",
|
||||
"loadVideo() called on LocalDirector in wrong state",
|
||||
"LoggingProperties are not in proto format"
|
||||
)
|
||||
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
// iterating through all classes is expensive
|
||||
for (classDef in data.classes) {
|
||||
var mutableClass: MutableClass? = null
|
||||
|
||||
method@ for (method in classDef.methods) {
|
||||
var mutableMethod: MutableMethod? = null
|
||||
|
||||
if (method.implementation == null) continue@method
|
||||
|
||||
val instructions = method.implementation!!.instructions
|
||||
instructions.forEachIndexed { index, instruction ->
|
||||
when (instruction.opcode) {
|
||||
Opcode.CONST -> {
|
||||
// TODO: find a way to de-duplicate code.
|
||||
// The issue is we need to save mutableClass and mutableMethod to the existing fields
|
||||
when ((instruction as Instruction31i).wideLiteral) {
|
||||
resourceIds[0] -> { // general ads
|
||||
// and is followed by an instruction with the mnemonic INVOKE_VIRTUAL
|
||||
val insertIndex = index + 1
|
||||
val invokeInstruction = instructions.elementAt(insertIndex)
|
||||
if (invokeInstruction.opcode != Opcode.INVOKE_VIRTUAL) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
// insert hide call to hide the view corresponding to the resource
|
||||
val viewRegister = (invokeInstruction as Instruction35c).registerC
|
||||
mutableMethod!!.implementation!!.injectHideCall(insertIndex, viewRegister)
|
||||
|
||||
}
|
||||
|
||||
resourceIds[1] -> { // reel ads
|
||||
// and is followed by an instruction at insertIndex with the mnemonic IPUT_OBJECT
|
||||
val insertIndex = index + 4
|
||||
val iPutInstruction = instructions.elementAt(insertIndex)
|
||||
if (iPutInstruction.opcode != Opcode.IPUT_OBJECT) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
val viewRegister = (iPutInstruction as Instruction22c).registerA
|
||||
mutableMethod!!.implementation!!.injectHideCall(insertIndex, viewRegister)
|
||||
}
|
||||
|
||||
resourceIds[2] -> { // info cards ads
|
||||
// and is followed by an instruction with the mnemonic INVOKE_VIRTUAL
|
||||
val removeIndex = index - 1
|
||||
val invokeInstruction = instructions.elementAt(removeIndex)
|
||||
if (invokeInstruction.opcode != Opcode.INVOKE_VIRTUAL) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
mutableMethod!!.implementation!!.removeInstruction(removeIndex)
|
||||
}
|
||||
|
||||
resourceIds[3], resourceIds[4], resourceIds[5] -> { // end screen ads
|
||||
// and is followed by an instruction with the mnemonic IPUT_OBJECT
|
||||
val insertIndex = index + 7
|
||||
val invokeInstruction = instructions.elementAt(insertIndex)
|
||||
if (invokeInstruction.opcode != Opcode.IPUT_OBJECT) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
// TODO: dynamically get registers
|
||||
mutableMethod!!.implementation!!.addInstructions(
|
||||
insertIndex, """
|
||||
const/16 v1, 0x8
|
||||
invoke-virtual {v0,v1}, Landroid/widget/FrameLayout;->setVisibility(I)V
|
||||
""".trimIndent().toInstructions()
|
||||
)
|
||||
}
|
||||
|
||||
resourceIds[6] -> {
|
||||
// and is followed by an instruction with the mnemonic INVOKE_DIRECT
|
||||
val insertIndex = index + 3
|
||||
val invokeInstruction = instructions.elementAt(insertIndex)
|
||||
if (invokeInstruction.opcode != Opcode.INVOKE_DIRECT) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
// insert hide call to hide the view corresponding to the resource
|
||||
val viewRegister = (invokeInstruction as Instruction35c).registerE
|
||||
mutableMethod!!.implementation!!.injectHideCall(insertIndex, viewRegister)
|
||||
}
|
||||
|
||||
resourceIds[7] -> {
|
||||
// TODO, go to class, hide the inflated view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcode.CONST_STRING -> {
|
||||
|
||||
when (((instruction as Instruction21c).reference as StringReference).string) {
|
||||
stringReferences[0] -> {
|
||||
val stringInstruction = instructions.elementAt(3)
|
||||
if (stringInstruction.opcode == Opcode.CONST_STRING) return@forEachIndexed
|
||||
|
||||
// create proxied method, make sure to not re-resolve() the current class
|
||||
if (mutableClass == null) mutableClass = data.proxy(classDef).resolve()
|
||||
if (mutableMethod == null) mutableMethod =
|
||||
mutableClass!!.findMutableMethodOf(method)
|
||||
|
||||
// return the method
|
||||
val insertIndex = 1 // after super constructor
|
||||
mutableMethod!!.implementation!!.addInstruction(
|
||||
insertIndex, BuilderInstruction10x(Opcode.RETURN_VOID)
|
||||
)
|
||||
}
|
||||
|
||||
stringReferences[1] -> {
|
||||
// TODO: migrate video ads patch to here if necessary
|
||||
}
|
||||
|
||||
stringReferences[2] -> { // Litho ads
|
||||
// create proxied method.
|
||||
val proxy = data.proxy(classDef)
|
||||
val mutableClass = proxy.resolve()
|
||||
|
||||
// add getIsEmpty method
|
||||
mutableClass.addGetIsEmptyMethod()
|
||||
|
||||
// get required method to patch and get references from
|
||||
val lithoMethod = getLithoMethod(mutableClass)
|
||||
?: return PatchResultError("Could not find required litho method to patch.")
|
||||
val lithoMethodImplementation = lithoMethod.implementation!!
|
||||
|
||||
// create and add getTemplateName method
|
||||
val getTemplateMethod =
|
||||
mutableClass.createGetTemplateNameMethod(lithoMethodImplementation)
|
||||
mutableClass.addMethod(getTemplateMethod)
|
||||
|
||||
val lithoInstructions = lithoMethodImplementation.instructions
|
||||
val thisType = mutableClass.type
|
||||
val templateNameParameterType = getTemplateMethod.parameterTypes.first()
|
||||
|
||||
// get reference descriptors
|
||||
val indexOfReference1 = lithoInstructions.indexOfFirst {
|
||||
it.opcode == Opcode.INVOKE_STATIC_RANGE
|
||||
}
|
||||
val descriptor1 =
|
||||
lithoInstructions.elementAt(indexOfReference1).toDescriptor<MethodReference>()
|
||||
val descriptor2 = lithoInstructions.elementAt(indexOfReference1 + 2)
|
||||
.toDescriptor<FieldReference>()
|
||||
|
||||
// create label
|
||||
val lithoRemoveLabel = lithoMethodImplementation.newLabelForIndex(0)
|
||||
|
||||
// create branch instructions
|
||||
val ifEqzFirstInstruction =
|
||||
BuilderInstruction21t(Opcode.IF_EQZ, 0, lithoRemoveLabel)
|
||||
val ifEqzSecondInstruction =
|
||||
BuilderInstruction21t(Opcode.IF_EQZ, 1, lithoRemoveLabel)
|
||||
|
||||
// create blocks
|
||||
val parameters = lithoMethod.parameterTypes.joinToString("") { it }
|
||||
val registers = lithoMethodImplementation.registerCount
|
||||
val block1 = """
|
||||
invoke-static/range {p3}, $thisType->getTemplateName($templateNameParameterType)Ljava/lang/String;
|
||||
move-result-object v0
|
||||
""".trimIndent().toInstructions(parameters, registers, false)
|
||||
val block2 = """
|
||||
move-object/from16 v1, p3
|
||||
iget-object v2, v1, $templateNameParameterType->b:Ljava/nio/ByteBuffer;
|
||||
invoke-static {v0, v2}, Lfi/razerman/youtube/litho/LithoAdRemoval;->containsAd(Ljava/lang/String;Ljava/nio/ByteBuffer;)Z
|
||||
move-result v1
|
||||
""".trimIndent().toInstructions(parameters, registers, false)
|
||||
val block3 = """
|
||||
move-object/from16 v2, p1
|
||||
invoke-static {v2}, $descriptor1
|
||||
move-result-object v0
|
||||
iget-object v0, v0, $descriptor2
|
||||
return-object v0
|
||||
""".trimIndent().toInstructions(parameters, registers, false)
|
||||
|
||||
// insert blocks and branch instructions
|
||||
lithoMethodImplementation.insertBlocks(
|
||||
0,
|
||||
block1,
|
||||
listOf(ifEqzFirstInstruction),
|
||||
block2,
|
||||
listOf(ifEqzSecondInstruction),
|
||||
block3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else -> return@forEachIndexed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
private fun getLithoMethod(mutableClass: MutableClass) = mutableClass.methods.firstOrNull {
|
||||
it.implementation?.instructions?.any { instruction ->
|
||||
instruction.opcode == Opcode.CONST && (instruction as Instruction31i).narrowLiteral == lithoConstant
|
||||
} ?: false
|
||||
}
|
||||
|
||||
private fun MutableClass.addGetIsEmptyMethod() {
|
||||
val getIsEmptyImplementation = MutableMethodImplementation(1)
|
||||
|
||||
// create target instructions
|
||||
val firstTargetInstruction = BuilderInstruction11n(Opcode.CONST_4, 0, 1)
|
||||
val secondTargetInstruction = BuilderInstruction11n(Opcode.CONST_4, 0, 0)
|
||||
|
||||
// add instructions to the instruction list
|
||||
getIsEmptyImplementation.addInstructions(
|
||||
0, listOf(
|
||||
// BuilderInstruction21t(Opcode.IF_EQZ, 0, first),
|
||||
BuilderInstruction35c(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
ImmutableMethodReference("Ljava/lang/String;", "isEmpty", null, "Z")
|
||||
),
|
||||
BuilderInstruction11x(Opcode.MOVE_RESULT, 0),
|
||||
// BuilderInstruction21t(Opcode.IF_EQZ, 0, second),
|
||||
// BuilderInstruction10t(Opcode.GOTO, first),
|
||||
secondTargetInstruction,
|
||||
BuilderInstruction11x(Opcode.RETURN, 0),
|
||||
firstTargetInstruction,
|
||||
BuilderInstruction11x(Opcode.RETURN, 0),
|
||||
)
|
||||
)
|
||||
|
||||
val getIsEmptyInstructions = getIsEmptyImplementation.instructions
|
||||
|
||||
// create labels for the target instructions
|
||||
val firstLabel =
|
||||
getIsEmptyImplementation.newLabelForIndex(getIsEmptyInstructions.indexOf(firstTargetInstruction))
|
||||
val secondLabel =
|
||||
getIsEmptyImplementation.newLabelForIndex(getIsEmptyInstructions.indexOf(secondTargetInstruction))
|
||||
|
||||
// create branch instructions to the labels
|
||||
val ifEqzFirstInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 0, firstLabel)
|
||||
val ifEqzSecondInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 0, secondLabel)
|
||||
val gotoInstruction = BuilderInstruction10t(Opcode.GOTO, firstLabel)
|
||||
|
||||
// insert remaining branch instructions, order of adding those instructions is important
|
||||
getIsEmptyImplementation.addInstructions(
|
||||
2, listOf(
|
||||
ifEqzSecondInstruction, gotoInstruction
|
||||
)
|
||||
)
|
||||
getIsEmptyImplementation.addInstruction(
|
||||
0, ifEqzFirstInstruction
|
||||
)
|
||||
|
||||
this.addMethod(
|
||||
createMutableMethod(
|
||||
this.type, "getIsEmpty", "Z", "Ljava/lang/String;", getIsEmptyImplementation
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableClass.createGetTemplateNameMethod(lithoMethodImplementation: MethodImplementation): MutableMethod {
|
||||
var counter = 1
|
||||
val descriptors = buildList {
|
||||
for (instruction in lithoMethodImplementation.instructions) {
|
||||
if (instruction !is ReferenceInstruction) continue
|
||||
if (counter++ > 4) break
|
||||
|
||||
add(instruction.toDescriptor<MethodReference>())
|
||||
}
|
||||
}
|
||||
|
||||
val getTemplateNameImplementation = MutableMethodImplementation(2)
|
||||
|
||||
// create code blocks
|
||||
val block1 = """
|
||||
invoke-virtual {p0}, ${descriptors[0]}
|
||||
move-result-object p0
|
||||
const v0, $lithoConstant
|
||||
invoke-static {p0, v0}, ${descriptors[1]}
|
||||
move-result-object p0
|
||||
""".trimIndent().toInstructions()
|
||||
val block2 = """
|
||||
invoke-static {p0}, ${descriptors[2]}
|
||||
move-result-object p0
|
||||
invoke-virtual {p0}, ${descriptors[3]}
|
||||
move-result-object v0
|
||||
invoke-static {v0}, ${this.type}->getIsEmpty(Ljava/lang/String;)Z
|
||||
move-result v0
|
||||
""".trimIndent().toInstructions()
|
||||
val block3 = """
|
||||
invoke-virtual {p0}, ${descriptors[3]}
|
||||
move-result-object p0
|
||||
return-object p0
|
||||
""".trimIndent().toInstructions()
|
||||
|
||||
// create target instruction
|
||||
val targetInstruction = BuilderInstruction11n(Opcode.CONST_4, 1, 0)
|
||||
// and remaining instruction
|
||||
val returnInstruction = BuilderInstruction11x(Opcode.RETURN_OBJECT, 1)
|
||||
|
||||
// insert blocks and instructions
|
||||
getTemplateNameImplementation.insertBlocks(
|
||||
0,
|
||||
block1,
|
||||
block2,
|
||||
block3,
|
||||
listOf(
|
||||
targetInstruction, returnInstruction
|
||||
),
|
||||
)
|
||||
|
||||
// create label for target instruction
|
||||
val targetInstructionLabel =
|
||||
getTemplateNameImplementation.newLabelForIndex(getTemplateNameImplementation.instructions.size - 2)
|
||||
|
||||
// create branch instructions to the label
|
||||
val ifEqzInstruction = BuilderInstruction21t(Opcode.IF_EQZ, 1, targetInstructionLabel)
|
||||
val ifNezInstruction = BuilderInstruction21t(Opcode.IF_NEZ, 0, targetInstructionLabel)
|
||||
|
||||
// insert branch instructions
|
||||
getTemplateNameImplementation.addInstruction(
|
||||
block1.size, ifEqzInstruction
|
||||
)
|
||||
getTemplateNameImplementation.addInstruction(
|
||||
block1.size + block2.size + 1, ifNezInstruction
|
||||
)
|
||||
|
||||
// create the method
|
||||
return createMutableMethod(
|
||||
this.type,
|
||||
"getTemplateName",
|
||||
"Ljava/lang/String;",
|
||||
descriptors[0].split("->")[0], // a bit weird to get the type this way,
|
||||
getTemplateNameImplementation
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.revanced.patches.youtube.ad.general.bytecode.utils
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.iface.MethodImplementation
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||
import org.jf.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
internal object MethodUtils {
|
||||
internal fun createMutableMethod(
|
||||
definingClass: String, name: String, returnType: String, parameter: String, implementation: MethodImplementation
|
||||
) = ImmutableMethod(
|
||||
definingClass, name, listOf(
|
||||
ImmutableMethodParameter(
|
||||
parameter, null, null
|
||||
)
|
||||
), returnType, AccessFlags.PRIVATE or AccessFlags.STATIC, null, null, implementation
|
||||
).toMutable()
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package app.revanced.patches.youtube.ad.general.resource.patch
|
||||
|
||||
import app.revanced.extensions.doRecursively
|
||||
import app.revanced.extensions.startsWithAny
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility
|
||||
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Dependencies(dependencies = [FixLocaleConfigErrorPatch::class])
|
||||
@Name("general-resource-ads")
|
||||
@Description("Patch to remove general ads in resources.")
|
||||
@GeneralAdsCompatibility
|
||||
@Version("0.0.1")
|
||||
class GeneralResourceAdsPatch : ResourcePatch() {
|
||||
// list of resource file names which need to be hidden
|
||||
private val resourceFileNames = arrayOf(
|
||||
"compact_promoted_video_item.xml",
|
||||
"inline_muted_metadata_swap.xml",
|
||||
"interstitial_promo_view.xml",
|
||||
"pip_ad_overlay.xml",
|
||||
"promoted_",
|
||||
"watch_metadata_companion_cards.xml",
|
||||
//"watch_while_activity.xml" // FIXME: find out why patching this resource fails
|
||||
)
|
||||
|
||||
// the attributes to change the value of
|
||||
private val replacements = arrayOf(
|
||||
"height",
|
||||
"width",
|
||||
"marginTop",
|
||||
)
|
||||
|
||||
override fun execute(data: ResourceData): PatchResult {
|
||||
data.forEach {
|
||||
if (!it.name.startsWithAny(*resourceFileNames)) return@forEach
|
||||
|
||||
// for each file in the "layouts" directory replace all necessary attributes content
|
||||
data.getXmlEditor(it.absolutePath).use { editor ->
|
||||
editor.file.doRecursively { node ->
|
||||
replacements.forEach replacement@{ replacement ->
|
||||
if (node !is Element) return@replacement
|
||||
|
||||
node.getAttributeNode("android:layout_$replacement")?.let { attribute ->
|
||||
attribute.textContent = "1.0dip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
||||
package app.revanced.patches.youtube.ad.home.patch
|
||||
|
||||
import app.revanced.extensions.injectHideCall
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.implementation.toMethodWalker
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.DirectPatternScanMethod
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.youtube.ad.home.annotation.PromotionsCompatibility
|
||||
import app.revanced.patches.youtube.ad.home.signatures.PromotedDiscoveryActionParentSignature
|
||||
import app.revanced.patches.youtube.ad.home.signatures.PromotedDiscoveryAppParentSignature
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
||||
|
||||
//@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Name("home-promo-ads")
|
||||
@Description("Patch to remove promoted ads in YouTube.")
|
||||
@PromotionsCompatibility
|
||||
@Version("0.0.1")
|
||||
class PromotionsPatch : BytecodePatch(
|
||||
listOf(
|
||||
PromotedDiscoveryAppParentSignature, PromotedDiscoveryActionParentSignature
|
||||
)
|
||||
) {
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
for (signature in signatures) {
|
||||
val result = signature.result!!
|
||||
|
||||
val requiredMethod =
|
||||
result.findParentMethod(@Name("promotion-ads-signature") @MatchingMethod(name = "d") @DirectPatternScanMethod @PromotionsCompatibility @Version(
|
||||
"0.0.1"
|
||||
) object : MethodSignature(
|
||||
"V", AccessFlags.PRIVATE or AccessFlags.FINAL, listOf("Z", "Z"), null
|
||||
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
||||
|
||||
val toBePatchedInvokeOffset =
|
||||
requiredMethod.immutableMethod.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_DIRECT }
|
||||
val toBePatchedMethod =
|
||||
data.toMethodWalker(requiredMethod.immutableMethod).nextMethod(toBePatchedInvokeOffset, true)
|
||||
.getMethod() as MutableMethod
|
||||
|
||||
val implementation = toBePatchedMethod.implementation!!
|
||||
val invokeVirtualOffset = implementation.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_VIRTUAL }
|
||||
|
||||
val moveResultInstruction = implementation.instructions[invokeVirtualOffset + 1]
|
||||
if (moveResultInstruction.opcode != Opcode.MOVE_RESULT_OBJECT) return PatchResultError("The toBePatchedInvokeOffset offset was wrong in ${this.javaClass.patchName}")
|
||||
|
||||
val register = (moveResultInstruction as Instruction11x).registerA
|
||||
implementation.injectHideCall(invokeVirtualOffset + 2, register)
|
||||
}
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package app.revanced.patches.youtube.ad.home.signatures
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||
import app.revanced.patches.youtube.ad.home.annotation.PromotionsCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("promoted-discovery-app-parent-signature")
|
||||
@MatchingMethod(
|
||||
"Ljqb;", "lG"
|
||||
)
|
||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||
@PromotionsCompatibility
|
||||
@Version("0.0.1")
|
||||
object PromotedDiscoveryActionParentSignature : MethodSignature(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
||||
listOf("L", "L"),
|
||||
listOf(
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL_RANGE,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.CONST_4,
|
||||
Opcode.XOR_INT_2ADDR,
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT
|
||||
)
|
||||
)
|
||||
@@ -1,61 +0,0 @@
|
||||
package app.revanced.patches.youtube.ad.home.signatures
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||
import app.revanced.patches.youtube.ad.home.annotation.PromotionsCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("promoted-discovery-action-parent-signature")
|
||||
@MatchingMethod(
|
||||
"Ljqj;",
|
||||
"lG"
|
||||
)
|
||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||
@PromotionsCompatibility
|
||||
@Version("0.0.1")
|
||||
|
||||
object PromotedDiscoveryAppParentSignature : MethodSignature(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
||||
listOf("L", "L"),
|
||||
listOf(
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.IGET_BOOLEAN,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.NEW_ARRAY,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_GE,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.APUT_OBJECT,
|
||||
Opcode.ADD_INT_LIT8,
|
||||
Opcode.GOTO
|
||||
)
|
||||
)
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -6,6 +6,7 @@ import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -20,7 +21,8 @@ import app.revanced.patches.youtube.ad.video.signatures.ShowVideoAdsConstructorS
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
|
||||
@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Patch
|
||||
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||
@Name("video-ads")
|
||||
@Description("Patch to remove ads in the YouTube video player.")
|
||||
@VideoAdsCompatibility
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -5,6 +5,7 @@ import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -21,8 +22,8 @@ import org.jf.dexlib2.iface.Method
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11n
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
|
||||
@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Patch
|
||||
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||
@Name("seekbar-tapping")
|
||||
@Description("Enable tapping on the seekbar of the YouTube player.")
|
||||
@SeekbarTappingCompatibility
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -4,6 +4,7 @@ import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -13,7 +14,8 @@ import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatc
|
||||
import org.w3c.dom.Element
|
||||
import java.io.File
|
||||
|
||||
@Patch(
|
||||
@Patch
|
||||
@Dependencies(
|
||||
dependencies = [
|
||||
FixLocaleConfigErrorPatch::class
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -4,6 +4,7 @@ import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -16,7 +17,8 @@ import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Patch
|
||||
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||
@Name("disable-create-button")
|
||||
@Description("Disable the create button.")
|
||||
@CreateButtonCompatibility
|
||||
@@ -32,8 +34,7 @@ class CreateButtonRemoverPatch : BytecodePatch(
|
||||
// Get the required register which holds the view object we need to pass to the method hideCreateButton
|
||||
val implementation = result.method.implementation!!
|
||||
val instruction = implementation.instructions[result.scanResult.endIndex + 1]
|
||||
if (instruction.opcode != Opcode.INVOKE_STATIC)
|
||||
return PatchResultError("Could not find the correct register")
|
||||
if (instruction.opcode != Opcode.INVOKE_STATIC) return PatchResultError("Could not find the correct register")
|
||||
val register = (instruction as Instruction35c).registerC
|
||||
|
||||
// Hide the button view via proxy by passing it to the hideCreateButton method
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -6,6 +6,7 @@ import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -22,7 +23,8 @@ import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
||||
|
||||
@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Patch
|
||||
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||
@Name("old-quality-layout")
|
||||
@Description("Enable the original quality flyout menu.")
|
||||
@OldQualityLayoutCompatibility
|
||||
|
||||
@@ -11,7 +11,7 @@ import app.revanced.patcher.util.smali.toInstruction
|
||||
import app.revanced.patches.youtube.layout.reels.annotations.HideReelsCompatibility
|
||||
import app.revanced.patches.youtube.layout.reels.signatures.HideReelsSignature
|
||||
|
||||
//@Patch TODO: converted to litho
|
||||
//@Patch TODO: this is currently in the general-bytecode-ads patch due to the integrations having a preference for including reels or not. Move it here.
|
||||
@Name("hide-reels")
|
||||
@Description("Hide reels on the page.")
|
||||
@HideReelsCompatibility
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -4,6 +4,7 @@ import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
@@ -15,7 +16,8 @@ import app.revanced.patches.youtube.layout.shorts.button.signatures.PivotBarButt
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
||||
|
||||
@Patch(dependencies = [IntegrationsPatch::class])
|
||||
@Patch
|
||||
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||
@Name("shorts-button")
|
||||
@Description("Hide the shorts button.")
|
||||
@ShortsButtonCompatibility
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.03.38", "17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.03.38", "17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36")
|
||||
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34", "17.19.36", "17.20.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
|
||||
@@ -11,7 +11,7 @@ import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Name("resource-id-mapping-provider-resource-patch-dependency")
|
||||
@Description("This patch works as a acts as a provider for resources mapped to their ids.")
|
||||
@Description("This patch acts as a provider/ dependency for resource mappings.")
|
||||
@Version("0.0.1")
|
||||
class ResourceIdMappingProviderResourcePatch : ResourcePatch() {
|
||||
companion object {
|
||||
|
||||
Reference in New Issue
Block a user