mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-29 21:30:19 +02:00
build(revanced-patcher): bump version
This commit is contained in:
parent
8958c3870f
commit
0f5350e691
@ -20,7 +20,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("app.revanced:revanced-patcher:7.0.0")
|
implementation("app.revanced:revanced-patcher:7.1.1")
|
||||||
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
||||||
// Required for meta
|
// Required for meta
|
||||||
implementation("com.google.code.gson:gson:2.10.1")
|
implementation("com.google.code.gson:gson:2.10.1")
|
||||||
|
@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Description
|
|||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.*
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
@ -25,23 +25,19 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class DisableAutoCaptionsPatch : BytecodePatch(
|
class DisableAutoCaptionsPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(SubtitleTrackFingerprint)
|
||||||
SubtitleTrackFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
SubtitleTrackFingerprint.result?.mutableMethod?.let {
|
SubtitleTrackFingerprint.result?.mutableMethod?.let {
|
||||||
with (it.implementation!!.instructions) {
|
val index = it.implementation!!.instructions.size - 1
|
||||||
val index = size - 1
|
val register = it.instruction<OneRegisterInstruction>(index).registerA
|
||||||
val register = (elementAt(index) as OneRegisterInstruction).registerA
|
it.addInstructions(
|
||||||
it.addInstructions(
|
index, """
|
||||||
index, """
|
|
||||||
invoke-static {v$register}, $MUSIC_LAYOUT->disableAutoCaptions(Z)Z
|
invoke-static {v$register}, $MUSIC_LAYOUT->disableAutoCaptions(Z)Z
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} ?: return SubtitleTrackFingerprint.toErrorResult()
|
} ?: return SubtitleTrackFingerprint.toErrorResult()
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_disable_auto_captions", "false")
|
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_disable_auto_captions", "false")
|
||||||
|
@ -2,8 +2,8 @@ package app.revanced.patches.music.layout.blacknavbar.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.colorGreyId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -16,11 +16,6 @@ object TabLayoutFingerprint : MethodFingerprint(
|
|||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT
|
Opcode.MOVE_RESULT
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(colorGreyId) }
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.colorGreyLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatc
|
|||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import app.revanced.util.enum.CategoryType
|
import app.revanced.util.enum.CategoryType
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("enable-black-navbar")
|
@Name("enable-black-navbar")
|
||||||
@ -32,21 +32,17 @@ import org.jf.dexlib2.iface.instruction.formats.Instruction11x
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class BlackNavbarPatch : BytecodePatch(
|
class BlackNavbarPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(TabLayoutFingerprint)
|
||||||
TabLayoutFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
TabLayoutFingerprint.result?.let {
|
TabLayoutFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
val insertIndex = endIndex + 1
|
val targetRegister = instruction<OneRegisterInstruction>(targetIndex).registerA
|
||||||
|
|
||||||
val targetRegister = (instruction(endIndex) as Instruction11x).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex, """
|
targetIndex + 1, """
|
||||||
invoke-static {}, $MUSIC_LAYOUT->enableBlackNavbar()I
|
invoke-static {}, $MUSIC_LAYOUT->enableBlackNavbar()I
|
||||||
move-result v$targetRegister
|
move-result v$targetRegister
|
||||||
"""
|
"""
|
||||||
|
@ -25,9 +25,7 @@ import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class HideCastButtonPatch : BytecodePatch(
|
class HideCastButtonPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(HideCastButtonParentFingerprint)
|
||||||
HideCastButtonParentFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patches.music.layout.categorybar.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.chipCloudId
|
||||||
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object ChipCloudFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT
|
||||||
|
),
|
||||||
|
customFingerprint = { it.isWideLiteralExists(chipCloudId) }
|
||||||
|
)
|
||||||
|
|
@ -1,82 +1,54 @@
|
|||||||
package app.revanced.patches.music.layout.categorybar.patch
|
package app.revanced.patches.music.layout.categorybar.patch
|
||||||
|
|
||||||
import app.revanced.extensions.findMutableMethodOf
|
import app.revanced.extensions.toErrorResult
|
||||||
import app.revanced.extensions.toResult
|
|
||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.addInstruction
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patches.music.layout.categorybar.fingerprints.ChipCloudFingerprint
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
||||||
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import app.revanced.patches.shared.patch.mapping.ResourceMappingPatch
|
|
||||||
import app.revanced.util.enum.CategoryType
|
import app.revanced.util.enum.CategoryType
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("hide-category-bar")
|
@Name("hide-category-bar")
|
||||||
@Description("Hides the music category bar at the top of the homepage.")
|
@Description("Hides the music category bar at the top of the homepage.")
|
||||||
@DependsOn(
|
@DependsOn(
|
||||||
[
|
[
|
||||||
ResourceMappingPatch::class,
|
SharedResourceIdPatch::class,
|
||||||
MusicSettingsPatch::class
|
MusicSettingsPatch::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class CategoryBarPatch : BytecodePatch() {
|
class CategoryBarPatch : BytecodePatch(
|
||||||
|
listOf(ChipCloudFingerprint)
|
||||||
// list of resource names to get the id of
|
) {
|
||||||
private val resourceIds = arrayOf(
|
|
||||||
"layout" to "chip_cloud"
|
|
||||||
).map { (type, name) ->
|
|
||||||
ResourceMappingPatch
|
|
||||||
.resourceMappings
|
|
||||||
.single { it.type == type && it.name == name }.id
|
|
||||||
}
|
|
||||||
private var patchSuccessArray = Array(resourceIds.size) {false}
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
context.classes.forEach { classDef ->
|
ChipCloudFingerprint.result?.let {
|
||||||
classDef.methods.forEach { method ->
|
it.mutableMethod.apply {
|
||||||
with(method.implementation) {
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
this?.instructions?.forEachIndexed { index, instruction ->
|
val targetRegister = instruction<OneRegisterInstruction>(targetIndex).registerA
|
||||||
when (instruction.opcode) {
|
|
||||||
Opcode.CONST -> {
|
|
||||||
when ((instruction as Instruction31i).wideLiteral) {
|
|
||||||
resourceIds[0] -> { // compact header
|
|
||||||
val insertIndex = index + 4
|
|
||||||
val invokeInstruction = instructions.elementAt(insertIndex)
|
|
||||||
if (invokeInstruction.opcode != Opcode.CHECK_CAST) return@forEachIndexed
|
|
||||||
|
|
||||||
val mutableMethod = context.proxy(classDef).mutableClass.findMutableMethodOf(method)
|
addInstruction(
|
||||||
|
targetIndex + 1,
|
||||||
val register = (invokeInstruction as Instruction21c).registerA
|
"invoke-static { v$targetRegister }, $MUSIC_LAYOUT->hideCategoryBar(Landroid/view/View;)V"
|
||||||
|
)
|
||||||
mutableMethod.addInstruction(
|
|
||||||
insertIndex,
|
|
||||||
"invoke-static { v$register }, $MUSIC_LAYOUT->hideCategoryBar(Landroid/view/View;)V"
|
|
||||||
)
|
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_hide_category_bar", "true")
|
|
||||||
|
|
||||||
patchSuccessArray[0] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> return@forEachIndexed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} ?: return ChipCloudFingerprint.toErrorResult()
|
||||||
return toResult(patchSuccessArray.indexOf(false))
|
|
||||||
|
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_hide_category_bar", "true")
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package app.revanced.patches.music.layout.miniplayercolor.fingerprints
|
package app.revanced.patches.music.layout.colormatchplayer.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
object MiniplayerColorFingerprint : MethodFingerprint(
|
object ColorMatchPlayerFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf("L", "J"),
|
parameters = listOf("L", "J"),
|
@ -0,0 +1,86 @@
|
|||||||
|
package app.revanced.patches.music.layout.colormatchplayer.patch
|
||||||
|
|
||||||
|
import app.revanced.extensions.toErrorResult
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
|
import app.revanced.patcher.extensions.removeInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import app.revanced.patches.music.layout.colormatchplayer.fingerprints.ColorMatchPlayerFingerprint
|
||||||
|
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
||||||
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
|
import app.revanced.patches.shared.fingerprints.ColorMatchPlayerParentFingerprint
|
||||||
|
import app.revanced.util.enum.CategoryType
|
||||||
|
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("enable-color-match-player")
|
||||||
|
@Description("Matches the fullscreen player color with the minimized one.")
|
||||||
|
@DependsOn([MusicSettingsPatch::class])
|
||||||
|
@YouTubeMusicCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class ColorMatchPlayerPatch : BytecodePatch(
|
||||||
|
listOf(ColorMatchPlayerParentFingerprint)
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
ColorMatchPlayerParentFingerprint.result?.let { parentResult ->
|
||||||
|
ColorMatchPlayerFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
targetMethod = parentResult.mutableMethod
|
||||||
|
|
||||||
|
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||||
|
val jumpInstruction = instruction<Instruction>(insertIndex)
|
||||||
|
|
||||||
|
val type = it.classDef.type
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
insertIndex, """
|
||||||
|
invoke-static {}, $MUSIC_LAYOUT->enableColorMatchPlayer()Z
|
||||||
|
move-result v2
|
||||||
|
if-eqz v2, :off
|
||||||
|
iget v0, p0, ${descriptor(4)}
|
||||||
|
if-eq v0, v2, :abswitch
|
||||||
|
iput v2, p0, ${descriptor(4)}
|
||||||
|
iget-object v0, p0, ${descriptor(5)}
|
||||||
|
invoke-virtual {v0, v2, p2, p3}, ${descriptor(6)}
|
||||||
|
:abswitch
|
||||||
|
iget v0, p0, ${descriptor(10)}
|
||||||
|
if-eq v0, v1, :exit
|
||||||
|
iput v1, p0, ${descriptor(10)}
|
||||||
|
iget-object v0, p0, ${descriptor(11)}
|
||||||
|
invoke-virtual {v0, v1, p2, p3}, ${descriptor(12)}
|
||||||
|
goto :exit
|
||||||
|
:off
|
||||||
|
invoke-direct {p0}, ${type}->${parentResult.mutableMethod.name}()V
|
||||||
|
""", listOf(ExternalLabel("exit", jumpInstruction))
|
||||||
|
)
|
||||||
|
removeInstruction(insertIndex - 1)
|
||||||
|
}
|
||||||
|
} ?: return ColorMatchPlayerFingerprint.toErrorResult()
|
||||||
|
} ?: return ColorMatchPlayerParentFingerprint.toErrorResult()
|
||||||
|
|
||||||
|
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_color_match_player", "true")
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
private companion object {
|
||||||
|
private lateinit var targetMethod: MutableMethod
|
||||||
|
|
||||||
|
fun descriptor(index: Int):String {
|
||||||
|
return (targetMethod.instruction(index) as ReferenceInstruction).reference.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,8 @@ package app.revanced.patches.music.layout.compactdialog.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.dialogSolidId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -16,11 +16,6 @@ object DialogSolidFingerprint : MethodFingerprint(
|
|||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.INVOKE_STATIC
|
Opcode.INVOKE_STATIC
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(dialogSolidId) }
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.dialogSolidLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
|||||||
import app.revanced.util.enum.CategoryType
|
import app.revanced.util.enum.CategoryType
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
||||||
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("enable-compact-dialog")
|
@Name("enable-compact-dialog")
|
||||||
@Description("Enable compact dialog on phone.")
|
@Description("Enable compact dialog on phone.")
|
||||||
@ -32,9 +33,7 @@ import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class CompactDialogPatch : BytecodePatch(
|
class CompactDialogPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(DialogSolidFingerprint)
|
||||||
DialogSolidFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
DialogSolidFingerprint.result?.let {
|
DialogSolidFingerprint.result?.let {
|
||||||
|
@ -2,19 +2,15 @@ package app.revanced.patches.music.layout.floatingbutton.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.util.bytecode.isNarrowLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
|
|
||||||
|
|
||||||
object FloatingButtonParentFingerprint : MethodFingerprint(
|
object FloatingButtonParentFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
access = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
access = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
||||||
opcodes = listOf(Opcode.INVOKE_DIRECT),
|
opcodes = listOf(Opcode.INVOKE_DIRECT),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isNarrowLiteralExists(259982244) }
|
||||||
methodDef.implementation!!.instructions.any {
|
|
||||||
((it as? NarrowLiteralInstruction)?.narrowLiteral == 259982244)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,9 +33,7 @@ import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class NewPlaylistButtonPatch : BytecodePatch(
|
class NewPlaylistButtonPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(FloatingButtonParentFingerprint)
|
||||||
FloatingButtonParentFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package app.revanced.patches.music.layout.landscapemode.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.isTabletId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -16,11 +16,6 @@ object TabletIdentifierFingerprint : MethodFingerprint(
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.MOVE_RESULT
|
Opcode.MOVE_RESULT
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(isTabletId) }
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.isTabletLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,18 +30,16 @@ import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class LandScapeModePatch : BytecodePatch(
|
class LandScapeModePatch : BytecodePatch(
|
||||||
listOf(
|
listOf(TabletIdentifierFingerprint)
|
||||||
TabletIdentifierFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
TabletIdentifierFingerprint.result?.let {
|
TabletIdentifierFingerprint.result?.let {
|
||||||
it.mutableMethod.addInstructions(
|
it.mutableMethod.addInstructions(
|
||||||
it.scanResult.patternScanResult!!.endIndex + 1, """
|
it.scanResult.patternScanResult!!.endIndex + 1, """
|
||||||
invoke-static {p0}, $MUSIC_LAYOUT->enableLandScapeMode(Z)Z
|
invoke-static {p0}, $MUSIC_LAYOUT->enableLandScapeMode(Z)Z
|
||||||
move-result p0
|
move-result p0
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
} ?: return TabletIdentifierFingerprint.toErrorResult()
|
} ?: return TabletIdentifierFingerprint.toErrorResult()
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_landscape_mode", "true")
|
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_landscape_mode", "true")
|
||||||
|
@ -25,9 +25,7 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class MinimizedPlayerPatch : BytecodePatch(
|
class MinimizedPlayerPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(MinimizedPlayerFingerprint)
|
||||||
MinimizedPlayerFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
package app.revanced.patches.music.layout.miniplayercolor.patch
|
|
||||||
|
|
||||||
import app.revanced.extensions.toErrorResult
|
|
||||||
import app.revanced.patcher.annotation.Description
|
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.removeInstruction
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
import app.revanced.patches.music.layout.miniplayercolor.fingerprints.MiniplayerColorFingerprint
|
|
||||||
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
|
||||||
import app.revanced.patches.shared.fingerprints.MiniplayerColorParentFingerprint
|
|
||||||
import app.revanced.util.enum.CategoryType
|
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
@Name("enable-color-match-player")
|
|
||||||
@Description("Matches the fullscreen player color with the minimized one.")
|
|
||||||
@DependsOn([MusicSettingsPatch::class])
|
|
||||||
@YouTubeMusicCompatibility
|
|
||||||
@Version("0.0.1")
|
|
||||||
class MiniplayerColorPatch : BytecodePatch(
|
|
||||||
listOf(
|
|
||||||
MiniplayerColorFingerprint, MiniplayerColorParentFingerprint
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
|
||||||
|
|
||||||
MiniplayerColorParentFingerprint.result?.let { parentResult ->
|
|
||||||
|
|
||||||
with (parentResult.mutableMethod.implementation!!.instructions) {
|
|
||||||
firstReference =
|
|
||||||
(elementAt(4) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
secondReference =
|
|
||||||
(elementAt(5) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
thirdReference =
|
|
||||||
(elementAt(6) as ReferenceInstruction).reference as MethodReference
|
|
||||||
|
|
||||||
fourthReference =
|
|
||||||
(elementAt(10) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
fifthReference =
|
|
||||||
(elementAt(11) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
sixthReference =
|
|
||||||
(elementAt(12) as ReferenceInstruction).reference as MethodReference
|
|
||||||
}
|
|
||||||
|
|
||||||
MiniplayerColorFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
|
||||||
with (it.mutableMethod) {
|
|
||||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
|
||||||
val jumpInstruction = implementation!!.instructions[insertIndex] as Instruction
|
|
||||||
|
|
||||||
val type = it.classDef.type
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
insertIndex, """
|
|
||||||
invoke-static {}, $MUSIC_LAYOUT->enableColorMatchPlayer()Z
|
|
||||||
move-result v2
|
|
||||||
if-eqz v2, :off
|
|
||||||
iget v0, p0, ${type}->${firstReference.name}:${firstReference.type}
|
|
||||||
if-eq v0, v2, :abswitch
|
|
||||||
iput v2, p0, ${type}->${firstReference.name}:${firstReference.type}
|
|
||||||
iget-object v0, p0, ${type}->${secondReference.name}:${secondReference.type}
|
|
||||||
invoke-virtual {v0, v2, p2, p3}, $thirdReference
|
|
||||||
:abswitch
|
|
||||||
iget v0, p0, ${type}->${fourthReference.name}:${fourthReference.type}
|
|
||||||
if-eq v0, v1, :exit
|
|
||||||
iput v1, p0, ${type}->${fourthReference.name}:${fourthReference.type}
|
|
||||||
iget-object v0, p0, ${type}->${fifthReference.name}:${fifthReference.type}
|
|
||||||
invoke-virtual {v0, v1, p2, p3}, $sixthReference
|
|
||||||
goto :exit
|
|
||||||
:off
|
|
||||||
invoke-direct {p0}, ${type}->${parentResult.mutableMethod.name}()V
|
|
||||||
""", listOf(ExternalLabel("exit", jumpInstruction))
|
|
||||||
)
|
|
||||||
removeInstruction(insertIndex - 1)
|
|
||||||
}
|
|
||||||
} ?: return MiniplayerColorFingerprint.toErrorResult()
|
|
||||||
} ?: return MiniplayerColorParentFingerprint.toErrorResult()
|
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_color_match_player", "true")
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
private companion object {
|
|
||||||
private lateinit var firstReference: FieldReference
|
|
||||||
private lateinit var secondReference: FieldReference
|
|
||||||
private lateinit var thirdReference: MethodReference
|
|
||||||
|
|
||||||
private lateinit var fourthReference: FieldReference
|
|
||||||
private lateinit var fifthReference: FieldReference
|
|
||||||
private lateinit var sixthReference: MethodReference
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,9 @@ import app.revanced.patcher.annotation.Description
|
|||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.*
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
|
import app.revanced.patcher.extensions.removeInstruction
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
@ -15,12 +17,11 @@ import app.revanced.patcher.patch.annotations.Patch
|
|||||||
import app.revanced.patches.music.layout.zenmode.fingerprints.ZenModeFingerprint
|
import app.revanced.patches.music.layout.zenmode.fingerprints.ZenModeFingerprint
|
||||||
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
import app.revanced.patches.music.misc.settings.resource.patch.MusicSettingsPatch
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import app.revanced.patches.shared.fingerprints.MiniplayerColorParentFingerprint
|
import app.revanced.patches.shared.fingerprints.ColorMatchPlayerParentFingerprint
|
||||||
import app.revanced.util.enum.CategoryType
|
import app.revanced.util.enum.CategoryType
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
import app.revanced.util.integrations.Constants.MUSIC_LAYOUT
|
||||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("enable-zen-mode")
|
@Name("enable-zen-mode")
|
||||||
@ -29,24 +30,21 @@ import org.jf.dexlib2.iface.reference.FieldReference
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class ZenModePatch : BytecodePatch(
|
class ZenModePatch : BytecodePatch(
|
||||||
listOf(
|
listOf(ColorMatchPlayerParentFingerprint)
|
||||||
ZenModeFingerprint, MiniplayerColorParentFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
MiniplayerColorParentFingerprint.result?.let { parentResult ->
|
ColorMatchPlayerParentFingerprint.result?.let { parentResult ->
|
||||||
ZenModeFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
ZenModeFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val instructions = this.implementation!!.instructions
|
|
||||||
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
val firstRegister = (instruction(startIndex) as OneRegisterInstruction).registerA
|
val firstRegister = instruction<OneRegisterInstruction>(startIndex).registerA
|
||||||
val secondRegister = (instruction(startIndex + 2) as OneRegisterInstruction).registerA
|
val secondRegister = instruction<OneRegisterInstruction>(startIndex + 2).registerA
|
||||||
val dummyRegister = secondRegister + 1
|
val dummyRegister = secondRegister + 1
|
||||||
|
|
||||||
val referenceIndex = it.scanResult.patternScanResult!!.endIndex + 1
|
val referenceIndex = it.scanResult.patternScanResult!!.endIndex + 1
|
||||||
val fieldReference = (instructions.elementAt(referenceIndex) as ReferenceInstruction).reference as FieldReference
|
val targetReference = (instruction(referenceIndex) as ReferenceInstruction).reference.toString()
|
||||||
|
|
||||||
val insertIndex = referenceIndex + 1
|
val insertIndex = referenceIndex + 1
|
||||||
|
|
||||||
@ -60,13 +58,13 @@ class ZenModePatch : BytecodePatch(
|
|||||||
const v$firstRegister, -0xbfbfc0
|
const v$firstRegister, -0xbfbfc0
|
||||||
const v$secondRegister, -0xbfbfc0
|
const v$secondRegister, -0xbfbfc0
|
||||||
:off
|
:off
|
||||||
sget-object v0, ${fieldReference.type}->${fieldReference.name}:${fieldReference.type}
|
sget-object v0, $targetReference
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
removeInstruction(referenceIndex)
|
removeInstruction(referenceIndex)
|
||||||
}
|
}
|
||||||
} ?: return ZenModeFingerprint.toErrorResult()
|
} ?: return ZenModeFingerprint.toErrorResult()
|
||||||
} ?: return MiniplayerColorParentFingerprint.toErrorResult()
|
} ?: return ColorMatchPlayerParentFingerprint.toErrorResult()
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_zen_mode", "false")
|
MusicSettingsPatch.addMusicPreference(CategoryType.LAYOUT, "revanced_enable_zen_mode", "false")
|
||||||
|
|
||||||
|
@ -25,10 +25,14 @@ class ClientSpoofMusicPatch : BytecodePatch(
|
|||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
UserAgentHeaderBuilderFingerprint.result?.let {
|
UserAgentHeaderBuilderFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 1
|
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 1
|
||||||
val packageNameRegister = (instruction(insertIndex) as FiveRegisterInstruction).registerD
|
val packageNameRegister = instruction<FiveRegisterInstruction>(insertIndex).registerD
|
||||||
addInstruction(insertIndex, "const-string v$packageNameRegister, \"$MUSIC_PACKAGE_NAME\"")
|
|
||||||
|
addInstruction(
|
||||||
|
insertIndex,
|
||||||
|
"const-string v$packageNameRegister, \"$MUSIC_PACKAGE_NAME\""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} ?: return UserAgentHeaderBuilderFingerprint.toErrorResult()
|
} ?: return UserAgentHeaderBuilderFingerprint.toErrorResult()
|
||||||
|
|
||||||
|
@ -38,28 +38,26 @@ class CodecsUnlockPatch : BytecodePatch(
|
|||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
AllCodecsParentFingerprint.result?.let { parentResult ->
|
AllCodecsParentFingerprint.result?.let { parentResult ->
|
||||||
AllCodecsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let { result ->
|
AllCodecsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
||||||
allCodecsMethod =
|
allCodecsMethod = context.toMethodWalker(it.method)
|
||||||
context.toMethodWalker(result.method)
|
.nextMethod(it.scanResult.patternScanResult!!.endIndex)
|
||||||
.nextMethod(result.scanResult.patternScanResult!!.endIndex)
|
|
||||||
.getMethod()
|
.getMethod()
|
||||||
|
|
||||||
} ?: return AllCodecsFingerprint.toErrorResult()
|
} ?: return AllCodecsFingerprint.toErrorResult()
|
||||||
} ?: return AllCodecsParentFingerprint.toErrorResult()
|
} ?: return AllCodecsParentFingerprint.toErrorResult()
|
||||||
|
|
||||||
CodecsLockFingerprint.result?.let { result ->
|
CodecsLockFingerprint.result?.let {
|
||||||
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
it.mutableMethod.apply {
|
||||||
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
with(result.mutableMethod) {
|
val targetRegister = instruction<OneRegisterInstruction>(targetIndex).registerA
|
||||||
val register = (instruction(endIndex) as OneRegisterInstruction).registerA
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
endIndex + 1, """
|
targetIndex + 1, """
|
||||||
invoke-static {}, $MUSIC_MISC_PATH/OpusCodecPatch;->enableOpusCodec()Z
|
invoke-static {}, $MUSIC_MISC_PATH/OpusCodecPatch;->enableOpusCodec()Z
|
||||||
move-result v7
|
move-result v7
|
||||||
if-eqz v7, :mp4a
|
if-eqz v7, :mp4a
|
||||||
invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;
|
invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;
|
||||||
move-result-object v$register
|
move-result-object v$targetRegister
|
||||||
""", listOf(ExternalLabel("mp4a", instruction(endIndex + 1)))
|
""", listOf(ExternalLabel("mp4a", instruction(targetIndex + 1)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: return CodecsLockFingerprint.toErrorResult()
|
} ?: return CodecsLockFingerprint.toErrorResult()
|
||||||
|
@ -20,9 +20,7 @@ import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class ExclusiveAudioPatch : BytecodePatch(
|
class ExclusiveAudioPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(AudioOnlyEnablerFingerprint)
|
||||||
AudioOnlyEnablerFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
@ -11,64 +11,56 @@ import app.revanced.patcher.patch.PatchResultSuccess
|
|||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import app.revanced.patches.shared.fingerprints.LithoFingerprint
|
import app.revanced.patches.shared.fingerprints.LithoFingerprint
|
||||||
|
import app.revanced.util.bytecode.getNarrowLiteralIndex
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_ADS_PATH
|
import app.revanced.util.integrations.Constants.MUSIC_ADS_PATH
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
|
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class MusicLithoFilterPatch : BytecodePatch(
|
class MusicLithoFilterPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(LithoFingerprint)
|
||||||
LithoFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
LithoFingerprint.result?.let { result ->
|
LithoFingerprint.result?.let {
|
||||||
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
it.mutableMethod.apply {
|
||||||
val method = result.mutableMethod
|
val targetInstruction = implementation!!.instructions
|
||||||
|
|
||||||
with (method.implementation!!.instructions) {
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
val bufferIndex = indexOfFirst {
|
val bufferIndex = getNarrowLiteralIndex(168777401)
|
||||||
it.opcode == Opcode.CONST &&
|
val bufferRegister = instruction<OneRegisterInstruction>(bufferIndex).registerA
|
||||||
(it as Instruction31i).narrowLiteral == 168777401
|
val targetIndex = targetInstruction.indexOfFirst { instruction ->
|
||||||
}
|
instruction.opcode == Opcode.CONST_STRING &&
|
||||||
val bufferRegister = (method.instruction(bufferIndex) as Instruction31i).registerA
|
(instruction as BuilderInstruction21c).reference.toString() == "Element missing type extension"
|
||||||
|
|
||||||
val targetIndex = indexOfFirst {
|
|
||||||
it.opcode == Opcode.CONST_STRING &&
|
|
||||||
(it as BuilderInstruction21c).reference.toString() == "Element missing type extension"
|
|
||||||
} + 2
|
} + 2
|
||||||
|
|
||||||
val builderMethodDescriptor = (elementAt(targetIndex) as ReferenceInstruction).reference as MethodReference
|
val builderMethodDescriptor = (instruction(targetIndex) as ReferenceInstruction).reference as MethodReference
|
||||||
val emptyComponentFieldDescriptor = (elementAt(targetIndex + 2) as ReferenceInstruction).reference as FieldReference
|
val emptyComponentFieldDescriptor = (instruction(targetIndex + 2) as ReferenceInstruction).reference as FieldReference
|
||||||
|
val identifierRegister = instruction<OneRegisterInstruction>(endIndex).registerA
|
||||||
|
|
||||||
val identifierRegister = (method.instruction(endIndex) as OneRegisterInstruction).registerA
|
targetInstruction.filter { instruction ->
|
||||||
|
|
||||||
filter { instruction ->
|
|
||||||
val fieldReference = (instruction as? ReferenceInstruction)?.reference as? FieldReference
|
val fieldReference = (instruction as? ReferenceInstruction)?.reference as? FieldReference
|
||||||
fieldReference?.let { it.type == "Ljava/lang/StringBuilder;" } == true
|
fieldReference?.let { reference -> reference.type == "Ljava/lang/StringBuilder;" } == true
|
||||||
}.forEach { instruction ->
|
}.forEach { instruction ->
|
||||||
val insertIndex = indexOf(instruction)
|
val insertIndex = targetInstruction.indexOf(instruction)
|
||||||
val stringBuilderRegister = (method.instruction(insertIndex) as TwoRegisterInstruction).registerA
|
val stringBuilderRegister = instruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
method.addInstructions(
|
addInstructions(
|
||||||
insertIndex, // right after setting the component.pathBuilder field,
|
insertIndex, """
|
||||||
"""
|
invoke-static {v$stringBuilderRegister, v$identifierRegister}, $MUSIC_ADS_PATH/MusicLithoFilterPatch;->filter(Ljava/lang/StringBuilder;Ljava/lang/String;)Z
|
||||||
invoke-static {v$stringBuilderRegister, v$identifierRegister}, $MUSIC_ADS_PATH/MusicLithoFilterPatch;->filter(Ljava/lang/StringBuilder;Ljava/lang/String;)Z
|
move-result v$bufferRegister
|
||||||
move-result v$bufferRegister
|
if-eqz v$bufferRegister, :not_an_ad
|
||||||
if-eqz v$bufferRegister, :not_an_ad
|
move-object/from16 v$identifierRegister, p1
|
||||||
move-object/from16 v$identifierRegister, p1
|
invoke-static {v$identifierRegister}, $builderMethodDescriptor
|
||||||
invoke-static {v$identifierRegister}, $builderMethodDescriptor
|
move-result-object v0
|
||||||
move-result-object v0
|
iget-object v0, v0, $emptyComponentFieldDescriptor
|
||||||
iget-object v0, v0, $emptyComponentFieldDescriptor
|
return-object v0
|
||||||
return-object v0
|
""", listOf(ExternalLabel("not_an_ad", instruction(insertIndex)))
|
||||||
""", listOf(ExternalLabel("not_an_ad", method.instruction(insertIndex)))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,7 @@ import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class MinimizedPlaybackPatch : BytecodePatch(
|
class MinimizedPlaybackPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(MinimizedPlaybackManagerFingerprint)
|
||||||
MinimizedPlaybackManagerFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.patches.music.misc.premium.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.privacyTosFooterId
|
||||||
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object AccountMenuFooterFingerprint : MethodFingerprint(
|
||||||
|
returnType = "L",
|
||||||
|
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.IGET_OBJECT
|
||||||
|
),
|
||||||
|
customFingerprint = { it.isWideLiteralExists(privacyTosFooterId) }
|
||||||
|
)
|
@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patches.music.misc.premium.patch
|
package app.revanced.patches.music.misc.premium.patch
|
||||||
|
|
||||||
import app.revanced.extensions.findMutableMethodOf
|
|
||||||
import app.revanced.extensions.toErrorResult
|
import app.revanced.extensions.toErrorResult
|
||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
@ -8,48 +7,43 @@ import app.revanced.patcher.annotation.Version
|
|||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.data.toMethodWalker
|
import app.revanced.patcher.data.toMethodWalker
|
||||||
import app.revanced.patcher.extensions.addInstruction
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.music.misc.premium.fingerprints.AccountMenuFooterFingerprint
|
||||||
import app.revanced.patches.music.misc.premium.fingerprints.HideGetPremiumFingerprint
|
import app.revanced.patches.music.misc.premium.fingerprints.HideGetPremiumFingerprint
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.privacyTosFooterId
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import app.revanced.patches.shared.patch.mapping.ResourceMappingPatch
|
import app.revanced.util.bytecode.getWideLiteralIndex
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
|
import org.jf.dexlib2.iface.reference.Reference
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("hide-get-premium")
|
@Name("hide-get-premium")
|
||||||
@Description("Removes all \"Get Premium\" evidences from the avatar menu.")
|
@Description("Removes all \"Get Premium\" evidences from the avatar menu.")
|
||||||
@DependsOn([ResourceMappingPatch::class])
|
@DependsOn([SharedResourceIdPatch::class])
|
||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class HideGetPremiumPatch : BytecodePatch(
|
class HideGetPremiumPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
|
AccountMenuFooterFingerprint,
|
||||||
HideGetPremiumFingerprint
|
HideGetPremiumFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// list of resource names to get the id of
|
|
||||||
private val resourceIds = arrayOf(
|
|
||||||
"id" to "privacy_tos_footer"
|
|
||||||
).map { (type, name) ->
|
|
||||||
ResourceMappingPatch
|
|
||||||
.resourceMappings
|
|
||||||
.single { it.type == type && it.name == name }.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
HideGetPremiumFingerprint.result?.let {
|
HideGetPremiumFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
val register = (implementation!!.instructions[insertIndex] as TwoRegisterInstruction).registerA
|
val register = instruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
insertIndex + 1,
|
insertIndex + 1,
|
||||||
@ -58,54 +52,40 @@ class HideGetPremiumPatch : BytecodePatch(
|
|||||||
}
|
}
|
||||||
} ?: return HideGetPremiumFingerprint.toErrorResult()
|
} ?: return HideGetPremiumFingerprint.toErrorResult()
|
||||||
|
|
||||||
context.classes.forEach { classDef ->
|
|
||||||
classDef.methods.forEach { method ->
|
|
||||||
with(method.implementation) {
|
|
||||||
this?.instructions?.forEachIndexed { index, instruction ->
|
|
||||||
when (instruction.opcode) {
|
|
||||||
Opcode.CONST -> {
|
|
||||||
when ((instruction as Instruction31i).wideLiteral) {
|
|
||||||
resourceIds[0] -> {
|
|
||||||
val viewIndex = index + 5
|
|
||||||
val viewInstruction = instructions.elementAt(viewIndex)
|
|
||||||
if (viewInstruction.opcode != Opcode.IGET_OBJECT) return@forEachIndexed
|
|
||||||
|
|
||||||
val mutableMethod = context.proxy(classDef).mutableClass.findMutableMethodOf(method)
|
AccountMenuFooterFingerprint.result?.let {
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val targetIndex = getWideLiteralIndex(privacyTosFooterId) + 4
|
||||||
|
targetReference = instruction<ReferenceInstruction>(targetIndex + 1).reference
|
||||||
|
|
||||||
val viewReference = (viewInstruction as? ReferenceInstruction)?.reference as? FieldReference
|
with (context
|
||||||
|
.toMethodWalker(this)
|
||||||
|
.nextMethod(targetIndex, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
) {
|
||||||
|
this.implementation!!.instructions.apply {
|
||||||
|
for ((index, instruction) in withIndex()) {
|
||||||
|
if (instruction.opcode != Opcode.IGET_OBJECT) continue
|
||||||
|
|
||||||
with (context
|
if (instruction<ReferenceInstruction>(index).reference == targetReference) {
|
||||||
.toMethodWalker(mutableMethod)
|
val targetRegister = instruction<OneRegisterInstruction>(index + 2).registerA
|
||||||
.nextMethod(viewIndex - 1, true)
|
|
||||||
.getMethod() as MutableMethod
|
|
||||||
) {
|
|
||||||
val viewInstructions = implementation!!.instructions
|
|
||||||
|
|
||||||
for ((targetIndex, targetInstruction) in viewInstructions.withIndex()) {
|
addInstruction(
|
||||||
if (targetInstruction.opcode != Opcode.IGET_OBJECT) continue
|
index,
|
||||||
|
"const/16 v$targetRegister, 0x8"
|
||||||
|
)
|
||||||
|
|
||||||
val indexReference = (targetInstruction as ReferenceInstruction).reference as FieldReference
|
break
|
||||||
|
|
||||||
if (indexReference == viewReference) {
|
|
||||||
val targetRegister = (viewInstructions.elementAt(targetIndex + 2) as OneRegisterInstruction).registerA
|
|
||||||
addInstruction(
|
|
||||||
targetIndex,
|
|
||||||
"const/16 v$targetRegister, 0x8"
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> return@forEachIndexed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} ?: return AccountMenuFooterFingerprint.toErrorResult()
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
private companion object{
|
||||||
|
lateinit var targetReference: Reference
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
package app.revanced.patches.music.misc.quality.fingerprints
|
package app.revanced.patches.music.misc.quality.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.qualityAutoId
|
||||||
import org.jf.dexlib2.Opcode
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
|
||||||
|
|
||||||
object MusicVideoQualitySetterParentFingerprint : MethodFingerprint(
|
object MusicVideoQualitySetterParentFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(qualityAutoId)}
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.qualityAutoLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
@ -1,17 +1,11 @@
|
|||||||
package app.revanced.patches.music.misc.quality.fingerprints
|
package app.revanced.patches.music.misc.quality.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.qualityTitleId
|
||||||
import org.jf.dexlib2.Opcode
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
|
||||||
|
|
||||||
object MusicVideoQualitySettingsParentFingerprint : MethodFingerprint(
|
object MusicVideoQualitySettingsParentFingerprint : MethodFingerprint(
|
||||||
returnType = "L",
|
returnType = "L",
|
||||||
parameters = listOf(),
|
parameters = listOf(),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(qualityTitleId)}
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.qualityTitleLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
@ -25,6 +25,7 @@ import app.revanced.util.integrations.Constants.MUSIC_MISC_PATH
|
|||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.reference.Reference
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("remember-video-quality")
|
@Name("remember-video-quality")
|
||||||
@ -48,46 +49,48 @@ class VideoQualityPatch : BytecodePatch(
|
|||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
MusicVideoQualitySetterParentFingerprint.result?.let { parentResult ->
|
MusicVideoQualitySetterParentFingerprint.result?.let { parentResult ->
|
||||||
MusicVideoQualitySetterFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let { result ->
|
MusicVideoQualitySetterFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
||||||
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
it.mutableMethod.apply {
|
||||||
val instructions = result.method.implementation!!.instructions
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
|
|
||||||
qualityFieldReference =
|
qualityReference = instruction<ReferenceInstruction>(endIndex).reference
|
||||||
(instructions.elementAt(endIndex) as ReferenceInstruction).reference as FieldReference
|
qualityFieldReference = qualityReference as FieldReference
|
||||||
|
|
||||||
qIndexMethodName =
|
qIndexMethodName = context
|
||||||
context.classes.single { it.type == qualityFieldReference.type }.methods.single { it.parameterTypes.first() == "I" }.name
|
.classes.single { classDef -> classDef.type == qualityFieldReference.type }
|
||||||
|
.methods.single { method -> method.parameterTypes.first() == "I" }.name
|
||||||
|
}
|
||||||
} ?: return MusicVideoQualitySetterFingerprint.toErrorResult()
|
} ?: return MusicVideoQualitySetterFingerprint.toErrorResult()
|
||||||
} ?: return MusicVideoQualitySetterParentFingerprint.toErrorResult()
|
} ?: return MusicVideoQualitySetterParentFingerprint.toErrorResult()
|
||||||
|
|
||||||
MusicVideoQualitySettingsParentFingerprint.result?.let { parentResult ->
|
MusicVideoQualitySettingsParentFingerprint.result?.let { parentResult ->
|
||||||
MusicVideoQualitySettingsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
|
MusicVideoQualitySettingsFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.mutableMethod?.addInstructions(
|
||||||
it.mutableMethod.addInstructions(
|
0, """
|
||||||
0, """
|
const-string v0, "$qIndexMethodName"
|
||||||
const-string v0, "$qIndexMethodName"
|
sput-object v0, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->qIndexMethod:Ljava/lang/String;
|
||||||
sput-object v0, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->qIndexMethod:Ljava/lang/String;
|
iget-object v0, p0, $qualityReference
|
||||||
iget-object v0, p0, ${it.classDef.type}->${qualityFieldReference.name}:${qualityFieldReference.type}
|
invoke-static {p1, p2, v0}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;)I
|
||||||
invoke-static {p1, p2, v0}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;)I
|
move-result p2
|
||||||
move-result p2
|
"""
|
||||||
"""
|
) ?: return MusicVideoQualitySettingsFingerprint.toErrorResult()
|
||||||
)
|
|
||||||
} ?: return MusicVideoQualitySettingsFingerprint.toErrorResult()
|
|
||||||
} ?: return MusicVideoQualitySettingsParentFingerprint.toErrorResult()
|
} ?: return MusicVideoQualitySettingsParentFingerprint.toErrorResult()
|
||||||
|
|
||||||
UserQualityChangeFingerprint.result?.let {
|
UserQualityChangeFingerprint.result?.let {
|
||||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
it.mutableMethod.apply {
|
||||||
val qualityChangedClass =
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
context.findClass((it.mutableMethod.instruction(endIndex) as BuilderInstruction21c)
|
val qualityChangedClass =
|
||||||
.reference.toString())!!
|
context.findClass((instruction<BuilderInstruction21c>(endIndex))
|
||||||
.mutableClass
|
.reference.toString())!!
|
||||||
|
.mutableClass
|
||||||
|
|
||||||
for (method in qualityChangedClass.methods) {
|
for (method in qualityChangedClass.methods) {
|
||||||
with (qualityChangedClass.findMutableMethodOf(method)) {
|
with (qualityChangedClass.findMutableMethodOf(method)) {
|
||||||
if (this.name == "onItemClick") {
|
if (this.name == "onItemClick") {
|
||||||
addInstruction(
|
addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static {p3}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userChangedQuality(I)V"
|
"invoke-static {p3}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userChangedQuality(I)V"
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +107,6 @@ class VideoQualityPatch : BytecodePatch(
|
|||||||
|
|
||||||
private lateinit var qIndexMethodName: String
|
private lateinit var qIndexMethodName: String
|
||||||
private lateinit var qualityFieldReference: FieldReference
|
private lateinit var qualityFieldReference: FieldReference
|
||||||
|
private lateinit var qualityReference: Reference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,15 @@ import app.revanced.util.enum.ResourceType.*
|
|||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class SharedResourceIdPatch : ResourcePatch {
|
class SharedResourceIdPatch : ResourcePatch {
|
||||||
internal companion object {
|
internal companion object {
|
||||||
var colorGreyLabelId: Long = -1
|
var chipCloudId: Long = -1
|
||||||
var dialogSolidLabelId: Long = -1
|
var colorGreyId: Long = -1
|
||||||
var disabledIconLabelId: Long = -1
|
var dialogSolidId: Long = -1
|
||||||
var isTabletLabelId: Long = -1
|
var disabledIconId: Long = -1
|
||||||
var notifierShelfLabelId: Long = -1
|
var isTabletId: Long = -1
|
||||||
var qualityAutoLabelId: Long = -1
|
var notifierShelfId: Long = -1
|
||||||
var qualityTitleLabelId: Long = -1
|
var privacyTosFooterId: Long = -1
|
||||||
|
var qualityAutoId: Long = -1
|
||||||
|
var qualityTitleId: Long = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute(context: ResourceContext): PatchResult {
|
override fun execute(context: ResourceContext): PatchResult {
|
||||||
@ -33,13 +35,15 @@ class SharedResourceIdPatch : ResourcePatch {
|
|||||||
.resourceMappings
|
.resourceMappings
|
||||||
.single { it.type == type.value && it.name == name }.id
|
.single { it.type == type.value && it.name == name }.id
|
||||||
|
|
||||||
colorGreyLabelId = find(COLOR, "ytm_color_grey_12")
|
chipCloudId = find(LAYOUT, "chip_cloud")
|
||||||
dialogSolidLabelId = find(STYLE, "Theme.YouTubeMusic.Dialog.Solid")
|
colorGreyId = find(COLOR, "ytm_color_grey_12")
|
||||||
disabledIconLabelId = find(DIMEN, "disabled_icon_alpha")
|
dialogSolidId = find(STYLE, "Theme.YouTubeMusic.Dialog.Solid")
|
||||||
isTabletLabelId = find(BOOL, "is_tablet")
|
disabledIconId = find(DIMEN, "disabled_icon_alpha")
|
||||||
notifierShelfLabelId = find(LAYOUT, "music_notifier_shelf")
|
isTabletId = find(BOOL, "is_tablet")
|
||||||
qualityAutoLabelId = find(STRING, "quality_auto")
|
notifierShelfId = find(LAYOUT, "music_notifier_shelf")
|
||||||
qualityTitleLabelId = find(STRING, "quality_title")
|
privacyTosFooterId = find(ID, "privacy_tos_footer")
|
||||||
|
qualityAutoId = find(STRING, "quality_auto")
|
||||||
|
qualityTitleId = find(STRING, "quality_title")
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,10 @@ class MusicSettingsBytecodePatch : BytecodePatch(
|
|||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
SettingsHeadersFragmentFingerprint.result?.let {
|
SettingsHeadersFragmentFingerprint.result?.let {
|
||||||
with(it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
val targetRegister = (instruction(targetIndex) as OneRegisterInstruction).registerA
|
val targetRegister = instruction<OneRegisterInstruction>(targetIndex).registerA
|
||||||
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
targetIndex + 1,
|
targetIndex + 1,
|
||||||
"invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setActivity(Ljava/lang/Object;)V"
|
"invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setActivity(Ljava/lang/Object;)V"
|
||||||
@ -41,10 +42,11 @@ class MusicSettingsBytecodePatch : BytecodePatch(
|
|||||||
} ?: return SettingsHeadersFragmentFingerprint.toErrorResult()
|
} ?: return SettingsHeadersFragmentFingerprint.toErrorResult()
|
||||||
|
|
||||||
PreferenceFingerprint.result?.let {
|
PreferenceFingerprint.result?.let {
|
||||||
with(it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
val keyRegister = (instruction(targetIndex) as FiveRegisterInstruction).registerD
|
val keyRegister = instruction<FiveRegisterInstruction>(targetIndex).registerD
|
||||||
val valueRegister = (instruction(targetIndex) as FiveRegisterInstruction).registerE
|
val valueRegister = instruction<FiveRegisterInstruction>(targetIndex).registerE
|
||||||
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
targetIndex,
|
targetIndex,
|
||||||
"invoke-static {v$keyRegister, v$valueRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onPreferenceChanged(Ljava/lang/String;Z)V"
|
"invoke-static {v$keyRegister, v$valueRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onPreferenceChanged(Ljava/lang/String;Z)V"
|
||||||
|
@ -44,7 +44,6 @@ class MusicSettingsPatch : AbstractSettingsResourcePatch(
|
|||||||
/**
|
/**
|
||||||
* Copy colors
|
* Copy colors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
context.xmlEditor["res/values/colors.xml"].use { editor ->
|
context.xmlEditor["res/values/colors.xml"].use { editor ->
|
||||||
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class ShareButtonHookPatch : BytecodePatch(
|
|||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
SharePanelFingerprint.result?.let {
|
SharePanelFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val targetIndex = it.scanResult.patternScanResult!!.startIndex
|
val targetIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
|
@ -2,8 +2,8 @@ package app.revanced.patches.music.misc.shuffle.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.disabledIconId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -18,12 +18,6 @@ object ShuffleClassFingerprint : MethodFingerprint(
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.RETURN_VOID
|
Opcode.RETURN_VOID
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.name == "<init>" && it.isWideLiteralExists(disabledIconId) }
|
||||||
methodDef.name == "<init>" &&
|
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.disabledIconLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ import app.revanced.patcher.annotation.Description
|
|||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.*
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
@ -23,9 +26,10 @@ import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
|||||||
import app.revanced.util.enum.CategoryType
|
import app.revanced.util.enum.CategoryType
|
||||||
import app.revanced.util.integrations.Constants.MUSIC_MISC_PATH
|
import app.revanced.util.integrations.Constants.MUSIC_MISC_PATH
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.reference.Reference
|
||||||
import org.jf.dexlib2.immutable.ImmutableField
|
import org.jf.dexlib2.immutable.ImmutableField
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
@ -51,31 +55,31 @@ class EnforceShufflePatch : BytecodePatch(
|
|||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
ShuffleClassReferenceFingerprint.result?.let { result ->
|
ShuffleClassReferenceFingerprint.result?.let {
|
||||||
val startIndex = result.scanResult.patternScanResult!!.startIndex
|
it.mutableMethod.apply {
|
||||||
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
SHUFFLE_CLASS = result.classDef.type
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
|
val imageViewIndex = implementation!!.instructions.indexOfFirst { instruction ->
|
||||||
with (result.mutableMethod.implementation!!.instructions) {
|
((instruction as? ReferenceInstruction)?.reference as? FieldReference)?.type == "Landroid/widget/ImageView;"
|
||||||
firstRef = (elementAt(startIndex) as ReferenceInstruction).reference as FieldReference
|
|
||||||
secondRef = (elementAt(startIndex + 1) as ReferenceInstruction).reference as DexBackedMethodReference
|
|
||||||
thirdRef = (elementAt(endIndex) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
this.filter { instruction ->
|
|
||||||
val fieldReference = (instruction as? ReferenceInstruction)?.reference as? FieldReference
|
|
||||||
fieldReference?.let { it.type == "Landroid/widget/ImageView;" } == true
|
|
||||||
}.forEach { instruction ->
|
|
||||||
fourthRef = (instruction as ReferenceInstruction).reference as FieldReference
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SHUFFLE_CLASS = it.classDef.type
|
||||||
|
|
||||||
|
shuffleReference1 = instruction(startIndex).descriptor
|
||||||
|
shuffleReference2 = instruction(startIndex + 1).descriptor
|
||||||
|
shuffleReference3 = instruction(endIndex).descriptor
|
||||||
|
shuffleReference4 = instruction(imageViewIndex).descriptor
|
||||||
}
|
}
|
||||||
} ?: return ShuffleClassReferenceFingerprint.toErrorResult()
|
} ?: return ShuffleClassReferenceFingerprint.toErrorResult()
|
||||||
|
|
||||||
|
|
||||||
ShuffleClassFingerprint.result?.let {
|
ShuffleClassFingerprint.result?.let {
|
||||||
it.mutableMethod.addInstruction(
|
it.mutableMethod.apply {
|
||||||
it.scanResult.patternScanResult!!.endIndex,
|
addInstruction(
|
||||||
"sput-object p0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS"
|
it.scanResult.patternScanResult!!.endIndex,
|
||||||
)
|
"sput-object p0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
context.traverseClassHierarchy(it.mutableClass) {
|
context.traverseClassHierarchy(it.mutableClass) {
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
|
||||||
@ -93,68 +97,70 @@ class EnforceShufflePatch : BytecodePatch(
|
|||||||
}
|
}
|
||||||
} ?: return ShuffleClassFingerprint.toErrorResult()
|
} ?: return ShuffleClassFingerprint.toErrorResult()
|
||||||
|
|
||||||
|
|
||||||
MusicPlaybackControlsFingerprint.result?.let {
|
MusicPlaybackControlsFingerprint.result?.let {
|
||||||
with (it.mutableMethod.implementation!!.instructions) {
|
it.mutableMethod.apply {
|
||||||
fifthRef = (elementAt(0) as ReferenceInstruction).reference as FieldReference
|
shuffleReference5 = instruction(0).descriptor
|
||||||
sixthRef = (elementAt(1) as ReferenceInstruction).reference as DexBackedMethodReference
|
shuffleReference6 = instruction(1).descriptor
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
0, """
|
||||||
|
invoke-virtual {v0, v1}, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->buttonHook(Z)V
|
||||||
|
return-void
|
||||||
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.mutableClass.staticFields.add(
|
it.mutableClass.apply {
|
||||||
ImmutableField(
|
staticFields.add(
|
||||||
it.mutableMethod.definingClass,
|
ImmutableField(
|
||||||
"shuffleclass",
|
it.mutableMethod.definingClass,
|
||||||
SHUFFLE_CLASS,
|
"shuffleclass",
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
SHUFFLE_CLASS,
|
||||||
null,
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
null,
|
null,
|
||||||
null
|
null,
|
||||||
).toMutable()
|
null
|
||||||
)
|
).toMutable()
|
||||||
|
)
|
||||||
|
|
||||||
it.mutableMethod.addInstructions(
|
val shuffleFieldReference = shuffleReference3 as FieldReference
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-virtual {v0, v1}, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->buttonHook(Z)V
|
|
||||||
return-void
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
it.mutableClass.methods.add(
|
methods.add(
|
||||||
ImmutableMethod(
|
ImmutableMethod(
|
||||||
it.classDef.type,
|
it.classDef.type,
|
||||||
"buttonHook",
|
"buttonHook",
|
||||||
listOf(ImmutableMethodParameter("Z", null, null)),
|
listOf(ImmutableMethodParameter("Z", null, null)),
|
||||||
"V",
|
"V",
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
ImmutableMethodImplementation(
|
ImmutableMethodImplementation(
|
||||||
5, """
|
5, """
|
||||||
invoke-static {}, $MUSIC_MISC_PATH/ForceShufflePatch;->enableForceShuffle()Z
|
invoke-static {}, $MUSIC_MISC_PATH/ForceShufflePatch;->enableForceShuffle()Z
|
||||||
move-result v0
|
move-result v0
|
||||||
if-eqz v0, :cond_0
|
if-eqz v0, :cond_0
|
||||||
new-instance v0, $SHUFFLE_CLASS
|
new-instance v0, $SHUFFLE_CLASS
|
||||||
sget-object v0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS
|
sget-object v0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS
|
||||||
iget-object v1, v0, $SHUFFLE_CLASS->${firstRef.name}:${firstRef.type}
|
iget-object v1, v0, $shuffleReference1
|
||||||
invoke-interface {v1}, $secondRef
|
invoke-interface {v1}, $shuffleReference2
|
||||||
move-result-object v1
|
move-result-object v1
|
||||||
check-cast v1, ${thirdRef.definingClass}
|
check-cast v1, ${shuffleFieldReference.definingClass}
|
||||||
iget-object v1, v1, ${thirdRef.definingClass}->${thirdRef.name}:${thirdRef.type}
|
iget-object v1, v1, $shuffleReference3
|
||||||
invoke-virtual {v1}, ${thirdRef.type}->ordinal()I
|
invoke-virtual {v1}, ${shuffleFieldReference.type}->ordinal()I
|
||||||
move-result v1
|
move-result v1
|
||||||
iget-object v2, v0, $SHUFFLE_CLASS->${fourthRef.name}:Landroid/widget/ImageView;
|
iget-object v2, v0, $shuffleReference4
|
||||||
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
||||||
if-eqz v1, :cond_0
|
if-eqz v1, :cond_0
|
||||||
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
||||||
:cond_0
|
:cond_0
|
||||||
iput-boolean v4, v3, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->${fifthRef.name}:${fifthRef.type}
|
iput-boolean v4, v3, $shuffleReference5
|
||||||
invoke-virtual {v3}, $sixthRef
|
invoke-virtual {v3}, $shuffleReference6
|
||||||
return-void
|
return-void
|
||||||
""".toInstructions(), null, null
|
""".toInstructions(), null, null
|
||||||
)
|
)
|
||||||
).toMutable()
|
).toMutable()
|
||||||
)
|
)
|
||||||
|
}
|
||||||
} ?: return MusicPlaybackControlsFingerprint.toErrorResult()
|
} ?: return MusicPlaybackControlsFingerprint.toErrorResult()
|
||||||
|
|
||||||
MusicSettingsPatch.addMusicPreference(CategoryType.MISC, "revanced_enable_force_shuffle", "true")
|
MusicSettingsPatch.addMusicPreference(CategoryType.MISC, "revanced_enable_force_shuffle", "true")
|
||||||
@ -162,18 +168,19 @@ class EnforceShufflePatch : BytecodePatch(
|
|||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private companion object {
|
||||||
const val MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR =
|
const val MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR =
|
||||||
"Lcom/google/android/apps/youtube/music/watchpage/MusicPlaybackControls;"
|
"Lcom/google/android/apps/youtube/music/watchpage/MusicPlaybackControls;"
|
||||||
|
|
||||||
private lateinit var SHUFFLE_CLASS: String
|
lateinit var SHUFFLE_CLASS: String
|
||||||
|
lateinit var shuffleReference1: Reference
|
||||||
|
lateinit var shuffleReference2: Reference
|
||||||
|
lateinit var shuffleReference3: Reference
|
||||||
|
lateinit var shuffleReference4: Reference
|
||||||
|
lateinit var shuffleReference5: Reference
|
||||||
|
lateinit var shuffleReference6: Reference
|
||||||
|
|
||||||
private lateinit var firstRef: FieldReference
|
val Instruction.descriptor
|
||||||
private lateinit var secondRef: DexBackedMethodReference
|
get() = (this as ReferenceInstruction).reference
|
||||||
private lateinit var thirdRef: FieldReference
|
|
||||||
private lateinit var fourthRef: FieldReference
|
|
||||||
|
|
||||||
private lateinit var fifthRef: FieldReference
|
|
||||||
private lateinit var sixthRef: DexBackedMethodReference
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,14 @@ import app.revanced.patcher.annotation.Name
|
|||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patches.music.misc.tastebuilder.fingerprints.TasteBuilderConstructorFingerprint
|
import app.revanced.patches.music.misc.tastebuilder.fingerprints.TasteBuilderConstructorFingerprint
|
||||||
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
import app.revanced.patches.shared.annotation.YouTubeMusicCompatibility
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("hide-taste-builder")
|
@Name("hide-taste-builder")
|
||||||
@ -20,15 +21,13 @@ import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class RemoveTasteBuilderPatch : BytecodePatch(
|
class RemoveTasteBuilderPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(TasteBuilderConstructorFingerprint)
|
||||||
TasteBuilderConstructorFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
TasteBuilderConstructorFingerprint.result?.let {
|
TasteBuilderConstructorFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 8
|
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 8
|
||||||
val register = (implementation!!.instructions[insertIndex] as Instruction22c).registerA
|
val register = instruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex, """
|
insertIndex, """
|
||||||
|
@ -2,8 +2,8 @@ package app.revanced.patches.music.misc.upgradebutton.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourceIdPatch.Companion.notifierShelfId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -16,11 +16,6 @@ object NotifierShelfFingerprint : MethodFingerprint(
|
|||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT_OBJECT
|
Opcode.MOVE_RESULT_OBJECT
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(notifierShelfId)}
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.notifierShelfLabelId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package app.revanced.patches.music.misc.upgradebutton.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.util.bytecode.isNarrowLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
|
|
||||||
|
|
||||||
object PivotBarConstructorFingerprint : MethodFingerprint(
|
object PivotBarConstructorFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
@ -13,10 +13,5 @@ object PivotBarConstructorFingerprint : MethodFingerprint(
|
|||||||
Opcode.IPUT_OBJECT,
|
Opcode.IPUT_OBJECT,
|
||||||
Opcode.RETURN_VOID
|
Opcode.RETURN_VOID
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.name == "<init>" && it.isNarrowLiteralExists(117501096) }
|
||||||
methodDef.name == "<init>"
|
|
||||||
&& methodDef.implementation!!.instructions.any {
|
|
||||||
((it as? NarrowLiteralInstruction)?.narrowLiteral == 117501096)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
@ -22,7 +22,6 @@ import app.revanced.util.integrations.Constants.INTEGRATIONS_PATH
|
|||||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import org.jf.dexlib2.iface.reference.FieldReference
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("hide-upgrade-button")
|
@Name("hide-upgrade-button")
|
||||||
@ -43,20 +42,12 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
|
|||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
PivotBarConstructorFingerprint.result?.let {
|
PivotBarConstructorFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val targetIndex = it.scanResult.patternScanResult!!.startIndex
|
val targetIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
|
val targetRegisterA = instruction<TwoRegisterInstruction>(targetIndex).registerA
|
||||||
|
val targetRegisterB = instruction<TwoRegisterInstruction>(targetIndex).registerB
|
||||||
|
|
||||||
val targetRegisterA = (instruction(targetIndex) as TwoRegisterInstruction).registerA
|
val replaceReference = instruction<ReferenceInstruction>(targetIndex).reference.toString()
|
||||||
val targetRegisterB = (instruction(targetIndex) as TwoRegisterInstruction).registerB
|
|
||||||
|
|
||||||
val replaceReference =
|
|
||||||
(instruction(targetIndex) as ReferenceInstruction).reference as FieldReference
|
|
||||||
|
|
||||||
val replaceReferenceToCall = replaceReference.definingClass +
|
|
||||||
"->" +
|
|
||||||
replaceReference.name +
|
|
||||||
":" +
|
|
||||||
replaceReference.type
|
|
||||||
|
|
||||||
replaceInstruction(
|
replaceInstruction(
|
||||||
targetIndex,
|
targetIndex,
|
||||||
@ -69,16 +60,16 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
|
|||||||
if-le v1, v2, :dismiss
|
if-le v1, v2, :dismiss
|
||||||
invoke-interface {v$targetRegisterA, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
invoke-interface {v$targetRegisterA, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
||||||
:dismiss
|
:dismiss
|
||||||
iput-object v$targetRegisterA, v$targetRegisterB, $replaceReferenceToCall
|
iput-object v$targetRegisterA, v$targetRegisterB, $replaceReference
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: return PivotBarConstructorFingerprint.toErrorResult()
|
} ?: return PivotBarConstructorFingerprint.toErrorResult()
|
||||||
|
|
||||||
NotifierShelfFingerprint.result?.let {
|
NotifierShelfFingerprint.result?.let {
|
||||||
with (it.mutableMethod) {
|
it.mutableMethod.apply {
|
||||||
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
val targetRegister = (instruction(targetIndex) as OneRegisterInstruction).registerA
|
val targetRegister = instruction<OneRegisterInstruction>(targetIndex).registerA
|
||||||
addInstruction(
|
addInstruction(
|
||||||
targetIndex + 1,
|
targetIndex + 1,
|
||||||
"invoke-static {v$targetRegister}, $INTEGRATIONS_PATH/adremover/AdRemoverAPI;->HideViewWithLayout1dp(Landroid/view/View;)V"
|
"invoke-static {v$targetRegister}, $INTEGRATIONS_PATH/adremover/AdRemoverAPI;->HideViewWithLayout1dp(Landroid/view/View;)V"
|
||||||
|
@ -20,18 +20,15 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
@YouTubeMusicCompatibility
|
@YouTubeMusicCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class MusicVideoIdPatch : BytecodePatch(
|
class MusicVideoIdPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(MusicVideoIdFingerprint)
|
||||||
MusicVideoIdFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext): PatchResult {
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
MusicVideoIdFingerprint.result?.let {
|
MusicVideoIdFingerprint.result?.let {
|
||||||
insertIndex = it.scanResult.patternScanResult!!.endIndex
|
it.mutableMethod.apply {
|
||||||
|
insertIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
with (it.mutableMethod) {
|
|
||||||
insertMethod = this
|
insertMethod = this
|
||||||
videoIdRegister = (implementation!!.instructions[insertIndex] as OneRegisterInstruction).registerA
|
videoIdRegister = instruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
}
|
}
|
||||||
offset++ // offset so setVideoId is called before any injected call
|
offset++ // offset so setVideoId is called before any injected call
|
||||||
} ?: return MusicVideoIdFingerprint.toErrorResult()
|
} ?: return MusicVideoIdFingerprint.toErrorResult()
|
||||||
|
@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
object MiniplayerColorParentFingerprint : MethodFingerprint(
|
object ColorMatchPlayerParentFingerprint : MethodFingerprint(
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
access = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
access = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||||
opcodes = listOf(
|
opcodes = listOf(
|
@ -4,7 +4,5 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|||||||
|
|
||||||
object LayoutConstructorFingerprint : MethodFingerprint(
|
object LayoutConstructorFingerprint : MethodFingerprint(
|
||||||
strings = listOf("1.0x"),
|
strings = listOf("1.0x"),
|
||||||
customFingerprint = {
|
customFingerprint = { it.definingClass.endsWith("YouTubeControlsOverlay;") }
|
||||||
it.definingClass.endsWith("YouTubeControlsOverlay;")
|
|
||||||
}
|
|
||||||
)
|
)
|
@ -2,8 +2,8 @@ package app.revanced.patches.shared.fingerprints
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.youtube.misc.resourceid.patch.SharedResourceIdPatch
|
import app.revanced.patches.youtube.misc.resourceid.patch.SharedResourceIdPatch.Companion.imageOnlyTabId
|
||||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
import app.revanced.util.bytecode.isWideLiteralExists
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@ -14,10 +14,5 @@ object PivotBarCreateButtonViewFingerprint : MethodFingerprint(
|
|||||||
Opcode.MOVE_OBJECT,
|
Opcode.MOVE_OBJECT,
|
||||||
Opcode.INVOKE_DIRECT_RANGE, // unique instruction anchor
|
Opcode.INVOKE_DIRECT_RANGE, // unique instruction anchor
|
||||||
),
|
),
|
||||||
customFingerprint = { methodDef ->
|
customFingerprint = { it.isWideLiteralExists(imageOnlyTabId) }
|
||||||
methodDef.implementation?.instructions?.any {
|
|
||||||
it.opcode.ordinal == Opcode.CONST.ordinal &&
|
|
||||||
(it as? WideLiteralInstruction)?.wideLiteral == SharedResourceIdPatch.imageOnlyTabId
|
|
||||||
} == true
|
|
||||||
}
|
|
||||||
)
|
)
|
30
src/main/kotlin/app/revanced/util/bytecode/BytecodeUtils.kt
Normal file
30
src/main/kotlin/app/revanced/util/bytecode/BytecodeUtils.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package app.revanced.util.bytecode
|
||||||
|
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.Method
|
||||||
|
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
|
|
||||||
|
fun Method.getNarrowLiteralIndex(value: Int): Int {
|
||||||
|
return implementation?.let {
|
||||||
|
it.instructions.indexOfFirst { instruction ->
|
||||||
|
instruction.opcode == Opcode.CONST && (instruction as NarrowLiteralInstruction).narrowLiteral == value
|
||||||
|
}
|
||||||
|
} ?: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Method.isNarrowLiteralExists(value: Int): Boolean {
|
||||||
|
return getNarrowLiteralIndex(value) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Method.getWideLiteralIndex(value: Long): Int {
|
||||||
|
return implementation?.let {
|
||||||
|
it.instructions.indexOfFirst { instruction ->
|
||||||
|
instruction.opcode == Opcode.CONST && (instruction as WideLiteralInstruction).wideLiteral == value
|
||||||
|
}
|
||||||
|
} ?: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Method.isWideLiteralExists(value: Long): Boolean {
|
||||||
|
return getWideLiteralIndex(value) != -1
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user