diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java
index f3d142e882..c82c28353a 100644
--- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java
@@ -15,8 +15,8 @@ import java.util.regex.Pattern;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ByteTrieSearch;
-import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
+import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
/**
* Allows custom filtering using a path and optionally a proto buffer string.
@@ -147,7 +147,7 @@ public final class CustomFilter extends Filter {
@Override
public boolean isFiltered(String identifier, String path, byte[] buffer,
- StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
+ StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
// All callbacks are custom filter groups.
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
if (custom.startsWith && contentIndex != 0) {
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java
similarity index 95%
rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java
rename to extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java
index 73169e3123..ddcfe41cc0 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java
@@ -1,4 +1,4 @@
-package app.revanced.extension.youtube.patches.litho;
+package app.revanced.extension.shared.patches.litho;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -11,12 +11,10 @@ import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
-import app.revanced.extension.shared.patches.litho.Filter;
import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.StringTrieSearch;
-import app.revanced.extension.youtube.patches.VersionCheckPatch;
-import app.revanced.extension.youtube.settings.Settings;
+import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
@SuppressWarnings("unused")
public final class LithoFilterPatch {
@@ -39,11 +37,11 @@ public final class LithoFilterPatch {
public String toString() {
// Estimate the percentage of the buffer that are Strings.
StringBuilder builder = new StringBuilder(Math.max(100, buffer.length / 2));
- builder.append( "ID: ");
+ builder.append("ID: ");
builder.append(identifier);
builder.append(" Path: ");
builder.append(path);
- if (Settings.DEBUG_PROTOBUFFER.get()) {
+ if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) {
builder.append(" BufferStrings: ");
findAsciiStrings(builder, buffer);
}
@@ -83,9 +81,10 @@ public final class LithoFilterPatch {
/**
* Placeholder for actual filters.
*/
- private static final class DummyFilter extends Filter { }
+ private static final class DummyFilter extends Filter {
+ }
- private static final Filter[] filters = new Filter[] {
+ private static final Filter[] filters = new Filter[]{
new DummyFilter() // Replaced during patching, do not touch.
};
@@ -106,12 +105,15 @@ public final class LithoFilterPatch {
private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1;
/**
- * 20.22+ cannot use the thread buffer, because frequently the buffer is not correct,
- * especially for components that are recreated such as dragging off screen then back on screen.
+ * For YouTube 20.22+, this is set to true by a patch,
+ * because it cannot use the thread buffer due to the buffer frequently not being correct,
+ * especially for components that are recreated such as dragging off-screen then back on screen.
* Instead, parse the identifier found near the start of the buffer and use that to
* identify the correct buffer to use when filtering.
+ *
+ * This is set during patching, do not change manually.
*/
- private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = VersionCheckPatch.IS_20_22_OR_GREATER;
+ private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = false;
/**
* Turns on additional logging, used for development purposes only.
@@ -122,9 +124,11 @@ public final class LithoFilterPatch {
* String suffix for components.
* Can be any of: ".eml", ".e-b", ".eml-js", "e-js-b"
*/
- private static final String LITHO_COMPONENT_EXTENSION = ".e";
- private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = LITHO_COMPONENT_EXTENSION.getBytes(StandardCharsets.US_ASCII);
+ private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = ".e".getBytes(StandardCharsets.US_ASCII);
+ /**
+ * Used as placeholder for litho id/path filters that do not use a buffer
+ */
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt
index 0ed3e3e77e..ebb0edc8a6 100644
--- a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt
@@ -1,9 +1,12 @@
package app.revanced.patches.music.misc.litho.filter
+import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.shared.conversionContextToStringMethod
+import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR
import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch
+import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
@@ -13,6 +16,12 @@ val lithoFilterPatch = lithoFilterPatch(
indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
},
getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get,
+ insertProtobufHook = {
+ protobufBufferReferenceLegacyMethod.addInstruction(
+ 0,
+ "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
+ )
+ },
) {
dependsOn(sharedExtensionPatch)
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt
index 3131edc413..59c52e34f5 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt
@@ -1,21 +1,7 @@
package app.revanced.patches.shared.misc.litho.filter
-import app.revanced.patcher.accessFlags
-import app.revanced.patcher.composingFirstMethod
-import app.revanced.patcher.custom
-import app.revanced.patcher.definingClass
-import app.revanced.patcher.gettingFirstMethod
-import app.revanced.patcher.gettingFirstMethodDeclaratively
-import app.revanced.patcher.gettingFirstMutableMethod
-import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
-import app.revanced.patcher.immutableClassDef
-import app.revanced.patcher.instructions
-import app.revanced.patcher.invoke
-import app.revanced.patcher.opcodes
-import app.revanced.patcher.parameterTypes
+import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
-import app.revanced.patcher.returnType
-import app.revanced.patcher.strings
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -24,10 +10,30 @@ internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMutableMethod
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
}
+internal val BytecodePatchContext.protobufBufferReferenceMethodMatch by composingFirstMethod {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returnType("V")
+ parameterTypes("[B")
+
+ var methodDefiningClass = ""
+ custom {
+ methodDefiningClass = definingClass
+ true
+ }
+
+ instructions(
+ allOf(
+ Opcode.IGET_OBJECT(),
+ field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" },
+ ),
+ method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" },
+ )
+}
+
/**
* Matches a method that use the protobuf of our component.
*/
-internal val BytecodePatchContext.protobufBufferReferenceMethod by gettingFirstMutableMethodDeclaratively {
+internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("I", "Ljava/nio/ByteBuffer;")
@@ -38,12 +44,11 @@ internal val BytecodePatchContext.componentContextParserMethodMatch by composing
instructions("Number of bits must be positive"())
}
-internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively("EmptyComponent") {
+internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
parameterTypes()
- custom {
- immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1
- }
+ instructions("EmptyComponent"())
+ custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 }
}
internal val BytecodePatchContext.componentCreateMethod by gettingFirstMutableMethod(
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt
index d759ab4c01..3d97331776 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt
@@ -2,9 +2,9 @@
package app.revanced.patches.shared.misc.litho.filter
-import app.revanced.patcher.extensions.addInstruction
+import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableEncodedValue.Companion.toMutable
+import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.addInstructions
-import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.removeInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.firstClassDef
@@ -13,15 +13,12 @@ import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.shared.conversionContextToStringMethod
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findFreeRegister
-import app.revanced.util.getReference
-import app.revanced.util.indexOfFirstInstructionReversedOrThrow
+import app.revanced.util.findFieldFromToString
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.Method
-import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
-import com.android.tools.smali.dexlib2.iface.reference.FieldReference
+import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
/**
* Used to add a hook point to the extension stub.
@@ -34,20 +31,24 @@ lateinit var addLithoFilter: (String) -> Unit
*/
private var filterCount = 0
-private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;"
+internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;"
/**
* A patch that allows to filter Litho components based on their identifier or path.
*
* @param componentCreateInsertionIndex The index to insert the filtering code in the component create method.
- * @param getConversionContextToStringMethod The Method of the conversion context to string method.
+ * @param insertProtobufHook This method injects a setProtoBuffer call in the protobuf decoding logic.
+ * @param getConversionContextToStringMethod The getter of the conversion context toString method.
+ * @param getExtractIdentifierFromBuffer Whether to extract the identifier from the protobuf buffer.
* @param executeBlock The additional execution block of the patch.
* @param block The additional block to build the patch.
*/
internal fun lithoFilterPatch(
componentCreateInsertionIndex: Method.() -> Int,
- getConversionContextToStringMethod: BytecodePatchContext.() -> Method,
+ insertProtobufHook: BytecodePatchContext.() -> Unit,
executeBlock: BytecodePatchContext.() -> Unit = {},
+ getConversionContextToStringMethod: BytecodePatchContext.() -> Method,
+ getExtractIdentifierFromBuffer: () -> Boolean = { false },
block: BytecodePatchBuilder.() -> Unit = {},
) = bytecodePatch(
description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",
@@ -111,11 +112,14 @@ internal fun lithoFilterPatch(
}
}
+ // Tell the extension whether to extract the identifier from the buffer.
+ if (getExtractIdentifierFromBuffer()) {
+ lithoFilterMethod.classDef.fields.first { it.name == "EXTRACT_IDENTIFIER_FROM_BUFFER" }
+ .initialValue = ImmutableBooleanEncodedValue.forBoolean(true).toMutable()
+ }
+
// Add an interceptor to steal the protobuf of our component.
- protobufBufferReferenceMethod.addInstruction(
- 0,
- "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
- )
+ insertProtobufHook()
// Hook the method that parses bytes into a ComponentContext.
// Allow the method to run to completion, and override the
@@ -123,18 +127,11 @@ internal fun lithoFilterPatch(
// It is important to allow the original code to always run to completion,
// otherwise high memory usage and poor app performance can occur.
- // Find the identifier/path fields of the conversion context.
- val conversionContextIdentifierField = componentContextParserMethodMatch.let {
- // Identifier field is loaded just before the string declaration.
- val index = it.method.indexOfFirstInstructionReversedOrThrow(it[0]) {
- // Our instruction reads a String from a field of the ConversionContext class.
- val reference = getReference()
- reference?.definingClass == conversionContextToStringMethod.immutableClassDef.type &&
- reference.type == "Ljava/lang/String;"
- }
+ val conversionContextToStringMethod = getConversionContextToStringMethod()
- it.method.getInstruction(index).getReference()!!
- }
+ // Find the identifier/path fields of the conversion context.
+ val conversionContextIdentifierField = conversionContextToStringMethod
+ .findFieldFromToString("identifierProperty=")
val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
@@ -162,7 +159,11 @@ internal fun lithoFilterPatch(
insertIndex,
"""
move-object/from16 v$freeRegister, p2 # ConversionContext parameter
- check-cast v$freeRegister, ${getConversionContextToStringMethod().immutableClassDef.type} # Check we got the actual ConversionContext
+
+ # In YT 20.41 the field is the abstract superclass.
+ # Check it's the actual ConversionContext just in case.
+ instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type}
+ if-eqz v$identifierRegister, :unfiltered
# Get identifier and path from ConversionContext
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt
index a61663dbb5..bc8f231755 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt
@@ -13,9 +13,9 @@ import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt
index e493fefdf2..c614b2ebb9 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt
@@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt
index f446e45912..ea9e4dd25b 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt
@@ -9,7 +9,7 @@ import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.*
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_20_26_or_greater
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt
index 91d388f7fc..d42b037990 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt
@@ -12,8 +12,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt
index ae7f24c87f..9fae036084 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt
@@ -5,7 +5,7 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt
index 3328f6306e..fb1a8e82a6 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt
@@ -16,8 +16,8 @@ import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.*
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt
index 7fdd49c3b1..bb81a31c0c 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt
@@ -14,8 +14,8 @@ import app.revanced.patches.shared.misc.settings.preference.NonInteractivePrefer
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.*
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt
index 756d6eb6d9..a0cda67c8d 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt
@@ -5,66 +5,6 @@ import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
-internal val BytecodePatchContext.componentCreateMethod by gettingFirstMutableMethodDeclaratively {
- instructions(
- "Element missing correct type extension"(),
- "Element missing type"(),
- )
-}
-
-internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMutableMethodDeclaratively {
- definingClass { endsWith("/LithoFilterPatch;") }
- accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
-}
-
-internal val BytecodePatchContext.protobufBufferReferenceMethodMatch by composingFirstMethod {
- accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
- returnType("V")
- parameterTypes("[B")
-
- var methodDefiningClass = ""
- custom {
- methodDefiningClass = definingClass
- true
- }
-
- instructions(
- allOf(
- Opcode.IGET_OBJECT(),
- field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" },
- ),
- method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" },
- )
-}
-
-internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMutableMethodDeclaratively {
- accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
- returnType("V")
- parameterTypes("I", "Ljava/nio/ByteBuffer;")
- opcodes(
- Opcode.IPUT,
- Opcode.INVOKE_VIRTUAL,
- Opcode.MOVE_RESULT,
- Opcode.SUB_INT_2ADDR,
- )
-}
-
-internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively {
- accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
- parameterTypes()
- instructions("EmptyComponent"())
- custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 }
-}
-
-internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMutableMethodDeclaratively {
- accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
- parameterTypes("I", "I", "I")
- instructions(1L()) // 1L = default thread timeout.
- custom {
- immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;"
- }
-}
-
internal val BytecodePatchContext.lithoComponentNameUpbFeatureFlagMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Z")
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt
index b25df92928..be87c62780 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt
@@ -3,91 +3,29 @@
package app.revanced.patches.youtube.misc.litho.filter
import app.revanced.patcher.extensions.addInstruction
-import app.revanced.patcher.extensions.addInstructions
-import app.revanced.patcher.extensions.removeInstructions
-import app.revanced.patcher.extensions.replaceInstruction
-import app.revanced.patcher.firstClassDef
-import app.revanced.patcher.immutableClassDef
-import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patcher.patch.BytecodePatchContext
+import app.revanced.patches.youtube.shared.conversionContextToStringMethod
+import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR
+import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch
+import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod
+import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceMethodMatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.*
-import app.revanced.patches.youtube.shared.conversionContextToStringMethod
-import app.revanced.util.*
-import com.android.tools.smali.dexlib2.AccessFlags
+import app.revanced.util.indexOfFirstInstructionOrThrow
+import app.revanced.util.insertLiteralOverride
+import app.revanced.util.returnLate
import com.android.tools.smali.dexlib2.Opcode
-lateinit var addLithoFilter: (String) -> Unit
- private set
-
-private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/LithoFilterPatch;"
-
-val lithoFilterPatch = bytecodePatch(
- description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",
-) {
- dependsOn(
- sharedExtensionPatch,
- versionCheckPatch,
- )
-
- var filterCount = 0
-
- /**
- * The following patch inserts a hook into the method that parses the bytes into a ComponentContext.
- * This method contains a StringBuilder object that represents the pathBuilder of the component.
- * The pathBuilder is used to filter components by their path.
- *
- * Additionally, the method contains a reference to the component's identifier.
- * The identifier is used to filter components by their identifier.
- *
- * The protobuf buffer is passed along from a different injection point before the filtering occurs.
- * The buffer is a large byte array that represents the component tree.
- * This byte array is searched for strings that indicate the current component.
- *
- * All modifications done here must allow all the original code to still execute
- * even when filtering, otherwise memory leaks or poor app performance may occur.
- *
- * The following pseudocode shows how this patch works:
- *
- * class SomeOtherClass {
- * // Called before ComponentContextParser.parseComponent() method.
- * public void someOtherMethod(ByteBuffer byteBuffer) {
- * ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
- * ...
- * }
- * }
- *
- * class ComponentContextParser {
- * public Component parseComponent() {
- * ...
- *
- * if (extensionClass.shouldFilter()) { // Inserted by this patch.
- * return emptyComponent;
- * }
- * return originalUnpatchedComponent; // Original code.
- * }
- * }
- */
- apply {
- // Remove dummy filter from extenion static field
- // and add the filters included during patching.
- lithoFilterMethod.apply {
- removeInstructions(2, 4) // Remove dummy filter.
-
- addLithoFilter = { classDescriptor ->
- addInstructions(
- 2,
- """
- new-instance v1, $classDescriptor
- invoke-direct { v1 }, $classDescriptor->()V
- const/16 v2, ${filterCount++}
- aput-object v1, v0, v2
- """,
- )
- }
+val lithoFilterPatch = lithoFilterPatch(
+ componentCreateInsertionIndex = {
+ if (is_19_17_or_greater) {
+ indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
+ } else {
+ // 19.16 clobbers p2 so must check at start of the method and not at the return index.
+ 0
}
-
- // region Pass the buffer into extension.
-
+ },
+ insertProtobufHook = {
if (is_20_22_or_greater) {
// Hook method that bridges between UPB buffer native code and FB Litho.
// Method is found in 19.25+, but is forcefully turned off for 20.21 and lower.
@@ -100,91 +38,15 @@ val lithoFilterPatch = bytecodePatch(
}
}
- // Legacy Non native buffer.
+ // Legacy non-native buffer.
protobufBufferReferenceLegacyMethod.addInstruction(
0,
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
)
-
- // endregion
-
- // region Modify the create component method and
- // if the component is filtered then return an empty component.
-
- // Find the identifier/path fields of the conversion context.
-
- val conversionContextIdentifierField = conversionContextToStringMethod
- .findFieldFromToString("identifierProperty=")
-
- val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef
- .fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
-
- // Find class and methods to create an empty component.
- val builderMethodDescriptor = emptyComponentMethod.immutableClassDef.methods.single {
- // The only static method in the class.
- method ->
- AccessFlags.STATIC.isSet(method.accessFlags)
- }
-
- val emptyComponentField = firstClassDef(builderMethodDescriptor.returnType).fields.single()
-
- componentCreateMethod.apply {
- val insertIndex = if (is_19_17_or_greater) {
- indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT)
- } else {
- // 19.16 clobbers p2 so must check at start of the method and not at the return index.
- 0
- }
-
- val freeRegister = findFreeRegister(insertIndex)
- val identifierRegister = findFreeRegister(insertIndex, freeRegister)
- val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister)
-
- addInstructionsAtControlFlowLabel(
- insertIndex,
- """
- move-object/from16 v$freeRegister, p2
-
- # 20.41 field is the abstract superclass.
- # Verify it's the expected subclass just in case.
- instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type}
- if-eqz v$identifierRegister, :unfiltered
-
- iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
- iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
- invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
- move-result v$freeRegister
- if-eqz v$freeRegister, :unfiltered
-
- # Return an empty component
- move-object/from16 v$freeRegister, p1
- invoke-static { v$freeRegister }, $builderMethodDescriptor
- move-result-object v$freeRegister
- iget-object v$freeRegister, v$freeRegister, $emptyComponentField
- return-object v$freeRegister
-
- :unfiltered
- nop
- """,
- )
- }
-
- // endregion
-
- // region Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube.
-
- lithoThreadExecutorMethod.addInstructions(
- 0,
- """
- invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I
- move-result p1
- invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I
- move-result p2
- """,
- )
-
- // endregion
-
+ },
+ getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get,
+ getExtractIdentifierFromBuffer = { is_20_21_or_greater },
+ executeBlock = {
// region A/B test of new Litho native code.
// Turn off native code that handles litho component names. If this feature is on then nearly
@@ -205,11 +67,8 @@ val lithoFilterPatch = bytecodePatch(
false,
)
}
-
// endregion
}
-
- afterDependents {
- lithoFilterMethod.replaceInstruction(0, "const/16 v0, $filterCount")
- }
+) {
+ dependsOn(sharedExtensionPatch, versionCheckPatch)
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt
index d1c376b7d4..77ac486946 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt
@@ -10,8 +10,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt
index a283b07d8f..5f3273718f 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt
@@ -16,9 +16,9 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
+import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.interaction.seekbar.customTapAndHoldMethodMatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater