From 45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sat, 22 Feb 2025 11:40:06 +0200 Subject: [PATCH] fix(YouTube - Spoof app version): Force old settings menus if spoofing to older app targets (#4490) --- .../youtube/settings/LicenseActivityHook.java | 5 ++ .../extension/youtube/settings/Settings.java | 6 --- .../layout/spoofappversion/Fingerprints.kt | 21 ++++++++ .../spoofappversion/SpoofAppVersionPatch.kt | 53 ++++++++++++++++++- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java index 528703310..40bfe2016 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java @@ -19,6 +19,7 @@ import app.revanced.extension.shared.settings.AppLanguage; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.patches.VersionCheckPatch; +import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch; import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment; import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment; @@ -63,6 +64,10 @@ public class LicenseActivityHook { if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) { return false; } + // Spoofing can cause half broken settings menus of old and new settings. + if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) { + return false; + } // On the first launch of a clean install, forcing the cairo menu can give a // half broken appearance because all the preference icons may not be available yet. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 4bc75a413..5ba20c758 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -405,12 +405,6 @@ public class Settings extends BaseSettings { migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER); - // Old spoof versions that no longer work reliably. - if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) { - Logger.printInfo(() -> "Resetting spoof app version target"); - SPOOF_APP_VERSION_TARGET.resetToDefault(); - } - // Migrate renamed enum. //noinspection deprecation if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt index 969d90e02..ac458cec2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt @@ -1,8 +1,29 @@ package app.revanced.patches.youtube.layout.spoofappversion import app.revanced.patcher.fingerprint +import app.revanced.util.containsLiteralInstruction +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val toolBarButtonFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Landroid/view/MenuItem;") + custom { method, _ -> + method.containsLiteralInstruction(menuItemView) && + indexOfGetDrawableInstruction(method) >= 0 + } +} + +internal fun indexOfGetDrawableInstruction(method: Method) = method.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Landroid/content/res/Resources;" && + reference.name == "getDrawable" +} internal val spoofAppVersionFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt index b233693de..48caf9c1b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt @@ -1,10 +1,16 @@ package app.revanced.patches.youtube.layout.spoofappversion import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch @@ -12,7 +18,25 @@ import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal var menuItemView = -1L + private set + +internal val spoofAppVersionResourcePatch = resourcePatch { + dependsOn( + resourceMappingPatch + ) + + execute { + menuItemView = resourceMappings["id", "menu_item_view"] + } +} private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch;" @@ -24,6 +48,7 @@ val spoofAppVersionPatch = bytecodePatch( "Patching 19.16.39 includes additional older spoofing targets.", ) { dependsOn( + spoofAppVersionResourcePatch, sharedExtensionPatch, settingsPatch, addResourcesPatch, @@ -62,6 +87,32 @@ val spoofAppVersionPatch = bytecodePatch( } ) + /** + * If a user really wants to spoof to very old versions with the latest app target + * they can modify the import/export spoof version. But when spoofing the 19.20.xx + * or earlier the Library tab can crash due to missing image resources trying to load. + * As a temporary workaround, do not set an image in the toolbar when the enum name is UNKNOWN. + */ + toolBarButtonFingerprint.method.apply { + val getDrawableIndex = indexOfGetDrawableInstruction(this) + val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) { + opcode == Opcode.INVOKE_INTERFACE && + getReference()?.returnType == "I" + } + val insertIndex = enumOrdinalIndex + 2 + val insertRegister = getInstruction(insertIndex - 1).registerA + val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.name == "setImageDrawable" + } + 1 + + addInstructionsWithLabels( + insertIndex, + "if-eqz v$insertRegister, :ignore", + ExternalLabel("ignore", getInstruction(jumpIndex)) + ) + } + val insertIndex = spoofAppVersionFingerprint.patternMatch!!.startIndex + 1 val buildOverrideNameRegister = spoofAppVersionFingerprint.method.getInstruction(insertIndex - 1).registerA @@ -71,7 +122,7 @@ val spoofAppVersionPatch = bytecodePatch( """ invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; move-result-object v$buildOverrideNameRegister - """, + """ ) } }