a couple more fixes

This commit is contained in:
oSumAtrIX 2026-02-02 15:59:38 +01:00
parent a8572bd4c7
commit 4a1d850bd4
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
34 changed files with 184 additions and 155 deletions

View file

@ -7,13 +7,16 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method 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.Instruction
@Deprecated(
"Use forEachInstructionAsSequence directly within a bytecodePatch", ReplaceWith(
"bytecodePatch { apply { forEachInstructionAsSequence(filterMap, transform) } }",
"app.revanced.util.forEachInstructionAsSequence",
"app.revanced.patcher.patch.bytecodePatch",
)
)
fun <T> transformInstructionsPatch( fun <T> transformInstructionsPatch(
filterMap: (ClassDef, Method, Instruction, Int) -> T?, filterMap: (ClassDef, Method, Instruction, Int) -> T?,
transform: (MutableMethod, T) -> Unit, transform: (MutableMethod, T) -> Unit,
) = bytecodePatch { ) = bytecodePatch {
apply { apply { forEachInstructionAsSequence(filterMap, transform) }
forEachInstructionAsSequence { classDef, method, i, instruction ->
transform(method, filterMap(classDef, method, instruction, i) ?: return@forEachInstructionAsSequence)
}
}
} }

View file

@ -6,7 +6,7 @@ import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused") @Suppress("unused")
val hideSuggestedContent = bytecodePatch( val hideSuggestedContentPatch = bytecodePatch(
name = "Hide suggested content", name = "Hide suggested content",
description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).", description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).",
use = false, use = false,

View file

@ -1,9 +1,6 @@
package app.revanced.patches.music.misc.androidauto package app.revanced.patches.music.misc.androidauto
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.util.returnEarly
@Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch")) @Deprecated("This patch is useless by itself and has been merged into another patch.", ReplaceWith("unlockAndroidAutoMediaBrowserPatch"))
@Suppress("unused") @Suppress("unused")

View file

@ -5,7 +5,6 @@ import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val signatureDetectionPatch = bytecodePatch( val signatureDetectionPatch = bytecodePatch(
description = "Disables detection of incorrect signature.", description = "Disables detection of incorrect signature.",
) { ) {

View file

@ -3,7 +3,6 @@ package app.revanced.patches.photomath.misc.unlock.bookpoint
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly import app.revanced.util.returnEarly
@Suppress("unused")
val enableBookpointPatch = bytecodePatch( val enableBookpointPatch = bytecodePatch(
description = "Enables textbook access", description = "Enables textbook access",
) { ) {

View file

@ -3,7 +3,6 @@ package app.revanced.patches.reddit.ad.comments
import app.revanced.patcher.extensions.replaceInstructions import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val hideCommentAdsPatch = bytecodePatch( val hideCommentAdsPatch = bytecodePatch(
description = "Removes ads in the comments." description = "Removes ads in the comments."
) { ) {

View file

@ -60,34 +60,41 @@ internal fun sanitizeSharingLinksPatch(
}, },
) )
fun Match.hook(
getInsertIndex: List<Int>.() -> Int, fun Match.hookUrlString(matchIndex: Int) {
getUrlRegister: MutableMethod.(insertIndex: Int) -> Int, val index = get(matchIndex)
) { val urlRegister = method.getInstruction<OneRegisterInstruction>(index).registerA
val insertIndex = indices[0].getInsertIndex()
val urlRegister = method.getUrlRegister(insertIndex)
method.addInstructions( method.addInstructions(
insertIndex, index + 1,
""" """
invoke-static {v$urlRegister}, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister move-result-object v$urlRegister
""", """
) )
} }
// YouTube share sheet.\ fun Match.hookIntentPutExtra(matchIndex: Int) {
youTubeShareSheetMethodMatch.hook(getInsertIndex = { first() + 1 }) { insertIndex -> val index = get(matchIndex)
getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA val urlRegister = method.getInstruction<FiveRegisterInstruction>(index).registerE
method.addInstructionsAtControlFlowLabel(
index,
"""
invoke-static { v$urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister
"""
)
} }
// YouTube share sheet copy link.
youTubeCopyTextMethodMatch.hookUrlString(0)
// YouTube share sheet other apps.
youTubeShareSheetMethodMatch.hookIntentPutExtra(3)
// Native system share sheet. // Native system share sheet.
youTubeSystemShareSheetMethodMatch.hook(getInsertIndex = { last() }) { insertIndex -> youTubeSystemShareSheetMethodMatch.hookIntentPutExtra(3)
getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
}
youTubeCopyTextMethodMatch.hook(getInsertIndex = { first() + 2 }) { insertIndex ->
getInstruction<TwoRegisterInstruction>(insertIndex - 2).registerA
}
} }
} }

