add suport YouTube v18.06.35

This commit is contained in:
inotia00 2023-02-18 14:18:23 +09:00
parent 9030abe616
commit 8427f28208
25 changed files with 315 additions and 194 deletions

View File

@ -3,7 +3,7 @@ package app.revanced.patches.shared.annotation
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.google.android.youtube", arrayOf("18.05.40"))])
@Compatibility([Package("com.google.android.youtube", arrayOf("18.06.35"))])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class YouTubeCompatibility

View File

@ -1,13 +1,10 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object MainstreamVideoAdsFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = listOf("L","Z"),
opcodes = listOf(
Opcode.IGET_OBJECT,

View File

@ -10,7 +10,6 @@ import org.jf.dexlib2.Opcode
object PivotBarCreateButtonViewFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Z"),
customFingerprint = { methodDef ->
methodDef.implementation?.instructions?.any {
it.opcode.ordinal == Opcode.CONST.ordinal &&

View File

@ -8,6 +8,7 @@ import app.revanced.patcher.data.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
@ -26,34 +27,30 @@ class GeneralVideoAdsPatch : BytecodePatch(
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val LegacyVideoAdsResult = LegacyVideoAdsFingerprint.result ?: return LegacyVideoAdsFingerprint.toErrorResult()
val legacyVideoAdsResult = LegacyVideoAdsFingerprint.result ?: return LegacyVideoAdsFingerprint.toErrorResult()
LegacyVideoAdsMethod =
context.toMethodWalker(LegacyVideoAdsResult.method)
legacyVideoAdsMethod =
context.toMethodWalker(legacyVideoAdsResult.method)
.nextMethod(13, true)
.getMethod() as MutableMethod
MainstreamVideoAdsFingerprint.resolve(context, MainstreamVideoAdsParentFingerprint.result!!.classDef)
val MainstreamAdsResult = MainstreamVideoAdsFingerprint.result ?: return MainstreamVideoAdsFingerprint.toErrorResult()
MainstreamVideoAdsMethod = MainstreamAdsResult.mutableMethod
InsertIndex = MainstreamAdsResult.scanResult.patternScanResult!!.endIndex
MainstreamVideoAdsParentFingerprint.result?.let { parentResult ->
MainstreamVideoAdsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
mainstreamVideoAdsResult = it
} ?: return MainstreamVideoAdsFingerprint.toErrorResult()
} ?: return MainstreamVideoAdsParentFingerprint.toErrorResult()
return PatchResultSuccess()
}
internal companion object {
var InsertIndex: Int = 0
lateinit var LegacyVideoAdsMethod: MutableMethod
lateinit var MainstreamVideoAdsMethod: MutableMethod
lateinit var legacyVideoAdsMethod: MutableMethod
lateinit var mainstreamVideoAdsResult: MethodFingerprintResult
fun injectLegacyAds(
descriptor: String
) {
LegacyVideoAdsMethod.addInstructions(
legacyVideoAdsMethod.addInstructions(
0, """
invoke-static {}, $descriptor
move-result v1
@ -64,13 +61,16 @@ class GeneralVideoAdsPatch : BytecodePatch(
fun injectMainstreamAds(
descriptor: String
) {
MainstreamVideoAdsMethod.addInstructions(
InsertIndex, """
val mainstreamVideoAdsMethod = mainstreamVideoAdsResult.mutableMethod
val insertIndex = mainstreamVideoAdsResult.scanResult.patternScanResult!!.endIndex
mainstreamVideoAdsMethod.addInstructions(
insertIndex, """
invoke-static {}, $descriptor
move-result v1
if-nez v1, :show_video_ads
return-void
""", listOf(ExternalLabel("show_video_ads", MainstreamVideoAdsMethod.instruction(InsertIndex)))
""", listOf(ExternalLabel("show_video_ads", mainstreamVideoAdsMethod.instruction(insertIndex)))
)
}

View File

@ -20,7 +20,9 @@ import app.revanced.patches.youtube.layout.fullscreen.fullscreenpanels.fingerpri
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsPatch
import app.revanced.util.integrations.Constants.FULLSCREEN_LAYOUT
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
@Patch
@ -57,6 +59,29 @@ class HideFullscreenPanelsPatch : BytecodePatch(
}
} ?: return FullscreenViewAdderFingerprint.toErrorResult()
LayoutConstructorFingerprint.result?.mutableMethod?.let { method ->
val instructions = method.implementation!!.instructions
val registerIndex = instructions.indexOfFirst {
it.opcode == Opcode.CONST_STRING &&
(it as BuilderInstruction21c).reference.toString() == "1.0x"
}
val dummyRegister = (instructions[registerIndex] as Instruction21c).registerA
val invokeIndex = method.implementation!!.instructions.indexOfFirst {
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
((it as? BuilderInstruction35c)?.reference.toString() ==
"Landroid/widget/FrameLayout;->addView(Landroid/view/View;)V")
}
method.addInstructions(
invokeIndex, """
invoke-static {}, $FULLSCREEN_LAYOUT->hideFullscreenPanel()Z
move-result v$dummyRegister
if-nez v$dummyRegister, :hidden
""", listOf(ExternalLabel("hidden", method.instruction(invokeIndex + 1)))
)
} ?: return LayoutConstructorFingerprint.toErrorResult()
/*
* Add settings
*/
@ -70,22 +95,6 @@ class HideFullscreenPanelsPatch : BytecodePatch(
SettingsPatch.updatePatchStatus("hide-fullscreen-panels")
LayoutConstructorFingerprint.result?.mutableMethod?.let { method ->
val invokeIndex = method.implementation!!.instructions.indexOfFirst {
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
((it as? BuilderInstruction35c)?.reference.toString() ==
"Landroid/widget/FrameLayout;->addView(Landroid/view/View;)V")
}
method.addInstructions(
invokeIndex, """
invoke-static {}, $FULLSCREEN_LAYOUT->hideFullscreenPanel()Z
move-result v15
if-nez v15, :hidden
""", listOf(ExternalLabel("hidden", method.instruction(invokeIndex + 1)))
)
} ?: return LayoutConstructorFingerprint.toErrorResult()
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.layout.general.crowdfundingbox.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.resourceid.patch.SharedResourcdIdPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
object CrowdfundingBoxFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IPUT_OBJECT
),
customFingerprint = { methodDef ->
methodDef.implementation?.instructions?.any {
it.opcode.ordinal == Opcode.CONST.ordinal &&
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourcdIdPatch.donationCompanionResourceId
} == true
}
)

View File

@ -1,90 +1,66 @@
package app.revanced.patches.youtube.layout.general.crowdfundingbox.patch
import app.revanced.extensions.findMutableMethodOf
import app.revanced.extensions.injectHideCall
import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.shared.annotation.YouTubeCompatibility
import app.revanced.patches.shared.patch.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.layout.general.crowdfundingbox.fingerprints.CrowdfundingBoxFingerprint
import app.revanced.patches.youtube.misc.resourceid.patch.SharedResourcdIdPatch
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
import app.revanced.util.integrations.Constants.GENERAL_LAYOUT
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch
@Name("hide-crowdfunding-box")
@Description("Hides the crowdfunding box between the player and video description.")
@DependsOn(
[
ResourceMappingPatch::class,
SettingsPatch::class
SettingsPatch::class,
SharedResourcdIdPatch::class
]
)
@YouTubeCompatibility
@Version("0.0.1")
class CrowdfundingBoxPatch : BytecodePatch() {
// list of resource names to get the id of
private val resourceIds = arrayOf(
"donation_companion"
).map { name ->
ResourceMappingPatch.resourceMappings.single { it.name == name }.id
}
private var patchSuccessArray = Array(resourceIds.size) {false}
class CrowdfundingBoxPatch : BytecodePatch(
listOf(
CrowdfundingBoxFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
classDef.methods.forEach { method ->
with(method.implementation) {
this?.instructions?.forEachIndexed { index, instruction ->
when (instruction.opcode) {
Opcode.CONST -> {
when ((instruction as Instruction31i).wideLiteral) {
resourceIds[0] -> { // crowdfunding
val insertIndex = index + 3
val iPutInstruction = instructions.elementAt(insertIndex)
if (iPutInstruction.opcode != Opcode.IPUT_OBJECT) return@forEachIndexed
val mutableMethod = context.proxy(classDef).mutableClass.findMutableMethodOf(method)
CrowdfundingBoxFingerprint.result?.let {
with (it.mutableMethod) {
val insertIndex = it.scanResult.patternScanResult!!.endIndex
val register = (instruction(insertIndex) as TwoRegisterInstruction).registerA
val viewRegister = (iPutInstruction as Instruction22c).registerA
mutableMethod.implementation!!.injectHideCall(insertIndex, viewRegister, "layout/GeneralLayoutPatch", "hideCrowdfundingBox")
patchSuccessArray[0] = true;
}
}
}
else -> return@forEachIndexed
}
}
}
}
}
val errorIndex: Int = patchSuccessArray.indexOf(false)
if (errorIndex == -1) {
/*
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE: GENERAL_LAYOUT_SETTINGS",
"SETTINGS: HIDE_CROWDFUNDING_BOX"
addInstruction(
insertIndex,
"invoke-static {v$register}, $GENERAL_LAYOUT->hideCrowdfundingBox(Landroid/view/View;)V"
)
}
} ?: return CrowdfundingBoxFingerprint.toErrorResult()
/*
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE: GENERAL_LAYOUT_SETTINGS",
"SETTINGS: HIDE_CROWDFUNDING_BOX"
)
)
SettingsPatch.updatePatchStatus("hide-crowdfunding-box")
SettingsPatch.updatePatchStatus("hide-crowdfunding-box")
return PatchResultSuccess()
} else
return PatchResultError("Instruction not found: $errorIndex")
return PatchResultSuccess()
}
}

View File

@ -8,8 +8,8 @@ import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
object MiniPlayerDimensionsCalculatorFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
customFingerprint = { methodDef ->
methodDef.implementation?.instructions?.any {
it.opcode.ordinal == Opcode.CONST.ordinal &&

View File

@ -9,5 +9,5 @@ object MiniPlayerOverrideFingerprint : MethodFingerprint(
returnType = "Z",
access = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L"),
opcodes = listOf(Opcode.RETURN), // anchor to insert the instruction
opcodes = listOf(Opcode.INVOKE_STATIC), // anchor to insert the instruction
)

View File

@ -0,0 +1,12 @@
package app.revanced.patches.youtube.layout.general.tabletminiplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object MiniPlayerOverrideParentFingerprint : MethodFingerprint(
returnType = "L",
access = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L"),
strings = listOf("VIDEO_QUALITIES_QUICK_MENU_BOTTOM_SHEET_FRAGMENT")
)

View File

@ -5,6 +5,7 @@ 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.addInstructions
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult
@ -19,6 +20,7 @@ import app.revanced.patches.youtube.layout.general.tabletminiplayer.fingerprints
import app.revanced.patches.youtube.misc.resourceid.patch.SharedResourcdIdPatch
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsPatch
import app.revanced.util.integrations.Constants.GENERAL_LAYOUT
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch
@ -35,31 +37,33 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
class TabletMiniPlayerPatch : BytecodePatch(
listOf(
MiniPlayerDimensionsCalculatorFingerprint,
MiniPlayerResponseModelSizeCheckFingerprint
MiniPlayerResponseModelSizeCheckFingerprint,
MiniPlayerOverrideParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
MiniPlayerDimensionsCalculatorFingerprint.result?.let { parentResult ->
// first resolve the fingerprints via the parent fingerprint
val miniPlayerClass = parentResult.classDef
arrayOf(
MiniPlayerOverrideNoContextFingerprint,
MiniPlayerOverrideFingerprint,
MiniPlayerResponseModelSizeCheckFingerprint
).map {
it to (it.also { it.resolve(context, miniPlayerClass) }.result ?: return it.toErrorResult())
}.forEach { (fingerprint, result) ->
if (fingerprint == MiniPlayerOverrideNoContextFingerprint) {
val (method, _, parameterRegister) = result.addProxyCall()
method.insertOverride(method.implementation!!.instructions.size - 1, parameterRegister)
} else {
val (_, _, _) = result.addProxyCall()
}
}
MiniPlayerOverrideNoContextFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let { result ->
val (method, _, parameterRegister) = result.addProxyCall()
method.insertOverride(method.implementation!!.instructions.size - 1, parameterRegister)
} ?: return MiniPlayerOverrideNoContextFingerprint.toErrorResult()
} ?: return MiniPlayerDimensionsCalculatorFingerprint.toErrorResult()
MiniPlayerOverrideParentFingerprint.result?.let { parentResult ->
MiniPlayerOverrideFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let { result ->
(context.toMethodWalker(result.method)
.nextMethod(result.scanResult.patternScanResult!!.startIndex, true)
.getMethod() as MutableMethod)
.instructionProxyCall()
} ?: return MiniPlayerOverrideFingerprint.toErrorResult()
} ?: return MiniPlayerOverrideParentFingerprint.toErrorResult()
MiniPlayerResponseModelSizeCheckFingerprint.result?.let {
val (_, _, _) = it.addProxyCall()
} ?: return MiniPlayerResponseModelSizeCheckFingerprint.toErrorResult()
/*
* Add settings
*/
@ -94,6 +98,17 @@ class TabletMiniPlayerPatch : BytecodePatch(
)
}
fun MutableMethod.instructionProxyCall() {
val insertInstructions = this.implementation!!.instructions
for ((index, instruction) in insertInstructions.withIndex()) {
if (instruction.opcode != Opcode.RETURN) continue
val parameterRegister = (instruction as OneRegisterInstruction).registerA
this.insertOverride(index, parameterRegister)
this.insertOverride(insertInstructions.size - 1, parameterRegister)
break;
}
}
fun MethodFingerprintResult.unwrap(): Triple<MutableMethod, Int, Int> {
val scanIndex = this.scanResult.patternScanResult!!.endIndex
val method = this.mutableMethod

View File

@ -17,9 +17,12 @@ import app.revanced.patches.shared.fingerprints.LayoutConstructorFingerprint
import app.revanced.patches.shared.patch.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsPatch
import app.revanced.util.integrations.Constants.PLAYER_LAYOUT
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@ -46,6 +49,12 @@ class HideAutoplayButtonPatch : BytecodePatch(
LayoutConstructorFingerprint.result?.mutableMethod?.let { method ->
with (method.implementation!!.instructions) {
val registerIndex = indexOfFirst {
it.opcode == Opcode.CONST_STRING &&
(it as BuilderInstruction21c).reference.toString() == "1.0x"
}
val dummyRegister = (this[registerIndex] as Instruction21c).registerA
// where to insert the branch instructions and ...
val insertIndex = this.indexOfFirst {
(it as? WideLiteralInstruction)?.wideLiteral == autoNavPreviewStubId
@ -60,8 +69,8 @@ class HideAutoplayButtonPatch : BytecodePatch(
method.addInstructions(
insertIndex, """
invoke-static {}, $PLAYER_LAYOUT->hideAutoPlayButton()Z
move-result v15
if-nez v15, :hidden
move-result v$dummyRegister
if-nez v$dummyRegister, :hidden
""", listOf(ExternalLabel("hidden", jumpInstruction))
)
}

View File

@ -24,6 +24,7 @@ class SharedResourcdIdPatch : ResourcePatch {
var bottompaneloverlaytextLabelId: Long = -1
var bottomUiContainerResourceId: Long = -1
var controlsLayoutStubResourceId: Long = -1
var donationCompanionResourceId: Long = -1
var educationTextViewResourceId: Long = -1
var emptycolorLabelId: Long = -1
var floatybarQueueLabelId: Long = -1
@ -53,6 +54,7 @@ class SharedResourcdIdPatch : ResourcePatch {
bottompaneloverlaytextLabelId = findSharedResourceId("id", "bottom_panel_overlay_text")
bottomUiContainerResourceId = findSharedResourceId("id", "bottom_ui_container_stub")
controlsLayoutStubResourceId = findSharedResourceId("id", "controls_layout_stub")
donationCompanionResourceId = findSharedResourceId("layout", "donation_companion")
educationTextViewResourceId = findSharedResourceId("id", "user_education_text_view")
emptycolorLabelId = findSharedResourceId("color", "inline_time_bar_colorized_bar_empty_color_dark")
floatybarQueueLabelId = findSharedResourceId("string", "floaty_bar_queue_status")

View File

@ -1,11 +1,8 @@
package app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object DislikeFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PROTECTED or AccessFlags.CONSTRUCTOR,
strings = listOf("like/dislike")
)

View File

@ -1,11 +1,8 @@
package app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object LikeFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PROTECTED or AccessFlags.CONSTRUCTOR,
strings = listOf("like/like")
)

View File

@ -1,11 +1,8 @@
package app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object RemoveLikeFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PROTECTED or AccessFlags.CONSTRUCTOR,
strings = listOf("like/removelike")
)

View File

@ -10,6 +10,13 @@ object ShortsTextComponentParentFingerprint : MethodFingerprint(
access = AccessFlags.PROTECTED or AccessFlags.FINAL,
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,

View File

@ -1,17 +1,17 @@
package app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object TextComponentSpecFingerprint : MethodFingerprint(
returnType = "L",
access = AccessFlags.STATIC.getValue(),
opcodes = listOf(Opcode.CMPL_FLOAT),
customFingerprint = { methodDef ->
methodDef.implementation!!.instructions.any {
((it as? NarrowLiteralInstruction)?.narrowLiteral == 16842907)
}
}
returnType = "V",
access = AccessFlags.PROTECTED or AccessFlags.FINAL,
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_STATIC_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_DIRECT
)
)

View File

@ -0,0 +1,11 @@
package app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object TextComponentSpecParentFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PRIVATE or AccessFlags.CONSTRUCTOR,
strings = listOf("TextComponent")
)

View File

@ -7,10 +7,11 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
@ -20,7 +21,13 @@ import app.revanced.patches.youtube.misc.returnyoutubedislike.fingerprints.*
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsPatch
import app.revanced.patches.youtube.misc.videoid.mainstream.patch.MainstreamVideoIdPatch
import app.revanced.util.integrations.Constants.UTILS_PATH
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction3rc
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
import org.jf.dexlib2.iface.reference.FieldReference
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@Name("return-youtube-dislike")
@ -39,7 +46,7 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
LikeFingerprint,
RemoveLikeFingerprint,
ShortsTextComponentParentFingerprint,
TextComponentSpecFingerprint
TextComponentSpecParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
@ -60,7 +67,7 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
}
ShortsTextComponentParentFingerprint.result?.let {
with(context
with (context
.toMethodWalker(it.method)
.nextMethod(it.scanResult.patternScanResult!!.endIndex, true)
.getMethod() as MutableMethod
@ -69,30 +76,57 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
val insertIndex = insertInstructions.size - 1
val insertRegister = (insertInstructions.elementAt(insertIndex) as OneRegisterInstruction).registerA
addInstructions(
insertIndex, """
invoke-static {v$insertRegister}, ${INTEGRATIONS_RYD_CLASS_DESCRIPTOR}->onShortsComponentCreated(Landroid/text/Spanned;)Landroid/text/Spanned;
move-result-object v$insertRegister
"""
)
this.insertShorts(insertIndex, insertRegister)
}
with (it.mutableMethod) {
val insertInstructions = this.implementation!!.instructions
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
val insertRegister = (insertInstructions.elementAt(insertIndex - 1) as OneRegisterInstruction).registerA
this.insertShorts(insertIndex, insertRegister)
}
} ?: return ShortsTextComponentParentFingerprint.toErrorResult()
val createComponentResult = TextComponentSpecFingerprint.result ?: return PatchResultError("Failed to find TextComponentSpecFingerprint method.")
val createComponentMethod = createComponentResult.mutableMethod
TextComponentSpecParentFingerprint.result?.let { parentResult ->
TextComponentSpecFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
with (it.mutableMethod) {
val startIndex = it.scanResult.patternScanResult!!.startIndex
val endIndex = it.scanResult.patternScanResult!!.endIndex
val conversionContextParam = 5
val textRefParam = createComponentMethod.parameters.size - 2
val dummyRegister = (instruction(startIndex) as BuilderInstruction3rc).registerCount +
(instruction(startIndex) as BuilderInstruction3rc).startRegister - 1
val targetRegister = (instruction(endIndex) as Instruction35c).registerC
createComponentMethod.addInstructions(
0,
"""
move-object/from16 v7, p$conversionContextParam
move-object/from16 v8, p$textRefParam
invoke-static {v7, v8}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onComponentCreated(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;)V
"""
)
val instructions = implementation!!.instructions
val targetString =
((instructions.elementAt(startIndex) as ReferenceInstruction).reference as MethodReference).parameterTypes.first().toString()
for ((index, instruction) in instructions.withIndex()) {
if (instruction.opcode != Opcode.IGET_OBJECT) continue
val indexReference = (instruction as ReferenceInstruction).reference as FieldReference
if (indexReference.type != targetString) continue
targetReference = indexReference
targetIndex = index
break
}
if (targetIndex == 0) return TextComponentSpecFingerprint.toErrorResult()
val conversionContextParam = 0
addInstructions(
endIndex + 1, """
move-object/from16 v$dummyRegister, p$conversionContextParam
iget-object v$dummyRegister, v$dummyRegister, ${definingClass}->${targetReference.name}:${targetReference.type}
invoke-static {v$dummyRegister, v$targetRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onComponentCreated(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;)V
"""
)
}
} ?: return TextComponentSpecFingerprint.toErrorResult()
} ?: return TextComponentSpecParentFingerprint.toErrorResult()
MainstreamVideoIdPatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V")
@ -108,6 +142,9 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
private companion object {
const val INTEGRATIONS_RYD_CLASS_DESCRIPTOR =
"$UTILS_PATH/ReturnYouTubeDislikePatch;"
var targetIndex: Int = 0
private lateinit var targetReference: FieldReference
}
private fun MethodFingerprint.toPatch(voteKind: Vote) = VotePatch(this, voteKind)
@ -119,4 +156,13 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
DISLIKE(-1),
REMOVE_LIKE(0)
}
private fun MutableMethod.insertShorts(index: Int, register: Int) {
addInstructions(
index, """
invoke-static {v$register}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onShortsComponentCreated(Landroid/text/Spanned;)Landroid/text/Spanned;
move-result-object v$register
"""
)
}
}

View File

@ -3,17 +3,10 @@ package app.revanced.patches.youtube.video.quality.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object VideoQualitySetterFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("[L", "I", "I", "Z", "I"),
opcodes = listOf(
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_BOOLEAN,
)
parameters = listOf("L"),
strings = listOf("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT")
)

View File

@ -0,0 +1,17 @@
package app.revanced.patches.youtube.video.quality.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object VideoQualitySettingsFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IGET_OBJECT
)
)

View File

@ -0,0 +1,12 @@
package app.revanced.patches.youtube.video.quality.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object VideoQualitySettingsParentFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("[L", "I", "Z"),
strings = listOf("menu_item_video_quality")
)

View File

@ -3,16 +3,9 @@ package app.revanced.patches.youtube.video.quality.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object VideoUserQualityChangeFingerprint : MethodFingerprint(
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L","L","I","J"),
opcodes = listOf(
Opcode.MOVE,
Opcode.MOVE_WIDE,
Opcode.INVOKE_INTERFACE_RANGE,
Opcode.RETURN_VOID
)
customFingerprint = { it.name == "onItemClick" }
)

View File

@ -13,9 +13,7 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.shared.annotation.YouTubeCompatibility
import app.revanced.patches.youtube.misc.videoid.legacy.patch.LegacyVideoIdPatch
import app.revanced.patches.youtube.video.quality.bytecode.fingerprints.VideoQualityReferenceFingerprint
import app.revanced.patches.youtube.video.quality.bytecode.fingerprints.VideoQualitySetterFingerprint
import app.revanced.patches.youtube.video.quality.bytecode.fingerprints.VideoUserQualityChangeFingerprint
import app.revanced.patches.youtube.video.quality.bytecode.fingerprints.*
import app.revanced.util.integrations.Constants.VIDEO_PATH
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.FieldReference
@ -26,36 +24,44 @@ import org.jf.dexlib2.iface.reference.FieldReference
@Version("0.0.1")
class VideoQualityBytecodePatch : BytecodePatch(
listOf(
VideoQualitySetterFingerprint
VideoQualitySetterFingerprint,
VideoQualitySettingsParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
VideoQualitySetterFingerprint.result?.let { parentResult ->
VideoQualityReferenceFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let { result ->
val instructions = result.method.implementation!!.instructions
val qualityFieldReference =
qualityFieldReference =
(instructions.elementAt(0) as ReferenceInstruction).reference as FieldReference
val qIndexMethodName =
qIndexMethodName =
context.classes.single { it.type == qualityFieldReference.type }.methods.single { it.parameterTypes.first() == "I" }.name
parentResult.mutableMethod.addInstructions(
0, """
iget-object v0, p0, ${result.classDef.type}->${qualityFieldReference.name}:${qualityFieldReference.type}
const-string v1, "$qIndexMethodName"
invoke-static {p1, p2, v0, v1}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
move-result p2
""",
)
} ?: return VideoQualityReferenceFingerprint.toErrorResult()
VideoUserQualityChangeFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.mutableMethod?.addInstruction(
0,
"invoke-static {p3}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userChangedQuality(I)V"
0,
"invoke-static {p3}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userChangedQuality(I)V"
) ?: return VideoUserQualityChangeFingerprint.toErrorResult()
} ?: return VideoQualitySetterFingerprint.toErrorResult()
VideoQualitySettingsParentFingerprint.result?.let { parentResult ->
VideoQualitySettingsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.mutableMethod?.let {
relayFieldReference = (it.implementation!!.instructions.elementAt(0) as ReferenceInstruction).reference as FieldReference
} ?: return VideoQualitySettingsFingerprint.toErrorResult()
parentResult.mutableMethod.addInstructions(
0, """
iget-object v0, p0, ${parentResult.classDef.type}->${relayFieldReference.name}:${relayFieldReference.type}
iget-object v1, v0, ${relayFieldReference.type}->${qualityFieldReference.name}:${qualityFieldReference.type}
const-string v2, "$qIndexMethodName"
invoke-static {p1, p2, v1, v2}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
move-result p2
"""
)
} ?: return VideoQualitySettingsParentFingerprint.toErrorResult()
LegacyVideoIdPatch.injectCall("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
return PatchResultSuccess()
@ -63,5 +69,11 @@ class VideoQualityBytecodePatch : BytecodePatch(
private companion object {
const val INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR =
"$VIDEO_PATH/VideoQualityPatch;"
private lateinit var qIndexMethodName: String
private lateinit var relayFieldReference: FieldReference
private lateinit var qualityFieldReference: FieldReference
}
}