diff --git a/src/main/kotlin/app/revanced/patches/shared/fingerprints/litho/IdentifierFingerprint.kt b/src/main/kotlin/app/revanced/patches/shared/fingerprints/litho/IdentifierFingerprint.kt deleted file mode 100644 index 878d8b55a..000000000 --- a/src/main/kotlin/app/revanced/patches/shared/fingerprints/litho/IdentifierFingerprint.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.shared.fingerprints.litho - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -object IdentifierFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - opcodes = listOf( - Opcode.IPUT_OBJECT, - Opcode.INVOKE_INTERFACE_RANGE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.NEW_INSTANCE, - Opcode.CONST_STRING - ), - strings = listOf("Element missing type extension") -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/shared/patch/litho/ComponentParserPatch.kt b/src/main/kotlin/app/revanced/patches/shared/patch/litho/ComponentParserPatch.kt index eae9f56d3..421558cee 100644 --- a/src/main/kotlin/app/revanced/patches/shared/patch/litho/ComponentParserPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/patch/litho/ComponentParserPatch.kt @@ -4,11 +4,13 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.fingerprints.litho.EmptyComponentBuilderFingerprint -import app.revanced.patches.shared.fingerprints.litho.IdentifierFingerprint import app.revanced.util.exception +import app.revanced.util.getEmptyStringInstructionIndex +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction @@ -17,10 +19,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference import kotlin.properties.Delegates object ComponentParserPatch : BytecodePatch( - setOf( - EmptyComponentBuilderFingerprint, - IdentifierFingerprint - ) + setOf(EmptyComponentBuilderFingerprint) ) { private lateinit var emptyComponentLabel: String internal lateinit var insertMethod: MutableMethod @@ -60,15 +59,28 @@ object ComponentParserPatch : BytecodePatch( } } + private fun MutableMethod.getTargetIndexDownTo( + startIndex: Int, + opcode: Opcode + ): Int { + for (index in startIndex downTo 0) { + if (getInstruction(index).opcode != opcode) + continue + + return index + } + throw PatchException("Failed to find hook method") + } + override fun execute(context: BytecodeContext) { /** * Shared fingerprint */ - EmptyComponentBuilderFingerprint.result?.let { - it.mutableMethod.apply { + EmptyComponentBuilderFingerprint.result?.let { result -> + result.mutableMethod.apply { insertMethod = this - emptyComponentIndex = it.scanResult.patternScanResult!!.startIndex + 1 + emptyComponentIndex = result.scanResult.patternScanResult!!.startIndex + 1 val builderMethodDescriptor = getInstruction(emptyComponentIndex).reference @@ -94,22 +106,19 @@ object ComponentParserPatch : BytecodePatch( getInstruction(stringBuilderIndex).registerA insertIndex = stringBuilderIndex + 1 - } - } ?: throw EmptyComponentBuilderFingerprint.exception - - /** - * Only used in YouTube - */ - IdentifierFingerprint.result?.let { - it.mutableMethod.apply { - val identifierIndex = it.scanResult.patternScanResult!!.startIndex - val objectIndex = it.scanResult.patternScanResult!!.endIndex + 1 + val emptyStringIndex = getEmptyStringInstructionIndex() + val identifierIndex = getTargetIndexDownTo(emptyStringIndex, Opcode.IPUT_OBJECT) identifierRegister = - getInstruction(identifierIndex).registerA + getInstruction(identifierIndex).registerA + + val objectIndex = implementation!!.instructions.let { + emptyStringIndex + it.subList(emptyStringIndex, it.size - 1).indexOfFirst { instruction -> + instruction.opcode == Opcode.INVOKE_VIRTUAL + } + } objectRegister = getInstruction(objectIndex).registerC } - } - + } ?: throw EmptyComponentBuilderFingerprint.exception } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/shared/patch/transformation/AbstractTransformInstructionsPatch.kt b/src/main/kotlin/app/revanced/patches/shared/patch/transformation/AbstractTransformInstructionsPatch.kt new file mode 100644 index 000000000..e07a0b828 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/shared/patch/transformation/AbstractTransformInstructionsPatch.kt @@ -0,0 +1,58 @@ +package app.revanced.patches.shared.patch.transformation + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.util.findMutableMethodOf +import com.android.tools.smali.dexlib2.iface.ClassDef +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.Instruction + +@Suppress("MemberVisibilityCanBePrivate") +abstract class AbstractTransformInstructionsPatch : BytecodePatch(emptySet()) { + abstract fun filterMap( + classDef: ClassDef, + method: Method, + instruction: Instruction, + instructionIndex: Int + ): T? + + abstract fun transform(mutableMethod: MutableMethod, entry: T) + + // Returns the patch indices as a Sequence, which will execute lazily. + fun findPatchIndices(classDef: ClassDef, method: Method): Sequence? { + return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) -> + filterMap(classDef, method, instruction, index) + } + } + + override fun execute(context: BytecodeContext) { + // Find all methods to patch + buildMap { + context.classes.forEach { classDef -> + val methods = buildList { + classDef.methods.forEach { method -> + // Since the Sequence executes lazily, + // using any() results in only calling + // filterMap until the first index has been found. + if (findPatchIndices(classDef, method)?.any() == true) add(method) + } + } + + if (methods.isNotEmpty()) { + put(classDef, methods) + } + } + }.forEach { (classDef, methods) -> + // And finally transform the methods... + val mutableClass = context.proxy(classDef).mutableClass + + methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod -> + val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque()) + ?: return@methods + + while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast()) + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/general/dialog/RemoveViewerDiscretionDialogPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/general/dialog/RemoveViewerDiscretionDialogPatch.kt index 179eecd92..a6260bd4c 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/general/dialog/RemoveViewerDiscretionDialogPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/general/dialog/RemoveViewerDiscretionDialogPatch.kt @@ -10,7 +10,7 @@ import app.revanced.patches.youtube.utils.settings.SettingsPatch @Patch( name = "Remove viewer discretion dialog", - description = "Removes the dialog that appears when you try to watch a video that has been age-restricted " + + description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " + "by accepting it automatically. This does not bypass the age restriction.", dependencies = [SettingsPatch::class], compatiblePackages = [ diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/ambientmode/AmbientModeSwitchPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/ambientmode/AmbientModeSwitchPatch.kt index 7ca335375..83ca9caac 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/ambientmode/AmbientModeSwitchPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/ambientmode/AmbientModeSwitchPatch.kt @@ -112,4 +112,4 @@ object AmbientModeSwitchPatch : BytecodePatch( SettingsPatch.updatePatchStatus("Ambient mode switch") } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/ExternalBrowserPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/ExternalBrowserPatch.kt index 43ba903e6..a3085ebe9 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/ExternalBrowserPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/ExternalBrowserPatch.kt @@ -2,18 +2,18 @@ package app.revanced.patches.youtube.misc.externalbrowser import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserPrimaryFingerprint -import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserSecondaryFingerprint -import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserTertiaryFingerprint +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.shared.patch.transformation.AbstractTransformInstructionsPatch import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH import app.revanced.patches.youtube.utils.settings.SettingsPatch -import app.revanced.util.exception -import app.revanced.util.getStringInstructionIndex +import com.android.tools.smali.dexlib2.iface.ClassDef +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference @Patch( name = "Enable external browser", @@ -47,35 +47,36 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction ] ) @Suppress("unused") -object ExternalBrowserPatch : BytecodePatch( - setOf( - ExternalBrowserPrimaryFingerprint, - ExternalBrowserSecondaryFingerprint, - ExternalBrowserTertiaryFingerprint - ) +object ExternalBrowserPatch : AbstractTransformInstructionsPatch>( ) { + override fun filterMap( + classDef: ClassDef, + method: Method, + instruction: Instruction, + instructionIndex: Int + ): Pair? { + if (instruction !is ReferenceInstruction) return null + val reference = instruction.reference as? StringReference ?: return null + + if (reference.string != "android.support.customtabs.action.CustomTabsService") return null + + return instructionIndex to (instruction as OneRegisterInstruction).registerA + } + + override fun transform(mutableMethod: MutableMethod, entry: Pair) { + val (intentStringIndex, register) = entry + + // Hook the intent string. + mutableMethod.addInstructions( + intentStringIndex + 1, """ + invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register + """ + ) + } + override fun execute(context: BytecodeContext) { - - arrayOf( - ExternalBrowserPrimaryFingerprint, - ExternalBrowserSecondaryFingerprint, - ExternalBrowserTertiaryFingerprint - ).forEach { fingerprint -> - fingerprint.result?.let { - it.mutableMethod.apply { - val targetIndex = - getStringInstructionIndex("android.support.customtabs.action.CustomTabsService") - val register = getInstruction(targetIndex).registerA - - addInstructions( - targetIndex + 1, """ - invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$register - """ - ) - } - } ?: throw fingerprint.exception - } + super.execute(context) /** * Add settings @@ -87,6 +88,5 @@ object ExternalBrowserPatch : BytecodePatch( ) SettingsPatch.updatePatchStatus("Enable external browser") - } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserPrimaryFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserPrimaryFingerprint.kt deleted file mode 100644 index c1df5546d..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserPrimaryFingerprint.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.revanced.patches.youtube.misc.externalbrowser.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -object ExternalBrowserPrimaryFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, - opcodes = listOf( - Opcode.CHECK_CAST, - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.CONST_STRING - ), - strings = listOf("android.support.customtabs.action.CustomTabsService") -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserSecondaryFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserSecondaryFingerprint.kt deleted file mode 100644 index 554870540..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserSecondaryFingerprint.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.youtube.misc.externalbrowser.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags - -object ExternalBrowserSecondaryFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - strings = listOf("android.support.customtabs.action.CustomTabsService") -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserTertiaryFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserTertiaryFingerprint.kt deleted file mode 100644 index 7742a5d13..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/externalbrowser/fingerprints/ExternalBrowserTertiaryFingerprint.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.revanced.patches.youtube.misc.externalbrowser.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -object ExternalBrowserTertiaryFingerprint : MethodFingerprint( - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, - opcodes = listOf( - Opcode.CHECK_CAST, - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.CONST_STRING - ), - strings = listOf("android.support.customtabs.action.CustomTabsService") -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/language/LanguageSelectorPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/language/LanguageSelectorPatch.kt index 7ae1fd8f7..57c550fb2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/language/LanguageSelectorPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/language/LanguageSelectorPatch.kt @@ -54,18 +54,18 @@ object LanguageSelectorPatch : BytecodePatch( override fun execute(context: BytecodeContext) { val result = GeneralPrefsFingerprint.result // YouTube v18.33.xx ~ - ?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.33.xx + ?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.32.xx ?: throw GeneralPrefsFingerprint.exception result.let { it.mutableMethod.apply { - val targetIndex = it.scanResult.patternScanResult!!.endIndex - val targetRegister = getInstruction(targetIndex).registerA + val insertIndex = it.scanResult.patternScanResult!!.endIndex - 2 + val insertRegister = getInstruction(insertIndex).registerA addInstructions( - targetIndex, """ + insertIndex, """ invoke-static {}, $MISC_PATH/LanguageSelectorPatch;->enableLanguageSwitch()Z - move-result v$targetRegister + move-result v$insertRegister """ ) } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsFingerprint.kt index b4057cc94..f221d9300 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsFingerprint.kt @@ -3,15 +3,18 @@ package app.revanced.patches.youtube.misc.language.fingerprints import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.Opcode +/** + * Compatible with YouTube v18.33.40~ + */ object GeneralPrefsFingerprint : MethodFingerprint( returnType = "V", parameters = emptyList(), opcodes = listOf( - Opcode.IGET_OBJECT, - Opcode.SGET, Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT, Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT ), strings = listOf("bedtime_reminder_toggle"), customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsLegacyFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsLegacyFingerprint.kt index 8231530ed..edb07fb65 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsLegacyFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/language/fingerprints/GeneralPrefsLegacyFingerprint.kt @@ -3,6 +3,9 @@ package app.revanced.patches.youtube.misc.language.fingerprints import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.Opcode +/** + * Compatible with ~YouTube v18.32.39 + */ object GeneralPrefsLegacyFingerprint : MethodFingerprint( returnType = "V", parameters = emptyList(), @@ -12,7 +15,9 @@ object GeneralPrefsLegacyFingerprint : MethodFingerprint( Opcode.CONST_4, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, - Opcode.IF_NEZ + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT ), strings = listOf("bedtime_reminder_toggle"), customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") } diff --git a/src/main/kotlin/app/revanced/patches/youtube/player/seekmessage/SeekMessagePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/player/seekmessage/SeekMessagePatch.kt index 1c3bc6266..1920561bf 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/player/seekmessage/SeekMessagePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/player/seekmessage/SeekMessagePatch.kt @@ -19,8 +19,8 @@ import app.revanced.util.exception import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction -import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Patch( @@ -83,8 +83,8 @@ object SeekMessagePatch : BytecodePatch( /** * Added in YouTube v18.29.xx~ */ - SeekEduUndoOverlayFingerprint.result?.let { - it.mutableMethod.apply { + SeekEduUndoOverlayFingerprint.result?.let { result -> + result.mutableMethod.apply { val seekUndoCalls = implementation!!.instructions.withIndex() .filter { instruction -> (instruction.value as? WideLiteralInstruction)?.wideLiteral == SeekUndoEduOverlayStub @@ -92,38 +92,31 @@ object SeekMessagePatch : BytecodePatch( val insertIndex = seekUndoCalls.elementAt(seekUndoCalls.size - 1).index val insertRegister = getInstruction(insertIndex).registerA - for (index in insertIndex until implementation!!.instructions.size) { - val targetInstruction = getInstruction(index) - if (targetInstruction.opcode != Opcode.INVOKE_VIRTUAL) - continue - - if (((targetInstruction as Instruction35c).reference as MethodReference).name != "setOnClickListener") - continue - - // Force close occurs only in YouTube v18.36.xx unless we add this. - if (SettingsPatch.is1836) - addComponent(insertIndex, index - 1) - - addInstructionsWithLabels( - insertIndex, fixComponent + """ - invoke-static {}, $PLAYER->hideSeekUndoMessage()Z - move-result v$insertRegister - if-nez v$insertRegister, :default - """, ExternalLabel("default", getInstruction(index + 1)) - ) - - /** - * Add settings - */ - SettingsPatch.addPreference( - arrayOf( - "PREFERENCE: PLAYER_SETTINGS", - "SETTINGS: HIDE_SEEK_UNDO_MESSAGE" - ) - ) - - break + val jumpIndex = implementation!!.instructions.let { + insertIndex + it.subList(insertIndex, it.size - 1).indexOfFirst { instruction -> + instruction.opcode == Opcode.INVOKE_VIRTUAL + && ((instruction as? ReferenceInstruction)?.reference as? MethodReference)?.name == "setOnClickListener" + } } + val constComponent = getConstComponent(insertIndex, jumpIndex - 1) + + addInstructionsWithLabels( + insertIndex, constComponent + """ + invoke-static {}, $PLAYER->hideSeekUndoMessage()Z + move-result v$insertRegister + if-nez v$insertRegister, :default + """, ExternalLabel("default", getInstruction(jumpIndex + 1)) + ) + + /** + * Add settings + */ + SettingsPatch.addPreference( + arrayOf( + "PREFERENCE: PLAYER_SETTINGS", + "SETTINGS: HIDE_SEEK_UNDO_MESSAGE" + ) + ) } } @@ -141,30 +134,24 @@ object SeekMessagePatch : BytecodePatch( } - private var fixComponent: String = "" - - private fun MutableMethod.addComponent( + private fun MutableMethod.getConstComponent( startIndex: Int, endIndex: Int - ) { - val fixRegister = + ): String { + val constRegister = getInstruction(endIndex).registerE for (index in endIndex downTo startIndex) { - val opcode = getInstruction(index).opcode - if (opcode != Opcode.CONST_16) + if (getInstruction(index).opcode != Opcode.CONST_16) continue - val register = getInstruction(index).registerA - - if (register != fixRegister) + if (getInstruction(index).registerA != constRegister) continue - val fixValue = getInstruction(index).wideLiteral.toInt() + val constValue = getInstruction(index).wideLiteral.toInt() - fixComponent = "const/16 v$fixRegister, $fixValue" - - break + return "const/16 v$constRegister, $constValue" } + return "" } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/seekbar/append/AppendTimeStampInformationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/seekbar/append/AppendTimeStampInformationPatch.kt index 3b5ba181a..d07cb0279 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/seekbar/append/AppendTimeStampInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/seekbar/append/AppendTimeStampInformationPatch.kt @@ -1,11 +1,9 @@ package app.revanced.patches.youtube.seekbar.append import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.youtube.utils.fingerprints.TotalTimeFingerprint @@ -13,11 +11,16 @@ import app.revanced.patches.youtube.utils.integrations.Constants.SEEKBAR import app.revanced.patches.youtube.utils.overridequality.OverrideQualityHookPatch import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch +import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.TotalTime import app.revanced.patches.youtube.utils.settings.SettingsPatch import app.revanced.util.exception +import app.revanced.util.getReference +import app.revanced.util.getWideLiteralInstructionIndex +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Patch( name = "Append time stamps information", @@ -60,36 +63,28 @@ object AppendTimeStampInformationPatch : BytecodePatch( setOf(TotalTimeFingerprint) ) { override fun execute(context: BytecodeContext) { - TotalTimeFingerprint.result?.let { - it.mutableMethod.apply { - var setTextIndex = -1 - - for ((textViewIndex, textViewInstruction) in implementation!!.instructions.withIndex()) { - if (textViewInstruction.opcode != Opcode.INVOKE_VIRTUAL) continue - - if (getInstruction(textViewIndex).reference.toString() == - "Landroid/widget/TextView;->getText()Ljava/lang/CharSequence;" - ) { - setTextIndex = textViewIndex + 2 - val setTextRegister = getInstruction(setTextIndex).registerC - val textViewRegister = - getInstruction(textViewIndex).registerC - - addInstructions( - setTextIndex, """ - invoke-static {v$setTextRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$setTextRegister - """ - ) - addInstruction( - textViewIndex, - "invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V" - ) - break + TotalTimeFingerprint.result?.let { result -> + result.mutableMethod.apply { + val constIndex = getWideLiteralInstructionIndex(TotalTime) + val charSequenceIndex = implementation!!.instructions.let { + constIndex + it.subList(constIndex, it.size - 1).indexOfFirst { instruction -> + instruction.opcode == Opcode.MOVE_RESULT_OBJECT } } - if (setTextIndex == -1) - throw PatchException("target Instruction not found!") + val charSequenceRegister = getInstruction(charSequenceIndex).registerA + val textViewIndex = indexOfFirstInstruction { + getReference()?.name == "getText" + } + val textViewRegister = + getInstruction(textViewIndex).registerC + + addInstructions( + textViewIndex, """ + invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V + invoke-static {v$charSequenceRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$charSequenceRegister + """ + ) } } ?: throw TotalTimeFingerprint.exception diff --git a/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/DisableShortsOnStartupPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/DisableShortsOnStartupPatch.kt index debf6ea62..72d9bebbe 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/DisableShortsOnStartupPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/DisableShortsOnStartupPatch.kt @@ -11,7 +11,6 @@ import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserW import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS import app.revanced.patches.youtube.utils.settings.SettingsPatch import app.revanced.util.exception -import app.revanced.util.getWideLiteralInstructionIndex import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( @@ -53,7 +52,7 @@ object DisableShortsOnStartupPatch : BytecodePatch( UserWasInShortsFingerprint.result?.let { it.mutableMethod.apply { - val insertIndex = getWideLiteralInstructionIndex(45381394) + val insertIndex = it.scanResult.patternScanResult!!.startIndex val insertRegister = getInstruction(insertIndex).registerA addInstructionsWithLabels( diff --git a/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/fingerprints/UserWasInShortsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/fingerprints/UserWasInShortsFingerprint.kt index f85088131..9157b6f62 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/fingerprints/UserWasInShortsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/shorts/startupshortsreset/fingerprints/UserWasInShortsFingerprint.kt @@ -1,13 +1,14 @@ package app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.util.fingerprint.LiteralValueFingerprint +import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode -object UserWasInShortsFingerprint : LiteralValueFingerprint( +object UserWasInShortsFingerprint : MethodFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf("Ljava/lang/Object;"), - strings = listOf("Failed to read user_was_in_shorts proto after successful warmup"), - literalSupplier = { 45381394 } + opcodes = listOf(Opcode.CONST_WIDE_32), + strings = listOf("Failed to read user_was_in_shorts proto after successful warmup") ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/BrowseIdHookPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/BrowseIdHookPatch.kt index 1c3c2efde..bdf116d9e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/BrowseIdHookPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/BrowseIdHookPatch.kt @@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.shared.patch.litho.ComponentParserPatch import app.revanced.patches.youtube.utils.browseid.fingerprints.BrowseIdClassFingerprint @@ -13,6 +14,7 @@ import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.litho.LithoFilterPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch import app.revanced.util.exception +import app.revanced.util.getStringInstructionIndex import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference @@ -38,22 +40,29 @@ object BrowseIdHookPatch : BytecodePatch( * This class handles BrowseId. * Pass an instance of this class to integrations to use Java Reflection. */ - BrowseIdClassFingerprint.result - ?.mutableClass?.methods?.find { method -> method.name == "" } - ?.apply { - val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction -> - instruction.opcode == Opcode.IPUT_OBJECT - } - val browseIdFieldName = - (getInstruction(browseIdFieldIndex).reference as FieldReference).name + BrowseIdClassFingerprint.result?.let { + it.mutableMethod.apply { + val targetIndex = getStringInstructionIndex("VL") - 1 + val targetReference = getInstruction(targetIndex).reference + val targetClass = context.findClass((targetReference as FieldReference).definingClass)!!.mutableClass - addInstructions( - 1, """ - const-string v0, "$browseIdFieldName" - invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V - """ - ) - } ?: throw BrowseIdClassFingerprint.exception + targetClass.methods.find { method -> method.name == "" } + ?.apply { + val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.IPUT_OBJECT + } + val browseIdFieldName = + (getInstruction(browseIdFieldIndex).reference as FieldReference).name + + addInstructions( + 1, """ + const-string v0, "$browseIdFieldName" + invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V + """ + ) + } ?: throw PatchException("BrowseIdClass not found!") + } + } ?: throw BrowseIdClassFingerprint.exception /** * Set BrowseId to integrations. diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/fingerprints/BrowseIdClassFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/fingerprints/BrowseIdClassFingerprint.kt index ff0136c8c..d9bba4002 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/fingerprints/BrowseIdClassFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/browseid/fingerprints/BrowseIdClassFingerprint.kt @@ -1,7 +1,12 @@ package app.revanced.patches.youtube.utils.browseid.fingerprints +import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags object BrowseIdClassFingerprint : MethodFingerprint( - strings = listOf("\u0001\t\u0000\u0001\u0002\u0010\t\u0000\u0000\u0001\u0002\u1008\u0000\u0003\u1008\u0002\u0005\u1008\u0003\u0006\u1409\u0005\u0007\u1007\u0004\u0008\u1009\u0006\u000c\u1008\n\u000e\u180c\u000b\u0010\u1007\r") + returnType = "Ljava/lang/Object;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC, + parameters = listOf("Ljava/lang/Object;", "L"), + strings = listOf("VL") ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/litho/fingerprints/GeneralByteBufferFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/litho/fingerprints/GeneralByteBufferFingerprint.kt index 12e715b4c..3fccce60e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/litho/fingerprints/GeneralByteBufferFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/litho/fingerprints/GeneralByteBufferFingerprint.kt @@ -26,6 +26,5 @@ object GeneralByteBufferFingerprint : MethodFingerprint( Opcode.IPUT, Opcode.IPUT, Opcode.GOTO - ), - customFingerprint = { methodDef, _ -> methodDef.name == "f" } + ) ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt index c0b1741f9..2fa7405ae 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt @@ -12,29 +12,63 @@ import java.io.Closeable object PlayerResponsePatch : BytecodePatch( setOf(PlayerParameterBuilderFingerprint) ), Closeable, MutableSet by mutableSetOf() { - private const val VIDEO_ID_PARAMETER = 1 - private const val PLAYER_PARAMETER = 3 - private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11 + private var VIDEO_ID_PARAMETER = 1 + private var PLAYER_PARAMETER = 3 + private var IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11 + private var freeRegister = 0 + private var shouldApplyNewMethod = false private lateinit var playerResponseMethod: MutableMethod override fun execute(context: BytecodeContext) { playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod ?: throw PlayerParameterBuilderFingerprint.exception + + playerResponseMethod.apply { + freeRegister = implementation!!.registerCount - parameters.size - 2 + shouldApplyNewMethod = freeRegister > 2 + if (shouldApplyNewMethod) { + IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = freeRegister + PLAYER_PARAMETER = freeRegister - 1 + VIDEO_ID_PARAMETER = freeRegister - 2 + } + } } override fun close() { - fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction( - 0, - "invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook" - ) + fun hookVideoId(hook: Hook) { + playerResponseMethod.apply { + val instruction = + if (shouldApplyNewMethod) + "invoke-static {v$VIDEO_ID_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook" + else + "invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook" + addInstruction( + 0, + instruction + ) + } + } - fun hookPlayerParameter(hook: Hook) = playerResponseMethod.addInstructions( - 0, """ - invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook - move-result-object p$PLAYER_PARAMETER - """ - ) + fun hookPlayerParameter(hook: Hook) { + playerResponseMethod.apply { + val instruction = + if (shouldApplyNewMethod) + """ + invoke-static {v$VIDEO_ID_PARAMETER, v$PLAYER_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook + move-result-object p3 + """ + else + """ + invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook + move-result-object p$PLAYER_PARAMETER + """ + addInstructions( + 0, + instruction + ) + } + } // Reverse the order in order to preserve insertion order of the hooks. val beforeVideoIdHooks = filterIsInstance().asReversed() @@ -45,6 +79,16 @@ object PlayerResponsePatch : BytecodePatch( afterVideoIdHooks.forEach(::hookPlayerParameter) videoIdHooks.forEach(::hookVideoId) beforeVideoIdHooks.forEach(::hookPlayerParameter) + + if (shouldApplyNewMethod) { + playerResponseMethod.addInstructions( + 0, """ + move-object v$VIDEO_ID_PARAMETER, p1 + move-object v$PLAYER_PARAMETER, p3 + move/from16 v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER, p11 + """ + ) + } } internal abstract class Hook(private val methodDescriptor: String) { diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt index a1692390f..7e867966a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt @@ -22,9 +22,11 @@ import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouT import app.revanced.patches.youtube.utils.settings.SettingsPatch import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch import app.revanced.util.exception +import app.revanced.util.getReference import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference @Patch( name = "Return YouTube Dislike", @@ -100,14 +102,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch( it.mutableMethod.apply { val conversionContextFieldIndex = implementation!!.instructions.indexOfFirst { instruction -> instruction.opcode == Opcode.IGET_OBJECT - && (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/Map;") + && instruction.getReference()?.type == "Ljava/util/Map;" } - 1 val conversionContextFieldReference = getInstruction(conversionContextFieldIndex).reference val charSequenceIndex = implementation!!.instructions.indexOfFirst { instruction -> instruction.opcode == Opcode.IGET_OBJECT - && (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/BitSet;") + && instruction.getReference()?.type == "Ljava/util/BitSet;" } - 1 val charSequenceRegister = getInstruction(charSequenceIndex).registerA val freeRegister = getInstruction(charSequenceIndex).registerB diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt index a773b07ae..9af70f7f3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/settings/SettingsPatch.kt @@ -86,7 +86,6 @@ object SettingsPatch : AbstractSettingsResourcePatch( val playServicesVersion = node.textContent.toInt() - is1836 = playServicesVersion in 233700000..233801999 upward1828 = 232900000 <= playServicesVersion upward1831 = 233200000 <= playServicesVersion upward1834 = 233502000 <= playServicesVersion @@ -156,7 +155,6 @@ object SettingsPatch : AbstractSettingsResourcePatch( private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT) internal lateinit var contexts: ResourceContext - internal var is1836: Boolean = false internal var upward1828: Boolean = false internal var upward1831: Boolean = false internal var upward1834: Boolean = false diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/withoutshorts/fingerprint/VideoIdWithoutShortsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/withoutshorts/fingerprint/VideoIdWithoutShortsFingerprint.kt index a3752f9dc..7a18f1325 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/withoutshorts/fingerprint/VideoIdWithoutShortsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/withoutshorts/fingerprint/VideoIdWithoutShortsFingerprint.kt @@ -10,31 +10,6 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED, parameters = listOf("L"), opcodes = listOf( - Opcode.MONITOR_ENTER, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CONST_4, - Opcode.NEW_ARRAY, - Opcode.SGET_OBJECT, - Opcode.CONST_4, - Opcode.APUT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.IF_EQZ, - Opcode.CONST_4, - Opcode.IPUT_OBJECT, - Opcode.MONITOR_EXIT, - Opcode.RETURN_VOID, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.NEW_ARRAY, - Opcode.SGET_OBJECT, - Opcode.APUT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.IF_EQZ, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, Opcode.IF_EQZ, Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT, @@ -42,10 +17,7 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint( Opcode.MONITOR_EXIT, Opcode.RETURN_VOID, Opcode.MONITOR_EXIT, - Opcode.RETURN_VOID, - Opcode.MOVE_EXCEPTION, - Opcode.MONITOR_EXIT, - Opcode.THROW + Opcode.RETURN_VOID ), customFingerprint = { methodDef, classDef -> methodDef.name == "l" && classDef.methods.count() == 17 diff --git a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index 24842a1bb..3da94a31a 100644 --- a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -87,6 +87,13 @@ fun Method.getWideLiteralInstructionIndex(literal: Long) = implementation?.let { } } ?: -1 +fun Method.getEmptyStringInstructionIndex() = implementation?.let { + it.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.CONST_STRING + && (instruction as? BuilderInstruction21c)?.reference.toString().isEmpty() + } +} ?: -1 + fun Method.getStringInstructionIndex(value: String) = implementation?.let { it.instructions.indexOfFirst { instruction -> instruction.opcode == Opcode.CONST_STRING @@ -138,6 +145,15 @@ inline fun Instruction.getReference() = fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = this.implementation!!.instructions.indexOfFirst(predicate) +/** + * Get the index of the last [Instruction] that matches the predicate. + * + * @param predicate The predicate to match. + * @return The index of the first [Instruction] that matches the predicate. + */ +fun Method.indexOfLastInstruction(predicate: Instruction.() -> Boolean) = + this.implementation!!.instructions.indexOfFirst(predicate) + /** * Return the resolved methods of [MethodFingerprint]s early. */