chore(YouTube): replace with a fingerprint that supports a wider range of versions

This commit is contained in:
inotia00 2024-09-15 18:48:29 +09:00
parent 088f0ebf6b
commit 67d365e509
27 changed files with 392 additions and 143 deletions

View File

@ -102,8 +102,8 @@ abstract class BaseCronetImageUrlHookPatch(
// Add a helper get method that returns the URL field.
RequestFingerprint.resultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().single {
if (it.opcode != Opcode.IPUT_OBJECT) return@single false
val urlFieldInstruction = mutableMethod.getInstructions().first {
if (it.opcode != Opcode.IPUT_OBJECT) return@first false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"

View File

@ -2,11 +2,12 @@ package app.revanced.patches.youtube.general.autocaptions.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import com.android.tools.smali.dexlib2.AccessFlags
internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
parameters = listOf(PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR),
strings = listOf("#-1#")
)

View File

@ -11,6 +11,7 @@ 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.youtube.general.toolbar.fingerprints.ActionBarRingoBackgroundFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.ActionBarRingoConstructorFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.ActionBarRingoTextFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.AttributeResolverFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.CreateButtonDrawableFingerprint
@ -72,6 +73,7 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
ActionBarRingoBackgroundFingerprint,
ActionBarRingoConstructorFingerprint,
AttributeResolverFingerprint,
CreateButtonDrawableFingerprint,
CreateSearchSuggestionsFingerprint,
@ -178,7 +180,7 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
"invoke-static {v$viewRegister}, $GENERAL_CLASS_DESCRIPTOR->setWideSearchBarLayout(Landroid/view/View;)V"
)
val targetIndex = it.scanResult.patternScanResult!!.endIndex + 1
val targetIndex = ActionBarRingoBackgroundFingerprint.indexOfStaticInstruction(this) + 1
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
injectSearchBarHook(
@ -187,39 +189,11 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
"enableWideSearchBarWithHeaderInverse"
)
}
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
.apply {
val staticCalls = implementation!!.instructions.withIndex()
.filter { instruction ->
val methodReference =
((instruction.value as? ReferenceInstruction)?.reference as? MethodReference)
methodReference?.parameterTypes?.size == 1 &&
methodReference.returnType == "Z"
}
if (staticCalls.size != 2)
throw PatchException("Size of staticCalls does not match: ${staticCalls.size}")
mapOf(
staticCalls.elementAt(0).index to "enableWideSearchBar",
staticCalls.elementAt(1).index to "enableWideSearchBarWithHeader"
).forEach { (index, descriptor) ->
val walkerMethod = getWalkerMethod(context, index)
walkerMethod.apply {
injectSearchBarHook(
implementation!!.instructions.size - 1,
descriptor
)
}
}
}
}
ActionBarRingoTextFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex + 1
val targetIndex = ActionBarRingoTextFingerprint.indexOfStaticInstruction(this) + 1
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
injectSearchBarHook(
@ -230,6 +204,35 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
}
}
ActionBarRingoConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val staticCalls = implementation!!.instructions
.withIndex()
.filter { (_, instruction) ->
val methodReference = (instruction as? ReferenceInstruction)?.reference
instruction.opcode == Opcode.INVOKE_STATIC &&
methodReference is MethodReference &&
methodReference.parameterTypes.size == 1 &&
methodReference.returnType == "Z"
}
if (staticCalls.size != 2)
throw PatchException("Size of staticCalls does not match: ${staticCalls.size}")
mapOf(
staticCalls.elementAt(0).index to "enableWideSearchBar",
staticCalls.elementAt(1).index to "enableWideSearchBarWithHeader"
).forEach { (index, descriptor) ->
val walkerMethod = getWalkerMethod(context, index)
walkerMethod.apply {
injectSearchBarHook(
implementation!!.instructions.lastIndex,
descriptor
)
}
}
}
YouActionBarFingerprint.resolve(context, setActionBarRingoMutableClass)
YouActionBarFingerprint.resultOrThrow().let {
it.mutableMethod.apply {

View File

@ -1,15 +1,28 @@
package app.revanced.patches.youtube.general.toolbar.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.ActionBarRingoBackgroundFingerprint.indexOfStaticInstruction
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ActionBarRingoBackground
import app.revanced.util.fingerprint.LiteralValueFingerprint
import app.revanced.util.containsWideLiteralInstructionIndex
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object ActionBarRingoBackgroundFingerprint : LiteralValueFingerprint(
internal object ActionBarRingoBackgroundFingerprint : MethodFingerprint(
returnType = "Landroid/view/View;",
parameters = listOf("Landroid/view/LayoutInflater;"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC
),
literalSupplier = { ActionBarRingoBackground }
)
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionIndex(ActionBarRingoBackground) &&
indexOfStaticInstruction(methodDef) >= 0
}
) {
fun indexOfStaticInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
reference?.parameterTypes?.size == 1 &&
reference.parameterTypes.firstOrNull() == "Landroid/content/Context;" &&
reference.returnType == "Z"
}
}

