diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/annotations/ForceVP9Compatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/annotations/ForceVP9Compatibility.kt new file mode 100644 index 00000000..116b2a85 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/annotations/ForceVP9Compatibility.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.misc.forcevp9.annotations; + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package( + "com.google.android.youtube", arrayOf("17.26.35") + )] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class ForceVP9Compatibility { +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprint.kt new file mode 100644 index 00000000..549ae06f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprint.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.forcevp9.annotations.ForceVP9Compatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("force-vp9-codec-fingerprint") +@MatchingMethod( + "Lpzs;", "aI" +) +@DirectPatternScanMethod +@ForceVP9Compatibility +@Version("0.0.1") +object ForceVP9CodecFingerprint : MethodFingerprint( + "Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "I"), listOf( + Opcode.SGET, Opcode.IF_NEZ, Opcode.INVOKE_STATIC + ), null, null +) + +/* +public static boolean aI(Context context, int i) { + if (b == 0) { + aH(context); + } + return b >= i; // Override to: return Lapp/revanced/integrations/patches/ForceCodecPatch->shouldForceVP9() +} + +.method public static aI(Landroid/content/Context;I)Z + sget v0, Lpzs;->b:I + if-nez v0, :cond_7 + invoke-static {p0}, Lpzs;->aH(Landroid/content/Context;)V + :cond_7 + //remove after here, and inject only our code + sget p0, Lpzs;->b:I + if-lt p0, p1, :cond_d + const/4 p0, 0x1 + return p0 + :cond_d + const/4 p0, 0x0 + return p0 +.end method + */ \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprintTwo.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprintTwo.kt new file mode 100644 index 00000000..e1dd92d2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9CodecFingerprintTwo.kt @@ -0,0 +1,73 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.forcevp9.annotations.ForceVP9Compatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +@Name("force-vp9-codec-fingerprint-two") +@MatchingMethod( + "Lpzs;", "aO" +) +@DirectPatternScanMethod +@ForceVP9Compatibility +@Version("0.0.1") +object ForceVP9CodecFingerprintTwo : MethodFingerprint( + "Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("I"), listOf( + Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, Opcode.CONST_4 + ), null, null +) + +/* +public static boolean aO(int i) { + Pair aG = aG(); + return (aG == null ? 0 : Math.min(((Integer) aG.first).intValue(), ((Integer) aG.second).intValue())) >= i; + //replace line with: return Lapp/revanced/integrations/patches/ForceCodecPatch/shouldForceVP9(); +} + +becomes: +public static boolean aO(int i) { + return Lapp/revanced/integrations/patches/ForceCodecPatch/shouldForceVP9(); +} + +.method public static aO(I)Z + invoke-static {}, Lpzs;->aG()Landroid/util/Pair; + move-result-object v0 + const/4 v1, 0x0 + if-nez v0, :cond_9 + const/4 v0, 0x0 + goto :goto_1d + :cond_9 + iget-object v2, v0, Landroid/util/Pair;->first:Ljava/lang/Object; + check-cast v2, Ljava/lang/Integer; + invoke-virtual {v2}, Ljava/lang/Integer;->intValue()I + move-result v2 + iget-object v0, v0, Landroid/util/Pair;->second:Ljava/lang/Object; + check-cast v0, Ljava/lang/Integer; + invoke-virtual {v0}, Ljava/lang/Integer;->intValue()I + move-result v0 + invoke-static {v2, v0}, Ljava/lang/Math;->min(II)I + move-result v0 + :goto_1d + if-lt v0, p0, :cond_21 + const/4 p0, 0x1 + return p0 + :cond_21 + return v1 +.end method + + +becomes: +.method public static aO(I)Z + invoke-static {}, Lapp/revanced/integrations/patches/ForceCodecPatch;->shouldForceVP9()Z + move-result v0 + return v0 +.end method + + + */ \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9ParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9ParentFingerprint.kt new file mode 100644 index 00000000..e9574503 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ForceVP9ParentFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.forcevp9.annotations.ForceVP9Compatibility +import org.jf.dexlib2.AccessFlags + +@Name("force-vp9-codec-parent-fingerprint") +@MatchingMethod( + "Lpzs;", "Y" +) +@DirectPatternScanMethod +@ForceVP9Compatibility +@Version("0.0.1") +object ForceVP9ParentFingerprint : MethodFingerprint( + "V", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L", "L"), null, + listOf( + "Share video error: null watch uri" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoFingerprint.kt new file mode 100644 index 00000000..7556d4a0 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoFingerprint.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.forcevp9.annotations.ForceVP9Compatibility +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.reference.FieldReference + +@Name("replace-device-info-parent-fingerprint") +@MatchingMethod( + "Lvjb;", "e" +) +@DirectPatternScanMethod +@ForceVP9Compatibility +@Version("0.0.1") +object ReplaceDeviceInfoFingerprint : MethodFingerprint( + "L", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), null, + null, + customFingerprint = { methodDef -> + methodDef.implementation!!.instructions.any { + ((it as? ReferenceInstruction)?.reference as? FieldReference)?.definingClass.equals("Landroid/os/Build;") + && ((it as? ReferenceInstruction)?.reference as? FieldReference)?.name.equals("MANUFACTURER") + } + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoParentFingerprint.kt new file mode 100644 index 00000000..ca74032f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/fingerprints/ReplaceDeviceInfoParentFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.misc.forcevp9.annotations.ForceVP9Compatibility +import org.jf.dexlib2.AccessFlags + +@Name("replace-device-info-parent-fingerprint") +@MatchingMethod( + "Lvjb;", "b" +) +@DirectPatternScanMethod +@ForceVP9Compatibility +@Version("0.0.1") +object ReplaceDeviceInfoParentFingerprint : MethodFingerprint( + "L", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), null, + listOf( + "Failed to read the client side experiments map from the disk" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/patch/ForceVP9CodecPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/patch/ForceVP9CodecPatch.kt new file mode 100644 index 00000000..a830890c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/forcevp9/patch/ForceVP9CodecPatch.kt @@ -0,0 +1,87 @@ +package app.revanced.patches.youtube.misc.forcevp9.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.removeInstruction +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult +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.Dependencies +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.misc.forcevp9.annotations.ForceVP9Compatibility +import app.revanced.patches.youtube.misc.forcevp9.fingerprints.* +import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction +import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.reference.FieldReference + +@Patch(include = false) +@Dependencies([IntegrationsPatch::class]) +@Name("force-vp9-codec") +@Description("Forces the VP9 codec for videos.") +@ForceVP9Compatibility +@Version("0.0.1") +class ForceVP9CodecPatch : BytecodePatch( + listOf( + ForceVP9ParentFingerprint, ReplaceDeviceInfoParentFingerprint + ) +) { + override fun execute(data: BytecodeData): PatchResult { + val classDef = ForceVP9ParentFingerprint.result!!.classDef + ForceVP9CodecFingerprint.resolve(data, classDef) + ForceVP9CodecFingerprintTwo.resolve(data, classDef) + + replaceInstructions(ForceVP9CodecFingerprint.result!!) + replaceInstructions(ForceVP9CodecFingerprintTwo.result!!) + + ReplaceDeviceInfoFingerprint.resolve(data, ReplaceDeviceInfoParentFingerprint.result!!.classDef) + var method = ReplaceDeviceInfoFingerprint.result!!.mutableMethod + replaceDeviceInfos("Manufacturer", method) + replaceDeviceInfos("Model", method) + + return PatchResultSuccess() + } + + private fun replaceInstructions(result: MethodFingerprintResult) { + val method = result.mutableMethod + method.removeInstructions(0, method.implementation!!.instructions.size - 1) + method.addInstructions( + 0, """ + invoke-static {}, Lapp/revanced/integrations/patches/ForceCodecPatch;->shouldForceVP9()Z + move-result v0 + return v0 + """ + ) + } + + private fun replaceDeviceInfos(name: String, method: MutableMethod) { + var impl = method.implementation!! + //find target instruction for Build.name.uppercase() and replace that with our method + impl.instructions.filter { + ((it as? ReferenceInstruction)?.reference as? FieldReference)?.let { field -> + //sget-object v1, Landroid/os/Build;->MANUFACTURER:Ljava/lang/String; + //sget-object v1, Landroid/os/Build;->MODEL:Ljava/lang/String; + field.definingClass == "Landroid/os/Build;" && field.name == name.uppercase() + } == true + }.forEach { instruction -> + val index = method.implementation!!.instructions.indexOf(instruction) + val register = (instruction as OneRegisterInstruction).registerA + + // inject the call to + method.removeInstruction(index) + method.addInstructions( + index, """ + invoke-static {v$register}, Lapp/revanced/integrations/patches/ForceCodecPatch;->get$name()Ljava/lang/String; + move-result v$register + """ + ) + } + } +}