diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt index 17359ff76..e69de29bb 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/bytecode/patch/GeneralBytecodeAdsPatch.kt @@ -1,391 +0,0 @@ -package app.revanced.patches.youtube.ad.general.bytecode.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.instruction -import app.revanced.patcher.extensions.softCompareTo -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve -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.DependsOn -import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patcher.util.proxy.mutableTypes.MutableClass -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patcher.util.smali.toInstruction -import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility -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.ResourceMappingResourcePatch -import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch -import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource -import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference -import org.jf.dexlib2.Opcode -import org.jf.dexlib2.builder.MutableMethodImplementation -import org.jf.dexlib2.builder.instruction.BuilderInstruction10x -import org.jf.dexlib2.iface.Method -import org.jf.dexlib2.iface.instruction.Instruction -import org.jf.dexlib2.iface.instruction.OneRegisterInstruction -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.Reference -import org.jf.dexlib2.iface.reference.StringReference -import org.jf.dexlib2.immutable.reference.ImmutableMethodReference - -@Patch -@DependsOn([ResourceMappingResourcePatch::class, IntegrationsPatch::class, SettingsPatch::class, GeneralResourceAdsPatch::class]) -@Name("general-ads") -@Description("Removes general ads.") -@GeneralAdsCompatibility -@Version("0.0.1") -class GeneralBytecodeAdsPatch : BytecodePatch() { - // 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", - ).map { name -> - ResourceMappingResourcePatch.resourceMappings.single { it.name == name }.id - } - - private val stringReferences = arrayOf( - "Claiming to use more elements than provided", - "LoggingProperties are not in proto format" - ) - - override fun execute(context: BytecodeContext): PatchResult { - SettingsPatch.PreferenceScreen.ADS.addPreferences( - SwitchPreference( - "revanced_home_ads_removal", - StringResource("revanced_home_ads_removal_title", "Remove home ads"), - true, - StringResource("revanced_home_ads_removal_summary_on", "Home ads are hidden"), - StringResource("revanced_home_ads_removal_summary_off", "Home ads are shown") - ), - SwitchPreference( - "revanced_adremover_ad_removal", - StringResource("revanced_adremover_ad_removal_enabled_title", "Remove general ads"), - true, - StringResource("revanced_adremover_ad_removal_enabled_summary_on", "General ads are hidden"), - StringResource("revanced_adremover_ad_removal_enabled_summary_off", "General ads are shown") - ), - SwitchPreference( - "revanced_adremover_merchandise", - StringResource("revanced_adremover_merchandise_enabled_title", "Remove merchandise banners"), - true, - StringResource("revanced_adremover_merchandise_enabled_summary_on", "Merchandise banners are hidden"), - StringResource("revanced_adremover_merchandise_enabled_summary_off", "Merchandise banners are shown") - ), - SwitchPreference( - "revanced_adremover_community_posts_removal", - StringResource("revanced_adremover_community_posts_enabled_title", "Remove community posts"), - false, - StringResource("revanced_adremover_community_posts_enabled_summary_on", "Community posts are hidden"), - StringResource("revanced_adremover_community_posts_enabled_summary_off", "Community posts are shown") - ), - SwitchPreference( - "revanced_adremover_compact_banner_removal", - StringResource("revanced_adremover_compact_banner_enabled_title", "Remove compact banners"), - true, - StringResource("revanced_adremover_compact_banner_enabled_summary_on", "Compact banners are hidden"), - StringResource("revanced_adremover_compact_banner_enabled_summary_off", "Compact banners are shown") - ), - SwitchPreference( - "revanced_adremover_movie", - StringResource("revanced_adremover_movie_enabled_title", "Remove movies section"), - true, - StringResource("revanced_adremover_movie_enabled_summary_on", "Movies section is hidden"), - StringResource("revanced_adremover_movie_enabled_summary_off", "Movies section is shown") - ), - SwitchPreference( - "revanced_adremover_feed_survey", - StringResource("revanced_adremover_feed_survey_enabled_title", "Remove feed surveys"), - true, - StringResource("revanced_adremover_feed_survey_enabled_summary_on", "Feed surveys are hidden"), - StringResource("revanced_adremover_feed_survey_enabled_summary_off", "Feed surveys are shown") - ), - SwitchPreference( - "revanced_adremover_shorts", - StringResource("revanced_adremover_shorts_enabled_title", "Remove shorts"), - true, - StringResource("revanced_adremover_shorts_enabled_summary_on", "Shorts are hidden"), - StringResource("revanced_adremover_shorts_enabled_summary_off", "Shorts are shown") - ), - SwitchPreference( - "revanced_adremover_community_guidelines", - StringResource("revanced_adremover_community_guidelines_enabled_title", "Remove community guidelines"), - true, - StringResource("revanced_adremover_community_guidelines_enabled_summary_on", "Community guidelines are hidden"), - StringResource("revanced_adremover_community_guidelines_enabled_summary_off", "Community guidelines are shown") - ), - SwitchPreference( - "revanced_adremover_emergency_box_removal", - StringResource("revanced_adremover_emergency_box_enabled_title", "Remove emergency boxes"), - true, - StringResource("revanced_adremover_emergency_box_enabled_summary_on", "Emergency boxes are hidden"), - StringResource("revanced_adremover_emergency_box_enabled_summary_off", "Emergency boxes are shown") - ), - SwitchPreference( - "revanced_adremover_info_panel", - StringResource("revanced_adremover_info_panel_enabled_title", "Remove info panels"), - true, - StringResource("revanced_adremover_info_panel_enabled_summary_on", "Info panels are hidden"), - StringResource("revanced_adremover_info_panel_enabled_summary_off", "Info panels are shown") - ), - SwitchPreference( - "revanced_adremover_medical_panel", - StringResource("revanced_adremover_medical_panel_enabled_title", "Remove medical panels"), - true, - StringResource("revanced_adremover_medical_panel_enabled_summary_on", "Medical panels are hidden"), - StringResource("revanced_adremover_medical_panel_enabled_summary_off", "Medical panels are shown") - ), - SwitchPreference( - "revanced_adremover_paid_content", - StringResource("revanced_adremover_paid_content_enabled_title", "Remove paid content"), - true, - StringResource("revanced_adremover_paid_content_enabled_summary_on", "Paid content is hidden"), - StringResource("revanced_adremover_paid_content_enabled_summary_off", "Paid content is shown") - ), - SwitchPreference( - "revanced_adremover_hide_suggestions", - StringResource("revanced_adremover_hide_suggestions_enabled_title", "Hide suggestions"), - true, - StringResource("revanced_adremover_hide_suggestions_enabled_summary_on", "Suggestions are hidden"), - StringResource("revanced_adremover_hide_suggestions_enabled_summary_off", "Suggestions are shown") - ), - SwitchPreference( - "revanced_adremover_hide_latest_posts", - StringResource("revanced_adremover_hide_latest_posts_enabled_title", "Hide latest posts"), - true, - StringResource("revanced_adremover_hide_latest_posts_enabled_summary_on", "Latest posts are hidden"), - StringResource("revanced_adremover_hide_latest_posts_enabled_summary_off", "Latest posts are shown") - ), - SwitchPreference( - "revanced_adremover_hide_channel_guidelines", - StringResource("revanced_adremover_hide_channel_guidelines_enabled_title", "Hide channel guidelines"), - true, - StringResource( - "revanced_adremover_hide_channel_guidelines_enabled_summary_on", - "Channel guidelines are hidden" - ), - StringResource( - "revanced_adremover_hide_channel_guidelines_enabled_summary_off", - "Channel guidelines are shown" - ) - ), - SwitchPreference( - "revanced_adremover_self_sponsor", - StringResource("revanced_adremover_self_sponsor_enabled_title", "Hide self sponsored cards"), - true, - StringResource("revanced_adremover_self_sponsor_enabled_summary_on", "Self sponsored cards are hidden"), - StringResource("revanced_adremover_self_sponsor_enabled_summary_off", "Self sponsored cards are shown") - ), - SwitchPreference( - "revanced_adremover_chapter_teaser", - StringResource( - "revanced_adremover_chapter_teaser_enabled_title", - "Hide chapter teaser under videos" - ), - true, - StringResource( - "revanced_adremover_chapter_teaser_enabled_summary_on", - "Chapter teasers are hidden" - ), - StringResource( - "revanced_adremover_chapter_teaser_enabled_summary_off", - "Chapter teasers are shown" - ) - ) - ) - - // iterating through all classes is expensive - for (classDef in context.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 = context.proxy(classDef).mutableClass - 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 = context.proxy(classDef).mutableClass - if (mutableMethod == null) mutableMethod = - mutableClass!!.findMutableMethodOf(method) - - val viewRegister = (iPutInstruction as Instruction22c).registerA - mutableMethod!!.implementation!!.injectHideCall(insertIndex, viewRegister) - } - } - } - - 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 = context.proxy(classDef).mutableClass - if (mutableMethod == null) mutableMethod = - mutableClass!!.findMutableMethodOf(method) - - // return the method - val insertIndex = 1 // after super constructor - //ToDo: Add setting here - mutableMethod!!.implementation!!.addInstruction( - insertIndex, BuilderInstruction10x(Opcode.RETURN_VOID) - ) - } - - stringReferences[1] -> { // Litho ads - val proxy = context.proxy(classDef) - val proxiedClass = proxy.mutableClass - - val lithoMethod = getLithoMethod(proxiedClass) - ?: return PatchResultError("Could not find required Litho method to patch.") - - val instructionWithNeededDescriptor = - lithoMethod.implementation!!.instructions.indexOfFirst { - it.opcode == Opcode.INVOKE_STATIC_RANGE - } - - // get required descriptors - val createEmptyComponentDescriptor = - lithoMethod.instruction(instructionWithNeededDescriptor) - .toDescriptor() - val emptyComponentFieldDescriptor = - lithoMethod.instruction(instructionWithNeededDescriptor + 2) - .toDescriptor() - - val pathBuilderAnchorFingerprint = object : MethodFingerprint( - opcodes = listOf( - Opcode.CONST_16, - Opcode.INVOKE_VIRTUAL, - Opcode.IPUT_OBJECT - ) - ) {} - - val pathBuilderScanResult = pathBuilderAnchorFingerprint.also { - it.resolve(context, lithoMethod, classDef) - }.result!!.scanResult.patternScanResult!! - - val clobberedRegister = - (lithoMethod.instruction(pathBuilderScanResult.startIndex) as OneRegisterInstruction).registerA - - val insertIndex = pathBuilderScanResult.endIndex + 1 - lithoMethod.addInstructions( - insertIndex, // right after setting the component.pathBuilder field, - """ - invoke-static {v5, v2}, Lapp/revanced/integrations/patches/LithoFilterPatch;->filter(Ljava/lang/StringBuilder;Ljava/lang/String;)Z - move-result v$clobberedRegister - if-eqz v$clobberedRegister, :not_an_ad - move-object/from16 v2, p1 - invoke-static {v2}, $createEmptyComponentDescriptor - move-result-object v0 - iget-object v0, v0, $emptyComponentFieldDescriptor - return-object v0 - """, - listOf(ExternalLabel("not_an_ad", lithoMethod.instruction(insertIndex))) - ) - } - } - - } - - 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.findMutableMethodOf( - method: Method - ) = this.methods.first { - it.softCompareTo( - ImmutableMethodReference( - method.definingClass, method.name, method.parameters, method.returnType - ) - ) - } - - private inline fun 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") - } - } - - private 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() - ) - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/patch/GeneralAdsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/patch/GeneralAdsPatch.kt new file mode 100644 index 000000000..e59bc5258 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/patch/GeneralAdsPatch.kt @@ -0,0 +1,179 @@ +package app.revanced.patches.youtube.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.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.DependsOn +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patches.reddit.ad.general.annotations.GeneralAdsCompatibility +import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch +import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch +import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch +import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource +import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference + +@Patch +@DependsOn(dependencies = [FixLocaleConfigErrorPatch::class, LithoFilterPatch::class, SettingsPatch::class]) +@Name("general-ads") +@Description("Removes general ads.") +@GeneralAdsCompatibility +@Version("0.0.1") +class GeneralAdsPatch : ResourcePatch { + override fun execute(context: ResourceContext): PatchResult { + SettingsPatch.PreferenceScreen.ADS.addPreferences( + SwitchPreference( + "revanced_home_ads_removal", + StringResource("revanced_home_ads_removal_title", "Remove home ads"), + true, + StringResource("revanced_home_ads_removal_summary_on", "Home ads are hidden"), + StringResource("revanced_home_ads_removal_summary_off", "Home ads are shown") + ), + SwitchPreference( + "revanced_adremover_ad_removal", + StringResource("revanced_adremover_ad_removal_enabled_title", "Remove general ads"), + true, + StringResource("revanced_adremover_ad_removal_enabled_summary_on", "General ads are hidden"), + StringResource("revanced_adremover_ad_removal_enabled_summary_off", "General ads are shown") + ), + SwitchPreference( + "revanced_adremover_merchandise", + StringResource("revanced_adremover_merchandise_enabled_title", "Remove merchandise banners"), + true, + StringResource("revanced_adremover_merchandise_enabled_summary_on", "Merchandise banners are hidden"), + StringResource("revanced_adremover_merchandise_enabled_summary_off", "Merchandise banners are shown") + ), + SwitchPreference( + "revanced_adremover_community_posts_removal", + StringResource("revanced_adremover_community_posts_enabled_title", "Remove community posts"), + false, + StringResource("revanced_adremover_community_posts_enabled_summary_on", "Community posts are hidden"), + StringResource("revanced_adremover_community_posts_enabled_summary_off", "Community posts are shown") + ), + SwitchPreference( + "revanced_adremover_compact_banner_removal", + StringResource("revanced_adremover_compact_banner_enabled_title", "Remove compact banners"), + true, + StringResource("revanced_adremover_compact_banner_enabled_summary_on", "Compact banners are hidden"), + StringResource("revanced_adremover_compact_banner_enabled_summary_off", "Compact banners are shown") + ), + SwitchPreference( + "revanced_adremover_movie", + StringResource("revanced_adremover_movie_enabled_title", "Remove movies section"), + true, + StringResource("revanced_adremover_movie_enabled_summary_on", "Movies section is hidden"), + StringResource("revanced_adremover_movie_enabled_summary_off", "Movies section is shown") + ), + SwitchPreference( + "revanced_adremover_feed_survey", + StringResource("revanced_adremover_feed_survey_enabled_title", "Remove feed surveys"), + true, + StringResource("revanced_adremover_feed_survey_enabled_summary_on", "Feed surveys are hidden"), + StringResource("revanced_adremover_feed_survey_enabled_summary_off", "Feed surveys are shown") + ), + SwitchPreference( + "revanced_adremover_shorts", + StringResource("revanced_adremover_shorts_enabled_title", "Remove shorts"), + true, + StringResource("revanced_adremover_shorts_enabled_summary_on", "Shorts are hidden"), + StringResource("revanced_adremover_shorts_enabled_summary_off", "Shorts are shown") + ), + SwitchPreference( + "revanced_adremover_community_guidelines", + StringResource("revanced_adremover_community_guidelines_enabled_title", "Remove community guidelines"), + true, + StringResource( + "revanced_adremover_community_guidelines_enabled_summary_on", + "Community guidelines are hidden" + ), + StringResource( + "revanced_adremover_community_guidelines_enabled_summary_off", + "Community guidelines are shown" + ) + ), + SwitchPreference( + "revanced_adremover_emergency_box_removal", + StringResource("revanced_adremover_emergency_box_enabled_title", "Remove emergency boxes"), + true, + StringResource("revanced_adremover_emergency_box_enabled_summary_on", "Emergency boxes are hidden"), + StringResource("revanced_adremover_emergency_box_enabled_summary_off", "Emergency boxes are shown") + ), + SwitchPreference( + "revanced_adremover_info_panel", + StringResource("revanced_adremover_info_panel_enabled_title", "Remove info panels"), + true, + StringResource("revanced_adremover_info_panel_enabled_summary_on", "Info panels are hidden"), + StringResource("revanced_adremover_info_panel_enabled_summary_off", "Info panels are shown") + ), + SwitchPreference( + "revanced_adremover_medical_panel", + StringResource("revanced_adremover_medical_panel_enabled_title", "Remove medical panels"), + true, + StringResource("revanced_adremover_medical_panel_enabled_summary_on", "Medical panels are hidden"), + StringResource("revanced_adremover_medical_panel_enabled_summary_off", "Medical panels are shown") + ), + SwitchPreference( + "revanced_adremover_paid_content", + StringResource("revanced_adremover_paid_content_enabled_title", "Remove paid content"), + true, + StringResource("revanced_adremover_paid_content_enabled_summary_on", "Paid content is hidden"), + StringResource("revanced_adremover_paid_content_enabled_summary_off", "Paid content is shown") + ), + SwitchPreference( + "revanced_adremover_hide_suggestions", + StringResource("revanced_adremover_hide_suggestions_enabled_title", "Hide suggestions"), + true, + StringResource("revanced_adremover_hide_suggestions_enabled_summary_on", "Suggestions are hidden"), + StringResource("revanced_adremover_hide_suggestions_enabled_summary_off", "Suggestions are shown") + ), + SwitchPreference( + "revanced_adremover_hide_latest_posts", + StringResource("revanced_adremover_hide_latest_posts_enabled_title", "Hide latest posts"), + true, + StringResource("revanced_adremover_hide_latest_posts_enabled_summary_on", "Latest posts are hidden"), + StringResource("revanced_adremover_hide_latest_posts_enabled_summary_off", "Latest posts are shown") + ), + SwitchPreference( + "revanced_adremover_hide_channel_guidelines", + StringResource("revanced_adremover_hide_channel_guidelines_enabled_title", "Hide channel guidelines"), + true, + StringResource( + "revanced_adremover_hide_channel_guidelines_enabled_summary_on", + "Channel guidelines are hidden" + ), + StringResource( + "revanced_adremover_hide_channel_guidelines_enabled_summary_off", + "Channel guidelines are shown" + ) + ), + SwitchPreference( + "revanced_adremover_self_sponsor", + StringResource("revanced_adremover_self_sponsor_enabled_title", "Hide self sponsored cards"), + true, + StringResource("revanced_adremover_self_sponsor_enabled_summary_on", "Self sponsored cards are hidden"), + StringResource("revanced_adremover_self_sponsor_enabled_summary_off", "Self sponsored cards are shown") + ), + SwitchPreference( + "revanced_adremover_chapter_teaser", + StringResource( + "revanced_adremover_chapter_teaser_enabled_title", + "Hide chapter teaser under videos" + ), + true, + StringResource( + "revanced_adremover_chapter_teaser_enabled_summary_on", + "Chapter teasers are hidden" + ), + StringResource( + "revanced_adremover_chapter_teaser_enabled_summary_off", + "Chapter teasers are shown" + ) + ) + ) + + return PatchResultSuccess() + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/GeneralResourceAdsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/GeneralResourceAdsPatch.kt deleted file mode 100644 index 37b2711eb..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/GeneralResourceAdsPatch.kt +++ /dev/null @@ -1,55 +0,0 @@ -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.ResourceContext -import app.revanced.patcher.patch.PatchResult -import app.revanced.patcher.patch.PatchResultSuccess -import app.revanced.patcher.patch.annotations.DependsOn -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patches.youtube.ad.general.annotation.GeneralAdsCompatibility -import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch -import org.w3c.dom.Element - -@DependsOn(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_", - "promoted_video_", - ) - - // the attributes to change the value of - private val replacements = arrayOf( - "height", - "width", - "marginTop", - ) - - override fun execute(context: ResourceContext): PatchResult { - context.forEach { - if (!it.name.startsWithAny(*resourceFileNames)) return@forEach - - // for each file in the "layouts" directory replace all necessary attributes content - context.xmlEditor[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() - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/patch/HideButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/patch/HideButtonsPatch.kt index 335e848ca..51a7e1f2b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/patch/HideButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/patch/HideButtonsPatch.kt @@ -9,7 +9,7 @@ import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.youtube.ad.general.bytecode.patch.GeneralBytecodeAdsPatch +import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch import app.revanced.patches.youtube.layout.buttons.annotations.HideButtonsCompatibility import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch @@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.settings.framework.components.impl.Stri import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference @Patch -@DependsOn([ResourceMappingResourcePatch::class, GeneralBytecodeAdsPatch::class]) +@DependsOn([ResourceMappingResourcePatch::class, LithoFilterPatch::class]) @Name("hide-video-buttons") @Description("Adds options to hide action buttons under a video.") @HideButtonsCompatibility diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/comments/bytecode/patch/CommentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/comments/bytecode/patch/CommentsPatch.kt index b0816b31d..ddbcc7862 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/comments/bytecode/patch/CommentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/comments/bytecode/patch/CommentsPatch.kt @@ -13,16 +13,10 @@ import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.youtube.ad.general.bytecode.patch.GeneralBytecodeAdsPatch import app.revanced.patches.youtube.layout.comments.annotations.CommentsCompatibility import app.revanced.patches.youtube.layout.comments.bytecode.fingerprints.ShortsCommentsButtonFingerprint import app.revanced.patches.youtube.layout.comments.resource.patch.CommentsResourcePatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch -import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch -import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch -import app.revanced.patches.youtube.misc.settings.framework.components.impl.PreferenceScreen -import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource -import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference import org.jf.dexlib2.Opcode import org.jf.dexlib2.iface.instruction.OneRegisterInstruction diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hideartistcard/patch/HideArtistCardPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hideartistcard/patch/HideArtistCardPatch.kt index 57a9f096c..ca7b1acc7 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hideartistcard/patch/HideArtistCardPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hideartistcard/patch/HideArtistCardPatch.kt @@ -9,7 +9,7 @@ import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.youtube.ad.general.bytecode.patch.GeneralBytecodeAdsPatch +import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch import app.revanced.patches.youtube.layout.buttons.annotations.HideArtistCardCompatibility import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch @@ -17,7 +17,7 @@ import app.revanced.patches.youtube.misc.settings.framework.components.impl.Stri import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference @Patch -@DependsOn([ResourceMappingResourcePatch::class, GeneralBytecodeAdsPatch::class]) +@DependsOn([ResourceMappingResourcePatch::class, LithoFilterPatch::class]) @Name("hide-artist-card") @Description("Hides the artist card below the searchbar.") @HideArtistCardCompatibility diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hideinfocards/patch/HideInfocardsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hideinfocards/patch/HideInfocardsPatch.kt index 94de409d7..9d5f0409b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hideinfocards/patch/HideInfocardsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hideinfocards/patch/HideInfocardsPatch.kt @@ -16,8 +16,8 @@ import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.layout.hideinfocards.annotations.HideInfocardsCompatibility import app.revanced.patches.youtube.layout.hideinfocards.fingerprints.InfocardsIncognitoFingerprint -import app.revanced.patches.youtube.layout.hideinfocards.fingerprints.InfocardsMethodCallFingerprint import app.revanced.patches.youtube.layout.hideinfocards.fingerprints.InfocardsIncognitoParentFingerprint +import app.revanced.patches.youtube.layout.hideinfocards.fingerprints.InfocardsMethodCallFingerprint import app.revanced.patches.youtube.layout.hideinfocards.resource.patch.HideInfocardsResourcePatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import org.jf.dexlib2.Opcode @@ -39,16 +39,17 @@ class HideInfocardsPatch : BytecodePatch( with(InfocardsIncognitoFingerprint.also { it.resolve(context, InfocardsIncognitoParentFingerprint.result!!.classDef) }.result!!.mutableMethod) { - val invokeInstructionIndex = implementation!!.instructions.indexOfFirst { - it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal && - ((it as? BuilderInstruction35c)?.reference.toString() == - "Landroid/view/View;->setVisibility(I)V") - } + val invokeInstructionIndex = implementation!!.instructions.indexOfFirst { + it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal && + ((it as? BuilderInstruction35c)?.reference.toString() == + "Landroid/view/View;->setVisibility(I)V") + } - replaceInstruction(invokeInstructionIndex, """ - invoke-static {v${(instruction(invokeInstructionIndex) as? BuilderInstruction35c)?.registerC}}, Lapp/revanced/integrations/patches/HideInfocardsPatch;->hideInfocardsIncognito(Landroid/view/View;)V - """ - ) + replaceInstruction( + invokeInstructionIndex, + "invoke-static {v${(instruction(invokeInstructionIndex) as? BuilderInstruction35c)?.registerC}}," + + " Lapp/revanced/integrations/patches/HideInfocardsPatch;->hideInfocardsIncognito(Landroid/view/View;)V" + ) } with(InfocardsMethodCallFingerprint.result!!) { @@ -62,7 +63,12 @@ class HideInfocardsPatch : BytecodePatch( invoke-static {}, Lapp/revanced/integrations/patches/HideInfocardsPatch;->hideInfocardsMethodCall()Z move-result v$toggleRegister if-nez v$toggleRegister, :hide_info_cards - """, listOf(ExternalLabel("hide_info_cards", hideInfocardsCallMethod.instruction(invokeInterfaceIndex + 1))) + """, + listOf( + ExternalLabel( + "hide_info_cards", hideInfocardsCallMethod.instruction(invokeInterfaceIndex + 1) + ) + ) ) } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/annotation/LithoFilterCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/annotation/LithoFilterCompatibility.kt new file mode 100644 index 000000000..5d8541ac9 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/annotation/LithoFilterCompatibility.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.misc.litho.filter.annotation + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package( + "com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36") + )] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class LithoFilterCompatibility + diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ComponentContextParserFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ComponentContextParserFingerprint.kt new file mode 100644 index 000000000..10d0fcaf9 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ComponentContextParserFingerprint.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.youtube.misc.litho.filter.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility +import org.jf.dexlib2.Opcode + +@Name("component-context-parser-fingerprint") +@LithoFilterCompatibility +@Version("0.0.1") +object ComponentContextParserFingerprint : MethodFingerprint( + opcodes = listOf( + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.GOTO, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_OBJECT, + Opcode.NEW_INSTANCE + ), + strings = listOf("LoggingProperties are not in proto format") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/EmptyComponentBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/EmptyComponentBuilderFingerprint.kt new file mode 100644 index 000000000..dc0ef9b5d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/EmptyComponentBuilderFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.misc.litho.filter.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility +import org.jf.dexlib2.Opcode + +@Name("empty-component-builder-fingerprint") +@LithoFilterCompatibility +@Version("0.0.1") +object EmptyComponentBuilderFingerprint : MethodFingerprint( + opcodes = listOf( + Opcode.INVOKE_STATIC_RANGE + ), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt new file mode 100644 index 000000000..851d39ed5 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt @@ -0,0 +1,86 @@ +package app.revanced.patches.youtube.misc.litho.filter.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +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.DependsOn +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch +import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility +import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ComponentContextParserFingerprint +import app.revanced.patches.youtube.misc.litho.filter.fingerprints.EmptyComponentBuilderFingerprint +import org.jf.dexlib2.iface.instruction.Instruction +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction +import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.reference.FieldReference +import org.jf.dexlib2.iface.reference.MethodReference + +@DependsOn([IntegrationsPatch::class]) +@Description("Hooks the method which parses the bytes into a ComponentContext to filter components.") +@LithoFilterCompatibility +@Version("0.0.1") +class LithoFilterPatch : BytecodePatch( + listOf(ComponentContextParserFingerprint) +) { + override fun execute(context: BytecodeContext): PatchResult { + ComponentContextParserFingerprint.result?.let { result -> + val builderMethodIndex = EmptyComponentBuilderFingerprint + .also { it.resolve(context, result.mutableMethod, result.mutableClass) } + .let { it.result!!.scanResult.patternScanResult!!.startIndex } + + val emptyComponentFieldIndex = builderMethodIndex + 2 + + with(result.mutableMethod) { + val insertHookIndex = result.scanResult.patternScanResult!!.endIndex + val clobberedRegister = (instruction(insertHookIndex - 3) as OneRegisterInstruction).registerA + + val builderMethodDescriptor = instruction(builderMethodIndex).toDescriptor() + val emptyComponentFieldDescriptor = instruction(emptyComponentFieldIndex).toDescriptor() + + addInstructions( + insertHookIndex, // right after setting the component.pathBuilder field, + """ + invoke-static {v5, v2}, Lapp/revanced/integrations/patches/LithoFilterPatch;->filter(Ljava/lang/StringBuilder;Ljava/lang/String;)Z + move-result v$clobberedRegister + if-eqz v$clobberedRegister, :not_an_ad + move-object/from16 v2, p1 + invoke-static {v2}, $builderMethodDescriptor + move-result-object v0 + iget-object v0, v0, $emptyComponentFieldDescriptor + return-object v0 + """, + listOf(ExternalLabel("not_an_ad", instruction(insertHookIndex))) + ) + } + } ?: return PatchResultError("Could not find the method to hook.") + + return PatchResultSuccess() + } + + private companion object { + fun Instruction.toDescriptor() = when (val reference = (this as ReferenceInstruction).reference) { + 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") + } + } +} \ No newline at end of file