diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/annotations/TabletMiniPlayerCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/annotations/TabletMiniPlayerCompatibility.kt new file mode 100644 index 000000000..a610e860b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/annotations/TabletMiniPlayerCompatibility.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.annotations + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package( + "com.google.android.youtube", arrayOf("17.26.35", "17.29.34") + )] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class TabletMiniPlayerCompatibility diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorFingerprint.kt new file mode 100644 index 000000000..8d1b8ae6c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorFingerprint.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.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.tabletminiplayer.annotations.TabletMiniPlayerCompatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("mini-player-dimensions-calculator-fingerprint") +@MatchingMethod( + "Lkkr;", "pM" +) +@FuzzyPatternScanMethod(2) // TODO: Find a good threshold value +@TabletMiniPlayerCompatibility +@Version("0.0.1") +object MiniPlayerDimensionsCalculatorFingerprint : MethodFingerprint( + "V", + AccessFlags.PUBLIC or AccessFlags.FINAL, + listOf("L"), + listOf( + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.MUL_FLOAT, + Opcode.INVOKE_INTERFACE, + Opcode.IGET_BOOLEAN, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.INVOKE_DIRECT, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.FLOAT_TO_DOUBLE, + Opcode.CONST_WIDE_HIGH16, + Opcode.CMPL_DOUBLE, + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt new file mode 100644 index 000000000..859783d81 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.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.tabletminiplayer.annotations.TabletMiniPlayerCompatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("mini-player-override-fingerprint") +@MatchingMethod( + "Lkkr;", "j" +) +@TabletMiniPlayerCompatibility +@Version("0.0.1") +object MiniPlayerOverrideFingerprint : MethodFingerprint( + "Z", AccessFlags.STATIC or AccessFlags.PUBLIC ,null, + listOf(Opcode.RETURN), // anchor to insert the instruction +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt new file mode 100644 index 000000000..5e4c96050 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.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.tabletminiplayer.annotations.TabletMiniPlayerCompatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("mini-player-override-no-context-fingerprint") +@MatchingMethod( + "Lkkr;", "k" +) +@TabletMiniPlayerCompatibility +@Version("0.0.1") +object MiniPlayerOverrideNoContextFingerprint : MethodFingerprint( + "Z", AccessFlags.FINAL or AccessFlags.PRIVATE ,null, + listOf(Opcode.RETURN), // anchor to insert the instruction +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt new file mode 100644 index 000000000..f36608934 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt @@ -0,0 +1,37 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.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.tabletminiplayer.annotations.TabletMiniPlayerCompatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("mini-player-response-model-size-check-fingerprint") +@MatchingMethod( + "Lenv;", "a" +) +@TabletMiniPlayerCompatibility +@Version("0.0.1") +object MiniPlayerResponseModelSizeCheckFingerprint : MethodFingerprint( + "L", + AccessFlags.PUBLIC or AccessFlags.FINAL, + listOf("L", "L"), + listOf( + Opcode.OR_INT_LIT8, + Opcode.IPUT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.RETURN_OBJECT, + Opcode.RETURN_OBJECT, + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + ), + null +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/patch/TabletMiniPlayerPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/patch/TabletMiniPlayerPatch.kt new file mode 100644 index 000000000..5a561f2f6 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/patch/TabletMiniPlayerPatch.kt @@ -0,0 +1,92 @@ +package app.revanced.patches.youtube.layout.tabletminiplayer.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.addInstructions +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve +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.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.layout.tabletminiplayer.annotations.TabletMiniPlayerCompatibility +import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerDimensionsCalculatorFingerprint +import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideFingerprint +import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideNoContextFingerprint +import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint +import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction + +@Patch +@DependsOn([IntegrationsPatch::class]) +@Name("tablet-mini-player") +@Description("Enables the tablet mini player layout.") +@TabletMiniPlayerCompatibility +@Version("0.0.1") +class TabletMiniPlayerPatch : BytecodePatch( + listOf( + MiniPlayerDimensionsCalculatorFingerprint, + MiniPlayerResponseModelSizeCheckFingerprint + ) +) { + override fun execute(data: BytecodeData): PatchResult { + // first resolve the fingerprints via the parent fingerprint + val miniPlayerClass = MiniPlayerDimensionsCalculatorFingerprint.result!!.classDef + + /* + * no context parameter method + */ + MiniPlayerOverrideNoContextFingerprint.resolve(data, miniPlayerClass) + val (method, _, parameterRegister) = MiniPlayerOverrideNoContextFingerprint.addProxyCall() + // - 1 means to insert before the return instruction + val secondInsertIndex = method.implementation!!.instructions.size - 1 + method.insertOverride(secondInsertIndex, parameterRegister /** same register used to return **/) + + /* + * method with context parameter + */ + MiniPlayerOverrideFingerprint.resolve(data, miniPlayerClass) + val (_, _, _) = MiniPlayerOverrideFingerprint.addProxyCall() + + /* + * size check return value override + */ + val (_, _, _) = MiniPlayerResponseModelSizeCheckFingerprint.addProxyCall() + + return PatchResultSuccess() + } + + // helper methods + private companion object { + fun MethodFingerprint.addProxyCall(): Triple { + val (method, scanIndex, parameterRegister) = this.unwrap() + method.insertOverride(scanIndex, parameterRegister) + + return Triple(method, scanIndex, parameterRegister) + } + + fun MutableMethod.insertOverride(index: Int, overrideRegister: Int) { + this.addInstructions( + index, + """ + invoke-static {v$overrideRegister}, Lapp/revanced/integrations/patches/TabletMiniPlayerOverridePatch;->getTabletMiniPlayerOverride(Z)Z + move-result v$overrideRegister + """ + ) + } + + fun MethodFingerprint.unwrap(): Triple { + val result = this.result!! + val scanIndex = result.patternScanResult!!.endIndex + val method = result.mutableMethod + val instructions = method.implementation!!.instructions + val parameterRegister = (instructions[scanIndex] as OneRegisterInstruction).registerA + + return Triple(method, scanIndex, parameterRegister) + } + } +}