original -> immutable

This commit is contained in:
oSumAtrIX
2026-01-25 20:30:19 +01:00
parent c82691380c
commit 1026a8fbd7
27 changed files with 69 additions and 75 deletions

View File

@@ -35,7 +35,7 @@ val `Hide music video ads` by creatingBytecodePatch(
SwitchPreference("revanced_music_hide_video_ads"),
)
navigate(showVideoAdsParentMethod.originalMethod)
navigate(showVideoAdsParentMethod.immutableMethod)
.to(showVideoAdsParentMethod.instructionMatches.first().index + 1)
.stop()
.addInstructions(

View File

@@ -75,7 +75,7 @@ val `Change miniplayer color` by creatingBytecodePatch(
miniPlayerConstructorMethod.classDef.methods.single { method ->
method.accessFlags == AccessFlags.PUBLIC.value or AccessFlags.FINAL.value &&
method.returnType == "V" &&
method.parameters == it.originalMethod.parameters
method.parameters == it.immutableMethod.parameters
}.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.INVOKE_DIRECT)
val freeRegister = findFreeRegister(insertIndex)

View File

@@ -19,7 +19,7 @@ private val customThemeBytecodePatch = bytecodePatch {
dependsOn(sharedExtensionPatch)
apply {
val colorSpaceUtilsClassDef = colorSpaceUtilsClassMethod.originalClassDef
val colorSpaceUtilsClassDef = colorSpaceUtilsClassMethod.immutableClassDef
// Hook a util method that converts ARGB to RGBA in the sRGB color space to replace hardcoded accent colors.
convertArgbToRgbaMethod.match(colorSpaceUtilsClassDef).method.apply {

View File

@@ -17,7 +17,7 @@ val `Fix Facebook login` by creatingBytecodePatch(
// Override the Facebook SDK to always handle the login using the web browser, which does not perform
// signature checks.
val katanaProxyLoginMethodHandlerClass = katanaProxyLoginMethodHandlerClassMethod.originalClassDef
val katanaProxyLoginMethodHandlerClass = katanaProxyLoginMethodHandlerClassMethod.immutableClassDef
// Always return 0 (no Intent was launched) as the result of trying to authorize with the Facebook app to
// make the login fallback to a web browser window.
katanaProxyLoginMethodTryAuthorizeMethod

View File

@@ -59,7 +59,7 @@ val `Change lyrics provider` by creatingBytecodePatch(
}
apply {
val httpClientBuilderMethod = httpClientBuilderMethod.originalMethod
val httpClientBuilderMethod = httpClientBuilderMethod.immutableMethod
// region Create a modified copy of the HTTP client builder method with the custom lyrics provider host.

View File

@@ -25,7 +25,7 @@ val `Sanitize sharing links` by creatingBytecodePatch(
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
"sanitizeSharingLink(Ljava/lang/String;)Ljava/lang/String;"
val copyFingerprint = if (shareCopyUrlMethod.originalMethodOrNull != null) {
val copyFingerprint = if (shareCopyUrlMethod.immutableMethodOrNull != null) {
shareCopyUrlMethod
} else {
oldShareCopyUrlMethod
@@ -48,8 +48,8 @@ val `Sanitize sharing links` by creatingBytecodePatch(
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
val shareUrlParameter: String
val shareSheetFingerprint = if (formatAndroidShareSheetUrlMethod.originalMethodOrNull != null) {
val methodAccessFlags = formatAndroidShareSheetUrlMethod.originalMethod
val shareSheetFingerprint = if (formatAndroidShareSheetUrlMethod.immutableMethodOrNull != null) {
val methodAccessFlags = formatAndroidShareSheetUrlMethod.immutableMethod
shareUrlParameter = if (AccessFlags.STATIC.isSet(methodAccessFlags.accessFlags)) {
// In newer implementations the method is static, so p0 is not `this`.
"p1"

View File

@@ -18,8 +18,8 @@ val `Disable subscription suggestions` by creatingBytecodePatch {
val label = "original"
val className = getModulesMethodMatch.classDef.type
val originalMethod = getModulesMethodMatch.method
val returnType = originalMethod.returnType
val immutableMethod = getModulesMethodMatch.method
val returnType = immutableMethod.returnType
getModulesMethodMatch.classDef.methods.add(
ImmutableMethod(
@@ -51,8 +51,8 @@ val `Disable subscription suggestions` by creatingBytecodePatch {
)
val getModulesIndex = getModulesMethodMatch.indices.first()
originalMethod.removeInstruction(getModulesIndex)
originalMethod.addInstructions(
immutableMethod.removeInstruction(getModulesIndex)
immutableMethod.addInstructions(
getModulesIndex,
"""
invoke-direct {p0}, $className->$helperMethodName()$returnType

View File

@@ -43,7 +43,7 @@ val `Playback speed` by creatingBytecodePatch(
"""
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
const/4 v0, 0x1
invoke-virtual { p0, v0 }, ${getEnterFromMethod.originalMethod}
invoke-virtual { p0, v0 }, ${getEnterFromMethod.immutableMethod}
move-result-object v0
# Model of current video retrieved using getCurrentAweme method.

View File

@@ -24,15 +24,15 @@ val overrideFeatureFlagsPatch = bytecodePatch(
) {
apply {
val configurationClass = getFeatureValueMethod.originalMethod.definingClass
val featureClass = getFeatureValueMethod.originalMethod.parameterTypes[0].toString()
val configurationClass = getFeatureValueMethod.immutableMethod.definingClass
val featureClass = getFeatureValueMethod.immutableMethod.parameterTypes[0].toString()
// The method we want to inject into does not have enough registers, so we inject a helper method
// and inject more instructions into it later, see addOverride.
// This is not in an extension since the unused variable would get compiled away and the method would
// get compiled to only have one register, which is not enough for our later injected instructions.
val helperMethod = ImmutableMethod(
getFeatureValueMethod.originalMethod.definingClass,
getFeatureValueMethod.immutableMethod.definingClass,
"getValueOverride",
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
"Ljava/lang/String;",

View File

@@ -56,7 +56,7 @@ val `Disable double tap actions` by creatingBytecodePatch(
val doubleTapInfoGetSeekSourceFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("Z")
returnType(seekTypeEnumMethod.originalClassDef.type)
returnType(seekTypeEnumMethod.immutableClassDef.type)
opcodes(
Opcode.IF_EQZ,
Opcode.SGET_OBJECT,

View File

@@ -53,7 +53,7 @@ val `Change form factor` by creatingBytecodePatch(
instructions(
fieldAccess(smali = "Landroid/os/Build;->MODEL:Ljava/lang/String;"),
fieldAccess(
definingClass = formFactorEnumConstructorMethod.originalClassDef.type,
definingClass = formFactorEnumConstructorMethod.immutableClassDef.type,
type = "I",
afterAtMost(50),
),

View File

@@ -45,12 +45,12 @@ val `Hide end screen suggested video` by creatingBytecodePatch(
)
removeOnLayoutChangeListenerMethod.let {
val endScreenMethod = navigate(it.originalMethod).to(it.indices.last()).stop() // TODO
val endScreenMethod = navigate(it.immutableMethod).to(it.indices.last()).stop() // TODO
endScreenMethod.apply {
val autoNavStatusMethodName = autoNavStatusMethod.match(
autoNavConstructorMethod.classDef,
).originalMethod.name
).immutableMethod.name
val invokeIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()

View File

@@ -270,7 +270,7 @@ val `Hide layout components` by creatingBytecodePatch(
// region Watermark (legacy code for old versions of YouTube)
showWatermarkMethod.match(
playerOverlayMethod.originalClassDef,
playerOverlayMethod.immutableClassDef,
).method.apply {
val index = implementation!!.instructions.size - 5

View File

@@ -63,7 +63,7 @@ val `Hide info cards` by creatingBytecodePatch(
)
// Edit: This old non litho code may be obsolete and no longer used by any supported versions.
infocardsIncognitoMethod.match(infocardsIncognitoParentMethod.originalClassDef).method.apply {
infocardsIncognitoMethod.match(infocardsIncognitoParentMethod.immutableClassDef).method.apply {
val invokeInstructionIndex = implementation!!.instructions.indexOfFirst {
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")

View File

@@ -43,7 +43,7 @@ val `Hide related video overlay` by creatingBytecodePatch(
)
relatedEndScreenResultsMethod.match(
relatedEndScreenResultsParentMethod.originalClassDef,
relatedEndScreenResultsParentMethod.immutableClassDef,
).method.apply {
addInstructionsWithLabels(
0,

View File

@@ -207,7 +207,7 @@ val `Hide Shorts componentsby creatingBytecodePatch(
// Hook to get the pivotBar view.
setPivotBarVisibilityMethod.match(
setPivotBarVisibilityParentMethod.originalClassDef,
setPivotBarVisibilityParentMethod.immutableClassDef,
).let { result ->
result.method.apply {
val insertIndex = result.indices.last()
@@ -230,7 +230,7 @@ val `Hide Shorts componentsby creatingBytecodePatch(
} else {
legacyRenderBottomNavigationBarLegacyParentMethod
}
).originalClassDef,
).immutableClassDef,
).method.addInstruction(
0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V",

View File

@@ -236,7 +236,7 @@ val Miniplayer by creatingBytecodePatch(
if (!is_20_37_or_greater) {
miniplayerOverrideNoContextMethod.match(
miniplayerDimensionsCalculatorParentMethod.originalClassDef,
miniplayerDimensionsCalculatorParentMethod.immutableClassDef,
).method.apply {
findReturnIndicesReversed().forEach { index ->
insertLegacyTabletMiniplayerOverride(
@@ -250,7 +250,7 @@ val Miniplayer by creatingBytecodePatch(
// region Legacy tablet miniplayer hooks.
miniplayerOverrideMethod.let {
val appNameStringIndex = it.indices.last()
navigate(it.originalMethod).to(appNameStringIndex).stop().apply {
navigate(it.immutableMethod).to(appNameStringIndex).stop().apply {
findReturnIndicesReversed().forEach { index ->
insertLegacyTabletMiniplayerOverride(
index,
@@ -368,7 +368,7 @@ val Miniplayer by creatingBytecodePatch(
// Fix this, by swapping the drawable resource values with each other.
if (!is_19_17_or_greater) {
miniplayerModernExpandCloseDrawablesMethod.match(
miniplayerModernViewParentMethod.originalClassDef,
miniplayerModernViewParentMethod.immutableClassDef,
).method.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,

View File

@@ -121,16 +121,16 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
// This hook handles all situations, as it's where the created Spans are stored and later reused.
// Find the field name of the conversion context.
val conversionContextClass = conversionContextToStringMethod.originalClassDef
val textComponentConversionContextField = textComponentConstructorMethod.originalClassDef.fields.find {
val conversionContextClass = conversionContextToStringMethod.immutableClassDef
val textComponentConversionContextField = textComponentConstructorMethod.immutableClassDef.fields.find {
it.type == conversionContextClass.type ||
// 20.41+ uses superclass field type.
it.type == conversionContextClass.superclass
} ?: throw PatchException("Could not find conversion context field")
textComponentLookupMethod.match(textComponentConstructorMethod.originalClassDef).method.apply {
textComponentLookupMethod.match(textComponentConstructorMethod.immutableClassDef).method.apply {
// Find the instruction for creating the text data object.
val textDataClassType = textComponentDataMethod.originalClassDef.type
val textDataClassType = textComponentDataMethod.immutableClassDef.type
val insertIndex: Int
val charSequenceRegister: Int
@@ -267,7 +267,7 @@ val `Return YouTube Dislike` by creatingBytecodePatch(
// Additional text measurement method. Used if YouTube decides not to animate the likes count
// and sometimes used for initial video load.
rollingNumberMeasureStaticLabelMethod.match(
rollingNumberMeasureStaticLabelParentMethod.originalClassDef,
rollingNumberMeasureStaticLabelParentMethod.immutableClassDef,
).let {
val measureTextIndex = it.instructionMatches.first().index + 1
it.method.apply {

View File

@@ -64,7 +64,7 @@ val `Wide search bar` by creatingBytecodePatch(
setWordmarkHeaderMethod.let {
// Navigate to the method that checks if the YT logo is shown beside the search bar.
val shouldShowLogoMethod = with(it.originalMethod) {
val shouldShowLogoMethod = with(it.immutableMethod) {
val invokeStaticIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.returnType == "Z"

View File

@@ -58,7 +58,7 @@ val seekbarColorPatch = bytecodePatch(
it.method.addColorChangeInstructions(it.instructionMatches.first().index)
}
setSeekbarClickedColorMethod.originalMethod.let {
setSeekbarClickedColorMethod.immutableMethod.let {
val setColorMethodIndex = setSeekbarClickedColorMethod.instructionMatches.first().index + 1
navigate(it).to(setColorMethodIndex).stop().apply {
@@ -154,7 +154,7 @@ val seekbarColorPatch = bytecodePatch(
// Hook the splash animation to set the a seekbar color.
mainActivityOnCreateMethod.apply {
val setAnimationIntMethodName =
lottieAnimationViewSetAnimationIntMethod.originalMethod.name
lottieAnimationViewSetAnimationIntMethod.immutableMethod.name
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
@@ -176,7 +176,7 @@ val seekbarColorPatch = bytecodePatch(
lottieAnimationViewSetAnimationIntMethod.classDef.methods.apply {
val addedMethodName = "patch_setAnimation"
val setAnimationIntName = lottieAnimationViewSetAnimationIntMethod
.originalMethod.name
.immutableMethod.name
add(
ImmutableMethod(
@@ -202,8 +202,8 @@ val seekbarColorPatch = bytecodePatch(
val factoryStreamName: CharSequence
val factoryStreamReturnType: CharSequence
lottieCompositionFactoryFromJsonInputStreamMethod.match(
lottieCompositionFactoryZipMethod.originalClassDef,
).originalMethod.apply {
lottieCompositionFactoryZipMethod.immutableClassDef,
).immutableMethod.apply {
factoryStreamClass = definingClass
factoryStreamName = name
factoryStreamReturnType = returnType
@@ -214,11 +214,11 @@ val seekbarColorPatch = bytecodePatch(
parameterTypes(factoryStreamReturnType.toString())
returnType("V")
custom { _, classDef ->
classDef.type == lottieAnimationViewSetAnimationIntMethod.originalClassDef.type
classDef.type == lottieAnimationViewSetAnimationIntMethod.immutableClassDef.type
}
}
val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint
.originalMethod.name
.immutableMethod.name
add(
ImmutableMethod(

View File

@@ -81,7 +81,7 @@ val `Remove background playback restrictions` by creatingBytecodePatch(
}
// Enable background playback option in YouTube settings
backgroundPlaybackSettingsMethod.originalMethod.apply {
backgroundPlaybackSettingsMethod.immutableMethod.apply {
val booleanCalls = instructions.withIndex().filter {
it.value.getReference<MethodReference>()?.returnType == "Z"
}

View File

@@ -31,13 +31,13 @@ val cronetImageUrlHookPatch = bytecodePatch(
apply {
loadImageUrlMethod = messageDigestImageUrlMethod
.match(messageDigestImageUrlParentMethod.originalClassDef).method
.match(messageDigestImageUrlParentMethod.immutableClassDef).method
loadImageSuccessCallbackMethod = onSucceededMethod
.match(onResponseStartedMethod.originalClassDef).method
.match(onResponseStartedMethod.immutableClassDef).method
loadImageErrorCallbackMethod = onFailureMethod
.match(onResponseStartedMethod.originalClassDef).method
.match(onResponseStartedMethod.immutableClassDef).method
// The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field.

View File

@@ -115,7 +115,7 @@ val lithoFilterPatch = bytecodePatch(
val conversionContextIdentifierField = conversionContextToStringMethod.method
.findFieldFromToString("identifierProperty=")
val conversionContextPathBuilderField = conversionContextToStringMethod.originalClassDef
val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
// Find class and methods to create an empty component.

View File

@@ -169,7 +169,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
interfaces.add(EXTENSION_TOOLBAR_INTERFACE)
val definingClass = type
val obfuscatedMethodName = it.originalMethod.name
val obfuscatedMethodName = it.immutableMethod.name
val returnType = "Landroid/graphics/drawable/Drawable;"
methods.add(

View File

@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -90,21 +91,20 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
// region Force old video quality menu.
// Replace the speeds float array with custom speeds.
speedArrayGeneratorMethod.let {
val matches = it.instructionMatches
speedArrayGeneratorMethodMatch.let {
it.method.apply {
val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F"
// Apply changes from last index to first to preserve indexes.
val originalArrayFetchIndex = matches[5].index
val originalArrayFetchDestination = matches[5].getInstruction<OneRegisterInstruction>().registerA
val originalArrayFetchIndex = it.indices[5]
val originalArrayFetchDestination = getInstruction<OneRegisterInstruction>(it.indices[5]).registerA
replaceInstruction(
originalArrayFetchIndex,
"sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType",
)
val arrayLengthConstDestination = matches[3].getInstruction<OneRegisterInstruction>().registerA
val newArrayIndex = matches[4].index
val arrayLengthConstDestination = getInstruction<OneRegisterInstruction>(it.indices[3]).registerA
val newArrayIndex = it.indices[4]
addInstructions(
newArrayIndex,
"""
@@ -113,7 +113,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
""",
)
val sizeCallIndex = matches[0].index + 1
val sizeCallIndex = it.indices[0] + 1
val sizeCallResultRegister = getInstruction<OneRegisterInstruction>(sizeCallIndex).registerA
replaceInstruction(sizeCallIndex, "const/4 v$sizeCallResultRegister, 0x0")
}
@@ -123,9 +123,9 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
// This is later used to call "showOldPlaybackSpeedMenu" on the instance.
val instanceField = ImmutableField(
getOldPlaybackSpeedsMethod.originalClassDef.type,
getOldPlaybackSpeedsMethod.immutableClassDef.type,
"INSTANCE",
getOldPlaybackSpeedsMethod.originalClassDef.type,
getOldPlaybackSpeedsMethod.immutableClassDef.type,
AccessFlags.PUBLIC.value or AccessFlags.STATIC.value,
null,
null,

View File

@@ -1,21 +1,15 @@
package app.revanced.patches.youtube.video.speed.custom
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import app.revanced.patches.shared.misc.mapping.ResourceType
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.getOldPlaybackSpeedsMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.getOldPlaybackSpeedsMethod by gettingFirstMutableMethodDeclaratively(
"menu_item_playback_speed",
) {
parameterTypes("[L", "I")
strings("menu_item_playback_speed")
}
internal val BytecodePatchContext.showOldPlaybackSpeedMenuMethod by gettingFirstMethodDeclaratively {
@@ -25,10 +19,10 @@ internal val BytecodePatchContext.showOldPlaybackSpeedMenuMethod by gettingFirst
}
internal val BytecodePatchContext.showOldPlaybackSpeedMenuExtensionMethod by gettingFirstMethodDeclaratively {
custom { method, _ -> method.name == "showOldPlaybackSpeedMenu" }
name("showOldPlaybackSpeedMenu")
}
internal val BytecodePatchContext.serverSideMaxSpeedFeatureFlagMethod by gettingFirstMethodDeclaratively {
internal val BytecodePatchContext.serverSideMaxSpeedFeatureFlagMethod by gettingFirstMutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Z")
instructions(
@@ -36,17 +30,17 @@ internal val BytecodePatchContext.serverSideMaxSpeedFeatureFlagMethod by getting
)
}
internal val BytecodePatchContext.speedArrayGeneratorMethod by gettingFirstMethodDeclaratively {
internal val speedArrayGeneratorMethodMatch = firstMethodComposite {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("[L")
parameterTypes("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;")
instructions(
methodCall(name = "size", returnType = "I"),
newInstance("Ljava/text/DecimalFormat;"),
method { name == "size" && returnType == "I" },
allOf(Opcode.NEW_INSTANCE(), type("Ljava/text/DecimalFormat;")),
"0.0#"(),
7L(),
Opcode.NEW_ARRAY(),
fieldAccess(definingClass = "/PlayerConfigModel;", type = "[F"),
field { definingClass == "/PlayerConfigModel;" && type == "[F" },
)
}
@@ -58,8 +52,8 @@ internal val BytecodePatchContext.speedLimiterMethod by gettingFirstMutableMetho
returnType("V")
parameterTypes("F", "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;")
instructions(
0.25f(),
4.0f(),
0.25f.toRawBits().toLong()(),
4.0f.toRawBits().toLong()(),
)
}

View File

@@ -92,7 +92,7 @@ val videoIdPatch = bytecodePatch(
)
apply {
videoIdMethod.match(videoIdParentFingerprint.originalClassDef).let {
videoIdMethod.match(videoIdParentFingerprint.immutableClassDef).let {
it.method.apply {
videoIdMethod = this
val index = it.instructionMatches.first().index