revanced-patches/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/patch/TabletMiniPlayerPatch.kt

131 lines
5.7 KiB
Kotlin
Raw Normal View History

2022-08-14 17:32:32 +02:00
package app.revanced.patches.youtube.layout.tabletminiplayer.patch
import app.revanced.extensions.toErrorResult
2022-08-14 17:32:32 +02:00
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.BytecodeContext
2023-06-07 03:46:13 +02:00
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
2022-08-14 17:32:32 +02:00
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
2022-08-14 17:32:32 +02:00
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
2022-08-14 17:32:32 +02:00
import app.revanced.patches.youtube.layout.tabletminiplayer.annotations.TabletMiniPlayerCompatibility
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.*
2022-08-14 17:32:32 +02:00
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
2022-08-14 17:32:32 +02:00
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
@Name("Tablet mini player")
2022-08-14 17:32:32 +02:00
@Description("Enables the tablet mini player layout.")
@TabletMiniPlayerCompatibility
class TabletMiniPlayerPatch : BytecodePatch(
listOf(
MiniPlayerDimensionsCalculatorParentFingerprint,
MiniPlayerResponseModelSizeCheckFingerprint,
MiniPlayerOverrideParentFingerprint
2022-08-14 17:32:32 +02:00
)
) {
override fun execute(context: BytecodeContext): PatchResult {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
SwitchPreference(
"revanced_tablet_miniplayer",
StringResource("revanced_tablet_miniplayer_title", "Enable tablet mini player"),
StringResource("revanced_tablet_miniplayer_summary_on", "Mini player is enabled"),
StringResource("revanced_tablet_miniplayer_summary_off", "Mini player is disabled")
)
)
// First resolve the fingerprints via the parent fingerprint.
MiniPlayerDimensionsCalculatorParentFingerprint.result ?: return MiniPlayerDimensionsCalculatorParentFingerprint.toErrorResult()
val miniPlayerClass = MiniPlayerDimensionsCalculatorParentFingerprint.result!!.classDef
2022-08-14 17:32:32 +02:00
/*
* No context parameter method.
2022-08-14 17:32:32 +02:00
*/
MiniPlayerOverrideNoContextFingerprint.resolve(context, miniPlayerClass)
2022-08-14 17:32:32 +02:00
val (method, _, parameterRegister) = MiniPlayerOverrideNoContextFingerprint.addProxyCall()
// Insert right before the return instruction.
2022-08-14 17:32:32 +02:00
val secondInsertIndex = method.implementation!!.instructions.size - 1
method.insertOverride(
secondInsertIndex, parameterRegister
/** same register used to return **/
)
2022-08-14 17:32:32 +02:00
/*
* Method with context parameter.
*/
MiniPlayerOverrideParentFingerprint.result?.let {
if (!MiniPlayerOverrideFingerprint.resolve(context, it.classDef))
throw MiniPlayerOverrideFingerprint.toErrorResult()
} ?: return MiniPlayerOverrideParentFingerprint.toErrorResult()
/*
* Override every return instruction with the proxy call.
*/
MiniPlayerOverrideFingerprint.result!!.mutableMethod.apply {
implementation!!.let { implementation ->
val returnIndices = implementation.instructions
.withIndex()
.filter { (_, instruction) -> instruction.opcode == Opcode.RETURN }
.map { (index, _) -> index }
if (returnIndices.isEmpty()) throw PatchResultError("No return instructions found.")
// This method clobbers register p0 to return the value, calculate to override.
val returnedRegister = implementation.registerCount - parameters.size
// Hook the returned register on every return instruction.
returnIndices.forEach { index -> insertOverride(index, returnedRegister) }
}
}
2022-08-14 17:32:32 +02:00
/*
* Size check return value override.
2022-08-14 17:32:32 +02:00
*/
MiniPlayerResponseModelSizeCheckFingerprint.addProxyCall()
return PatchResultSuccess()
2022-08-14 17:32:32 +02:00
}
// Helper methods.
2022-08-14 17:32:32 +02:00
private companion object {
fun MethodFingerprint.addProxyCall(): Triple<MutableMethod, Int, Int> {
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
2023-06-07 03:46:13 +02:00
"""
2022-08-14 17:32:32 +02:00
)
}
fun MethodFingerprint.unwrap(): Triple<MutableMethod, Int, Int> {
val result = this.result!!
val scanIndex = result.scanResult.patternScanResult!!.endIndex
2022-08-14 17:32:32 +02:00
val method = result.mutableMethod
val instructions = method.implementation!!.instructions
val parameterRegister = (instructions[scanIndex] as OneRegisterInstruction).registerA
return Triple(method, scanIndex, parameterRegister)
}
}
}