fix: Adjusting to new API

This commit is contained in:
oSumAtrIX
2025-11-26 23:12:48 +01:00
parent 95c72ad300
commit 8c603802f7
32 changed files with 175 additions and 228 deletions

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.all.misc.transformation
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.dex.mutable.MutableMethod
import app.revanced.util.findMutableMethodOf
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@@ -11,39 +11,9 @@ fun <T> transformInstructionsPatch(
filterMap: (ClassDef, Method, Instruction, Int) -> T?,
transform: (MutableMethod, T) -> Unit,
) = bytecodePatch {
// Returns the patch indices as a Sequence, which will execute lazily.
fun findPatchIndices(classDef: ClassDef, method: Method): Sequence<T>? =
method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
filterMap(classDef, method, instruction, index)
}
execute {
// Find all methods to patch
buildMap {
classDefs.forEach { classDef ->
val methods = buildList {
classDef.methods.forEach { method ->
// Since the Sequence executes lazily,
// using any() results in only calling
// filterMap until the first index has been found.
if (findPatchIndices(classDef, method)?.any() == true) add(method)
}
}
if (methods.isNotEmpty()) {
put(classDef, methods)
}
}
}.forEach { (classDef, methods) ->
// And finally transform the methods...
val mutableClass = classDef.mutable()
methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod ->
val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque())
?: return@methods
while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast())
}
forEachInstructionAsSequence { classDef, method, i, instruction ->
transform(method, filterMap(classDef, method, instruction, i) ?: return@forEachInstructionAsSequence)
}
}
}

View File