View File

@ -0,0 +1,17 @@
package app.revanced.patches.youtube.general.toolbar.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.util.MethodUtil
internal object ActionBarRingoConstructorFingerprint : MethodFingerprint(
returnType = "V",
strings = listOf("default"),
customFingerprint = custom@{ methodDef, _ ->
if (!MethodUtil.isConstructor(methodDef)) {
return@custom false
}
val parameterTypes = methodDef.parameterTypes
parameterTypes.size >= 5 && parameterTypes[0] == "Landroid/content/Context;"
}
)

View File

@ -1,16 +1,36 @@
package app.revanced.patches.youtube.general.toolbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.fingerprint.MethodReferenceNameFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.general.toolbar.fingerprints.ActionBarRingoTextFingerprint.indexOfStaticInstruction
import app.revanced.patches.youtube.general.toolbar.fingerprints.ActionBarRingoTextFingerprint.indexOfStartDelayInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionReversed
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object ActionBarRingoTextFingerprint : MethodReferenceNameFingerprint(
returnType = "V",
internal object ActionBarRingoTextFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC
),
reference = { "setStartDelay" }
)
customFingerprint = { methodDef, _ ->
indexOfStartDelayInstruction(methodDef) >= 0 &&
indexOfStaticInstruction(methodDef) >= 0
}
) {
fun indexOfStartDelayInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setStartDelay"
}
fun indexOfStaticInstruction(methodDef: Method) =
methodDef.indexOfFirstInstructionReversed (indexOfStartDelayInstruction(methodDef)) {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
reference?.parameterTypes?.size == 1 &&
reference.parameterTypes.firstOrNull() == "Landroid/content/Context;" &&
reference.returnType == "Z"
}
}

View File

