fix: breaking changes by revanced-patcher
dependency
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
3bceed8359
commit
7e485b4ffe
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
|
||||||
implementation("app.revanced:revanced-patcher:1.+")
|
implementation("app.revanced:revanced-patcher:1.+")
|
||||||
|
implementation(kotlin("reflect"))
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
|
|
@ -2,17 +2,21 @@ package app.revanced.patches
|
||||||
|
|
||||||
import app.revanced.patcher.data.base.Data
|
import app.revanced.patcher.data.base.Data
|
||||||
import app.revanced.patcher.patch.base.Patch
|
import app.revanced.patcher.patch.base.Patch
|
||||||
import app.revanced.patches.music.audio.CodecsUnlockPatch
|
import app.revanced.patches.music.audio.codecs.patch.CodecsUnlockPatch
|
||||||
import app.revanced.patches.music.audio.EnableAudioOnlyPatch
|
import app.revanced.patches.music.audio.exclusiveaudio.patch.ExclusiveAudioPatch
|
||||||
import app.revanced.patches.music.layout.RemoveTasteBuilderPatch
|
import app.revanced.patches.music.layout.tastebuilder.patch.RemoveTasteBuilderPatch
|
||||||
import app.revanced.patches.music.layout.RemoveUpgradeTabPatch
|
import app.revanced.patches.music.layout.upgradebutton.patch.RemoveUpgradeButtonPatch
|
||||||
import app.revanced.patches.music.premium.BackgroundPlayPatch
|
import app.revanced.patches.music.premium.backgroundplay.patch.BackgroundPlayPatch
|
||||||
import app.revanced.patches.youtube.ad.HomePromoPatch
|
import app.revanced.patches.youtube.ad.home.patch.PromotionsPatch
|
||||||
import app.revanced.patches.youtube.ad.VideoAdsPatch
|
import app.revanced.patches.youtube.ad.video.patch.VideoAdsPatch
|
||||||
import app.revanced.patches.youtube.interaction.EnableSeekbarTappingPatch
|
import app.revanced.patches.youtube.interaction.seekbar.patch.EnableSeekbarTappingPatch
|
||||||
import app.revanced.patches.youtube.layout.*
|
import app.revanced.patches.youtube.layout.createbutton.patch.CreateButtonRemoverPatch
|
||||||
import app.revanced.patches.youtube.misc.FixLocaleConfigErrorPatch
|
import app.revanced.patches.youtube.layout.minimizedplayback.patch.MinimizedPlaybackPatch
|
||||||
import app.revanced.patches.youtube.misc.IntegrationsPatch
|
import app.revanced.patches.youtube.layout.oldqualitylayout.patch.OldQualityLayoutPatch
|
||||||
|
import app.revanced.patches.youtube.layout.reels.patch.HideReelsPatch
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.patch.ShortsButtonRemoverPatch
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index contains all the patches.
|
* Index contains all the patches.
|
||||||
|
@ -28,15 +32,15 @@ object Index {
|
||||||
::FixLocaleConfigErrorPatch,
|
::FixLocaleConfigErrorPatch,
|
||||||
//::HomeAdsPatch,
|
//::HomeAdsPatch,
|
||||||
::VideoAdsPatch,
|
::VideoAdsPatch,
|
||||||
::HomePromoPatch,
|
::PromotionsPatch,
|
||||||
::MinimizedPlaybackPatch,
|
::MinimizedPlaybackPatch,
|
||||||
::CreateButtonRemoverPatch,
|
::CreateButtonRemoverPatch,
|
||||||
::ShortsButtonRemoverPatch,
|
::ShortsButtonRemoverPatch,
|
||||||
::HideReelsPatch,
|
::HideReelsPatch,
|
||||||
::OldQualityLayoutPatch,
|
::OldQualityLayoutPatch,
|
||||||
::EnableSeekbarTappingPatch,
|
::EnableSeekbarTappingPatch,
|
||||||
::EnableAudioOnlyPatch,
|
::ExclusiveAudioPatch,
|
||||||
::RemoveUpgradeTabPatch,
|
::RemoveUpgradeButtonPatch,
|
||||||
::RemoveTasteBuilderPatch,
|
::RemoveTasteBuilderPatch,
|
||||||
::BackgroundPlayPatch,
|
::BackgroundPlayPatch,
|
||||||
::CodecsUnlockPatch
|
::CodecsUnlockPatch
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
package app.revanced.patches.music.audio
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.data.implementation.toMethodWalker
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstruction
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val packageMetadata = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
listOf("5.03.50")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val patchMetadata = PatchMetadata(
|
|
||||||
"codecs-unlock",
|
|
||||||
"Audio codecs unlock patch",
|
|
||||||
"Enables more audio codecs. Usually results in better audio quality but may depend on song and device.",
|
|
||||||
packageMetadata,
|
|
||||||
"0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
class CodecsUnlockPatch : BytecodePatch(
|
|
||||||
patchMetadata,
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"codec-lock-method",
|
|
||||||
MethodMetadata(
|
|
||||||
"Labwj;",
|
|
||||||
"a",
|
|
||||||
),
|
|
||||||
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
|
|
||||||
packageMetadata,
|
|
||||||
"Required signature for ${patchMetadata.name}. Discovered in version 5.03.50.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"L",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
listOf("L", "L", "L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.RETURN_OBJECT
|
|
||||||
)
|
|
||||||
),
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"all-codecs-reference-method",
|
|
||||||
MethodMetadata(
|
|
||||||
"Laari;",
|
|
||||||
"b",
|
|
||||||
),
|
|
||||||
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
|
|
||||||
packageMetadata,
|
|
||||||
"Required signature for ${patchMetadata.name}. Discovered in version 5.03.50.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"J",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.IPUT_BOOLEAN,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.IPUT_BOOLEAN,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.MOVE_EXCEPTION,
|
|
||||||
Opcode.INVOKE_SUPER,
|
|
||||||
Opcode.MOVE_RESULT_WIDE,
|
|
||||||
Opcode.RETURN_WIDE
|
|
||||||
),
|
|
||||||
listOf("itag")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
var result = signatures.first().result!!
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
val instructionIndex = result.scanData.startIndex
|
|
||||||
|
|
||||||
result = signatures.last().result!!
|
|
||||||
val codecMethod = data
|
|
||||||
.toMethodWalker(result.immutableMethod)
|
|
||||||
.walk(result.scanData.startIndex)
|
|
||||||
.getMethod()
|
|
||||||
|
|
||||||
implementation.replaceInstruction(
|
|
||||||
instructionIndex,
|
|
||||||
"invoke-static {}, ${codecMethod.definingClass}->${codecMethod.name}()Ljava/util/Set;".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
package app.revanced.patches.music.audio
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstruction
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
listOf("5.03.50")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class EnableAudioOnlyPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"audio-only-playback-patch",
|
|
||||||
"Audio Only Mode Patch",
|
|
||||||
"Add the option to play music without video.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"audio-only-method-signature",
|
|
||||||
MethodMetadata("Lgmd;", "c"),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("L", "Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQ,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.NOP,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.RETURN_VOID
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!.findParentMethod(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"audio-only-enabler-method",
|
|
||||||
MethodMetadata("Lgmd;", "d"),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"Z",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf(),
|
|
||||||
listOf(
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.RETURN
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) ?: return PatchResultError("Required method for ${metadata.shortName} not found.")
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
implementation.replaceInstruction(
|
|
||||||
implementation.instructions.count() - 1,
|
|
||||||
"const/4 v0, 0x1".toInstruction()
|
|
||||||
)
|
|
||||||
implementation.addInstruction(
|
|
||||||
"return v0".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.music.audio.codecs.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class CodecsUnlockCompatibility
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package app.revanced.patches.music.audio.codecs.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.data.implementation.toMethodWalker
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
|
import app.revanced.patches.music.audio.codecs.signatures.AllCodecsReferenceSignature
|
||||||
|
import app.revanced.patches.music.audio.codecs.signatures.CodecsLockSignature
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("codecs-unlock")
|
||||||
|
@Description("Enables more audio codecs. Usually results in better audio quality but may depend on song and device.")
|
||||||
|
@CodecsUnlockCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class CodecsUnlockPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
CodecsLockSignature, AllCodecsReferenceSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
var result = signatures.first().result!!
|
||||||
|
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
val instructionIndex = result.scanData.startIndex
|
||||||
|
|
||||||
|
result = signatures.last().result!!
|
||||||
|
val codecMethod = data.toMethodWalker(result.immutableMethod).walk(result.scanData.startIndex).getMethod()
|
||||||
|
|
||||||
|
implementation.replaceInstruction(
|
||||||
|
instructionIndex,
|
||||||
|
"invoke-static {}, ${codecMethod.definingClass}->${codecMethod.name}()Ljava/util/Set;".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package app.revanced.patches.music.audio.codecs.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("all-codecs-reference-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Laari;",
|
||||||
|
"b",
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@CodecsUnlockCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object AllCodecsReferenceSignature : MethodSignature(
|
||||||
|
"J", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.MOVE_EXCEPTION,
|
||||||
|
Opcode.INVOKE_SUPER,
|
||||||
|
Opcode.MOVE_RESULT_WIDE,
|
||||||
|
Opcode.RETURN_WIDE
|
||||||
|
), listOf("itag")
|
||||||
|
)
|
|
@ -0,0 +1,47 @@
|
||||||
|
package app.revanced.patches.music.audio.codecs.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("codec-lock-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Labwj;",
|
||||||
|
"a",
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@CodecsUnlockCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CodecsLockSignature : MethodSignature(
|
||||||
|
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L", "L", "L"), listOf(
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.RETURN_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.music.audio.exclusiveaudio.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class ExclusiveAudioCompatibility
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package app.revanced.patches.music.audio.exclusiveaudio.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
||||||
|
import app.revanced.patches.music.audio.exclusiveaudio.signatures.ExclusiveAudioSignature
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("exclusive-audio-playback")
|
||||||
|
@Description("Add the option to play music without video.")
|
||||||
|
@ExclusiveAudioCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class ExclusiveAudioPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
ExclusiveAudioSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!.findParentMethod(@Name("audio-only-enabler-method") @MatchingMethod(
|
||||||
|
"Lgmd;",
|
||||||
|
"d"
|
||||||
|
) @DirectPatternScanMethod @ExclusiveAudioCompatibility @Version(
|
||||||
|
"0.0.1"
|
||||||
|
) object : MethodSignature(
|
||||||
|
"Z", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.RETURN
|
||||||
|
)
|
||||||
|
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
||||||
|
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
implementation.replaceInstruction(
|
||||||
|
implementation.instructions.count() - 1, "const/4 v0, 0x1".toInstruction()
|
||||||
|
)
|
||||||
|
implementation.addInstruction(
|
||||||
|
"return v0".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package app.revanced.patches.music.audio.exclusiveaudio.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("exclusive-audio-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lgmd;", "c"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@ExclusiveAudioCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ExclusiveAudioSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("L", "Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQ,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.NOP,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,94 +0,0 @@
|
||||||
package app.revanced.patches.music.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
listOf("5.03.50")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class RemoveTasteBuilderPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"tasteBuilder-remover",
|
|
||||||
"Remove TasteBuilder Patch",
|
|
||||||
"Removes the \"Tell us which artists you like\" card from the Home screen. The same functionality can be triggered from the settings anyway.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"taste-builder-constructor",
|
|
||||||
MethodMetadata("Lkyu;", "<init>"),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Required signature for this patch.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
listOf("L", "L", "L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
val insertIndex = result.scanData.endIndex - 8
|
|
||||||
|
|
||||||
val register = (implementation.instructions[insertIndex] as Instruction22c).registerA
|
|
||||||
|
|
||||||
val instructionList =
|
|
||||||
"""
|
|
||||||
const/16 v1, 0x8
|
|
||||||
invoke-virtual {v${register}, v1}, Landroid/view/View;->setVisibility(I)V
|
|
||||||
""".trimIndent().toInstructions().toMutableList()
|
|
||||||
|
|
||||||
implementation.addInstructions(
|
|
||||||
insertIndex,
|
|
||||||
instructionList
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
package app.revanced.patches.music.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
listOf("5.03.50")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class RemoveUpgradeTabPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"upgrade-tab-remover",
|
|
||||||
"Remove Upgrade Tab Patch",
|
|
||||||
"Remove the upgrade tab from t he pivot bar in YouTube music.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"pivot-bar-constructor",
|
|
||||||
MethodMetadata("Lhfu;", "<init2>"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Required signature for this patch.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
listOf("L", "Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_BOOLEAN,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.NOP,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.RETURN_VOID
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
val pivotBarElementFieldRef =
|
|
||||||
(implementation.instructions[result.scanData.endIndex - 1] as Instruction22c).reference
|
|
||||||
|
|
||||||
val register = (implementation.instructions.first() as Instruction35c).registerC
|
|
||||||
// first compile all the needed instructions
|
|
||||||
val instructionList =
|
|
||||||
"""
|
|
||||||
invoke-interface { v0 }, Ljava/util/List;->size()I
|
|
||||||
move-result v1
|
|
||||||
const/4 v2, 0x3
|
|
||||||
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
|
||||||
iput-object v0, v$register, $pivotBarElementFieldRef
|
|
||||||
""".trimIndent().toInstructions().toMutableList()
|
|
||||||
|
|
||||||
|
|
||||||
// replace the instruction to retain the label at given index
|
|
||||||
implementation.replaceInstruction(
|
|
||||||
result.scanData.endIndex - 1,
|
|
||||||
instructionList[0] // invoke-interface
|
|
||||||
)
|
|
||||||
// do not forget to remove this instruction since we added it already
|
|
||||||
instructionList.removeFirst()
|
|
||||||
|
|
||||||
val exitInstruction = instructionList.last() // iput-object
|
|
||||||
implementation.addInstruction(
|
|
||||||
result.scanData.endIndex,
|
|
||||||
exitInstruction
|
|
||||||
)
|
|
||||||
// do not forget to remove this instruction since we added it already
|
|
||||||
instructionList.removeLast()
|
|
||||||
|
|
||||||
// add the necessary if statement to remove the upgrade tab button in case it exists
|
|
||||||
instructionList.add(
|
|
||||||
2, // if-le
|
|
||||||
BuilderInstruction22t(
|
|
||||||
Opcode.IF_LE,
|
|
||||||
1, 2,
|
|
||||||
implementation.newLabelForIndex(result.scanData.endIndex)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
implementation.addInstructions(
|
|
||||||
result.scanData.endIndex,
|
|
||||||
instructionList
|
|
||||||
)
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.music.layout.tastebuilder.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class RemoveTasteBuilderCompatibility
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package app.revanced.patches.music.layout.tastebuilder.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
||||||
|
import app.revanced.patches.music.layout.tastebuilder.signatures.TasteBuilderConstructorSignature
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("tasteBuilder-remover")
|
||||||
|
@Description("Removes the \"Tell us which artists you like\" card from the Home screen. The same functionality can be triggered from the settings anyway.")
|
||||||
|
@RemoveTasteBuilderCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class RemoveTasteBuilderPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
TasteBuilderConstructorSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
val insertIndex = result.scanData.endIndex - 8
|
||||||
|
|
||||||
|
val register = (implementation.instructions[insertIndex] as Instruction22c).registerA
|
||||||
|
|
||||||
|
val instructionList = """
|
||||||
|
const/16 v1, 0x8
|
||||||
|
invoke-virtual {v${register}, v1}, Landroid/view/View;->setVisibility(I)V
|
||||||
|
""".trimIndent().toInstructions().toMutableList()
|
||||||
|
|
||||||
|
implementation.addInstructions(
|
||||||
|
insertIndex, instructionList
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package app.revanced.patches.music.layout.tastebuilder.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("taste-builder-constructor")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lkyu;", "<init>"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@RemoveTasteBuilderCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object TasteBuilderConstructorSignature : MethodSignature(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L"), listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.music.layout.upgradebutton.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class RemoveUpgradeButtonCompatibility
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package app.revanced.patches.music.layout.upgradebutton.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
||||||
|
import app.revanced.patches.music.layout.upgradebutton.signatures.PivotBarConstructorSignature
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("upgrade-button-remover")
|
||||||
|
@Description("Remove the upgrade tab from t he pivot bar in YouTube music.")
|
||||||
|
@RemoveUpgradeButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class RemoveUpgradeButtonPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
PivotBarConstructorSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
val pivotBarElementFieldRef =
|
||||||
|
(implementation.instructions[result.scanData.endIndex - 1] as Instruction22c).reference
|
||||||
|
|
||||||
|
val register = (implementation.instructions.first() as Instruction35c).registerC
|
||||||
|
// first compile all the needed instructions
|
||||||
|
val instructionList = """
|
||||||
|
invoke-interface { v0 }, Ljava/util/List;->size()I
|
||||||
|
move-result v1
|
||||||
|
const/4 v2, 0x3
|
||||||
|
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
||||||
|
iput-object v0, v$register, $pivotBarElementFieldRef
|
||||||
|
""".trimIndent().toInstructions().toMutableList()
|
||||||
|
|
||||||
|
|
||||||
|
// replace the instruction to retain the label at given index
|
||||||
|
implementation.replaceInstruction(
|
||||||
|
result.scanData.endIndex - 1, instructionList[0] // invoke-interface
|
||||||
|
)
|
||||||
|
// do not forget to remove this instruction since we added it already
|
||||||
|
instructionList.removeFirst()
|
||||||
|
|
||||||
|
val exitInstruction = instructionList.last() // iput-object
|
||||||
|
implementation.addInstruction(
|
||||||
|
result.scanData.endIndex, exitInstruction
|
||||||
|
)
|
||||||
|
// do not forget to remove this instruction since we added it already
|
||||||
|
instructionList.removeLast()
|
||||||
|
|
||||||
|
// add the necessary if statement to remove the upgrade tab button in case it exists
|
||||||
|
instructionList.add(
|
||||||
|
2, // if-le
|
||||||
|
BuilderInstruction22t(
|
||||||
|
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(result.scanData.endIndex)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
implementation.addInstructions(
|
||||||
|
result.scanData.endIndex, instructionList
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package app.revanced.patches.music.layout.upgradebutton.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("pivot-bar-constructor")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lhfu;", "<init2>"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@RemoveUpgradeButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PivotBarConstructorSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
listOf("L", "Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.NOP,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,92 +0,0 @@
|
||||||
package app.revanced.patches.music.premium
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
listOf("5.03.50")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class BackgroundPlayPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"background-play",
|
|
||||||
"Enable Background Playback Patch",
|
|
||||||
"Enable playing music in the background.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"background-playback-disabler-method",
|
|
||||||
MethodMetadata("Lafgf;", "e"),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"Z",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
listOf("L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.AND_INT_LIT16,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.RETURN,
|
|
||||||
Opcode.RETURN,
|
|
||||||
Opcode.RETURN
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
signatures.first().result!!.method.implementation!!.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const/4 v0, 0x1
|
|
||||||
return v0
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.music.premium.backgroundplay.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class BackgroundPlayCompatibility
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package app.revanced.patches.music.premium.backgroundplay.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
||||||
|
import app.revanced.patches.music.premium.backgroundplay.signatures.BackgroundPlaybackDisableSignature
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("background-play")
|
||||||
|
@Description("Enable playing music in the background.")
|
||||||
|
@BackgroundPlayCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class BackgroundPlayPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
BackgroundPlaybackDisableSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
signatures.first().result!!.method.implementation!!.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package app.revanced.patches.music.premium.backgroundplay.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("background-playback-disabler-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lafgf;", "e"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@BackgroundPlayCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object BackgroundPlaybackDisableSignature : MethodSignature(
|
||||||
|
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.AND_INT_LIT16,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.RETURN,
|
||||||
|
Opcode.RETURN,
|
||||||
|
Opcode.RETURN
|
||||||
|
)
|
||||||
|
)
|
File diff suppressed because it is too large
Load diff
|
@ -1,188 +0,0 @@
|
||||||
package app.revanced.patches.youtube.ad
|
|
||||||
|
|
||||||
import app.revanced.extensions.injectHideCall
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.data.implementation.toMethodWalker
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.proxy.mutableTypes.MutableMethod
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.03.38", "17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val patchMetadata = PatchMetadata(
|
|
||||||
"home-promo-ads",
|
|
||||||
"Home Promo Ads Patch",
|
|
||||||
"Patch to remove promoted ads in YouTube",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
private val signatureDescription = "Required signature for ${patchMetadata.name}. Discovered in version 17.03.38."
|
|
||||||
|
|
||||||
class HomePromoPatch : BytecodePatch(
|
|
||||||
patchMetadata,
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"promoted-discovery-app-parent-method",
|
|
||||||
MethodMetadata(
|
|
||||||
"Ljre;",
|
|
||||||
"lP",
|
|
||||||
),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
signatureDescription,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
|
||||||
listOf("L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.NEW_ARRAY,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_GE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.APUT_OBJECT,
|
|
||||||
Opcode.ADD_INT_LIT8,
|
|
||||||
Opcode.GOTO
|
|
||||||
)
|
|
||||||
),
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"promoted-discovery-action-parent-method",
|
|
||||||
MethodMetadata(
|
|
||||||
"Ljqv;",
|
|
||||||
"lP",
|
|
||||||
),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
signatureDescription,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
|
||||||
listOf("L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_VIRTUAL_RANGE,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.XOR_INT_2ADDR,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
for (signature in signatures) {
|
|
||||||
val result = signature.result!!
|
|
||||||
|
|
||||||
val methodMetadata = MethodMetadata(signature.metadata.methodMetadata!!.definingClass, "d")
|
|
||||||
val requiredMethod = result.findParentMethod(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"promoted-discovery-action-method",
|
|
||||||
methodMetadata,
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
signatureDescription,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PRIVATE or AccessFlags.FINAL,
|
|
||||||
listOf("Z", "Z"),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
?: return PatchResultError("Required parent method ${methodMetadata.name} could not be found in ${methodMetadata.definingClass}")
|
|
||||||
|
|
||||||
val toBePatchedInvokeOffset =
|
|
||||||
requiredMethod.immutableMethod.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_DIRECT }
|
|
||||||
val toBePatchedMethod = data
|
|
||||||
.toMethodWalker(requiredMethod.immutableMethod)
|
|
||||||
.walk(toBePatchedInvokeOffset, true)
|
|
||||||
.getMethod() as MutableMethod
|
|
||||||
|
|
||||||
val implementation = toBePatchedMethod.implementation!!
|
|
||||||
val invokeVirtualOffset = implementation.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_VIRTUAL }
|
|
||||||
|
|
||||||
val moveResultInstruction = implementation.instructions[invokeVirtualOffset + 1]
|
|
||||||
if (moveResultInstruction.opcode != Opcode.MOVE_RESULT_OBJECT)
|
|
||||||
return PatchResultError("The toBePatchedInvokeOffset offset was wrong in ${metadata.name}")
|
|
||||||
|
|
||||||
val register = (moveResultInstruction as Instruction11x).registerA
|
|
||||||
implementation.injectHideCall(invokeVirtualOffset + 2, register)
|
|
||||||
}
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
package app.revanced.patches.youtube.ad
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val packageMetadata = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val patchMetadata = PatchMetadata(
|
|
||||||
"video-ads",
|
|
||||||
"YouTube Video Ads Patch",
|
|
||||||
"Patch to remove ads in the YouTube video player.",
|
|
||||||
packageMetadata,
|
|
||||||
"0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
class VideoAdsPatch : BytecodePatch(
|
|
||||||
patchMetadata,
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"show-video-ads-constructor",
|
|
||||||
MethodMetadata(
|
|
||||||
"Laadb",
|
|
||||||
"<init>",
|
|
||||||
),
|
|
||||||
PatternScanMethod.Fuzzy(2),// FIXME: Test this threshold and find the best value.
|
|
||||||
packageMetadata,
|
|
||||||
"Required signature for ${patchMetadata.name}. Discovered in version 17.14.35.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
listOf("L", "L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
null, // either CONST_4 or CONST_16
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IPUT_BOOLEAN,
|
|
||||||
Opcode.RETURN_VOID
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
var result = signatures.first().result!!
|
|
||||||
|
|
||||||
val responsibleMethodSignature = MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"show-video-ads-method",
|
|
||||||
MethodMetadata(
|
|
||||||
"zai",
|
|
||||||
null // unknown
|
|
||||||
),
|
|
||||||
PatternScanMethod.Direct(),
|
|
||||||
packageMetadata,
|
|
||||||
"Signature to find the method, which is responsible for showing the video ads. Discovered in version 17.14.35",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("Z"),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
result = result.findParentMethod(
|
|
||||||
responsibleMethodSignature
|
|
||||||
) ?: return PatchResultError(
|
|
||||||
"Could not find parent method with signature ${responsibleMethodSignature.metadata.name}"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Override the parameter by calling shouldShowAds and setting the parameter to the result
|
|
||||||
result.method.implementation!!.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { }, Lfi/vanced/libraries/youtube/whitelisting/Whitelist;->shouldShowAds()Z
|
|
||||||
move-result v1
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.youtube.ad.home.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.03.38", "17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class PromotionsCompatibility
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,65 @@
|
||||||
|
package app.revanced.patches.youtube.ad.home.patch
|
||||||
|
|
||||||
|
import app.revanced.extensions.injectHideCall
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.data.implementation.toMethodWalker
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.ad.home.annotation.PromotionsCompatibility
|
||||||
|
import app.revanced.patches.youtube.ad.home.signatures.PromotedDiscoveryActionParentSignature
|
||||||
|
import app.revanced.patches.youtube.ad.home.signatures.PromotedDiscoveryAppParentSignature
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("home-promo-ads")
|
||||||
|
@Description("Patch to remove promoted ads in YouTube.")
|
||||||
|
@PromotionsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class PromotionsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
PromotedDiscoveryAppParentSignature, PromotedDiscoveryActionParentSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
for (signature in signatures) {
|
||||||
|
val result = signature.result!!
|
||||||
|
|
||||||
|
val requiredMethod =
|
||||||
|
result.findParentMethod(@Name("promotion-ads-signature") @MatchingMethod(name = "d") @DirectPatternScanMethod @PromotionsCompatibility @Version(
|
||||||
|
"0.0.1"
|
||||||
|
) object : MethodSignature(
|
||||||
|
"V", AccessFlags.PRIVATE or AccessFlags.FINAL, listOf("Z", "Z"), null
|
||||||
|
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
||||||
|
|
||||||
|
val toBePatchedInvokeOffset =
|
||||||
|
requiredMethod.immutableMethod.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_DIRECT }
|
||||||
|
val toBePatchedMethod =
|
||||||
|
data.toMethodWalker(requiredMethod.immutableMethod).walk(toBePatchedInvokeOffset, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
|
||||||
|
val implementation = toBePatchedMethod.implementation!!
|
||||||
|
val invokeVirtualOffset = implementation.instructions.indexOfFirst { it.opcode == Opcode.INVOKE_VIRTUAL }
|
||||||
|
|
||||||
|
val moveResultInstruction = implementation.instructions[invokeVirtualOffset + 1]
|
||||||
|
if (moveResultInstruction.opcode != Opcode.MOVE_RESULT_OBJECT) return PatchResultError("The toBePatchedInvokeOffset offset was wrong in ${(this::class.annotations.find { it is Name } as Name).name}")
|
||||||
|
|
||||||
|
val register = (moveResultInstruction as Instruction11x).registerA
|
||||||
|
implementation.injectHideCall(invokeVirtualOffset + 2, register)
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package app.revanced.patches.youtube.ad.home.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("promoted-discovery-app-parent-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Ljre;", "lP"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PromotedDiscoveryActionParentSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
||||||
|
listOf("L", "L"),
|
||||||
|
listOf(
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_VIRTUAL_RANGE,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.XOR_INT_2ADDR,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,61 @@
|
||||||
|
package app.revanced.patches.youtube.ad.home.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("promoted-discovery-action-parent-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Ljqv;",
|
||||||
|
"lP"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
|
||||||
|
object PromotedDiscoveryAppParentSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
|
||||||
|
listOf("L", "L"),
|
||||||
|
listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.NEW_ARRAY,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_GE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.APUT_OBJECT,
|
||||||
|
Opcode.ADD_INT_LIT8,
|
||||||
|
Opcode.GOTO
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.youtube.ad.video.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class VideoAdsCompatibility
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package app.revanced.patches.youtube.ad.video.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.youtube.ad.video.annotations.VideoAdsCompatibility
|
||||||
|
import app.revanced.patches.youtube.ad.video.signatures.ShowVideoAdsConstructorSignature
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("video-ads")
|
||||||
|
@Description("Patch to remove ads in the YouTube video player.")
|
||||||
|
@VideoAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class VideoAdsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
ShowVideoAdsConstructorSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result =
|
||||||
|
signatures.first().result!!.findParentMethod(@Name("show-video-ads-method-signature") @MatchingMethod(
|
||||||
|
definingClass = "zai"
|
||||||
|
) @DirectPatternScanMethod @VideoAdsCompatibility @Version("0.0.1") object : MethodSignature(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), null
|
||||||
|
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
||||||
|
|
||||||
|
|
||||||
|
// Override the parameter by calling shouldShowAds and setting the parameter to the result
|
||||||
|
result.method.implementation!!.addInstructions(
|
||||||
|
0, """
|
||||||
|
invoke-static { }, Lfi/vanced/libraries/youtube/whitelisting/Whitelist;->shouldShowAds()Z
|
||||||
|
move-result v1
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.revanced.patches.youtube.ad.video.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("show-video-ads-constructor-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Laadb",
|
||||||
|
"<init>",
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ShowVideoAdsConstructorSignature : MethodSignature(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
null, // either CONST_4 or CONST_16
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,186 +0,0 @@
|
||||||
package app.revanced.patches.youtube.interaction
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
|
||||||
import org.jf.dexlib2.iface.Method
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11n
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class EnableSeekbarTappingPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"seekbar-tapping",
|
|
||||||
"Enable seekbar tapping patch",
|
|
||||||
"Enable tapping on the seekbar of the YouTube player.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"enable-seekbar-tapping-parent-signature",
|
|
||||||
MethodMetadata("Lzhj;", "J"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for a parent method, which is needed to find the actual method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"L",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf(),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.NEW_ARRAY,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_WIDE,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.APUT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_WIDE,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.APUT_OBJECT,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.RETURN_OBJECT
|
|
||||||
)
|
|
||||||
),
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"enable-seekbar-tapping-signature",
|
|
||||||
MethodMetadata("Lfao;", "onTouchEvent"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"Z",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_WIDE,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.DIV_INT_2ADDR,
|
|
||||||
Opcode.ADD_INT,
|
|
||||||
Opcode.SUB_INT_2ADDR,
|
|
||||||
Opcode.INT_TO_FLOAT,
|
|
||||||
Opcode.CMPG_FLOAT,
|
|
||||||
Opcode.IF_GTZ,
|
|
||||||
Opcode.INT_TO_FLOAT,
|
|
||||||
Opcode.CMPG_FLOAT,
|
|
||||||
Opcode.IF_GTZ,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
var result = signatures.first().result!!
|
|
||||||
|
|
||||||
val tapSeekMethods = mutableMapOf<String, Method>()
|
|
||||||
|
|
||||||
// find the methods which tap the seekbar
|
|
||||||
for (it in result.definingClassProxy.immutableClass.methods) {
|
|
||||||
if (it.implementation == null) continue
|
|
||||||
|
|
||||||
val instructions = it.implementation!!.instructions
|
|
||||||
// here we make sure we actually find the method because it has more then 7 instructions
|
|
||||||
if (instructions.count() < 7) continue
|
|
||||||
|
|
||||||
// we know that the 7th instruction has the opcode CONST_4
|
|
||||||
val instruction = instructions.elementAt(6)
|
|
||||||
if (instruction.opcode != Opcode.CONST_4) continue
|
|
||||||
|
|
||||||
// the literal for this instruction has to be either 1 or 2
|
|
||||||
val literal = (instruction as Instruction11n).narrowLiteral
|
|
||||||
|
|
||||||
// method founds
|
|
||||||
if (literal == 1) tapSeekMethods["P"] = it
|
|
||||||
if (literal == 2) tapSeekMethods["O"] = it
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace map because we dont need the upper one anymore
|
|
||||||
result = signatures.last().result!!
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
// if tap-seeking is enabled, do not invoke the two methods below
|
|
||||||
val pMethod = tapSeekMethods["P"]!!
|
|
||||||
val oMethod = tapSeekMethods["O"]!!
|
|
||||||
|
|
||||||
// get the required register
|
|
||||||
val instruction = implementation.instructions[result.scanData.endIndex]
|
|
||||||
if (instruction.opcode != Opcode.INVOKE_VIRTUAL)
|
|
||||||
return PatchResultError("Could not find the correct register")
|
|
||||||
val register = (instruction as Instruction35c).registerC
|
|
||||||
|
|
||||||
// the instructions are written in reverse order.
|
|
||||||
implementation.addInstructions(
|
|
||||||
result.scanData.endIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-virtual { v$register, v2 }, ${oMethod.definingClass}->${oMethod.name}(I)V
|
|
||||||
invoke-virtual { v$register, v2 }, ${pMethod.definingClass}->${pMethod.name}(I)V
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
|
|
||||||
// if tap-seeking is disabled, do not invoke the two methods above by jumping to the else label
|
|
||||||
val elseLabel = implementation.newLabelForIndex(result.scanData.endIndex + 1)
|
|
||||||
implementation.addInstruction(
|
|
||||||
result.scanData.endIndex + 1,
|
|
||||||
BuilderInstruction21t(Opcode.IF_EQZ, 0, elseLabel)
|
|
||||||
)
|
|
||||||
implementation.addInstructions(
|
|
||||||
result.scanData.endIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { }, Lfi/razerman/youtube/preferences/BooleanPreferences;->isTapSeekingEnabled()Z
|
|
||||||
move-result v0
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.youtube.interaction.seekbar.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class SeekbarTappingCompatibility
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package app.revanced.patches.youtube.interaction.seekbar.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.signatures.SeekbarTappingParentSignature
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.signatures.SeekbarTappingSignature
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction11n
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("seekbar-tapping")
|
||||||
|
@Description("Enable tapping on the seekbar of the YouTube player.")
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class EnableSeekbarTappingPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
SeekbarTappingParentSignature, SeekbarTappingSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
var result = signatures.first().result!!
|
||||||
|
|
||||||
|
val tapSeekMethods = mutableMapOf<String, Method>()
|
||||||
|
|
||||||
|
// find the methods which tap the seekbar
|
||||||
|
for (it in result.definingClassProxy.immutableClass.methods) {
|
||||||
|
if (it.implementation == null) continue
|
||||||
|
|
||||||
|
val instructions = it.implementation!!.instructions
|
||||||
|
// here we make sure we actually find the method because it has more then 7 instructions
|
||||||
|
if (instructions.count() < 7) continue
|
||||||
|
|
||||||
|
// we know that the 7th instruction has the opcode CONST_4
|
||||||
|
val instruction = instructions.elementAt(6)
|
||||||
|
if (instruction.opcode != Opcode.CONST_4) continue
|
||||||
|
|
||||||
|
// the literal for this instruction has to be either 1 or 2
|
||||||
|
val literal = (instruction as Instruction11n).narrowLiteral
|
||||||
|
|
||||||
|
// method founds
|
||||||
|
if (literal == 1) tapSeekMethods["P"] = it
|
||||||
|
if (literal == 2) tapSeekMethods["O"] = it
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace map because we dont need the upper one anymore
|
||||||
|
result = signatures.last().result!!
|
||||||
|
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
// if tap-seeking is enabled, do not invoke the two methods below
|
||||||
|
val pMethod = tapSeekMethods["P"]!!
|
||||||
|
val oMethod = tapSeekMethods["O"]!!
|
||||||
|
|
||||||
|
// get the required register
|
||||||
|
val instruction = implementation.instructions[result.scanData.endIndex]
|
||||||
|
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return PatchResultError("Could not find the correct register")
|
||||||
|
val register = (instruction as Instruction35c).registerC
|
||||||
|
|
||||||
|
// the instructions are written in reverse order.
|
||||||
|
implementation.addInstructions(
|
||||||
|
result.scanData.endIndex + 1, """
|
||||||
|
invoke-virtual { v$register, v2 }, ${oMethod.definingClass}->${oMethod.name}(I)V
|
||||||
|
invoke-virtual { v$register, v2 }, ${pMethod.definingClass}->${pMethod.name}(I)V
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
|
||||||
|
// if tap-seeking is disabled, do not invoke the two methods above by jumping to the else label
|
||||||
|
val elseLabel = implementation.newLabelForIndex(result.scanData.endIndex + 1)
|
||||||
|
implementation.addInstruction(
|
||||||
|
result.scanData.endIndex + 1, BuilderInstruction21t(Opcode.IF_EQZ, 0, elseLabel)
|
||||||
|
)
|
||||||
|
implementation.addInstructions(
|
||||||
|
result.scanData.endIndex + 1, """
|
||||||
|
invoke-static { }, Lfi/razerman/youtube/preferences/BooleanPreferences;->isTapSeekingEnabled()Z
|
||||||
|
move-result v0
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package app.revanced.patches.youtube.interaction.seekbar.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("enable-seekbar-tapping-parent")
|
||||||
|
@MatchingMethod("Lzhj;", "J")
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object SeekbarTappingParentSignature : MethodSignature(
|
||||||
|
"L", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.NEW_ARRAY,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_WIDE,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.APUT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_WIDE,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.APUT_OBJECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.RETURN_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,41 @@
|
||||||
|
package app.revanced.patches.youtube.interaction.seekbar.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.interaction.seekbar.annotation.SeekbarTappingCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("enable-seekbar-tapping-signature")
|
||||||
|
@MatchingMethod("Lfao;", "onTouchEvent")
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@SeekbarTappingCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object SeekbarTappingSignature : MethodSignature(
|
||||||
|
"Z", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_WIDE,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.DIV_INT_2ADDR,
|
||||||
|
Opcode.ADD_INT,
|
||||||
|
Opcode.SUB_INT_2ADDR,
|
||||||
|
Opcode.INT_TO_FLOAT,
|
||||||
|
Opcode.CMPG_FLOAT,
|
||||||
|
Opcode.IF_GTZ,
|
||||||
|
Opcode.INT_TO_FLOAT,
|
||||||
|
Opcode.CMPG_FLOAT,
|
||||||
|
Opcode.IF_GTZ,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,102 +0,0 @@
|
||||||
package app.revanced.patches.youtube.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstruction
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class CreateButtonRemoverPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"create-button",
|
|
||||||
"Create button patch",
|
|
||||||
"Disable the create button.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"create-button-method",
|
|
||||||
MethodMetadata("Lkne", "z"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.INVOKE_DIRECT_RANGE,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!
|
|
||||||
|
|
||||||
// Get the required register which holds the view object we need to pass to the method hideCreateButton
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
val instruction = implementation.instructions[result.scanData.endIndex + 1]
|
|
||||||
if (instruction.opcode != Opcode.INVOKE_STATIC)
|
|
||||||
return PatchResultError("Could not find the correct register")
|
|
||||||
val register = (instruction as Instruction35c).registerC
|
|
||||||
|
|
||||||
// Hide the button view via proxy by passing it to the hideCreateButton method
|
|
||||||
implementation.addInstruction(
|
|
||||||
result.scanData.endIndex + 1,
|
|
||||||
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->hideCreateButton(Landroid/view/View;)V".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package app.revanced.patches.youtube.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstruction
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class HideReelsPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"hide-reels",
|
|
||||||
"Hide reels patch",
|
|
||||||
"Hide reels on the page.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"hide-reels-signature",
|
|
||||||
MethodMetadata("Ljvy", "<init>"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(3), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
listOf(
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"L",
|
|
||||||
"[B",
|
|
||||||
"[B",
|
|
||||||
"[B",
|
|
||||||
"[B",
|
|
||||||
"[B",
|
|
||||||
"[B"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
// HideReel will hide the reel view before it is being used,
|
|
||||||
// so we pass the view to the HideReel method
|
|
||||||
implementation.addInstruction(
|
|
||||||
result.scanData.endIndex,
|
|
||||||
"invoke-static { v2 }, Lfi/razerman/youtube/XAdRemover;->HideReel(Landroid/view/View;)V".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package app.revanced.patches.youtube.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class MinimizedPlaybackPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"minimized-playback",
|
|
||||||
"Minimized Playback Patch",
|
|
||||||
"Enable minimized and background playback.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"minimized-playback-manager",
|
|
||||||
MethodMetadata("Lype", "j"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the method required to be patched.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"Z",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
listOf("L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.AND_INT_LIT16,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.IF_NE,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.IF_EQZ
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
// Instead of removing all instructions like Vanced,
|
|
||||||
// we return the method at the beginning instead
|
|
||||||
signatures.first().result!!.method.implementation!!.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
const/4 v0, 0x1
|
|
||||||
return v0
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
package app.revanced.patches.youtube.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class OldQualityLayoutPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"old-quality-layout",
|
|
||||||
"Old Quality Layout Patch",
|
|
||||||
"Enable the original quality flyout menu",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"old-quality-parent-method-signature",
|
|
||||||
MethodMetadata("Libh", "<init>"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature to find a parent method required by the Old Quality Layout patch.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
|
||||||
listOf("L", "L", "L", "L", "L", "L", "L"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET_BOOLEAN,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
var result = signatures.first().result!!
|
|
||||||
|
|
||||||
result = result.findParentMethod(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"old-quality-method-signature",
|
|
||||||
MethodMetadata("Libh", null), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature to find the method required by the Old Quality Layout patch",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"L",
|
|
||||||
AccessFlags.FINAL or AccessFlags.PRIVATE,
|
|
||||||
listOf("Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) ?: return PatchResultError("Method old-quality-patch-method has not been found")
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
|
|
||||||
// if useOldStyleQualitySettings == true, jump over all instructions
|
|
||||||
val jmpInstruction =
|
|
||||||
BuilderInstruction21t(
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
0,
|
|
||||||
implementation.instructions[result.scanData.endIndex].location.labels.first()
|
|
||||||
)
|
|
||||||
implementation.addInstruction(5, jmpInstruction)
|
|
||||||
implementation.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { }, Lfi/razerman/youtube/XGlobals;->useOldStyleQualitySettings()Z
|
|
||||||
move-result v0
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
package app.revanced.patches.youtube.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstruction
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class ShortsButtonRemoverPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"shorts-button",
|
|
||||||
"Shorts button patch",
|
|
||||||
"Hide the shorts button.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"pivotbar-buttons-method-tabenum",
|
|
||||||
MethodMetadata("Lkne", "z"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the pivotbar method that creates all button views.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.IGET,
|
|
||||||
Opcode.INVOKE_STATIC, // SomeEnum.fromValue(tabOrdinal)
|
|
||||||
Opcode.MOVE_RESULT_OBJECT
|
|
||||||
)
|
|
||||||
),
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"pivotbar-buttons-method-view",
|
|
||||||
MethodMetadata("Lkne", "z"), // unknown
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Signature for the pivotbar method that creates all button views.",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.NEW_INSTANCE, // new StateListDrawable()
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.NEW_ARRAY,
|
|
||||||
Opcode.CONST,
|
|
||||||
Opcode.CONST_16,
|
|
||||||
Opcode.APUT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.MOVE,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL_RANGE, // pivotBar.getView(drawable, tabName, z, i, map, akebVar, optional)
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result1 = signatures.first().result!!
|
|
||||||
val implementation1 = result1.method.implementation!!
|
|
||||||
val moveEnumInstruction = implementation1.instructions[result1.scanData.endIndex]
|
|
||||||
val enumRegister = (moveEnumInstruction as Instruction11x).registerA
|
|
||||||
|
|
||||||
val result2 = signatures.last().result!!
|
|
||||||
val implementation2 = result2.method.implementation!!
|
|
||||||
val moveViewInstruction = implementation2.instructions[result2.scanData.endIndex]
|
|
||||||
val viewRegister = (moveViewInstruction as Instruction11x).registerA
|
|
||||||
|
|
||||||
// Save the tab enum in XGlobals to avoid smali/register workarounds
|
|
||||||
implementation1.addInstruction(
|
|
||||||
result1.scanData.endIndex + 1,
|
|
||||||
"sput-object v$enumRegister, Lfi/razerman/youtube/XGlobals;->lastPivotTab:Ljava/lang/Enum;".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Hide the button view via proxy by passing it to the hideShortsButton method
|
|
||||||
// It only hides it if the last tab name is "TAB_SHORTS"
|
|
||||||
implementation2.addInstruction(
|
|
||||||
result2.scanData.endIndex + 2,
|
|
||||||
"invoke-static { v$viewRegister }, Lfi/razerman/youtube/XAdRemover;->hideShortsButton(Landroid/view/View;)V".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.layout.createbutton.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class CreateButtonCompatibility
|
|
@ -0,0 +1,46 @@
|
||||||
|
package app.revanced.patches.youtube.layout.createbutton.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import app.revanced.patches.youtube.layout.createbutton.signatures.CreateButtonSignature
|
||||||
|
import app.revanced.patches.youtube.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("disable-create-button")
|
||||||
|
@Description("Disable the create button.")
|
||||||
|
@MinimizedPlaybackCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class CreateButtonRemoverPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
CreateButtonSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!
|
||||||
|
|
||||||
|
// Get the required register which holds the view object we need to pass to the method hideCreateButton
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
val instruction = implementation.instructions[result.scanData.endIndex + 1]
|
||||||
|
if (instruction.opcode != Opcode.INVOKE_STATIC)
|
||||||
|
return PatchResultError("Could not find the correct register")
|
||||||
|
val register = (instruction as Instruction35c).registerC
|
||||||
|
|
||||||
|
// Hide the button view via proxy by passing it to the hideCreateButton method
|
||||||
|
implementation.addInstruction(
|
||||||
|
result.scanData.endIndex + 1,
|
||||||
|
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->hideCreateButton(Landroid/view/View;)V".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package app.revanced.patches.youtube.layout.createbutton.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.createbutton.annotations.CreateButtonCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("create-button-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lkne", "z"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@CreateButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CreateButtonSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.INVOKE_DIRECT_RANGE,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.layout.minimizedplayback.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class MinimizedPlaybackCompatibility
|
|
@ -0,0 +1,39 @@
|
||||||
|
package app.revanced.patches.youtube.layout.minimizedplayback.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.youtube.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
|
||||||
|
import app.revanced.patches.youtube.layout.minimizedplayback.signatures.MinimizedPlaybackManagerSignature
|
||||||
|
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("minimized-playback")
|
||||||
|
@Description("Enable minimized and background playback.")
|
||||||
|
@MinimizedPlaybackCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
|
||||||
|
class MinimizedPlaybackPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
MinimizedPlaybackManagerSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
// Instead of removing all instructions like Vanced,
|
||||||
|
// we return the method at the beginning instead
|
||||||
|
signatures.first().result!!.method.implementation!!.addInstructions(
|
||||||
|
0, """
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package app.revanced.patches.youtube.layout.minimizedplayback.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("minimized-playback-manager-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lype", "j"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@MinimizedPlaybackCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object MinimizedPlaybackManagerSignature : MethodSignature(
|
||||||
|
"Z",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
listOf("L"),
|
||||||
|
listOf(
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.AND_INT_LIT16,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.IF_EQZ
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.layout.oldqualitylayout.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class OldQualityLayoutCompatibility
|
|
@ -0,0 +1,66 @@
|
||||||
|
package app.revanced.patches.youtube.layout.oldqualitylayout.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.youtube.layout.oldqualitylayout.annotations.OldQualityLayoutCompatibility
|
||||||
|
import app.revanced.patches.youtube.layout.oldqualitylayout.signatures.OldQualityParentSignature
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("old-quality-layout")
|
||||||
|
@Description("Enable the original quality flyout menu.")
|
||||||
|
@OldQualityLayoutCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class OldQualityLayoutPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
OldQualityParentSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!.findParentMethod(@Name("old-quality-signature") @MatchingMethod(
|
||||||
|
definingClass = "Libh"
|
||||||
|
) @FuzzyPatternScanMethod(2) @OldQualityLayoutCompatibility @Version("0.0.1") object : MethodSignature(
|
||||||
|
"L", AccessFlags.FINAL or AccessFlags.PRIVATE, listOf("Z"), listOf(
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
)
|
||||||
|
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
||||||
|
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
// if useOldStyleQualitySettings == true, jump over all instructions
|
||||||
|
val jmpInstruction = BuilderInstruction21t(
|
||||||
|
Opcode.IF_NEZ, 0, implementation.instructions[result.scanData.endIndex].location.labels.first()
|
||||||
|
)
|
||||||
|
implementation.addInstruction(5, jmpInstruction)
|
||||||
|
implementation.addInstructions(
|
||||||
|
0, """
|
||||||
|
invoke-static { }, Lfi/razerman/youtube/XGlobals;->useOldStyleQualitySettings()Z
|
||||||
|
move-result v0
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package app.revanced.patches.youtube.layout.oldqualitylayout.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.oldqualitylayout.annotations.OldQualityLayoutCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("old-quality-parent-method-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Libh", "<init>"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@OldQualityLayoutCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object OldQualityParentSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
listOf("L", "L", "L", "L", "L", "L", "L"),
|
||||||
|
listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.layout.reels.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class HideReelsCompatibility
|
|
@ -0,0 +1,38 @@
|
||||||
|
package app.revanced.patches.youtube.layout.reels.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import app.revanced.patches.youtube.layout.reels.annotations.HideReelsCompatibility
|
||||||
|
import app.revanced.patches.youtube.layout.reels.signatures.HideReelsSignature
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("hide-reels")
|
||||||
|
@Description("Hide reels on the page.")
|
||||||
|
@HideReelsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class HideReelsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
HideReelsSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
|
||||||
|
// HideReel will hide the reel view before it is being used,
|
||||||
|
// so we pass the view to the HideReel method
|
||||||
|
implementation.addInstruction(
|
||||||
|
result.scanData.endIndex,
|
||||||
|
"invoke-static { v2 }, Lfi/razerman/youtube/XAdRemover;->HideReel(Landroid/view/View;)V".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package app.revanced.patches.youtube.layout.reels.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.reels.annotations.HideReelsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("hide-reels-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Ljvy", "<init>"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(3) // FIXME: Test this threshold and find the best value.
|
||||||
|
@HideReelsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object HideReelsSignature : MethodSignature(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf(
|
||||||
|
"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "[B", "[B", "[B", "[B", "[B", "[B"
|
||||||
|
), listOf(
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT_FROM16,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.layout.shorts.button.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class ShortsButtonCompatibility
|
|
@ -0,0 +1,53 @@
|
||||||
|
package app.revanced.patches.youtube.layout.shorts.button.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.signatures.PivotBarButtonTabenumSignature
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.signatures.PivotBarButtonsViewSignature
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("shorts-button")
|
||||||
|
@Description("Hide the shorts button.")
|
||||||
|
@ShortsButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class ShortsButtonRemoverPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
PivotBarButtonTabenumSignature, PivotBarButtonsViewSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result1 = signatures.first().result!!
|
||||||
|
val implementation1 = result1.method.implementation!!
|
||||||
|
val moveEnumInstruction = implementation1.instructions[result1.scanData.endIndex]
|
||||||
|
val enumRegister = (moveEnumInstruction as Instruction11x).registerA
|
||||||
|
|
||||||
|
val result2 = signatures.last().result!!
|
||||||
|
val implementation2 = result2.method.implementation!!
|
||||||
|
val moveViewInstruction = implementation2.instructions[result2.scanData.endIndex]
|
||||||
|
val viewRegister = (moveViewInstruction as Instruction11x).registerA
|
||||||
|
|
||||||
|
// Save the tab enum in XGlobals to avoid smali/register workarounds
|
||||||
|
implementation1.addInstruction(
|
||||||
|
result1.scanData.endIndex + 1,
|
||||||
|
"sput-object v$enumRegister, Lfi/razerman/youtube/XGlobals;->lastPivotTab:Ljava/lang/Enum;".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hide the button view via proxy by passing it to the hideShortsButton method
|
||||||
|
// It only hides it if the last tab name is "TAB_SHORTS"
|
||||||
|
implementation2.addInstruction(
|
||||||
|
result2.scanData.endIndex + 2,
|
||||||
|
"invoke-static { v$viewRegister }, Lfi/razerman/youtube/XAdRemover;->hideShortsButton(Landroid/view/View;)V".toInstruction()
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package app.revanced.patches.youtube.layout.shorts.button.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("pivotbar-buttons-tabenum-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lkne", "z"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@ShortsButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PivotBarButtonTabenumSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.INVOKE_STATIC, // SomeEnum.fromValue(tabOrdinal)
|
||||||
|
Opcode.MOVE_RESULT_OBJECT
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
package app.revanced.patches.youtube.layout.shorts.button.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.layout.shorts.button.annotations.ShortsButtonCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("pivotbar-buttons-view-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lkne", "z"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@ShortsButtonCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PivotBarButtonsViewSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.NEW_INSTANCE, // new StateListDrawable()
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.NEW_ARRAY,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST_16,
|
||||||
|
Opcode.APUT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL_RANGE, // pivotBar.getView(drawable, tabName, z, i, map, akebVar, optional)
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
)
|
||||||
|
)
|
|
@ -1,125 +0,0 @@
|
||||||
package app.revanced.patches.youtube.misc
|
|
||||||
|
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.MethodSignatureMetadata
|
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
|
||||||
import app.revanced.patcher.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
|
||||||
PackageMetadata(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
listOf("17.03.38", "17.14.35", "17.17.34")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class IntegrationsPatch : BytecodePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"integrations",
|
|
||||||
"Inject Integrations Patch",
|
|
||||||
"Applies mandatory patches to implement the ReVanced integrations into the application.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
listOf(
|
|
||||||
MethodSignature(
|
|
||||||
MethodSignatureMetadata(
|
|
||||||
"integrations-patch",
|
|
||||||
MethodMetadata("Lacnx", "onCreate"),
|
|
||||||
PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value.
|
|
||||||
compatiblePackages,
|
|
||||||
"Inject the integrations into the application with the method of this signature",
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC.value,
|
|
||||||
listOf(),
|
|
||||||
listOf(
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.MOVE_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
Opcode.INVOKE_INTERFACE_RANGE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.SPUT_OBJECT,
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.INVOKE_SUPER,
|
|
||||||
Opcode.INVOKE_VIRTUAL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
|
||||||
val result = signatures.first().result!!
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
val count = implementation.registerCount - 1
|
|
||||||
|
|
||||||
implementation.addInstructions(
|
|
||||||
result.scanData.endIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static {v$count}, Lpl/jakubweg/StringRef;->setContext(Landroid/content/Context;)V
|
|
||||||
sput-object v$count, Lapp/revanced/integrations/Globals;->context:Landroid/content/Context;
|
|
||||||
""".trimIndent().toInstructions()
|
|
||||||
)
|
|
||||||
|
|
||||||
val classDef = result.definingClassProxy.resolve()
|
|
||||||
classDef.methods.add(
|
|
||||||
ImmutableMethod(
|
|
||||||
classDef.type,
|
|
||||||
"getAppContext",
|
|
||||||
null,
|
|
||||||
"Landroid/content/Context;",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
1,
|
|
||||||
"""
|
|
||||||
invoke-static { }, Lapp/revanced/integrations/Globals;->getAppContext()Landroid/content/Context;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
""".trimIndent().toInstructions(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
).toMutable()
|
|
||||||
)
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.misc.integrations.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.03.38", "17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class IntegrationsCompatibility
|
|
@ -0,0 +1,65 @@
|
||||||
|
package app.revanced.patches.youtube.misc.integrations.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.implementation.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patcher.smali.toInstructions
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.annotations.IntegrationsCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.signatures.InitSignature
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("integrations")
|
||||||
|
@Description("Applies mandatory patches to implement the ReVanced integrations into the application.")
|
||||||
|
@IntegrationsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class IntegrationsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
InitSignature
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = signatures.first().result!!
|
||||||
|
|
||||||
|
val implementation = result.method.implementation!!
|
||||||
|
val count = implementation.registerCount - 1
|
||||||
|
|
||||||
|
implementation.addInstructions(
|
||||||
|
result.scanData.endIndex + 1, """
|
||||||
|
invoke-static {v$count}, Lpl/jakubweg/StringRef;->setContext(Landroid/content/Context;)V
|
||||||
|
sput-object v$count, Lapp/revanced/integrations/Globals;->context:Landroid/content/Context;
|
||||||
|
""".trimIndent().toInstructions()
|
||||||
|
)
|
||||||
|
|
||||||
|
val classDef = result.definingClassProxy.resolve()
|
||||||
|
classDef.methods.add(
|
||||||
|
ImmutableMethod(
|
||||||
|
classDef.type,
|
||||||
|
"getAppContext",
|
||||||
|
null,
|
||||||
|
"Landroid/content/Context;",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ImmutableMethodImplementation(
|
||||||
|
1, """
|
||||||
|
invoke-static { }, Lapp/revanced/integrations/Globals;->getAppContext()Landroid/content/Context;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
""".trimIndent().toInstructions(), null, null
|
||||||
|
)
|
||||||
|
).toMutable()
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package app.revanced.patches.youtube.misc.integrations.signatures
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.annotations.IntegrationsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("init-signature")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lacnx", "onCreate"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@IntegrationsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object InitSignature : MethodSignature(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC.value,
|
||||||
|
listOf(),
|
||||||
|
listOf(
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.CONST_STRING,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.CONST_STRING,
|
||||||
|
Opcode.INVOKE_INTERFACE_RANGE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.SPUT_OBJECT,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.INVOKE_SUPER,
|
||||||
|
Opcode.INVOKE_VIRTUAL
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.revanced.patches.youtube.misc.manifest.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.14.35", "17.17.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class FixLocaleConfigErrorCompatibility
|
|
@ -1,28 +1,22 @@
|
||||||
package app.revanced.patches.youtube.misc
|
package app.revanced.patches.youtube.misc.manifest.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.ResourceData
|
import app.revanced.patcher.data.implementation.ResourceData
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PackageMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.metadata.PatchMetadata
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.annotations.FixLocaleConfigErrorCompatibility
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
private val compatiblePackages = listOf(
|
@Patch
|
||||||
PackageMetadata(
|
@Name("locale-config-fix")
|
||||||
"com.google.android.youtube",
|
@Description("Fix an error when building the resources by patching the manifest file.")
|
||||||
listOf("17.14.35", "17.17.34")
|
@FixLocaleConfigErrorCompatibility
|
||||||
)
|
@Version("0.0.1")
|
||||||
)
|
class FixLocaleConfigErrorPatch : ResourcePatch() {
|
||||||
class FixLocaleConfigErrorPatch : ResourcePatch(
|
|
||||||
PatchMetadata(
|
|
||||||
"locale-config-fix",
|
|
||||||
"Manifest attribute fix patch",
|
|
||||||
"Fix an error when building the resources by patching the manifest file.",
|
|
||||||
compatiblePackages,
|
|
||||||
"0.0.1"
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
override fun execute(data: ResourceData): PatchResult {
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
// create an xml editor instance
|
// create an xml editor instance
|
||||||
val editor = data.getXmlEditor("AndroidManifest.xml")
|
val editor = data.getXmlEditor("AndroidManifest.xml")
|
||||||
|
@ -35,7 +29,7 @@ class FixLocaleConfigErrorPatch : ResourcePatch(
|
||||||
|
|
||||||
// by replacing the attributes name
|
// by replacing the attributes name
|
||||||
val attribute = "android:localeConfig"
|
val attribute = "android:localeConfig"
|
||||||
applicationNode.setAttribute("localeConfig", applicationNode.getAttribute(attribute))
|
applicationNode.setAttribute("localeConfig", applicationNode.getAttribute(attribute))
|
||||||
applicationNode.removeAttribute("android:localeConfig")
|
applicationNode.removeAttribute("android:localeConfig")
|
||||||
|
|
||||||
// close & save the modified file
|
// close & save the modified file
|
|
@ -1,17 +1,18 @@
|
||||||
package app.revanced.patches
|
package app.revanced.patches
|
||||||
|
|
||||||
import app.revanced.patcher.Patcher
|
import app.revanced.patcher.Patcher
|
||||||
import app.revanced.patcher.signature.MethodMetadata
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.PatternScanMethod
|
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.signature.implementation.method.annotation.PatternScanMethod
|
||||||
import org.jf.dexlib2.iface.Method
|
import org.jf.dexlib2.iface.Method
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class SignatureChecker {
|
internal class SignatureChecker {
|
||||||
@Test
|
@Test
|
||||||
fun checkSignatures() {
|
fun checkMethodSignatures() {
|
||||||
return
|
|
||||||
// FIXME: instead of having this as a test, it should be turned into a task which can be ran manually
|
// FIXME: instead of having this as a test, it should be turned into a task which can be ran manually
|
||||||
val file = File("stock.apk")
|
val file = File("stock.apk")
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
@ -19,24 +20,26 @@ internal class SignatureChecker {
|
||||||
}
|
}
|
||||||
val patcher = Patcher(file, "signatureCheckerCache", false)
|
val patcher = Patcher(file, "signatureCheckerCache", false)
|
||||||
patcher.addPatches(Index.patches.map { it() })
|
patcher.addPatches(Index.patches.map { it() })
|
||||||
val unresolved = mutableListOf<MethodSignature>()
|
val unresolved = mutableListOf<String>()
|
||||||
for (signature in patcher.resolveSignatures()) {
|
for (signature in patcher.resolveSignatures()) {
|
||||||
|
val signatureAnnotations = signature::class.annotations
|
||||||
|
|
||||||
|
val nameAnnotation = signatureAnnotations.find { it is Name } as Name
|
||||||
if (!signature.resolved) {
|
if (!signature.resolved) {
|
||||||
unresolved.add(signature)
|
unresolved.add(nameAnnotation.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val patternScanMethod = signature.metadata.patternScanMethod
|
val patternScanMethod =
|
||||||
if (patternScanMethod is PatternScanMethod.Fuzzy) {
|
signatureAnnotations.find { it::class.annotations.any { method -> method is PatternScanMethod } }
|
||||||
val warnings = patternScanMethod.warnings!!
|
if (patternScanMethod is FuzzyPatternScanMethod) {
|
||||||
|
val warnings = signature.result!!.scanData.warnings!!
|
||||||
val method = signature.result!!.method
|
val method = signature.result!!.method
|
||||||
val methodFromMetadata =
|
|
||||||
if (signature.metadata.methodMetadata != null) signature.metadata.methodMetadata!! else MethodMetadata(
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
println("Signature: ${signature.metadata.name}.\nMethod: ${methodFromMetadata.definingClass}->${methodFromMetadata.name} (Signature matches: ${method.definingClass}->${method.toStr()})\nWarnings: ${warnings.count()}")
|
val methodFromMetadata =
|
||||||
|
signatureAnnotations.find { it is MatchingMethod } as MatchingMethod? ?: MatchingMethod()
|
||||||
|
|
||||||
|
println("Signature: ${nameAnnotation}.\nMethod: ${methodFromMetadata.definingClass}->${methodFromMetadata.name} (Signature matches: ${method.definingClass}->${method.toStr()})\nWarnings: ${warnings.count()}")
|
||||||
for (warning in warnings) {
|
for (warning in warnings) {
|
||||||
println("${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})")
|
println("${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})")
|
||||||
}
|
}
|
||||||
|
@ -46,8 +49,8 @@ internal class SignatureChecker {
|
||||||
}
|
}
|
||||||
if (unresolved.isNotEmpty()) {
|
if (unresolved.isNotEmpty()) {
|
||||||
val base = Exception("${unresolved.size} signatures were not resolved.")
|
val base = Exception("${unresolved.size} signatures were not resolved.")
|
||||||
for (signature in unresolved) {
|
for (name in unresolved) {
|
||||||
base.addSuppressed(Exception("Signature ${signature.metadata.name} was not resolved!"))
|
base.addSuppressed(Exception("Signature $name was not resolved!"))
|
||||||
}
|
}
|
||||||
throw base
|
throw base
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue