diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/InjectionUtils.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/InjectionUtils.kt new file mode 100644 index 000000000..452a74d1b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/InjectionUtils.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.youtube.layout.pivotbar + +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import org.jf.dexlib2.Opcode.MOVE_RESULT_OBJECT +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction + +internal object InjectionUtils { + const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX" + + /** + * Injects an instruction into insertIndex of the hook. + * @param hook The hook to insert. + * @param insertIndex The index to insert the instruction at. + * [MOVE_RESULT_OBJECT] has to be the previous instruction before [insertIndex]. + */ + fun MutableMethod.injectHook(hook: String, insertIndex: Int) { + val injectTarget = this + + // Register to pass to the hook + val registerIndex = insertIndex - 1 // MOVE_RESULT_OBJECT is always the previous instruction + val register = (injectTarget.instruction(registerIndex) as OneRegisterInstruction).registerA + + injectTarget.addInstruction( + insertIndex, + hook.replace("REGISTER_INDEX", register.toString()), + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/createbutton/fingerprints/CreateButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt similarity index 79% rename from src/main/kotlin/app/revanced/patches/youtube/layout/createbutton/fingerprints/CreateButtonFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt index c160877be..9bfe7ceab 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/createbutton/fingerprints/CreateButtonFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt @@ -1,23 +1,23 @@ -package app.revanced.patches.youtube.layout.createbutton.fingerprints +package app.revanced.patches.youtube.layout.pivotbar.fingerprints import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod -import app.revanced.patches.youtube.layout.createbutton.annotations.CreateButtonCompatibility +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.Opcode -@Name("create-button-fingerprint") +@Name("pivot-bar-fingerprint") @MatchingMethod( "Lknw", "z" ) -@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value. -@CreateButtonCompatibility +@FuzzyPatternScanMethod(2) +// TODO: This fingerprint is used in multiple patches, so technically two compatibilities are needed +// @CreateButtonCompatibility @Version("0.0.1") -object CreateButtonFingerprint : MethodFingerprint( +object PivotBarFingerprint : MethodFingerprint( "V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/annotations/ShortsButtonCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt similarity index 86% rename from src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/annotations/ShortsButtonCompatibility.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt index 025759e2c..09c803a16 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/annotations/ShortsButtonCompatibility.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.layout.shorts.button.annotations +package app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations import app.revanced.patcher.annotation.Compatibility import app.revanced.patcher.annotation.Package diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarEnumFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarEnumFingerprint.kt new file mode 100644 index 000000000..d538acc58 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarEnumFingerprint.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations.ShortsButtonCompatibility +import org.jf.dexlib2.Opcode + +@Name("pivot-bar-enum-fingerprint") +@MatchingMethod( + "Lknw", "z" +) +@ShortsButtonCompatibility +@Version("0.0.1") +object PivotBarEnumFingerprint : MethodFingerprint( + opcodes = listOf( + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IGET, + Opcode.AND_INT_LIT8, // unique instruction anchor + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarShortsButtonViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarShortsButtonViewFingerprint.kt new file mode 100644 index 000000000..99570bb82 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/fingerprints/PivotBarShortsButtonViewFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations.ShortsButtonCompatibility +import org.jf.dexlib2.Opcode + +@Name("pivot-bar-shorts-button-view-fingerprint") +@MatchingMethod( + "Lknw", "z" +) +@ShortsButtonCompatibility +@Version("0.0.1") +object PivotBarShortsButtonViewFingerprint : MethodFingerprint( + opcodes = listOf( + Opcode.INVOKE_VIRTUAL_RANGE, // unique instruction anchor + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt new file mode 100644 index 000000000..caee9e8fb --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt @@ -0,0 +1,90 @@ +package app.revanced.patches.youtube.layout.pivotbar.shortsbutton.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.BytecodeData +import app.revanced.patcher.extensions.MethodFingerprintExtensions.name +import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultError +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patcher.patch.impl.BytecodePatch +import app.revanced.patches.youtube.layout.pivotbar.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT +import app.revanced.patches.youtube.layout.pivotbar.InjectionUtils.injectHook +import app.revanced.patches.youtube.layout.pivotbar.fingerprints.PivotBarFingerprint +import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations.ShortsButtonCompatibility +import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints.PivotBarEnumFingerprint +import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints.PivotBarShortsButtonViewFingerprint +import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch +import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch +import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource +import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference + +@Patch +@DependsOn([IntegrationsPatch::class, SettingsPatch::class]) +@Name("hide-shorts-button") +@Description("Hides the shorts button on the navigation bar.") +@ShortsButtonCompatibility +@Version("0.0.1") +class ShortsButtonRemoverPatch : BytecodePatch( + listOf(PivotBarFingerprint) +) { + override fun execute(data: BytecodeData): PatchResult { + SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( + SwitchPreference( + "revanced_shorts_button_enabled", + StringResource("revanced_shorts_button_enabled_title", "Show shorts button"), + false, + StringResource("revanced_shorts_button_summary_on", "Shorts button is shown"), + StringResource("revanced_shorts_button_summary_off", "Shorts button is hidden") + ) + ) + + /* + * Resolve fingerprints + */ + + val pivotBarResult = PivotBarFingerprint.result ?: return PatchResultError("PivotBarFingerprint failed") + val fingerprintResults = arrayOf(PivotBarEnumFingerprint, PivotBarShortsButtonViewFingerprint) + .onEach { + val resolutionSucceeded = it.resolve( + data, + pivotBarResult.method, + pivotBarResult.classDef + ) + + if (!resolutionSucceeded) return PatchResultError("${it.name} failed") + } + .map { it.result!!.patternScanResult!! } + + val enumScanResult = fingerprintResults[0] + val buttonViewResult = fingerprintResults[1] + + val enumHookInsertIndex = enumScanResult.startIndex + val buttonHookInsertIndex = buttonViewResult.endIndex + + /* + * Inject hooks + */ + + val integrationsClass = "Lapp/revanced/integrations/patches/HideShortsButtonPatch;" + + val enumHook = + "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, $integrationsClass->lastPivotTab:Ljava/lang/Enum;" + val buttonHook = + "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $integrationsClass->hideShortsButton(Landroid/view/View;)V" + + // Inject bottom to top to not mess up the indices + mapOf( + buttonHook to buttonHookInsertIndex, + enumHook to enumHookInsertIndex + ).forEach { (hook, insertIndex) -> + pivotBarResult.mutableMethod.injectHook(hook, insertIndex) + } + + return PatchResultSuccess() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonTabEnumFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonTabEnumFingerprint.kt deleted file mode 100644 index 1f72f5f82..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonTabEnumFingerprint.kt +++ /dev/null @@ -1,36 +0,0 @@ -package app.revanced.patches.youtube.layout.shorts.button.fingerprints - -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod -import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility -import org.jf.dexlib2.AccessFlags -import org.jf.dexlib2.Opcode - -@Name("pivotbar-buttons-tabenum-fingerprint") -@MatchingMethod( - "Lknw;", "z" -) -@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value. -@ShortsButtonCompatibility -@Version("0.0.1") -object PivotBarButtonTabEnumFingerprint : MethodFingerprint( - "V", - AccessFlags.PUBLIC or AccessFlags.FINAL, - listOf("Z"), - listOf( - Opcode.SGET_OBJECT, - Opcode.IGET, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IF_NEZ, - Opcode.SGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.IGET_OBJECT, - Opcode.CHECK_CAST, - ) -) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonsViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonsViewFingerprint.kt deleted file mode 100644 index 2b5cd3a41..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/fingerprints/PivotBarButtonsViewFingerprint.kt +++ /dev/null @@ -1,33 +0,0 @@ -package app.revanced.patches.youtube.layout.shorts.button.fingerprints - -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility -import org.jf.dexlib2.AccessFlags -import org.jf.dexlib2.Opcode - -@Name("pivotbar-buttons-view-fingerprint") -@MatchingMethod( - "Lknw;", "z" -) -@ShortsButtonCompatibility -@Version("0.0.1") -object PivotBarButtonsViewFingerprint : MethodFingerprint( - "V", - AccessFlags.PUBLIC or AccessFlags.FINAL, - listOf("Z"), - listOf( - Opcode.SGET_OBJECT, - Opcode.IGET, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IF_NEZ, - Opcode.SGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT, - Opcode.IGET - ) -) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/patch/ShortsButtonRemoverPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/patch/ShortsButtonRemoverPatch.kt deleted file mode 100644 index a5cd69edc..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/shorts/button/patch/ShortsButtonRemoverPatch.kt +++ /dev/null @@ -1,89 +0,0 @@ -package app.revanced.patches.youtube.layout.shorts.button.patch - -import app.revanced.patcher.annotation.Description -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version -import app.revanced.patcher.data.impl.BytecodeData -import app.revanced.patcher.extensions.addInstruction -import app.revanced.patcher.patch.PatchResult -import app.revanced.patcher.patch.PatchResultSuccess -import app.revanced.patcher.patch.annotations.DependsOn -import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patcher.patch.impl.BytecodePatch -import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility -import app.revanced.patches.youtube.layout.shorts.button.fingerprints.PivotBarButtonTabEnumFingerprint -import app.revanced.patches.youtube.layout.shorts.button.fingerprints.PivotBarButtonsViewFingerprint -import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch -import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource -import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference -import org.jf.dexlib2.iface.instruction.OneRegisterInstruction -import org.jf.dexlib2.Opcode - -@Patch -@DependsOn([IntegrationsPatch::class, SettingsPatch::class]) -@Name("hide-shorts-button") -@Description("Hides the shorts button on the navigation bar.") -@ShortsButtonCompatibility -@Version("0.0.1") -class ShortsButtonRemoverPatch : BytecodePatch( - listOf( - PivotBarButtonTabEnumFingerprint, PivotBarButtonsViewFingerprint - ) -) { - override fun execute(data: BytecodeData): PatchResult { - SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( - SwitchPreference( - "revanced_shorts_button_enabled", - StringResource("revanced_shorts_button_enabled_title", "Show shorts button"), - false, - StringResource("revanced_shorts_button_summary_on", "Shorts button is shown"), - StringResource("revanced_shorts_button_summary_off", "Shorts button is hidden") - ) - ) - - val tabEnumResult = PivotBarButtonTabEnumFingerprint.result!! - val tabEnumImplementation = tabEnumResult.mutableMethod.implementation!! - val scanResultEndIndex = tabEnumResult.patternScanResult!!.endIndex - val tabEnumIndex = scanResultEndIndex + - if (tabEnumImplementation.instructions[scanResultEndIndex + 1].opcode == Opcode.IGET_OBJECT) { - // for 17.31.xx and lower - 7 - } else { - // since 17.32.xx - 10 - } - val moveEnumInstruction = tabEnumImplementation.instructions[tabEnumIndex] - val enumRegister = (moveEnumInstruction as OneRegisterInstruction).registerA - - val buttonsViewResult = PivotBarButtonsViewFingerprint.result!! - val buttonsViewImplementation = buttonsViewResult.mutableMethod.implementation!! - val scanResultStartIndex = buttonsViewResult.patternScanResult!!.startIndex - val buttonsViewIndex = scanResultStartIndex + - if (buttonsViewImplementation.instructions[scanResultStartIndex - 1].opcode == Opcode.GOTO) { - // since 17.32.xx - -7 - } else { - // for 17.31.xx and lower - -3 - } - val moveViewInstruction = buttonsViewImplementation.instructions[buttonsViewIndex - 1] - val viewRegister = (moveViewInstruction as OneRegisterInstruction).registerA - - - // Save the tab enum in XGlobals to avoid smali/register workarounds - tabEnumResult.mutableMethod.addInstruction( - tabEnumIndex, - "sput-object v$enumRegister, Lapp/revanced/integrations/patches/HideShortsButtonPatch;->lastPivotTab:Ljava/lang/Enum;" - ) - - // Hide the button view via proxy by passing it to the hideShortsButton method - // It only hides it if the last tab name is "TAB_SHORTS" - buttonsViewResult.mutableMethod.addInstruction( - buttonsViewIndex + 1, - "invoke-static { v$viewRegister }, Lapp/revanced/integrations/patches/HideShortsButtonPatch;->hideShortsButton(Landroid/view/View;)V" - ) - - return PatchResultSuccess() - } -} \ No newline at end of file