diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextComponentParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextComponentParentFingerprint.kt deleted file mode 100644 index f4e6f7801..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextComponentParentFingerprint.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints - -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import org.jf.dexlib2.Opcode - -object ShortsTextComponentParentFingerprint : MethodFingerprint( - returnType = "V", - parameters = listOf("L", "L"), - opcodes = listOf( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.GOTO, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.RETURN_VOID, - Opcode.IGET_OBJECT, - Opcode.CHECK_CAST, - Opcode.IGET_BOOLEAN, - Opcode.IF_EQZ, - Opcode.INVOKE_STATIC - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextViewFingerprint.kt new file mode 100644 index 000000000..9d5c2ec2a --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextViewFingerprint.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.Opcode + +object ShortsTextViewFingerprint : MethodFingerprint( + returnType = "V", + parameters = listOf("L", "L"), + opcodes = listOf( + Opcode.INVOKE_SUPER, // first instruction of method + Opcode.IF_NEZ, + Opcode.RETURN_VOID, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.SGET_OBJECT, // insertion point, must be after constructor call to parent class + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.CONST_4, + Opcode.IF_EQ, + Opcode.CONST_4, + Opcode.IF_EQ, + Opcode.RETURN_VOID, + Opcode.IGET_OBJECT, // TextView field + Opcode.CHECK_CAST, + Opcode.IGET_BOOLEAN, // boolean field + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/patch/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/patch/ReturnYouTubeDislikePatch.kt index 5038b85fc..9bf0fef91 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/patch/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/patch/ReturnYouTubeDislikePatch.kt @@ -5,7 +5,6 @@ import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.data.toMethodWalker import app.revanced.patcher.extensions.MethodFingerprintExtensions.name import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.addInstructions @@ -19,14 +18,12 @@ 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.MutableMethod import app.revanced.patches.youtube.layout.returnyoutubedislike.annotations.ReturnYouTubeDislikeCompatibility import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.* import app.revanced.patches.youtube.layout.returnyoutubedislike.resource.patch.ReturnYouTubeDislikeResourcePatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch import app.revanced.patches.youtube.video.videoid.patch.VideoIdPatch -import org.jf.dexlib2.builder.instruction.BuilderInstruction35c import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction @@ -48,7 +45,7 @@ import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction class ReturnYouTubeDislikePatch : BytecodePatch( listOf( TextComponentConstructorFingerprint, - ShortsTextComponentParentFingerprint, + ShortsTextViewFingerprint, DislikesOldLayoutTextViewFingerprint, LikeFingerprint, DislikeFingerprint, @@ -137,35 +134,40 @@ class ReturnYouTubeDislikePatch : BytecodePatch( // region Hook for Short videos. - ShortsTextComponentParentFingerprint.result?.let { - context - .toMethodWalker(it.method) - .nextMethod(it.scanResult.patternScanResult!!.endIndex, true) - .getMethod().let { method -> - with(method as MutableMethod) { - // After walking, verify the found method is what's expected. - if (returnType != ("Ljava/lang/CharSequence;") || parameterTypes.size != 1) - return PatchResultError("Method signature did not match: $this $parameterTypes") + ShortsTextViewFingerprint.result?.let { + it.mutableMethod.apply { + val patternResult = it.scanResult.patternScanResult!! - val insertIndex = implementation!!.instructions.size - 1 - val spannedParameterRegister = instruction(insertIndex).registerA - val parameter = instruction(insertIndex - 2).reference + // If the field is true, the TextView is for a dislike button. + val isDisLikesBooleanReference = instruction(patternResult.endIndex).reference - if (!parameter.toString().endsWith("Landroid/text/Spanned;")) - return PatchResultError("Method signature parameter did not match: $parameter") + val textViewFieldReference = // Like/Dislike button TextView field + instruction(patternResult.endIndex - 2).reference - insertShorts(insertIndex, spannedParameterRegister) - } - } - - // Additional hook, called after user dislikes. - with(it.mutableMethod) { - val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2 - val overwriteRegister = (implementation!!.instructions.elementAt(insertIndex - 1) - as OneRegisterInstruction).registerA - insertShorts(insertIndex, overwriteRegister) + // Check if the hooked TextView object is that of the dislike button. + // If RYD is disabled, or the TextView object is not that of the dislike button, the execution flow is not interrupted. + // Otherwise, the TextView object is modified, and the execution flow is interrupted to prevent it from being changed afterward. + val insertIndex = patternResult.startIndex + 6 + addInstructions( + insertIndex, """ + # Check, if the TextView is for a dislike button + iget-boolean v0, p0, $isDisLikesBooleanReference + if-eqz v0, :is_like + + # Hook the TextView, if it is for the dislike button + iget-object v0, p0, $textViewFieldReference + invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->setShortsDislikes(Landroid/view/View;)Z + move-result v0 + if-eqz v0, :ryd_disabled + return-void + + :is_like + :ryd_disabled + nop + """ + ) } - } ?: return ShortsTextComponentParentFingerprint.toErrorResult() + } ?: return ShortsTextViewFingerprint.toErrorResult() // endregion @@ -201,14 +203,5 @@ class ReturnYouTubeDislikePatch : BytecodePatch( DISLIKE(-1), REMOVE_LIKE(0) } - - private fun MutableMethod.insertShorts(index: Int, register: Int) { - addInstructions( - index, """ - invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->onShortsComponentCreated(Landroid/text/Spanned;)Landroid/text/Spanned; - move-result-object v$register - """ - ) - } } } diff --git a/src/main/resources/returnyoutubedislike/host/values/strings.xml b/src/main/resources/returnyoutubedislike/host/values/strings.xml index 31ca93597..ba571239f 100644 --- a/src/main/resources/returnyoutubedislike/host/values/strings.xml +++ b/src/main/resources/returnyoutubedislike/host/values/strings.xml @@ -13,6 +13,10 @@ Dislikes are shown Dislikes are not shown + Show dislikes on Shorts + Dislikes shown on Shorts + Dislikes hidden on Shorts + Dislikes as percentage Dislikes shown as percentage Dislikes shown as number