@@ -21,7 +21,7 @@ val hideAdsPatch = bytecodePatch(
execute {
// Get obfuscated "enableAds" field from toString method.
val enableAdsField = videoUrlReadyToStringFingerprint.let {
val strIndex = videoUrlReadyToStringFingerprint.stringMatches!!.last().index
val strIndex = videoUrlReadyToStringFingerprint.stringMatches.last().index
val fieldIndex = it.method.indexOfFirstInstruction(strIndex, Opcode.IGET_BOOLEAN)
it.method.getInstruction<ReferenceInstruction>(fieldIndex).getReference<FieldReference>()!!
}

View File

@@ -21,7 +21,7 @@ val enableDebugMenuPatch = bytecodePatch(
buildConfigProviderConstructorFingerprint.match(
buildConfigProviderToStringFingerprint.classDef
).let {
val index = it.patternMatch!!.startIndex
val index = it.patternMatch.startIndex
it.method.apply {
val register = getInstruction<OneRegisterInstruction>(index).registerA

View File

@@ -17,7 +17,7 @@ val skipEnergyRechargeAdsPatch = bytecodePatch(
.method.apply {
val energyField = energyConfigToStringFingerprint.method
.findFieldFromToString("energy=")
val insertIndex = initializeEnergyConfigFingerprint.patternMatch!!.startIndex
val insertIndex = initializeEnergyConfigFingerprint.patternMatch.startIndex
addInstructions(
insertIndex,

View File

@@ -11,7 +11,7 @@ context(BytecodePatchContext)
internal fun Fingerprint.replaceJsonFieldWithBogus(
key: String,
) {
val targetStringIndex = stringMatches!!.first { match -> match.string == key }.index
val targetStringIndex = stringMatches.first { match -> match.string == key }.index
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
/**

View File

@@ -12,7 +12,7 @@ val hideStoriesPatch = bytecodePatch(
execute {
val addStoryMethod = getOrCreateAvatarViewFingerprint.method // Creates Story
val addStoryEndIndex = getOrCreateAvatarViewFingerprint.patternMatch!!.endIndex
val addStoryEndIndex = getOrCreateAvatarViewFingerprint.patternMatch.endIndex
// Remove addView of Story.
addStoryMethod.removeInstruction(addStoryEndIndex)

View File

@@ -22,7 +22,7 @@ val enableDeveloperMenuPatch = bytecodePatch(
execute {
with(clearNotificationReceiverFingerprint.method) {
indexOfFirstInstructionReversedOrThrow(clearNotificationReceiverFingerprint.stringMatches!!.first().index) {
indexOfFirstInstructionReversedOrThrow(clearNotificationReceiverFingerprint.stringMatches.first().index) {
val reference = getReference<MethodReference>()
opcode in listOf(Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC_RANGE) &&
reference?.parameterTypes?.size == 1 &&

View File

@@ -19,7 +19,7 @@ internal fun editShareLinksPatch(block: MutableMethod.(index: Int, register: Int
for (fingerprint in fingerprintsToPatch) {
fingerprint.method.apply {
val putSharingUrlIndex = indexOfFirstInstruction(
permalinkResponseJsonParserFingerprint.stringMatches!!.first().index,
permalinkResponseJsonParserFingerprint.stringMatches.first().index,
Opcode.IPUT_OBJECT
)

View File

@@ -22,7 +22,7 @@ val removeMetaAIPatch = bytecodePatch(
execute {
getMobileConfigBoolFingerprint.method.apply {
val returnIndex = getMobileConfigBoolFingerprint.patternMatch!!.startIndex
val returnIndex = getMobileConfigBoolFingerprint.patternMatch.startIndex
val returnRegister = getInstruction<OneRegisterInstruction>(returnIndex).registerA
addInstructions(
@@ -42,7 +42,7 @@ val removeMetaAIPatch = bytecodePatch(
// Replace placeholder in the extension method.
with(extensionMethodFingerprint) {
method.replaceInstruction(
stringMatches!!.first().index,
stringMatches.first().index,
"""
const-string v1, "$relevantDigits"
"""

View File

@@ -73,7 +73,7 @@ val hideButtons = bytecodePatch(
historyMenuItemOfflineTabFingerprint
).forEach { fingerprint ->
fingerprint.method.apply {
val targetIndex = fingerprint.patternMatch!!.startIndex
val targetIndex = fingerprint.patternMatch.startIndex
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerD
addInstructions(

View File

@@ -46,7 +46,7 @@ val hideCategoryBar = bytecodePatch(
chipCloud = getResourceId(ResourceType.LAYOUT, "chip_cloud")
chipCloudFingerprint.method.apply {
val targetIndex = chipCloudFingerprint.patternMatch!!.endIndex
val targetIndex = chipCloudFingerprint.patternMatch.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(

View File

@@ -100,7 +100,7 @@ val navigationBarPatch = bytecodePatch(
)
// Set navigation enum and hide navigation buttons.
val enumIndex = tabLayoutTextFingerprint.patternMatch!!.startIndex + 3
val enumIndex = tabLayoutTextFingerprint.patternMatch.startIndex + 3
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2

View File

@@ -27,7 +27,7 @@ val hideAdsPatch = bytecodePatch(
// Filter injected content from API calls out of lists.
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
// Index of instruction moving result of BlockPage;->getBlocks(...).
val moveGetBlocksResultObjectIndex = it.patternMatch!!.startIndex
val moveGetBlocksResultObjectIndex = it.patternMatch.startIndex
it.method.apply {
val moveInstruction = getInstruction<OneRegisterInstruction>(moveGetBlocksResultObjectIndex)

View File

@@ -22,7 +22,7 @@ val skipAdsPatch = bytecodePatch(
// Force doTrigger() access to public so we can call it from our extension.
doTriggerFingerprint.method.accessFlags = AccessFlags.PUBLIC.value;
val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch!!.startIndex
val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch.startIndex
enterServerInsertedAdBreakStateFingerprint.method.apply {
// Get register that stores VideoPlayer:
// invoke-virtual ->getPrimaryPlayer()

View File

@@ -23,7 +23,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/au
execute {
fun Fingerprint.patch(replacementString: String) {
val clientIdIndex = stringMatches!!.first().index
val clientIdIndex = stringMatches.first().index
method.apply {
val clientIdRegister = getInstruction<OneRegisterInstruction>(clientIdIndex).registerA

View File

@@ -24,7 +24,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com")
val randomName = (0..100000).random()
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
buildUserAgentFingerprint.let {
val userAgentTemplateIndex = it.stringMatches!!.first().index
val userAgentTemplateIndex = it.stringMatches.first().index
val register = it.method.getInstruction<OneRegisterInstruction>(userAgentTemplateIndex).registerA
it.method.replaceInstruction(userAgentTemplateIndex, "const-string v$register, \"$userAgent\"")

View File

@@ -34,7 +34,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl
string: String,
getReplacementIndex: List<Match.StringMatch>.() -> Int,
) = method.apply {
val replacementIndex = stringMatches!!.getReplacementIndex()
val replacementIndex = stringMatches.getReplacementIndex()
val clientIdRegister = getInstruction<OneRegisterInstruction>(replacementIndex).registerA
replaceInstruction(replacementIndex, "const-string v$clientIdRegister, \"$string\"")

View File

@@ -11,7 +11,7 @@ val lithoColorHookPatch = bytecodePatch(
) {
execute {
var insertionIndex = lithoOnBoundsChangeFingerprint.patternMatch!!.endIndex - 1
var insertionIndex = lithoOnBoundsChangeFingerprint.patternMatch.endIndex - 1
lithoColorOverrideHook = { targetMethodClass, targetMethodName ->
lithoOnBoundsChangeFingerprint.method.addInstructions(

View File

@@ -79,33 +79,41 @@ fun gmsCoreSupportPatch(
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
execute {
fun transformStringReferences(transform: (str: String) -> String?) = classDefs.forEach {
val mutableClass by lazy { it.mutable() }
fun transformStringReferences(transform: (str: String) -> String?) {
val transformations = mutableListOf<() -> Unit>()
it.methods.forEach classLoop@{ method ->
val implementation = method.implementation ?: return@classLoop
classDefs.forEach { classDef ->
val mutableClass by lazy { classDef.mutable() }
val mutableMethod by lazy {
mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, method) }
}
classDef.methods.forEach classLoop@{ method ->
val implementation = method.implementation ?: return@classLoop
implementation.instructions.forEachIndexed { index, instruction ->
val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string
?: return@forEachIndexed
val mutableMethod by lazy {
mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, method) }
}
// Apply transformation.
val transformedString = transform(string) ?: return@forEachIndexed
implementation.instructions.forEachIndexed { index, instruction ->
val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string
?: return@forEachIndexed
mutableMethod.replaceInstruction(
index,
BuilderInstruction21c(
Opcode.CONST_STRING,
instruction.registerA,
ImmutableStringReference(transformedString),
),
)
// Apply transformation.
val transformedString = transform(string) ?: return@forEachIndexed
transformations += {
mutableMethod.replaceInstruction(
index,
BuilderInstruction21c(
Opcode.CONST_STRING,
instruction.registerA,
ImmutableStringReference(transformedString),
),
)
}
}
}
}
transformations.forEach { it() }
}
// region Collection of transformations that are applied to all strings.

View File

@@ -2,11 +2,13 @@ package app.revanced.patches.shared.misc.mapping
import app.revanced.patcher.InstructionLocation
import app.revanced.patcher.LiteralFilter
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.literal
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.resourcePatch
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import org.w3c.dom.Element
import java.util.Collections
import java.util.*
enum class ResourceType(val value: String) {
ANIM("anim"),
@@ -35,6 +37,9 @@ enum class ResourceType(val value: String) {
VALUES("values"),
XML("xml");
operator fun invoke(name: String): Instruction.() -> Boolean =
getResourceId(this, name).let { { wideLiteral(it) } }
companion object {
private val VALUE_MAP: Map<String, ResourceType> = entries.associateBy { it.value }
@@ -77,10 +82,9 @@ fun hasResourceId(type: ResourceType, name: String) = resourceMappings[type.valu
fun resourceLiteral(
type: ResourceType,
name: String,
location : InstructionLocation = InstructionLocation.MatchAfterAnywhere()
location: InstructionLocation = InstructionLocation.MatchAfterAnywhere()
) = literal({ getResourceId(type, name) }, null, location)
val resourceMappingPatch = resourcePatch {
execute {
// Use a stream of the file, since no modifications are done

View File

@@ -283,7 +283,7 @@ internal fun spoofVideoStreamsPatch(
// If SABR is disabled, it seems 'MediaFetchHotConfig' may no longer need an override (not confirmed).
val (mediaFetchEnumClass, sabrFieldReference) = with(mediaFetchEnumConstructorFingerprint.method) {
val stringIndex = mediaFetchEnumConstructorFingerprint.stringMatches!!.first {
val stringIndex = mediaFetchEnumConstructorFingerprint.stringMatches.first {
it.string == DISABLED_BY_SABR_STREAMING_URI_STRING
}.index

View File

@@ -20,7 +20,7 @@ val hideAdsPatch = bytecodePatch(
val method = findAdStringFingerprint.method
// Find the ads free string index
val stringIndex = findAdStringFingerprint.stringMatches!!.first().index
val stringIndex = findAdStringFingerprint.stringMatches.first().index
// Search backwards from the string to find the `new-instance` (TypeReference) instruction
val typeRefIndex = method.indexOfFirstInstructionReversedOrThrow(stringIndex) { this.opcode == Opcode.NEW_INSTANCE }

View File

@@ -1,7 +1,9 @@
package app.revanced.patches.youtube.ad.general
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
@@ -17,12 +19,10 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findMutableMethodOf
import app.revanced.util.forEachInstructionAsSequence
import app.revanced.util.injectHideViewCall
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
internal var adAttributionId = -1L
private set
@@ -84,7 +84,7 @@ val hideAdsPatch = bytecodePatch(
)
execute {
// Hide end screen store banner
// Hide end screen store banner.
fullScreenEngagementAdContainerFingerprint.method.apply {
val addListIndex = indexOfAddListInstruction(this)
@@ -99,42 +99,19 @@ val hideAdsPatch = bytecodePatch(
)
}
// Hide ad views
// Hide ad views.
classDefs.forEach { classDef ->
classDef.methods.forEach { method ->
with(method.implementation) {
this?.instructions?.forEachIndexed { index, instruction ->
if (instruction.opcode != Opcode.CONST) {
return@forEachIndexed
}
// Instruction to store the id adAttribution into a register
if ((instruction as Instruction31i).wideLiteral != adAttributionId) {
return@forEachIndexed
}
forEachInstructionAsSequence { _, method, index, instruction ->
if (instruction.opcode != Opcode.CONST) return@forEachInstructionAsSequence
if (instruction.wideLiteral != adAttributionId) return@forEachInstructionAsSequence
val insertIndex = index + 1
// Call to get the view with the id adAttribution
with(instructions.elementAt(insertIndex)) {
if (opcode != Opcode.INVOKE_VIRTUAL) {
return@forEachIndexed
}
val insertIndex = index + 1
// Hide the view
val viewRegister = (this as Instruction35c).registerC
classDef.mutable()
.findMutableMethodOf(method)
.injectHideViewCall(
insertIndex,
viewRegister,
EXTENSION_CLASS_DESCRIPTOR,
"hideAdAttributionView",
)
}
}
}
}
// Call to get the view with the id adAttribution,
if (method.instructions[insertIndex].opcode != Opcode.INVOKE_VIRTUAL) return@forEachInstructionAsSequence
val viewRegister = method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView")
}
}
}

View File

@@ -2,6 +2,7 @@ package app.revanced.patches.youtube.layout.branding.header
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
@@ -19,7 +20,7 @@ import app.revanced.util.ResourceGroup
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.copyResources
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import java.io.File
@@ -75,12 +76,14 @@ private val changeHeaderBytecodePatch = bytecodePatch {
"ytWordmarkHeader",
"ytPremiumWordmarkHeader"
).forEach { resourceName ->
val resourceId = getResourceId(ResourceType.ATTR, resourceName)
val id = getResourceId(ResourceType.ATTR, resourceName)
forEachLiteralValueInstruction(resourceId) { literalIndex ->
val register = getInstruction<OneRegisterInstruction>(literalIndex).registerA
addInstructions(
literalIndex + 1,
forEachInstructionAsSequence { _, method, i, instruction ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence
val register = method.getInstruction<OneRegisterInstruction>(i).registerA
method.addInstructions(
i + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getHeaderAttributeId(I)I
move-result v$register
@@ -214,21 +217,24 @@ val changeHeaderPatch = resourcePatch(
if (custom != null) {
val customFile = File(custom!!.trim())
if (!customFile.exists()) {
throw PatchException("The custom header path cannot be found: " +
customFile.absolutePath
throw PatchException(
"The custom header path cannot be found: " +
customFile.absolutePath
)
}
if (!customFile.isDirectory) {
throw PatchException("The custom header path must be a folder: "
+ customFile.absolutePath)
throw PatchException(
"The custom header path must be a folder: "
+ customFile.absolutePath
)
}
var copiedFiles = false
// For each source folder, copy the files to the target resource directories.
customFile.listFiles {
file -> file.isDirectory && file.name in targetResourceDirectoryNames
customFile.listFiles { file ->
file.isDirectory && file.name in targetResourceDirectoryNames
}!!.forEach { dpiSourceFolder ->
val targetDpiFolder = get("res").resolve(dpiSourceFolder.name)
if (!targetDpiFolder.exists()) {
@@ -241,8 +247,9 @@ val changeHeaderPatch = resourcePatch(
}!!
if (customFiles.isNotEmpty() && customFiles.size != variants.size) {
throw PatchException("Both light/dark mode images " +
"must be specified but only found: " + customFiles.map { it.name })
throw PatchException(
"Both light/dark mode images " +
"must be specified but only found: " + customFiles.map { it.name })
}
customFiles.forEach { imgSourceFile ->
@@ -254,9 +261,11 @@ val changeHeaderPatch = resourcePatch(
}
if (!copiedFiles) {
throw PatchException("Expected to find directories and files: "
+ customHeaderResourceFileNames.contentToString()
+ "\nBut none were found in the provided option file path: " + customFile.absolutePath)
throw PatchException(
"Expected to find directories and files: "
+ customHeaderResourceFileNames.contentToString()
+ "\nBut none were found in the provided option file path: " + customFile.absolutePath
)
}
}
}

View File

@@ -2,26 +2,15 @@ package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.Match
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.extensions.*
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.getResourceId
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.shared.misc.settings.preference.*
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
@@ -32,13 +21,13 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
internal var albumCardId = -1L
private set
@@ -390,7 +379,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
// region hide view count
hideViewCountFingerprint.method.apply {
val startIndex = hideViewCountFingerprint.patternMatch!!.startIndex
val startIndex = hideViewCountFingerprint.patternMatch.startIndex
var returnStringRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
// Find the instruction where the text dimension is retrieved.

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.hide.shorts
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.wideLiteral
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
@@ -17,19 +18,10 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_45_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.playservice.*
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.removeFromParent
import app.revanced.util.returnLate
import app.revanced.util.*
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import java.util.logging.Logger
@@ -194,16 +186,18 @@ val hideShortsComponentsPatch = bytecodePatch(
execute {
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
forEachLiteralValueInstruction(
getResourceId(ResourceType.DIMEN, "reel_player_right_pivot_v2_size")
) { literalInstructionIndex ->
val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) {
val id = getResourceId(ResourceType.DIMEN, "reel_player_right_pivot_v2_size")
forEachInstructionAsSequence { _, method, i, instruction ->
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence
val targetIndex = method.indexOfFirstInstructionOrThrow(i) {
getReference<MethodReference>()?.name == "getDimensionPixelSize"
} + 1
val sizeRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
method.addInstructions(
targetIndex + 1,
"""
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
@@ -212,6 +206,7 @@ val hideShortsComponentsPatch = bytecodePatch(
)
}
// endregion
// region Hide the navigation bar.
@@ -226,7 +221,7 @@ val hideShortsComponentsPatch = bytecodePatch(
addInstruction(
insertIndex,
"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",
)
}
}

View File

@@ -88,7 +88,8 @@ val backgroundPlaybackPatch = bytecodePatch(
}
val settingsBooleanIndex = booleanCalls.elementAt(1).index
val settingsBooleanMethod by navigate(this).to(settingsBooleanIndex)
val settingsBooleanMethod = navigate(this).to(settingsBooleanIndex).stop()
settingsBooleanMethod.returnEarly(true)
}

View File

@@ -25,7 +25,7 @@ internal val fixBackToExitGesturePatch = bytecodePatch(
scrollPositionFingerprint.let {
navigate(it.originalMethod)
.to(it.patternMatch!!.startIndex + 1)
.to(it.patternMatch.startIndex + 1)
.stop().apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.definingClass ==

View File

@@ -1,9 +1,9 @@
package app.revanced.patches.youtube.misc.fix.playbackspeed
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.extensions.ExternalLabel
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
@@ -29,7 +29,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
* 6. Resume the video
* 7. Playback speed will incorrectly change to 1.0x.
*/
val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{
val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
playerTypeHookPatch,

View File

@@ -1,11 +1,7 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.*
import app.revanced.patcher.InstructionLocation.MatchAfterWithin
import app.revanced.patcher.checkCast
import app.revanced.patcher.fingerprint
import app.revanced.patcher.methodCall
import app.revanced.patcher.opcode
import app.revanced.patcher.string
import app.revanced.patches.shared.misc.mapping.ResourceType
import app.revanced.patches.shared.misc.mapping.resourceLiteral
import app.revanced.patches.youtube.layout.buttons.navigation.navigationButtonsPatch
@@ -79,6 +75,7 @@ internal val navigationEnumFingerprint = fingerprint {
"TAB_ACTIVITY",
"VIDEO_LIBRARY_WHITE",
"INCOGNITO_CIRCLE",
"UNKNOWN" // Required to distinguish from patch extension class.
)
}

View File

@@ -1,5 +1,7 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.dex.mutable.MutableMethod
import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
@@ -7,16 +9,10 @@ import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.dex.mutable.MutableMethod
import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_21_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_28_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_39_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.playservice.*
import app.revanced.patches.youtube.shared.mainActivityOnBackPressedFingerprint
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
@@ -74,7 +70,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
addInstruction(
insertIndex,
"invoke-static { v$register }, " +
"$EXTENSION_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
"$EXTENSION_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
)
}
}
@@ -84,7 +80,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
val navigationEnumClassName = navigationEnumFingerprint.classDef.type
addHook(NavigationHook.SET_LAST_APP_NAVIGATION_ENUM) {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == navigationEnumClassName
getReference<MethodReference>()?.definingClass == navigationEnumClassName
}
// Hook the creation of navigation tab views.
@@ -206,7 +202,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
navigationBarHookCallbackFingerprint.method.addInstruction(
0,
"invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" +
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}
@@ -221,21 +217,19 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
val cairoNotificationEnumReference = imageEnumConstructorFingerprint
.instructionMatches.last().getInstruction<ReferenceInstruction>().reference
setEnumMapFingerprint.let {
it.method.apply {
val setEnumIntegerIndex = it.instructionMatches.last().index
val enumMapRegister = getInstruction<FiveRegisterInstruction>(setEnumIntegerIndex).registerC
val insertIndex = setEnumIntegerIndex + 1
val freeRegister = findFreeRegister(insertIndex, enumMapRegister)
setEnumMapFingerprint.method.apply {
val setEnumIntegerIndex = setEnumMapFingerprint.instructionMatches.last().index
val enumMapRegister = getInstruction<FiveRegisterInstruction>(setEnumIntegerIndex).registerC
val insertIndex = setEnumIntegerIndex + 1
val freeRegister = findFreeRegister(insertIndex, enumMapRegister)
addInstructions(
insertIndex,
"""
sget-object v$freeRegister, $cairoNotificationEnumReference
invoke-static { v$enumMapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
"""
)
}
addInstructions(
insertIndex,
"""
sget-object v$freeRegister, $cairoNotificationEnumReference
invoke-static { v$enumMapRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
"""
)
}
}
}

View File

@@ -86,8 +86,10 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
return bestFreeRegisterFound
}
// This method is simple and does not follow branching.
throw IllegalArgumentException("Encountered a branch statement before " +
"a free register could be found from startIndex: $startIndex")
throw IllegalArgumentException(
"Encountered a branch statement before " +
"a free register could be found from startIndex: $startIndex"
)
}
if (instruction.isReturnInstruction) {
@@ -104,8 +106,10 @@ fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: Int): In
// Somehow every method register was read from before any register was wrote to.
// In practice this never occurs.
throw IllegalArgumentException("Could not find a free register from startIndex: " +
"$startIndex excluding: $registersToExclude")
throw IllegalArgumentException(
"Could not find a free register from startIndex: " +
"$startIndex excluding: $registersToExclude"
)
}
}
@@ -170,7 +174,7 @@ internal val Instruction.isReturnInstruction: Boolean
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
val stringIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
reference?.string?.contains(fieldName) == true
@@ -210,7 +214,8 @@ private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
val fieldSetOpcode = getInstruction(fieldSetIndex).opcode
if (fieldSetOpcode == MOVE_RESULT ||
fieldSetOpcode == MOVE_RESULT_WIDE ||
fieldSetOpcode == MOVE_RESULT_OBJECT) {
fieldSetOpcode == MOVE_RESULT_OBJECT
) {
fieldSetIndex--
}
@@ -223,7 +228,7 @@ private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
context(BytecodePatchContext)
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
internal fun Method.findMethodFromToString(fieldName: String): MutableMethod {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return navigate(this).to(methodUsageIndex).stop()
}
@@ -233,7 +238,7 @@ internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
internal fun Method.findFieldFromToString(fieldName: String): FieldReference {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
}
@@ -792,36 +797,34 @@ internal fun MutableMethod.insertLiteralOverride(literalIndex: Int, override: Bo
}
/**
* Called for _all_ methods with the given literal value.
* Method indices are iterated from last to first.
* 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.
*/
fun BytecodePatchContext.forEachLiteralValueInstruction(
literal: Long,
block: MutableMethod.(matchingIndex: Int) -> Unit,
fun BytecodePatchContext.forEachInstructionAsSequence(
block: (classDef: MutableClassDef, method: MutableMethod, matchingIndex: Int, instruction: Instruction) -> Unit,
) {
val matchingIndexes = ArrayList<Int>()
classDefs.asSequence().flatMap { classDef ->
val mutableClassDef by lazy { classDef.mutable() }
classDefs.forEach { classDef ->
classDef.methods.forEach { method ->
method.implementation?.instructions?.let { instructions ->
matchingIndexes.clear()
classDef.methods.asSequence().flatMap { method ->
val instructions =
method.instructionsOrNull as? List<Instruction> ?: return@flatMap emptySequence<() -> Unit>()
instructions.forEachIndexed { index, instruction ->
if ((instruction as? WideLiteralInstruction)?.wideLiteral == literal) {
matchingIndexes.add(index)
}
}
val mutableMethod by lazy { mutableClassDef.findMutableMethodOf(method) }
if (matchingIndexes.isNotEmpty()) {
val mutableMethod = classDef.mutable().findMutableMethodOf(method)
matchingIndexes.asReversed().forEach { index ->
block.invoke(mutableMethod, index)
}
}
instructions.asReversed().asSequence().mapIndexed { index, instruction ->
{
block(
mutableClassDef,
mutableMethod,
instructions.size - 1 - index,
instruction
)
} // Reverse indices again.
}
}
}
}.forEach { it() }
}
private const val RETURN_TYPE_MISMATCH = "Mismatch between override type and Method return type"
@@ -1090,7 +1093,7 @@ fun MutableMethod.returnLate(value: Void?) {
}
private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) {
val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;" ) {
val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
"""
const-string v0, "$value"
return-object v0