View file

@ -93,7 +93,7 @@ private val addGiveKudosButtonToLayoutPatch = resourcePatch {
} }
@Suppress("unused") @Suppress("unused")
val addGiveGroupKudosButtonToGroupActivity = bytecodePatch( val addGiveGroupKudosButtonToGroupActivityPatch = bytecodePatch(
name = "Add 'Give Kudos' button to 'Group Activity'", name = "Add 'Give Kudos' button to 'Group Activity'",
description = "Adds a button that triggers the same action as shaking your phone would.", description = "Adds a button that triggers the same action as shaking your phone would.",
) { ) {

View file

@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.instructions import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -19,7 +19,7 @@ val feedFilterPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
) )
compatibleWith( compatibleWith(

View file

@ -3,7 +3,7 @@ package app.revanced.patches.tiktok.interaction.downloads
import app.revanced.patcher.extensions.* import app.revanced.patcher.extensions.*
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
@ -15,12 +15,13 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/download/DownloadsPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/download/DownloadsPatch;"
@Suppress("unused") @Suppress("unused")
val Downloads = bytecodePatch( val downloadsPatch = bytecodePatch(
name = "Downloads",
description = "Removes download restrictions and changes the default path to download to.", description = "Removes download restrictions and changes the default path to download to.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
) )
compatibleWith( compatibleWith(

View file

@ -12,7 +12,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;" "Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;"
val Settings = bytecodePatch( val settingsPatch = bytecodePatch(
name = "Settings",
description = "Adds ReVanced settings to TikTok.", description = "Adds ReVanced settings to TikTok.",
) { ) {
dependsOn(sharedExtensionPatch, addBrandLicensePatch) dependsOn(sharedExtensionPatch, addBrandLicensePatch)

View file

@ -6,7 +6,7 @@ import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstMutableMethod import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.Settings import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadMethod
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -21,7 +21,7 @@ val sIMSpoofPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
) )
compatibleWith( compatibleWith(

View file

@ -9,7 +9,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused") @Suppress("unused")
val blockAudioAdsPatch = bytecodePatch( val blockAudioAdsPatch = bytecodePatch(
@ -18,7 +18,7 @@ val blockAudioAdsPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
addResourcesPatch, addResourcesPatch,
) )

View file

@ -7,7 +7,7 @@ import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.twitch.ad.video.blockVideoAdsPatch import app.revanced.patches.twitch.ad.video.blockVideoAdsPatch
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused") @Suppress("unused")
val blockEmbeddedAdsPatch = bytecodePatch( val blockEmbeddedAdsPatch = bytecodePatch(
@ -17,7 +17,7 @@ val blockEmbeddedAdsPatch = bytecodePatch(
dependsOn( dependsOn(
blockVideoAdsPatch, blockVideoAdsPatch,
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
) )
compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0")) compatibleWith("tv.twitch.android.app"("16.9.1", "25.3.0"))

View file

@ -12,7 +12,7 @@ import app.revanced.patches.twitch.ad.shared.util.ReturnMethod
import app.revanced.patches.twitch.ad.shared.util.adPatch import app.revanced.patches.twitch.ad.shared.util.adPatch
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")
val blockVideoAdsPatch = bytecodePatch( val blockVideoAdsPatch = bytecodePatch(
@ -24,7 +24,7 @@ val blockVideoAdsPatch = bytecodePatch(
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
addResourcesPatch, addResourcesPatch,
adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods -> adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods ->

View file

@ -10,7 +10,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused") @Suppress("unused")
val showDeletedMessagesPatch = bytecodePatch( val showDeletedMessagesPatch = bytecodePatch(
@ -19,7 +19,7 @@ val showDeletedMessagesPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
addResourcesPatch, addResourcesPatch,
) )

View file

@ -9,7 +9,7 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("unused") @Suppress("unused")
val autoClaimChannelPointsPatch = bytecodePatch( val autoClaimChannelPointsPatch = bytecodePatch(
@ -17,7 +17,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
description = "Automatically claim Channel Points.", description = "Automatically claim Channel Points.",
) { ) {
dependsOn( dependsOn(
Settings, settingsPatch,
addResourcesPatch, addResourcesPatch,
) )

View file

@ -7,7 +7,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch
import app.revanced.patches.twitch.misc.settings.PreferenceScreen import app.revanced.patches.twitch.misc.settings.PreferenceScreen
import app.revanced.patches.twitch.misc.settings.Settings import app.revanced.patches.twitch.misc.settings.settingsPatch
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")
val debugModePatch = bytecodePatch( val debugModePatch = bytecodePatch(
@ -17,7 +17,7 @@ val debugModePatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
Settings, settingsPatch,
addResourcesPatch, addResourcesPatch,
) )

View file

@ -36,7 +36,8 @@ fun addSettingPreference(screen: BasePreference) {
preferences += screen preferences += screen
} }
val Settings = bytecodePatch( val settingsPatch = bytecodePatch(
name = "Settings",
description = "Adds settings menu to Twitch.", description = "Adds settings menu to Twitch.",
) { ) {
dependsOn( dependsOn(

View file

@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.extensions.wideLiteral import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
@ -94,21 +95,25 @@ val hideAdsPatch = bytecodePatch(
replaceInstruction( replaceInstruction(
addListIndex, addListIndex,
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" + "invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V", "->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V",
) )
} }
// Hide ad views. // Hide ad views.
forEachInstructionAsSequence { _, method, index, instruction -> forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence null
if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence null
val insertIndex = index + 1 val insertIndex = index + 1
// Call to get the view with the id adAttribution, // Call to get the view with the id adAttribution.
if (method.instructions[insertIndex].opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence if (method.instructions.elementAt(insertIndex).opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence null
val viewRegister = method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC val viewRegister = method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
return@forEachInstructionAsSequence insertIndex to viewRegister
}) { method, (insertIndex, viewRegister) ->
method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView") method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView")
} }
} }

View file

@ -60,7 +60,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;" internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"
@Suppress("unused") @Suppress("unused")
val Downloads = bytecodePatch( val downloadsPatch = bytecodePatch(
name = "Downloads",
description = "Adds support to download videos with an external downloader app " + description = "Adds support to download videos with an external downloader app " +
"using the in-app download button or a video player action button.", "using the in-app download button or a video player action button.",
) { ) {

View file

@ -112,9 +112,9 @@ internal val BytecodePatchContext.seekbarTappingMethodMatch by composingFirstMet
Int.MAX_VALUE.toLong()(), Int.MAX_VALUE.toLong()(),
allOf(Opcode.NEW_INSTANCE(), type("Landroid/graphics/Point;")), allOf(Opcode.NEW_INSTANCE(), type("Landroid/graphics/Point;")),
after(method { toString() == "Landroid/graphics/Point;-><init>(II)V" }), after(method { toString() == "Landroid/graphics/Point;-><init>(II)V" }),
after(method { toString() == "Landroid/view/MotionEvent;->getX()F" }), after(method { toString() == "Lj$/util/Optional;->of(Ljava/lang/Object;)Lj$/util/Optional;" }),
after(Opcode.MOVE_RESULT_OBJECT()), after(Opcode.MOVE_RESULT_OBJECT()),
after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj\$/util/Optional;" })), after(allOf(Opcode.IPUT_OBJECT(), field { type == "Lj$/util/Optional;" })),
afterAtMost(10, Opcode.INVOKE_VIRTUAL()), afterAtMost(10, Opcode.INVOKE_VIRTUAL()),
) )
} }

View file

@ -13,7 +13,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.seekbarMethod import app.revanced.patches.youtube.shared.seekbarMethod
import app.revanced.patches.youtube.shared.seekbarOnDrawMethodMatch import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch
import app.revanced.util.insertLiteralOverride import app.revanced.util.insertLiteralOverride
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;"
@ -38,7 +38,7 @@ val hideSeekbarPatch = bytecodePatch(
SwitchPreference("revanced_fullscreen_large_seekbar"), SwitchPreference("revanced_fullscreen_large_seekbar"),
) )
seekbarMethod.immutableClassDef.seekbarOnDrawMethodMatch.method.addInstructionsWithLabels( seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().method.addInstructionsWithLabels(
0, 0,
""" """
const/4 v0, 0x0 const/4 v0, 0x0

View file

@ -3,7 +3,8 @@ package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused") @Suppress("unused")
val Seekbar = bytecodePatch( val seekbarPatch = bytecodePatch(
name = "Seekbar",
description = "Adds options to disable precise seeking when swiping up on the seekbar, " + description = "Adds options to disable precise seeking when swiping up on the seekbar, " +
"slide to seek instead of playing at 2x speed when pressing and holding, " + "slide to seek instead of playing at 2x speed when pressing and holding, " +
"tapping the player seekbar to seek, " + "tapping the player seekbar to seek, " +

View file

@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.branding.header
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.wideLiteral import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
@ -77,12 +78,15 @@ private val changeHeaderBytecodePatch = bytecodePatch {
).forEach { resourceName -> ).forEach { resourceName ->
val id = ResourceType.ATTR[resourceName] val id = ResourceType.ATTR[resourceName]
forEachInstructionAsSequence { _, method, i, instruction -> forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
val register = method.getInstruction<OneRegisterInstruction>(i).registerA val register = method.getInstruction<OneRegisterInstruction>(index).registerA
return@forEachInstructionAsSequence index to register
}) { method, (index, register) ->
method.addInstructions( method.addInstructions(
i + 1, index + 1,
""" """
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getHeaderAttributeId(I)I invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getHeaderAttributeId(I)I
move-result v$register move-result v$register
@ -218,14 +222,14 @@ val changeHeaderPatch = resourcePatch(
if (!customFile.exists()) { if (!customFile.exists()) {
throw PatchException( throw PatchException(
"The custom header path cannot be found: " + "The custom header path cannot be found: " +
customFile.absolutePath, customFile.absolutePath,
) )
} }
if (!customFile.isDirectory) { if (!customFile.isDirectory) {
throw PatchException( throw PatchException(
"The custom header path must be a folder: " + "The custom header path must be a folder: " +
customFile.absolutePath, customFile.absolutePath,
) )
} }
@ -248,7 +252,7 @@ val changeHeaderPatch = resourcePatch(
if (customFiles.isNotEmpty() && customFiles.size != variants.size) { if (customFiles.isNotEmpty() && customFiles.size != variants.size) {
throw PatchException( throw PatchException(
"Both light/dark mode images " + "Both light/dark mode images " +
"must be specified but only found: " + customFiles.map { it.name }, "must be specified but only found: " + customFiles.map { it.name },
) )
} }
@ -263,8 +267,8 @@ val changeHeaderPatch = resourcePatch(
if (!copiedFiles) { if (!copiedFiles) {
throw PatchException( throw PatchException(
"Expected to find directories and files: " + "Expected to find directories and files: " +
customHeaderResourceFileNames.contentToString() + customHeaderResourceFileNames.contentToString() +
"\nBut none were found in the provided option file path: " + customFile.absolutePath, "\nBut none were found in the provided option file path: " + customFile.absolutePath,
) )
} }
} }

View file

@ -3,7 +3,9 @@ package app.revanced.patches.youtube.layout.hide.shorts
import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.extensions.wideLiteral import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.firstMutableMethod
import app.revanced.patcher.immutableClassDef import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@ -66,9 +68,9 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
// FIXME: The buffer is very different for 20.22+ and these current cannot be hidden. // FIXME: The buffer is very different for 20.22+ and these current cannot be hidden.
Logger.getLogger(this::class.java.name).warning( Logger.getLogger(this::class.java.name).warning(
"\n!!!" + "\n!!!" +
"\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" + "\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" +
"\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" + "\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" +
"\n!!!", "\n!!!",
) )
} else { } else {
preferences.addAll( preferences.addAll(
@ -188,16 +190,18 @@ val hideShortsComponentsPatch = bytecodePatch(
val id = ResourceType.DIMEN["reel_player_right_pivot_v2_size"] val id = ResourceType.DIMEN["reel_player_right_pivot_v2_size"]
forEachInstructionAsSequence { _, method, i, instruction -> forEachInstructionAsSequence({ _, method, instruction, index ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
val targetIndex = method.indexOfFirstInstructionOrThrow(i) { val targetIndex = method.indexOfFirstInstructionOrThrow(index) {
getReference<MethodReference>()?.name == "getDimensionPixelSize" methodReference?.name == "getDimensionPixelSize"
} + 1 } + 1
val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
method.addInstructions( return@forEachInstructionAsSequence targetIndex to sizeRegister
}) { method, (targetIndex, sizeRegister) ->
firstMutableMethod(method).addInstructions(
targetIndex + 1, targetIndex + 1,
""" """
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
@ -218,24 +222,24 @@ val hideShortsComponentsPatch = bytecodePatch(
addInstruction( addInstruction(
insertIndex, insertIndex,
"invoke-static {v$viewRegister}," + "invoke-static {v$viewRegister}," +
" $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", " $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V",
) )
} }
} }
// Hook to hide the shared navigation bar when the Shorts player is opened. // Hook to hide the shared navigation bar when the Shorts player is opened.
( (
if (is_20_45_or_greater) { if (is_20_45_or_greater) {
renderBottomNavigationBarParentMethod renderBottomNavigationBarParentMethod
} else if (is_19_41_or_greater) { } else if (is_19_41_or_greater) {
renderBottomNavigationBarLegacy1941ParentMethod renderBottomNavigationBarLegacy1941ParentMethod
} else { } else {
legacyRenderBottomNavigationBarLegacyParentMethod legacyRenderBottomNavigationBarLegacyParentMethod
} }
).immutableClassDef.getRenderBottomNavigationBarMethodMatch().addInstruction( ).immutableClassDef.getRenderBottomNavigationBarMethodMatch().addInstruction(
0, 0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V", "invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V",
) )
// Hide the bottom bar container of the Shorts player. // Hide the bottom bar container of the Shorts player.
shortsBottomBarContainerMethodMatch.let { shortsBottomBarContainerMethodMatch.let {

View file

@ -62,7 +62,8 @@ private val miniplayerResourcePatch = resourcePatch {
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/MiniplayerPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/MiniplayerPatch;"
@Suppress("unused") @Suppress("unused")
val Miniplayer = bytecodePatch( val miniplayerPatch = bytecodePatch(
name = "Miniplayer",
description = "Adds options to change the in-app minimized player.", description = "Adds options to change the in-app minimized player.",
) { ) {
dependsOn( dependsOn(

View file

@ -164,27 +164,22 @@ val returnYouTubeDislikePatch = bytecodePatch(
charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
} }
val free1 = findFreeRegister(insertIndex, charSequenceRegister) val conversionContext = findFreeRegister(insertIndex, charSequenceRegister)
val free2 = findFreeRegister(insertIndex, charSequenceRegister, free1)
addInstructionsAtControlFlowLabel( addInstructionsAtControlFlowLabel(
insertIndex, insertIndex,
""" """
# Copy conversion context. # Copy conversion context.
move-object/from16 v$free1, p0 move-object/from16 v$conversionContext, p0
# 20.41 field is the abstract superclass. iget-object v$conversionContext, v$conversionContext, $textComponentConversionContextField
# Verify it's the expected subclass just in case.
instance-of v$free2, v$free1, ${textComponentConversionContextField.type}
if-eqz v$free2, :ignore
check-cast v$free1, $conversionContextClass invoke-static { v$conversionContext, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
invoke-static { v$free1, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister move-result-object v$charSequenceRegister
:ignore :ignore
nop nop
""", """
) )
} }

View file

@ -22,7 +22,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.getLayoutConstructorMethodMatch import app.revanced.patches.youtube.shared.getLayoutConstructorMethodMatch
import app.revanced.patches.youtube.shared.seekbarMethod import app.revanced.patches.youtube.shared.seekbarMethod
import app.revanced.patches.youtube.shared.seekbarOnDrawMethodMatch import app.revanced.patches.youtube.shared.getSeekbarOnDrawMethodMatch
import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.onCreateHook
import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.patches.youtube.video.information.videoTimeHook import app.revanced.patches.youtube.video.information.videoTimeHook
@ -111,7 +111,8 @@ private const val EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController;" "Lapp/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController;"
@Suppress("unused") @Suppress("unused")
val SponsorBlock = bytecodePatch( val sponsorBlockPatch = bytecodePatch(
name = "SponsorBlock",
description = "Adds options to enable and configure SponsorBlock, which can skip undesired video segments such as sponsored content.", description = "Adds options to enable and configure SponsorBlock, which can skip undesired video segments such as sponsored content.",
) { ) {
dependsOn( dependsOn(
@ -164,7 +165,7 @@ val SponsorBlock = bytecodePatch(
// Cannot match using original immutable class because // Cannot match using original immutable class because
// class may have been modified by other patches // class may have been modified by other patches
seekbarMethod.immutableClassDef.seekbarOnDrawMethodMatch.let { seekbarMethod.immutableClassDef.getSeekbarOnDrawMethodMatch().let {
it.method.apply { it.method.apply {
// Set seekbar thickness. // Set seekbar thickness.
val thicknessIndex = it[-1] val thicknessIndex = it[-1]

View file

@ -13,7 +13,8 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/announcements/AnnouncementsPatch;" "Lapp/revanced/extension/youtube/patches/announcements/AnnouncementsPatch;"
@Suppress("unused") @Suppress("unused")
val Announcements = bytecodePatch( val announcementsPatch = bytecodePatch(
name = "Announcements",
description = "Adds an option to show announcements from ReVanced on app startup.", description = "Adds an option to show announcements from ReVanced on app startup.",
) { ) {
dependsOn( dependsOn(

View file

@ -136,7 +136,9 @@ internal val BytecodePatchContext.imageEnumConstructorMethodMatch by composingFi
internal val BytecodePatchContext.setEnumMapMethodMatch by composingFirstMethod { internal val BytecodePatchContext.setEnumMapMethodMatch by composingFirstMethod {
instructions( instructions(
ResourceType.DRAWABLE("yt_fill_bell_black_24"), ResourceType.DRAWABLE("yt_fill_bell_black_24"),
afterAtMost(10, method { toString() == "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;" }), afterAtMost(
10,
method { toString() == "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;" }),
afterAtMost( afterAtMost(
10, 10,
method { toString() == "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;" }, method { toString() == "Ljava/util/EnumMap;->put(Ljava/lang/Enum;Ljava/lang/Object;)Ljava/lang/Object;" },

View file

@ -113,7 +113,7 @@ internal val BytecodePatchContext.seekbarMethod by gettingFirstMethodDeclarative
/** /**
* Matches to _mutable_ class found in [seekbarMethod]. * Matches to _mutable_ class found in [seekbarMethod].
*/ */
internal val ClassDef.seekbarOnDrawMethodMatch by ClassDefComposing.composingFirstMethod { internal fun ClassDef.getSeekbarOnDrawMethodMatch() = firstMethodComposite {
name("onDraw") name("onDraw")
instructions( instructions(
method { toString() == "Ljava/lang/Math;->round(F)I" }, method { toString() == "Ljava/lang/Math;->round(F)I" },

View file

@ -46,7 +46,8 @@ val disableVideoCodecsPatch = bytecodePatch(
method.replaceInstruction( method.replaceInstruction(
index, index,
"invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" + "disableHdrVideo(Landroid/view/Display\$HdrCapabilities;)[I", "invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" +
$$"disableHdrVideo(Landroid/view/Display$HdrCapabilities;)[I",
) )
}, },
), ),

View file

@ -18,6 +18,7 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.Opcode.* import com.android.tools.smali.dexlib2.Opcode.*
import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils
import com.android.tools.smali.dexlib2.formatter.DexFormatter import com.android.tools.smali.dexlib2.formatter.DexFormatter
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.* import com.android.tools.smali.dexlib2.iface.instruction.*
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@ -27,8 +28,8 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.iface.value.* import com.android.tools.smali.dexlib2.iface.value.*
import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.value.* import com.android.tools.smali.dexlib2.immutable.value.*
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.* import java.util.*
import kotlin.collections.ArrayDeque
/** /**
* Starting from and including the instruction at index [startIndex], * Starting from and including the instruction at index [startIndex],
@ -89,7 +90,7 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
// This method is simple and does not follow branching. // This method is simple and does not follow branching.
throw IllegalArgumentException( throw IllegalArgumentException(
"Encountered a branch statement before " + "Encountered a branch statement before " +
"a free register could be found from startIndex: $startIndex", "a free register could be found from startIndex: $startIndex",
) )
} }
@ -109,7 +110,7 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
// In practice this never occurs. // In practice this never occurs.
throw IllegalArgumentException( throw IllegalArgumentException(
"Could not find a free register from startIndex: " + "Could not find a free register from startIndex: " +
"$startIndex excluding: $registersToExclude", "$startIndex excluding: $registersToExclude",
) )
} }
} }
@ -193,7 +194,7 @@ private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
val stringUsageIndex = indexOfFirstInstruction(stringIndex) { val stringUsageIndex = indexOfFirstInstruction(stringIndex) {
val reference = getReference<MethodReference>() val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" && reference?.definingClass == "Ljava/lang/StringBuilder;" &&
(this as? FiveRegisterInstruction)?.registerD == stringRegister (this as? FiveRegisterInstruction)?.registerD == stringRegister
} }
if (stringUsageIndex < 0) { if (stringUsageIndex < 0) {
throw IllegalArgumentException("Could not find StringBuilder usage in: $this") throw IllegalArgumentException("Could not find StringBuilder usage in: $this")
@ -345,7 +346,8 @@ fun MutableMethod.addInstructionsAtControlFlowLabel(
* @throws PatchException if the resource cannot be found. * @throws PatchException if the resource cannot be found.
* @see [indexOfFirstResourceIdOrThrow], [indexOfFirstLiteralInstructionReversed] * @see [indexOfFirstResourceIdOrThrow], [indexOfFirstLiteralInstructionReversed]
*/ */
fun Method.indexOfFirstResourceId(resourceName: String): Int = indexOfFirstLiteralInstruction(ResourceType.ID[resourceName]) fun Method.indexOfFirstResourceId(resourceName: String): Int =
indexOfFirstLiteralInstruction(ResourceType.ID[resourceName])
/** /**
* Get the index of the first instruction with the id of the given resource name or throw a [PatchException]. * Get the index of the first instruction with the id of the given resource name or throw a [PatchException].
@ -458,7 +460,8 @@ fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Long): Int {
* @return the last literal instruction with the value, or -1 if not found. * @return the last literal instruction with the value, or -1 if not found.
* @see indexOfFirstLiteralInstructionOrThrow * @see indexOfFirstLiteralInstructionOrThrow
*/ */
fun Method.indexOfFirstLiteralInstructionReversed(literal: Float) = indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong()) fun Method.indexOfFirstLiteralInstructionReversed(literal: Float) =
indexOfFirstLiteralInstructionReversed(literal.toRawBits().toLong())
/** /**
* Find the index of the last wide literal instruction with the given float value, * Find the index of the last wide literal instruction with the given float value,
@ -478,7 +481,8 @@ fun Method.indexOfFirstLiteralInstructionReversedOrThrow(literal: Float): Int {
* @return the last literal instruction with the value, or -1 if not found. * @return the last literal instruction with the value, or -1 if not found.
* @see indexOfFirstLiteralInstructionOrThrow * @see indexOfFirstLiteralInstructionOrThrow
*/ */
fun Method.indexOfFirstLiteralInstructionReversed(literal: Double) = indexOfFirstLiteralInstructionReversed(literal.toRawBits()) fun Method.indexOfFirstLiteralInstructionReversed(literal: Double) =
indexOfFirstLiteralInstructionReversed(literal.toRawBits())
/** /**
* Find the index of the last wide literal instruction with the given double value, * Find the index of the last wide literal instruction with the given double value,
@ -550,9 +554,10 @@ fun Method.indexOfFirstInstruction(targetOpcode: Opcode): Int = indexOfFirstInst
* @return The index of the first opcode specified, or -1 if not found. * @return The index of the first opcode specified, or -1 if not found.
* @see indexOfFirstInstructionOrThrow * @see indexOfFirstInstructionOrThrow
*/ */
fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): Int = indexOfFirstInstruction(startIndex) { fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): Int =
opcode == targetOpcode indexOfFirstInstruction(startIndex) {
} opcode == targetOpcode
}
/** /**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex]. * Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
@ -587,9 +592,10 @@ fun Method.indexOfFirstInstructionOrThrow(targetOpcode: Opcode): Int = indexOfFi
* @throws PatchException * @throws PatchException
* @see indexOfFirstInstruction * @see indexOfFirstInstruction
*/ */
fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, targetOpcode: Opcode): Int = indexOfFirstInstructionOrThrow(startIndex) { fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, targetOpcode: Opcode): Int =
opcode == targetOpcode indexOfFirstInstructionOrThrow(startIndex) {
} opcode == targetOpcode
}
/** /**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex]. * Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
@ -615,9 +621,10 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, filter: Instructi
* @return -1 if the instruction is not found. * @return -1 if the instruction is not found.
* @see indexOfFirstInstructionReversedOrThrow * @see indexOfFirstInstructionReversedOrThrow
*/ */
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode: Opcode): Int = indexOfFirstInstructionReversed(startIndex) { fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode: Opcode): Int =
opcode == targetOpcode indexOfFirstInstructionReversed(startIndex) {
} opcode == targetOpcode
}
/** /**
* Get the index of matching instruction, * Get the index of matching instruction,
@ -654,9 +661,10 @@ fun Method.indexOfFirstInstructionReversed(targetOpcode: Opcode): Int = indexOfF
* @return The index of the instruction. * @return The index of the instruction.
* @see indexOfFirstInstructionReversed * @see indexOfFirstInstructionReversed
*/ */
fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, targetOpcode: Opcode): Int = indexOfFirstInstructionReversedOrThrow(startIndex) { fun Method.indexOfFirstInstructionReversedOrThrow(startIndex: Int? = null, targetOpcode: Opcode): Int =
opcode == targetOpcode indexOfFirstInstructionReversedOrThrow(startIndex) {
} opcode == targetOpcode
}
/** /**
* Get the index of matching instruction, * Get the index of matching instruction,
@ -713,7 +721,8 @@ fun Method.findInstructionIndicesReversedOrThrow(filter: Instruction.() -> Boole
* _Returns an empty list if no indices are found_ * _Returns an empty list if no indices are found_
* @see findInstructionIndicesReversedOrThrow * @see findInstructionIndicesReversedOrThrow
*/ */
fun Method.findInstructionIndicesReversed(opcode: Opcode): List<Int> = findInstructionIndicesReversed { this.opcode == opcode } fun Method.findInstructionIndicesReversed(opcode: Opcode): List<Int> =
findInstructionIndicesReversed { this.opcode == opcode }
/** /**
* @return An immutable list of indices of the opcode in reverse order. * @return An immutable list of indices of the opcode in reverse order.
@ -779,33 +788,30 @@ internal fun MutableMethod.insertLiteralOverride(literalIndex: Int, override: Bo
/** /**
* Iterates all instructions as sequence in all methods of all classes in the [BytecodePatchContext]. * Iterates all instructions as sequence in all methods of all classes in the [BytecodePatchContext].
* The instructions are provided in reverse order (last to first). *
* The [block] is invoked after collecting all instructions to avoid concurrent modification issues. * @param match A function that matches instructions. If a match is found, it returns a value of type [T], otherwise null.
* @param transform A function that transforms the matched instruction using the mutable method and the matched value
* of type [T].
*/ */
fun BytecodePatchContext.forEachInstructionAsSequence( fun <T> BytecodePatchContext.forEachInstructionAsSequence(
block: (classDef: MutableClassDef, method: MutableMethod, matchingIndex: Int, instruction: Instruction) -> Unit, match: (classDef: ClassDef, method: Method, instruction: Instruction, index: Int) -> T?,
transform: (MutableMethod, T) -> Unit
) { ) {
classDefs.asSequence().flatMap { classDef -> classDefs.flatMap { classDef ->
val mutableClassDef by lazy { classDefs.getOrReplaceMutable(classDef) } classDef.methods.mapNotNull { method ->
val matches = method.instructionsOrNull?.mapIndexedNotNull { index, instruction ->
match(classDef, method, instruction, index)
} ?: return@mapNotNull null
classDef.methods.asSequence().flatMap { method -> if (matches.any()) method to matches else null
val instructions =
method.instructionsOrNull as? List<Instruction> ?: return@flatMap emptySequence<() -> Unit>()
val mutableMethod by lazy { mutableClassDef.firstMutableMethod(method) }
instructions.asReversed().asSequence().mapIndexed { index, instruction ->
{
block(
mutableClassDef,
mutableMethod,
instructions.size - 1 - index,
instruction,
)
} // Reverse indices again.
}
} }
}.forEach { it() } }.forEach { (method, matches) ->
val method = firstMutableMethod(method)
val matches = matches.toCollection(ArrayDeque())
while (!matches.isEmpty()) transform(method, matches.removeLast())
}
} }
private fun MutableMethod.checkReturnType(expectedTypes: Iterable<Class<*>>) { private fun MutableMethod.checkReturnType(expectedTypes: Iterable<Class<*>>) {