Skip to content

Commit

Permalink
feat: custom-playback-speed patch (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
bogadana committed Jun 28, 2022
1 parent 81af14f commit 224254b
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.misc.customplaybackspeed.annotations

import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package

@Compatibility(
[Package(
"com.google.android.youtube", arrayOf("17.24.34")
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class CustomPlaybackSpeedCompatibility
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package app.revanced.patches.youtube.misc.customplaybackspeed.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.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode

@Name("speed-array-generator-fingerprint")
@MatchingMethod(
"Lzdj;", "d"
)
@FuzzyPatternScanMethod(2)
@CustomPlaybackSpeedCompatibility
@Version("0.0.1")
object SpeedArrayGeneratorFingerprint : MethodFingerprint(
"[L",
AccessFlags.PUBLIC or AccessFlags.STATIC,
null,
listOf(
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.GOTO,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
),
listOf("0.0#")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package app.revanced.patches.youtube.misc.customplaybackspeed.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.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode

@Name("speed-limiter-fingerprint")
@MatchingMethod(
"Lxgy;", "y"
)
@FuzzyPatternScanMethod(2)
@CustomPlaybackSpeedCompatibility
@Version("0.0.1")
object SpeedLimiterFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("F"),
listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.CONST_HIGH16,
Opcode.GOTO,
Opcode.CONST_HIGH16,
Opcode.CONST_HIGH16,
Opcode.INVOKE_STATIC,
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package app.revanced.patches.youtube.misc.customplaybackspeed.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.extensions.replaceInstruction
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.annotations.Dependencies
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patches.youtube.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedArrayGeneratorFingerprint
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedLimiterFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.reference.MethodReference
import org.jf.dexlib2.iface.reference.FieldReference

@Patch
@Name("custom-playback-speed")
@Description("Allows to change the default playback speed options")
@Dependencies(dependencies = [IntegrationsPatch::class])
@CustomPlaybackSpeedCompatibility
@Version("0.0.1")
class CustomPlaybackSpeedPatch : BytecodePatch(
listOf(
SpeedArrayGeneratorFingerprint, SpeedLimiterFingerprint
)
) {

override fun execute(data: BytecodeData): PatchResult {
val arrayGenMethod = SpeedArrayGeneratorFingerprint.result?.mutableMethod!!
val arrayGenMethodImpl = arrayGenMethod.implementation!!

val sizeCallIndex = arrayGenMethodImpl.instructions
.indexOfFirst { ((it as? ReferenceInstruction)?.reference as? MethodReference)?.name == "size" }

if (sizeCallIndex == -1) return PatchResultError("Couldn't find call to size()")

val sizeCallResultRegister =
(arrayGenMethodImpl.instructions.elementAt(sizeCallIndex + 1) as OneRegisterInstruction).registerA

arrayGenMethod.replaceInstruction(
sizeCallIndex + 1,
"const/4 v$sizeCallResultRegister, 0x0"
)

val (arrayLengthConstIndex, arrayLengthConst) = arrayGenMethodImpl.instructions.withIndex()
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 7 }

val arrayLengthConstDestination = (arrayLengthConst as OneRegisterInstruction).registerA

val videoSpeedsArrayType = "Lapp/revanced/integrations/videoplayer/videosettings/VideoSpeed;->videoSpeeds:[F"

arrayGenMethod.addInstructions(
arrayLengthConstIndex + 1,
"""
sget-object v$arrayLengthConstDestination, $videoSpeedsArrayType
array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination
"""
)

val (originalArrayFetchIndex, originalArrayFetch) = arrayGenMethodImpl.instructions.withIndex()
.first {
val reference = ((it.value as? ReferenceInstruction)?.reference as? FieldReference)
reference?.definingClass?.contains("PlayerConfigModel") ?: false &&
reference?.type == "[F"
}

val originalArrayFetchDestination = (originalArrayFetch as OneRegisterInstruction).registerA

arrayGenMethod.replaceInstruction(
originalArrayFetchIndex,
"sget-object v$originalArrayFetchDestination, $videoSpeedsArrayType"
)

val limiterMethod = SpeedLimiterFingerprint.result?.mutableMethod!!;
val limiterMethodImpl = limiterMethod.implementation!!

val speedLimitMin = 0.25f
val speedLimitMax = 100f

val (limiterMinConstIndex, limiterMinConst) = limiterMethodImpl.instructions.withIndex()
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 0.25f.toRawBits() }
val (limiterMaxConstIndex, limiterMaxConst) = limiterMethodImpl.instructions.withIndex()
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() }

val limiterMinConstDestination = (limiterMinConst as OneRegisterInstruction).registerA
val limiterMaxConstDestination = (limiterMaxConst as OneRegisterInstruction).registerA

fun hexFloat(float: Float): String = "0x%08x".format(float.toRawBits())

limiterMethod.replaceInstruction(
limiterMinConstIndex,
"const/high16 v$limiterMinConstDestination, ${hexFloat(speedLimitMin)}"
)
limiterMethod.replaceInstruction(
limiterMaxConstIndex,
"const/high16 v$limiterMaxConstDestination, ${hexFloat(speedLimitMax)}"
)

return PatchResultSuccess()
}
}

0 comments on commit 224254b

Please sign in to comment.