@ -4,6 +4,7 @@ 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.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackManagerFingerprint
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackSettingsFingerprint
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
@ -14,9 +15,12 @@ import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getWalkerMethod
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -40,13 +44,24 @@ object BackgroundPlaybackPatch : BaseBytecodePatch(
) {
override fun execute(context: BytecodeContext) {
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions(
0, """
invoke-static {}, $MISC_PATH/BackgroundPlaybackPatch;->playbackIsNotShort()Z
move-result v0
return v0
"""
)
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.apply {
findOpcodeIndicesReversed(Opcode.RETURN).forEach{ index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
// Replace to preserve control flow label.
replaceInstruction(
index,
"invoke-static { v$register }, $MISC_PATH/BackgroundPlaybackPatch;->allowBackgroundPlayback(Z)Z"
)
addInstructions(index + 1,
"""
move-result v$register
return v$register
"""
)
}
}
// Enable background playback option in YouTube settings
BackgroundPlaybackSettingsFingerprint.resultOrThrow().let {

View File

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
@ -11,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal object KidsBackgroundPlaybackPolicyControllerParentFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
parameters = listOf(PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR),
customFingerprint = { methodDef, _ ->
methodDef.indexOfFirstInstruction {
opcode == Opcode.SGET_OBJECT

View File

@ -10,6 +10,7 @@ import app.revanced.patches.shared.litho.LithoFilterPatch
import app.revanced.patches.youtube.player.descriptions.fingerprints.EngagementPanelTitleFingerprint
import app.revanced.patches.youtube.player.descriptions.fingerprints.EngagementPanelTitleParentFingerprint
import app.revanced.patches.youtube.player.descriptions.fingerprints.TextViewComponentFingerprint
import app.revanced.patches.youtube.player.descriptions.fingerprints.TextViewComponentFingerprint.indexOfTextIsSelectableInstruction
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.utils.fingerprints.RollingNumberTextViewFingerprint
@ -94,8 +95,7 @@ object DescriptionComponentsPatch : BaseBytecodePatch(
if (SettingsPatch.upward1902) {
TextViewComponentFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex =
getTargetIndexWithMethodReferenceNameOrThrow("setTextIsSelectable")
val insertIndex = indexOfTextIsSelectableInstruction(this)
val insertInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)
replaceInstruction(

View File

@ -1,14 +1,30 @@
package app.revanced.patches.youtube.player.descriptions.fingerprints
import app.revanced.util.fingerprint.MethodReferenceNameFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.player.descriptions.fingerprints.TextViewComponentFingerprint.indexOfTextIsSelectableInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
/**
* This fingerprint is compatible with YouTube v18.35.xx~
* Nonetheless, the patch works in YouTube v19.02.xx~
*/
internal object TextViewComponentFingerprint : MethodReferenceNameFingerprint(
internal object TextViewComponentFingerprint : MethodFingerprint(
returnType = "V",
opcodes = listOf(Opcode.CMPL_FLOAT),
reference = { "setBreakStrategy" }
)
customFingerprint = { methodDef, _ ->
methodDef.implementation != null &&
indexOfTextIsSelectableInstruction(methodDef) >= 0
},
) {
fun indexOfTextIsSelectableInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.name == "setTextIsSelectable" &&
reference.definingClass != "Landroid/widget/TextView;"
}
}

View File

@ -165,7 +165,7 @@ object FullscreenComponentsPatch : BaseBytecodePatch(
addInstruction(
checkCastIndex + 1,
"invoke-static {v$insertRegister}, $PLAYER_CLASS_DESCRIPTOR->setQuickActionMargin(Landroid/widget/FrameLayout;)V"
"invoke-static {v$insertRegister}, $PLAYER_CLASS_DESCRIPTOR->setQuickActionMargin(Landroid/view/View;)V"
)
addInstruction(

View File

@ -38,7 +38,6 @@ import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.getTargetIndexOrThrow
import app.revanced.util.getTargetIndexReversedOrThrow
import app.revanced.util.getTargetIndexWithReferenceOrThrow
import app.revanced.util.getWalkerMethod
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.literalInstructionHook
import app.revanced.util.patch.BaseBytecodePatch
@ -93,7 +92,7 @@ object ShortsComponentPatch : BaseBytecodePatch(
"SETTINGS: SHORTS_COMPONENTS"
)
if (SettingsPatch.upward1925) {
if (SettingsPatch.upward1925 && !SettingsPatch.upward1928) {
settingArray += "SETTINGS: SHORTS_TIME_STAMP"
}
@ -279,18 +278,15 @@ object ShortsComponentPatch : BaseBytecodePatch(
// region patch for hide paused header
ShortsPausedHeaderFingerprint.resultOrThrow().let {
val targetMethod =
it.getWalkerMethod(context, it.scanResult.patternScanResult!!.endIndex)
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
targetMethod.apply {
addInstructionsWithLabels(
0,
"""
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->hideShortsPausedHeader()Z
move-result v0
if-nez v0, :hide
""",
ExternalLabel("hide", getInstruction(implementation!!.instructions.lastIndex))
addInstructions(
insertIndex, """
invoke-static {v$insertRegister}, $SHORTS_CLASS_DESCRIPTOR->hideShortsPausedHeader(Z)Z
move-result v$insertRegister
"""
)
}
}

View File

@ -49,7 +49,7 @@ object ShortsNavigationBarPatch : BytecodePatch(
)
}
BottomNavigationBarFingerprint.resultOrThrow().let {
BottomNavigationBarFingerprint.result?.let {
it.mutableMethod.apply {
val targetIndex = getTargetIndexWithMethodReferenceNameOrThrow("findViewById") + 1
val insertRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA

View File

@ -29,7 +29,7 @@ object ShortsTimeStampPatch : BytecodePatch(
) {
override fun execute(context: BytecodeContext) {
if (!SettingsPatch.upward1925) return
if (!SettingsPatch.upward1925 || SettingsPatch.upward1928) return
// region patch for enable time stamp

View File

@ -10,8 +10,7 @@ internal object ShortsPausedHeaderFingerprint : MethodFingerprint(
Opcode.IGET_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("r_pfcv")
)

View File

@ -0,0 +1,17 @@
package app.revanced.patches.youtube.utils
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object PlayerResponseModelUtils {
const val PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR =
"Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
fun indexOfPlayerResponseModelInstruction(methodDef: Method) = methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
}

View File

@ -1,11 +1,12 @@
package app.revanced.patches.youtube.utils.pip.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import com.android.tools.smali.dexlib2.Opcode
internal object PiPPlaybackFingerprint : MethodFingerprint(
returnType = "Z",
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
parameters = listOf(PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,

View File

@ -129,7 +129,7 @@ object PlayerTypeHookPatch : BytecodePatch(
// so this works regardless which layout is used.
ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply {
val instructionIndex =
getTargetIndexWithMethodReferenceNameOrThrow("setLayoutDirection")
ActionBarSearchResultsFingerprint.indexOfLayoutDirectionInstruction(this)
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
addInstruction(

View File

@ -1,13 +1,28 @@
package app.revanced.patches.youtube.utils.playertype.fingerprint
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.playertype.fingerprint.ActionBarSearchResultsFingerprint.indexOfLayoutDirectionInstruction
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ActionBarSearchResultsViewMic
import app.revanced.util.fingerprint.LiteralValueFingerprint
import app.revanced.util.containsWideLiteralInstructionIndex
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object ActionBarSearchResultsFingerprint : LiteralValueFingerprint(
internal object ActionBarSearchResultsFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/view/View;",
parameters = listOf("Landroid/view/LayoutInflater;"),
literalSupplier = { ActionBarSearchResultsViewMic }
)
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionIndex(ActionBarSearchResultsViewMic) &&
indexOfLayoutDirectionInstruction(methodDef) >= 0
}
) {
fun indexOfLayoutDirectionInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Landroid/view/View;->setLayoutDirection(I)V"
}
}

View File

@ -4,6 +4,7 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.PatchException
import app.revanced.patches.shared.litho.LithoFilterPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.COMPONENTS_PATH
@ -18,11 +19,18 @@ import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouT
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.getReference
import app.revanced.util.getTargetIndexOrThrow
import app.revanced.util.getTargetIndexWithFieldReferenceType
import app.revanced.util.getTargetIndexWithFieldReferenceTypeOrThrow
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
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.MethodReference
@Suppress("unused")
object ReturnYouTubeDislikePatch : BaseBytecodePatch(
@ -75,15 +83,36 @@ object ReturnYouTubeDislikePatch : BaseBytecodePatch(
val conversionContextFieldReference =
getInstruction<ReferenceInstruction>(conversionContextFieldIndex).reference
val charSequenceIndex =
getTargetIndexWithFieldReferenceTypeOrThrow("Ljava/util/BitSet;") - 1
val charSequenceRegister =
getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
val freeRegister =
getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerB
val charSequenceIndex1932 =
getTargetIndexWithFieldReferenceType("Ljava/util/BitSet;") - 1
val charSequenceIndex1933 = indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.returnType == "V" &&
reference.parameterTypes.firstOrNull() == "Ljava/lang/CharSequence;"
}
val insertIndex: Int
val charSequenceRegister: Int
if (charSequenceIndex1932 > -2) {
charSequenceRegister =
getInstruction<TwoRegisterInstruction>(charSequenceIndex1932).registerA
insertIndex = charSequenceIndex1932 - 1
} else if (charSequenceIndex1933 > -1) {
charSequenceRegister =
getInstruction<FiveRegisterInstruction>(charSequenceIndex1933).registerD
insertIndex = charSequenceIndex1933
} else {
throw PatchException("Could not find insert index")
}
val freeRegister = getInstruction<TwoRegisterInstruction>(
getTargetIndexOrThrow(insertIndex, Opcode.IGET_OBJECT)
).registerA
addInstructions(
charSequenceIndex - 1, """
insertIndex, """
move-object/from16 v$freeRegister, p0
iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference
invoke-static {v$freeRegister, v$charSequenceRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;

View File

@ -15,6 +15,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.shared.fingerprints.MdxPlayerDirectorSetVideoStageFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.indexOfPlayerResponseModelInstruction
import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.SHARED_PATH
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
@ -88,9 +90,6 @@ object VideoInformationPatch : BytecodePatch(
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$SHARED_PATH/VideoInformation;"
private const val PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR =
"Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
private const val REGISTER_PLAYER_RESPONSE_MODEL = 8
private const val REGISTER_CHANNEL_ID = 0
@ -275,19 +274,17 @@ object VideoInformationPatch : BytecodePatch(
/**
* Set current video information
*/
channelIdMethodCall = ChannelIdFingerprint.getMethodName("Ljava/lang/String;")
channelNameMethodCall = ChannelNameFingerprint.getMethodName("Ljava/lang/String;")
videoIdMethodCall = VideoIdFingerprint.getMethodName("Ljava/lang/String;")
videoTitleMethodCall = VideoTitleFingerprint.getMethodName("Ljava/lang/String;")
videoLengthMethodCall = VideoLengthFingerprint.getMethodName("J")
videoIsLiveMethodCall = ChannelIdFingerprint.getMethodName("Z")
channelIdMethodCall = ChannelIdFingerprint.getPlayerResponseInstruction("Ljava/lang/String;")
channelNameMethodCall = ChannelNameFingerprint.getPlayerResponseInstruction("Ljava/lang/String;")
videoIdMethodCall = VideoIdFingerprint.getPlayerResponseInstruction("Ljava/lang/String;")
videoTitleMethodCall = VideoTitleFingerprint.getPlayerResponseInstruction("Ljava/lang/String;")
videoLengthMethodCall = VideoLengthFingerprint.getPlayerResponseInstruction("J")
videoIsLiveMethodCall = ChannelIdFingerprint.getPlayerResponseInstruction("Z")
PlaybackInitializationFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT
&& getReference<MethodReference>()?.returnType == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
} + 1
val targetIndex =
PlaybackInitializationFingerprint.indexOfPlayerResponseModelInstruction(this) + 1
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
@ -304,10 +301,7 @@ object VideoInformationPatch : BytecodePatch(
VideoIdFingerprintBackgroundPlay.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_INTERFACE
&& getReference<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
val targetIndex = indexOfPlayerResponseModelInstruction(this)
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerC
addInstruction(
@ -322,10 +316,7 @@ object VideoInformationPatch : BytecodePatch(
VideoIdFingerprintShorts.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_INTERFACE
&& getReference<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
val targetIndex = indexOfPlayerResponseModelInstruction(this)
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerC
addInstruction(
@ -557,14 +548,16 @@ object VideoInformationPatch : BytecodePatch(
"invoke-static { p1, p2 }, $targetMethodClass->$targetMethodName(J)V"
)
private fun MethodFingerprint.getMethodName(returnType: String): String {
private fun MethodFingerprint.getPlayerResponseInstruction(returnType: String): String {
resultOrThrow().mutableMethod.apply {
val targetIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_INTERFACE
&& getReference<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
&& getReference<MethodReference>()?.returnType == returnType
}
val targetReference = getInstruction<ReferenceInstruction>(targetIndex).reference
val targetReference = getInstruction<ReferenceInstruction>(
indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_INTERFACE &&
reference?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR &&
reference.returnType == returnType
}
).reference
return "invoke-interface {v${REGISTER_PLAYER_RESPONSE_MODEL}}, $targetReference"
}

View File

@ -2,7 +2,9 @@ package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@ -12,9 +14,10 @@ internal object OnPlaybackSpeedItemClickFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Landroid/widget/AdapterView;", "Landroid/view/View;", "I", "J"),
customFingerprint = { methodDef, _ ->
methodDef.name == "onItemClick" && methodDef.implementation?.instructions?.find {
it.opcode == Opcode.IGET_OBJECT &&
it.getReference<FieldReference>()!!.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
} != null
methodDef.name == "onItemClick" &&
methodDef.indexOfFirstInstruction {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.type == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
} >= 0
}
)

View File

@ -2,11 +2,26 @@ package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.video.information.fingerprints.PlaybackInitializationFingerprint.indexOfPlayerResponseModelInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object PlaybackInitializationFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
strings = listOf("play() called when the player wasn\'t loaded.")
)
strings = listOf("play() called when the player wasn\'t loaded."),
customFingerprint = { methodDef, _ ->
indexOfPlayerResponseModelInstruction(methodDef) >= 0
}
) {
fun indexOfPlayerResponseModelInstruction(methodDef: Method) = methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.returnType == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
}

View File

@ -1,8 +1,7 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.indexOfPlayerResponseModelInstruction
import com.android.tools.smali.dexlib2.Opcode
/**
@ -10,7 +9,6 @@ import com.android.tools.smali.dexlib2.Opcode
*/
internal object VideoIdFingerprintBackgroundPlay : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IF_EQZ,
@ -23,6 +21,8 @@ internal object VideoIdFingerprintBackgroundPlay : MethodFingerprint(
Opcode.RETURN_VOID
),
customFingerprint = { methodDef, classDef ->
methodDef.name == "l" && classDef.methods.count() == 17
methodDef.name == "l" &&
classDef.methods.count() == 17 &&
indexOfPlayerResponseModelInstruction(methodDef) >= 0
}
)

View File

@ -1,6 +1,7 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.PlayerResponseModelUtils.PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
import app.revanced.util.containsWideLiteralInstructionIndex
import app.revanced.util.getTargetIndexWithFieldReferenceName
import com.android.tools.smali.dexlib2.Opcode
@ -12,7 +13,7 @@ import com.android.tools.smali.dexlib2.Opcode
*/
internal object VideoIdFingerprintShorts : MethodFingerprint(
returnType = "V",
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
parameters = listOf(PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT

View File

@ -292,7 +292,7 @@ object VideoPlaybackPatch : BaseBytecodePatch(
addInstructions(
insertIndex + 1, """
invoke-static {v$insertRegister}, $INTEGRATIONS_AV1_CODEC_CLASS_DESCRIPTOR->replaceCodec(Ljava/lang/String;)Ljava/lang/String;
invoke-static/range {v$insertRegister .. v$insertRegister}, $INTEGRATIONS_AV1_CODEC_CLASS_DESCRIPTOR->replaceCodec(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$insertRegister
"""
)

View File

@ -227,7 +227,11 @@ fun Method.indexOfWideLiteralInstructionOrThrow(literal: Long): Int {
* @see indexOfFirstInstructionOrThrow
*/
fun Method.indexOfFirstInstruction(startIndex: Int = 0, predicate: Instruction.() -> Boolean): Int {
val index = this.implementation!!.instructions.drop(startIndex).indexOfFirst(predicate)
var instructions = this.implementation!!.instructions
if (startIndex != 0) {
instructions = instructions.drop(startIndex)
}
val index = instructions.indexOfFirst(predicate)
return if (index >= 0) {
startIndex + index
@ -236,6 +240,18 @@ fun Method.indexOfFirstInstruction(startIndex: Int = 0, predicate: Instruction.(
}
}
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
*
* @return the index of the instruction
* @throws PatchException
* @see indexOfFirstInstruction
*/
fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, targetOpcode: Opcode): Int =
indexOfFirstInstructionOrThrow(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
*
@ -254,17 +270,84 @@ fun Method.indexOfFirstInstructionOrThrow(
return index
}
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversedOrThrow
*/
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode: Opcode): Int =
indexOfFirstInstructionReversed(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversedOrThrow
*/
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, predicate: Instruction.() -> Boolean): Int {
var instructions = this.implementation!!.instructions
if (startIndex != null) {
instructions = instructions.take(startIndex + 1)
}
return instructions.indexOfLast(predicate)
}
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversed
*/
fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, targetOpcode: Opcode): Int =
indexOfFirstInstructionReversedOrThrow(startIndex) {
opcode == targetOpcode
}
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversed
*/
fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, predicate: Instruction.() -> Boolean): Int {
val index = indexOfFirstInstructionReversed(startIndex, predicate)
if (index < 0) {
throw PatchException("Could not find instruction index")
}
return index
}
/**
* @return The list of indices of the opcode in reverse order.
*/
fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> {
fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> =
findOpcodeIndicesReversed { this.opcode == opcode }
/**
* @return The list of indices of the opcode in reverse order.
*/
fun Method.findOpcodeIndicesReversed(filter: Instruction.() -> Boolean): List<Int> {
val indexes = implementation!!.instructions
.withIndex()
.filter { (_, instruction) -> instruction.opcode == opcode }
.filter { (_, instruction) -> filter.invoke(instruction) }
.map { (index, _) -> index }
.reversed()
if (indexes.isEmpty()) throw PatchException("No ${opcode.name} instructions found in: $this")
if (indexes.isEmpty()) throw PatchException("No matching instructions found in: $this")
return indexes
}
@ -341,8 +424,19 @@ inline fun <reified T : Reference> Instruction.getReference() =
* @param predicate The predicate to match.
* @return The index of the first [Instruction] that matches the predicate.
*/
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) =
this.implementation!!.instructions.indexOfFirst(predicate)
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
*
* @param startIndex Optional starting index to start searching from.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionOrThrow
*/
fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): Int =
indexOfFirstInstruction(startIndex) {
opcode == targetOpcode
}
fun MutableMethod.getTargetIndexOrThrow(opcode: Opcode) =
getTargetIndexOrThrow(0, opcode)