mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2024-11-10 09:07:46 +01:00
Merge pull request #2 from revanced/dalvik-patches
feat: migrate to Dalvik patches
This commit is contained in:
commit
0f1a08ed84
28 changed files with 3698 additions and 297 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -111,4 +111,7 @@ gradle-app.setting
|
||||||
.gradletasknamecache
|
.gradletasknamecache
|
||||||
|
|
||||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
# gradle/wrapper/gradle-wrapper.properties
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
|
# Potentially copyrighted test APK
|
||||||
|
*.apk
|
|
@ -1,5 +1,5 @@
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.6.10"
|
kotlin("jvm") version "1.6.20"
|
||||||
java
|
java
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,9 @@ group = "app.revanced"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
maven {
|
maven {
|
||||||
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patcher") // note the "r"!
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") // note the "r"!
|
||||||
credentials {
|
credentials {
|
||||||
// DO NOT set these variables in the project's gradle.properties.
|
// DO NOT set these variables in the project's gradle.properties.
|
||||||
// Instead, you should set them in:
|
// Instead, you should set them in:
|
||||||
|
@ -22,14 +23,10 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.10")
|
implementation(kotlin("stdlib"))
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
|
||||||
implementation("org.ow2.asm:asm:9.2")
|
implementation("app.revanced:revanced-patcher:1.0.0-dev.9")
|
||||||
implementation("org.ow2.asm:asm-util:9.2")
|
|
||||||
implementation("org.ow2.asm:asm-tree:9.2")
|
|
||||||
implementation("org.ow2.asm:asm-commons:9.2")
|
|
||||||
|
|
||||||
implementation("app.revanced:revanced-patcher:1.+") // use latest version.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -37,15 +34,21 @@ java {
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isGitHubCI = System.getenv("GITHUB_ACTOR") != null
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
if (isGitHubCI) {
|
||||||
name = "GitHubPackages"
|
maven {
|
||||||
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patches") // note the "s"!
|
name = "GitHubPackages"
|
||||||
credentials {
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patches") // note the "s"!
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
credentials {
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
publications {
|
publications {
|
||||||
|
@ -53,4 +56,4 @@ publishing {
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/main/kotlin/app/revanced/extensions/Extensions.kt
Normal file
14
src/main/kotlin/app/revanced/extensions/Extensions.kt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.smali.toInstruction
|
||||||
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
|
|
||||||
|
internal fun MutableMethodImplementation.injectHideCall(
|
||||||
|
index: Int,
|
||||||
|
register: Int
|
||||||
|
) {
|
||||||
|
this.addInstruction(
|
||||||
|
index,
|
||||||
|
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->HideView(Landroid/view/View;)V".toInstruction()
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,12 +1,22 @@
|
||||||
package app.revanced.patches
|
package app.revanced.patches
|
||||||
|
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.data.base.Data
|
||||||
import app.revanced.patches.ad.VideoAds
|
import app.revanced.patcher.patch.base.Patch
|
||||||
import app.revanced.patches.interaction.EnableSeekbarTapping
|
import app.revanced.patches.music.audio.CodecsUnlockPatch
|
||||||
import app.revanced.patches.layout.*
|
import app.revanced.patches.music.audio.EnableAudioOnlyPatch
|
||||||
|
import app.revanced.patches.music.layout.RemoveTasteBuilderPatch
|
||||||
|
import app.revanced.patches.music.layout.RemoveUpgradeTabPatch
|
||||||
|
import app.revanced.patches.music.premium.BackgroundPlayPatch
|
||||||
|
import app.revanced.patches.youtube.ad.HomeAdsPatch
|
||||||
|
import app.revanced.patches.youtube.ad.HomePromoPatch
|
||||||
|
import app.revanced.patches.youtube.ad.VideoAdsPatch
|
||||||
|
import app.revanced.patches.youtube.interaction.EnableSeekbarTappingPatch
|
||||||
|
import app.revanced.patches.youtube.layout.*
|
||||||
|
import app.revanced.patches.youtube.misc.FixLocaleConfigErrorPatch
|
||||||
|
import app.revanced.patches.youtube.misc.IntegrationsPatch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index contains all the patches and should be imported when using this library.
|
* Index contains all the patches.
|
||||||
*/
|
*/
|
||||||
@Suppress("Unused")
|
@Suppress("Unused")
|
||||||
object Index {
|
object Index {
|
||||||
|
@ -14,13 +24,22 @@ object Index {
|
||||||
* Array of patches.
|
* Array of patches.
|
||||||
* New patches should be added to the array.
|
* New patches should be added to the array.
|
||||||
*/
|
*/
|
||||||
val patches: Array<() -> Patch> = arrayOf(
|
val patches: List<() -> Patch<Data>> = listOf(
|
||||||
::VideoAds,
|
::IntegrationsPatch,
|
||||||
::MinimizedPlayback,
|
::FixLocaleConfigErrorPatch,
|
||||||
::CreateButtonRemover,
|
::HomeAdsPatch,
|
||||||
::HideReels,
|
::VideoAdsPatch,
|
||||||
::HideSuggestions,
|
::HomePromoPatch,
|
||||||
::OldQualityLayout,
|
::MinimizedPlaybackPatch,
|
||||||
::EnableSeekbarTapping
|
::CreateButtonRemoverPatch,
|
||||||
|
::ShortsButtonRemoverPatch,
|
||||||
|
::HideReelsPatch,
|
||||||
|
::OldQualityLayoutPatch,
|
||||||
|
::EnableSeekbarTappingPatch,
|
||||||
|
::EnableAudioOnlyPatch,
|
||||||
|
::RemoveUpgradeTabPatch,
|
||||||
|
::RemoveTasteBuilderPatch,
|
||||||
|
::BackgroundPlayPatch,
|
||||||
|
::CodecsUnlockPatch
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package app.revanced.patches.ad
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.Signature
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.Type
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode
|
|
||||||
|
|
||||||
class VideoAds : Patch("VideoAds") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val showVideoAdsMethodData = cache.methods["show-video-ads"].findParentMethod(
|
|
||||||
Signature(
|
|
||||||
"method",
|
|
||||||
Type.VOID_TYPE,
|
|
||||||
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
|
|
||||||
arrayOf(Type.BOOLEAN_TYPE),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
) ?: return PatchResultError("Could not find required method to patch")
|
|
||||||
|
|
||||||
showVideoAdsMethodData.method.instructions.insertAt(
|
|
||||||
0,
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/vanced/libraries/youtube/whitelisting/Whitelist",
|
|
||||||
"shouldShowAds",
|
|
||||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package app.revanced.patches.interaction
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.*
|
|
||||||
|
|
||||||
class EnableSeekbarTapping : Patch("enable-seekbar-tapping") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val patchData = cache.methods["enable-seekbar-tapping"]
|
|
||||||
val methodOPatchData = cache.methods["enable-seekbar-tapping-method-o"]
|
|
||||||
val methodPPatchData = cache.methods["enable-seekbar-tapping-method-p"]
|
|
||||||
|
|
||||||
val elseLabel = LabelNode()
|
|
||||||
patchData.method.instructions.insertAt(
|
|
||||||
patchData.scanData.endIndex,
|
|
||||||
InsnNode(Opcodes.ACONST_NULL),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/razerman/youtube/preferences/BooleanPreferences",
|
|
||||||
"isTapSeekingEnabled",
|
|
||||||
"()Z"
|
|
||||||
),
|
|
||||||
JumpInsnNode(Opcodes.IFEQ, elseLabel),
|
|
||||||
VarInsnNode(Opcodes.ALOAD, 0),
|
|
||||||
VarInsnNode(Opcodes.ILOAD, 6),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKEVIRTUAL,
|
|
||||||
methodOPatchData.declaringClass.name,
|
|
||||||
methodOPatchData.method.name,
|
|
||||||
"(I)V"
|
|
||||||
),
|
|
||||||
VarInsnNode(Opcodes.ALOAD, 0),
|
|
||||||
VarInsnNode(Opcodes.ILOAD, 6),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKEVIRTUAL,
|
|
||||||
methodPPatchData.declaringClass.name,
|
|
||||||
methodPPatchData.method.name,
|
|
||||||
"(I)V"
|
|
||||||
),
|
|
||||||
elseLabel
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package app.revanced.patches.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode
|
|
||||||
import org.objectweb.asm.tree.VarInsnNode
|
|
||||||
|
|
||||||
class CreateButtonRemover : Patch("create-button-remover") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val patchData = cache.methods["create-button-patch"]
|
|
||||||
|
|
||||||
patchData.method.instructions.insertAt(
|
|
||||||
patchData.scanData.endIndex - 1,
|
|
||||||
VarInsnNode(
|
|
||||||
Opcodes.ALOAD,
|
|
||||||
6
|
|
||||||
),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/razerman/youtube/XAdRemover",
|
|
||||||
"hideCreateButton",
|
|
||||||
"(Landroid/view/View;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package app.revanced.patches.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode
|
|
||||||
import org.objectweb.asm.tree.VarInsnNode
|
|
||||||
|
|
||||||
class HideReels : Patch("hide-reels") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val patchData = cache.methods["hide-reel-patch"]
|
|
||||||
|
|
||||||
patchData.method.instructions.insertAt(
|
|
||||||
patchData.scanData.endIndex + 1,
|
|
||||||
VarInsnNode(Opcodes.ALOAD, 18),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/razerman/youtube/XAdRemover",
|
|
||||||
"HideReels",
|
|
||||||
"(Landroid/view/View;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package app.revanced.patches.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.Signature
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.Type
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode
|
|
||||||
import org.objectweb.asm.tree.VarInsnNode
|
|
||||||
|
|
||||||
class HideSuggestions : Patch("hide-suggestions") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val method = cache.methods["hide-suggestions-patch"].findParentMethod(
|
|
||||||
Signature(
|
|
||||||
"hide-suggestions-method",
|
|
||||||
Type.VOID_TYPE,
|
|
||||||
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
|
|
||||||
arrayOf(Type.BOOLEAN_TYPE),
|
|
||||||
arrayOf(
|
|
||||||
Opcodes.ALOAD,
|
|
||||||
Opcodes.ILOAD,
|
|
||||||
Opcodes.PUTFIELD,
|
|
||||||
Opcodes.ALOAD,
|
|
||||||
Opcodes.GETFIELD
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) ?: return PatchResultError("Parent method hide-suggestions-method has not been found")
|
|
||||||
|
|
||||||
method.method.instructions.insertAt(
|
|
||||||
0,
|
|
||||||
VarInsnNode(Opcodes.ILOAD, 1),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"java/lang/Boolean",
|
|
||||||
"valueOf",
|
|
||||||
"(Z)Ljava/lang/Boolean"
|
|
||||||
),
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/razerman/youtube/XAdRemover",
|
|
||||||
"HideReels",
|
|
||||||
"(Landroid/view/View;)V"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package app.revanced.patches.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
|
|
||||||
class MinimizedPlayback : Patch("minimized-playback") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
cache.methods["minimized-playback-manager"].method.instructions.clear()
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package app.revanced.patches.layout
|
|
||||||
|
|
||||||
import app.revanced.patcher.cache.Cache
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.Signature
|
|
||||||
import app.revanced.patcher.util.ExtraTypes
|
|
||||||
import app.revanced.patcher.writer.ASMWriter.insertAt
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.JumpInsnNode
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode
|
|
||||||
import org.objectweb.asm.tree.VarInsnNode
|
|
||||||
|
|
||||||
class OldQualityLayout : Patch("old-quality-restore") {
|
|
||||||
override fun execute(cache: Cache): PatchResult {
|
|
||||||
val method = cache.methods["old-quality-patch"].findParentMethod(
|
|
||||||
Signature(
|
|
||||||
"old-quality-patch-method",
|
|
||||||
ExtraTypes.Any,
|
|
||||||
Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL,
|
|
||||||
arrayOf(),
|
|
||||||
arrayOf(
|
|
||||||
Opcodes.ALOAD,
|
|
||||||
Opcodes.GETFIELD,
|
|
||||||
Opcodes.ISTORE,
|
|
||||||
Opcodes.ICONST_3,
|
|
||||||
Opcodes.ISTORE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) ?: return PatchResultError("Parent method old-quality-patch-method has not been found")
|
|
||||||
|
|
||||||
method.method.instructions.insertAt(
|
|
||||||
0,
|
|
||||||
MethodInsnNode(
|
|
||||||
Opcodes.INVOKESTATIC,
|
|
||||||
"fi/razerman/youtube/XGlobals",
|
|
||||||
"useOldStyleQualitySettings",
|
|
||||||
"()Z"
|
|
||||||
),
|
|
||||||
VarInsnNode(Opcodes.ISTORE, 1),
|
|
||||||
VarInsnNode(Opcodes.ILOAD, 1),
|
|
||||||
JumpInsnNode(
|
|
||||||
Opcodes.IFNE,
|
|
||||||
(method.method.instructions[method.scanData.endIndex + 3] as JumpInsnNode).label
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
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,94 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
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,92 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
1706
src/main/kotlin/app/revanced/patches/youtube/ad/HomeAdsPatch.kt
Normal file
1706
src/main/kotlin/app/revanced/patches/youtube/ad/HomeAdsPatch.kt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,188 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(
|
||||||
|
"Ljjl;",
|
||||||
|
"lG",
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
"Ljjc;",
|
||||||
|
"lG",
|
||||||
|
),
|
||||||
|
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-parent-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()
|
||||||
|
}
|
||||||
|
}
|
112
src/main/kotlin/app/revanced/patches/youtube/ad/VideoAdsPatch.kt
Normal file
112
src/main/kotlin/app/revanced/patches/youtube/ad/VideoAdsPatch.kt
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(
|
||||||
|
"zai",
|
||||||
|
"<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,196 @@
|
||||||
|
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.14.35")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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("Lesa;", "<init>"), // 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("Lesa;", "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.INT_TO_FLOAT,
|
||||||
|
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.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.FLOAT_TO_INT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.FLOAT_TO_INT,
|
||||||
|
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,102 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class CreateButtonRemoverPatch : BytecodePatch(
|
||||||
|
PatchMetadata(
|
||||||
|
"create-button",
|
||||||
|
"Create button patch",
|
||||||
|
"Disable the create button.",
|
||||||
|
compatiblePackages,
|
||||||
|
"0.0.1"
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
MethodSignature(
|
||||||
|
MethodSignatureMetadata(
|
||||||
|
"create-button-method",
|
||||||
|
MethodMetadata(null, null), // 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
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.14.35")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(null, null), // 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",
|
||||||
|
"[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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(null, null), // 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
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.14.35")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(null, null), // 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", "[B"),
|
||||||
|
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(null, 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(null, null), // 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(null, null), // 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,46 @@
|
||||||
|
package app.revanced.patches.youtube.misc
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.implementation.ResourceData
|
||||||
|
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.PatchResultSuccess
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
private val compatiblePackages = listOf(
|
||||||
|
PackageMetadata(
|
||||||
|
"com.google.android.youtube",
|
||||||
|
listOf("17.14.35")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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 {
|
||||||
|
// create an xml editor instance
|
||||||
|
val editor = data.getXmlEditor("AndroidManifest.xml")
|
||||||
|
|
||||||
|
// edit the application nodes attribute...
|
||||||
|
val applicationNode = editor
|
||||||
|
.file
|
||||||
|
.getElementsByTagName("application")
|
||||||
|
.item(0) as Element
|
||||||
|
|
||||||
|
// by replacing the attributes name
|
||||||
|
val attribute = "android:localeConfig"
|
||||||
|
applicationNode.setAttribute("localeConfig", applicationNode.getAttribute(attribute))
|
||||||
|
applicationNode.removeAttribute("android:localeConfig")
|
||||||
|
|
||||||
|
// close & save the modified file
|
||||||
|
editor.close()
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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(null, null), // unknown
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
59
src/test/kotlin/app/revanced/patches/SignatureChecker.kt
Normal file
59
src/test/kotlin/app/revanced/patches/SignatureChecker.kt
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package app.revanced.patches
|
||||||
|
|
||||||
|
import app.revanced.patcher.Patcher
|
||||||
|
import app.revanced.patcher.signature.MethodMetadata
|
||||||
|
import app.revanced.patcher.signature.MethodSignature
|
||||||
|
import app.revanced.patcher.signature.PatternScanMethod
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal class SignatureChecker {
|
||||||
|
@Test
|
||||||
|
fun checkSignatures() {
|
||||||
|
return
|
||||||
|
// 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")
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw IllegalStateException("Missing $file! To run this test, please place stock.apk here: ${file.absolutePath}")
|
||||||
|
}
|
||||||
|
val patcher = Patcher(file, "signatureCheckerCache", false)
|
||||||
|
patcher.addPatches(Index.patches.map { it() })
|
||||||
|
val unresolved = mutableListOf<MethodSignature>()
|
||||||
|
for (signature in patcher.resolveSignatures()) {
|
||||||
|
if (!signature.resolved) {
|
||||||
|
unresolved.add(signature)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val patternScanMethod = signature.metadata.patternScanMethod
|
||||||
|
if (patternScanMethod is PatternScanMethod.Fuzzy) {
|
||||||
|
val warnings = patternScanMethod.warnings!!
|
||||||
|
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()}")
|
||||||
|
for (warning in warnings) {
|
||||||
|
println("${warning.instructionIndex} / ${warning.patternIndex}: ${warning.wrongOpcode} (expected: ${warning.correctOpcode})")
|
||||||
|
}
|
||||||
|
|
||||||
|
println("=".repeat(20))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unresolved.isNotEmpty()) {
|
||||||
|
val base = Exception("${unresolved.size} signatures were not resolved.")
|
||||||
|
for (signature in unresolved) {
|
||||||
|
base.addSuppressed(Exception("Signature ${signature.metadata.name} was not resolved!"))
|
||||||
|
}
|
||||||
|
throw base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Method.toStr(): String {
|
||||||
|
return "${this.name}(${this.parameterTypes.joinToString("")})${this.returnType}"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue