mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-23 02:31:03 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dc2875057 | ||
|
|
7b24071956 | ||
|
|
6c472aa71d | ||
|
|
ca66490930 | ||
|
|
5317a482d1 | ||
|
|
f21d1d83f6 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
|||||||
|
# [2.2.0](https://github.com/revanced/revanced-patches/compare/v2.1.0...v2.2.0) (2022-06-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* make resource mapping patch aware of types ([#77](https://github.com/revanced/revanced-patches/issues/77)) ([188491a](https://github.com/revanced/revanced-patches/commit/188491a707abccc1164413f075d8a66c145a1455))
|
||||||
|
|
||||||
|
# [2.1.0](https://github.com/revanced/revanced-patches/compare/v2.0.3...v2.1.0) (2022-06-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* `custom-playback-speed` patch ([#50](https://github.com/revanced/revanced-patches/issues/50)) ([224254b](https://github.com/revanced/revanced-patches/commit/224254bcce2b394bbfd2549089f0204ce4ed4a89))
|
||||||
|
|
||||||
|
## [2.0.3](https://github.com/revanced/revanced-patches/compare/v2.0.2...v2.0.3) (2022-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check if resource files exist ([ba1f3af](https://github.com/revanced/revanced-patches/commit/ba1f3af99be58edc44ed1b8f1875508d5034efd8))
|
||||||
|
|
||||||
## [2.0.2](https://github.com/revanced/revanced-patches/compare/v2.0.1...v2.0.2) (2022-06-27)
|
## [2.0.2](https://github.com/revanced/revanced-patches/compare/v2.0.1...v2.0.2) (2022-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 2.0.2
|
version = 2.2.0
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ class GeneralBytecodeAdsPatch : BytecodePatch(
|
|||||||
"endscreen_element_layout_icon",
|
"endscreen_element_layout_icon",
|
||||||
"promoted_video_item_land",
|
"promoted_video_item_land",
|
||||||
"promoted_video_item_full_bleed",
|
"promoted_video_item_full_bleed",
|
||||||
).map {
|
).map { name ->
|
||||||
ResourceIdMappingProviderResourcePatch.resourceMappings[it]!!
|
ResourceIdMappingProviderResourcePatch.resourceMappings.first { it.name == name }.id
|
||||||
}
|
}
|
||||||
|
|
||||||
private val stringReferences = arrayOf(
|
private val stringReferences = arrayOf(
|
||||||
|
|||||||
@@ -4,16 +4,17 @@ 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.impl.ResourceData
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
import app.revanced.patcher.patch.annotations.Dependencies
|
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
|
||||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Dependencies
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
import app.revanced.patches.youtube.layout.branding.header.annotations.PremiumHeadingCompatibility
|
import app.revanced.patches.youtube.layout.branding.header.annotations.PremiumHeadingCompatibility
|
||||||
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Dependencies(
|
@Dependencies(
|
||||||
@@ -34,9 +35,14 @@ class PremiumHeadingPatch : ResourcePatch() {
|
|||||||
arrayOf("xxxhdpi", "xxhdpi", "xhdpi", "hdpi", "mdpi").forEach { size ->
|
arrayOf("xxxhdpi", "xxhdpi", "xhdpi", "hdpi", "mdpi").forEach { size ->
|
||||||
val headingDirectory = resDirectory.resolve("drawable-$size")
|
val headingDirectory = resDirectory.resolve("drawable-$size")
|
||||||
modes.forEach {mode ->
|
modes.forEach {mode ->
|
||||||
|
val fromPath = headingDirectory.resolve("${original}_$mode.png").toPath()
|
||||||
|
val toPath = headingDirectory.resolve("${replacement}_$mode.png").toPath()
|
||||||
|
|
||||||
|
if (!fromPath.exists())
|
||||||
|
return PatchResultError("The file $fromPath does not exist in the resources. Therefore, this patch can not succeed.")
|
||||||
Files.copy(
|
Files.copy(
|
||||||
headingDirectory.resolve("${original}_$mode.png").toPath(),
|
fromPath,
|
||||||
headingDirectory.resolve("${replacement}_$mode.png").toPath(),
|
toPath,
|
||||||
StandardCopyOption.REPLACE_EXISTING
|
StandardCopyOption.REPLACE_EXISTING
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ class CreateButtonRemoverPatch : BytecodePatch(
|
|||||||
// Get the required register which holds the view object we need to pass to the method hideCreateButton
|
// Get the required register which holds the view object we need to pass to the method hideCreateButton
|
||||||
val implementation = result.mutableMethod.implementation!!
|
val implementation = result.mutableMethod.implementation!!
|
||||||
|
|
||||||
val imageOnlyLayout = ResourceIdMappingProviderResourcePatch.resourceMappings["image_only_tab"]
|
val imageOnlyLayout =
|
||||||
?: return PatchResultError("Required resource could not be found in the map")
|
ResourceIdMappingProviderResourcePatch.resourceMappings.first { it.type == "layout" && it.name == "image_only_tab" }
|
||||||
|
|
||||||
val imageOnlyLayoutConstIndex =
|
val imageOnlyLayoutConstIndex =
|
||||||
implementation.instructions.indexOfFirst { (it as? WideLiteralInstruction)?.wideLiteral == imageOnlyLayout }
|
implementation.instructions.indexOfFirst { (it as? WideLiteralInstruction)?.wideLiteral == imageOnlyLayout.id }
|
||||||
|
|
||||||
val (instructionIndex, instruction) = implementation.instructions.drop(imageOnlyLayoutConstIndex).withIndex()
|
val (instructionIndex, instruction) = implementation.instructions.drop(imageOnlyLayoutConstIndex).withIndex()
|
||||||
.first {
|
.first {
|
||||||
@@ -63,4 +63,4 @@ class CreateButtonRemoverPatch : BytecodePatch(
|
|||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.customplaybackspeed.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.24.34")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class CustomPlaybackSpeedCompatibility
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("speed-array-generator-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lzdj;", "d"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2)
|
||||||
|
@CustomPlaybackSpeedCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object SpeedArrayGeneratorFingerprint : MethodFingerprint(
|
||||||
|
"[L",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
listOf(
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
),
|
||||||
|
listOf("0.0#")
|
||||||
|
)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.youtube.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("speed-limiter-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lxgy;", "y"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2)
|
||||||
|
@CustomPlaybackSpeedCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object SpeedLimiterFingerprint : MethodFingerprint(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("F"),
|
||||||
|
listOf(
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.CONST_HIGH16,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.CONST_HIGH16,
|
||||||
|
Opcode.CONST_HIGH16,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.customplaybackspeed.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.annotations.Dependencies
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
|
import app.revanced.patches.youtube.misc.customplaybackspeed.annotations.CustomPlaybackSpeedCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedArrayGeneratorFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedLimiterFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("custom-playback-speed")
|
||||||
|
@Description("Allows to change the default playback speed options")
|
||||||
|
@Dependencies(dependencies = [IntegrationsPatch::class])
|
||||||
|
@CustomPlaybackSpeedCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class CustomPlaybackSpeedPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
SpeedArrayGeneratorFingerprint, SpeedLimiterFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val arrayGenMethod = SpeedArrayGeneratorFingerprint.result?.mutableMethod!!
|
||||||
|
val arrayGenMethodImpl = arrayGenMethod.implementation!!
|
||||||
|
|
||||||
|
val sizeCallIndex = arrayGenMethodImpl.instructions
|
||||||
|
.indexOfFirst { ((it as? ReferenceInstruction)?.reference as? MethodReference)?.name == "size" }
|
||||||
|
|
||||||
|
if (sizeCallIndex == -1) return PatchResultError("Couldn't find call to size()")
|
||||||
|
|
||||||
|
val sizeCallResultRegister =
|
||||||
|
(arrayGenMethodImpl.instructions.elementAt(sizeCallIndex + 1) as OneRegisterInstruction).registerA
|
||||||
|
|
||||||
|
arrayGenMethod.replaceInstruction(
|
||||||
|
sizeCallIndex + 1,
|
||||||
|
"const/4 v$sizeCallResultRegister, 0x0"
|
||||||
|
)
|
||||||
|
|
||||||
|
val (arrayLengthConstIndex, arrayLengthConst) = arrayGenMethodImpl.instructions.withIndex()
|
||||||
|
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 7 }
|
||||||
|
|
||||||
|
val arrayLengthConstDestination = (arrayLengthConst as OneRegisterInstruction).registerA
|
||||||
|
|
||||||
|
val videoSpeedsArrayType = "Lapp/revanced/integrations/videoplayer/videosettings/VideoSpeed;->videoSpeeds:[F"
|
||||||
|
|
||||||
|
arrayGenMethod.addInstructions(
|
||||||
|
arrayLengthConstIndex + 1,
|
||||||
|
"""
|
||||||
|
sget-object v$arrayLengthConstDestination, $videoSpeedsArrayType
|
||||||
|
array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val (originalArrayFetchIndex, originalArrayFetch) = arrayGenMethodImpl.instructions.withIndex()
|
||||||
|
.first {
|
||||||
|
val reference = ((it.value as? ReferenceInstruction)?.reference as? FieldReference)
|
||||||
|
reference?.definingClass?.contains("PlayerConfigModel") ?: false &&
|
||||||
|
reference?.type == "[F"
|
||||||
|
}
|
||||||
|
|
||||||
|
val originalArrayFetchDestination = (originalArrayFetch as OneRegisterInstruction).registerA
|
||||||
|
|
||||||
|
arrayGenMethod.replaceInstruction(
|
||||||
|
originalArrayFetchIndex,
|
||||||
|
"sget-object v$originalArrayFetchDestination, $videoSpeedsArrayType"
|
||||||
|
)
|
||||||
|
|
||||||
|
val limiterMethod = SpeedLimiterFingerprint.result?.mutableMethod!!;
|
||||||
|
val limiterMethodImpl = limiterMethod.implementation!!
|
||||||
|
|
||||||
|
val speedLimitMin = 0.25f
|
||||||
|
val speedLimitMax = 100f
|
||||||
|
|
||||||
|
val (limiterMinConstIndex, limiterMinConst) = limiterMethodImpl.instructions.withIndex()
|
||||||
|
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 0.25f.toRawBits() }
|
||||||
|
val (limiterMaxConstIndex, limiterMaxConst) = limiterMethodImpl.instructions.withIndex()
|
||||||
|
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() }
|
||||||
|
|
||||||
|
val limiterMinConstDestination = (limiterMinConst as OneRegisterInstruction).registerA
|
||||||
|
val limiterMaxConstDestination = (limiterMaxConst as OneRegisterInstruction).registerA
|
||||||
|
|
||||||
|
fun hexFloat(float: Float): String = "0x%08x".format(float.toRawBits())
|
||||||
|
|
||||||
|
limiterMethod.replaceInstruction(
|
||||||
|
limiterMinConstIndex,
|
||||||
|
"const/high16 v$limiterMinConstDestination, ${hexFloat(speedLimitMin)}"
|
||||||
|
)
|
||||||
|
limiterMethod.replaceInstruction(
|
||||||
|
limiterMaxConstIndex,
|
||||||
|
"const/high16 v$limiterMaxConstDestination, ${hexFloat(speedLimitMax)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,22 +15,26 @@ import org.w3c.dom.Element
|
|||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class ResourceIdMappingProviderResourcePatch : ResourcePatch() {
|
class ResourceIdMappingProviderResourcePatch : ResourcePatch() {
|
||||||
companion object {
|
companion object {
|
||||||
internal lateinit var resourceMappings: Map<String, Long>
|
internal lateinit var resourceMappings: List<ResourceElement>
|
||||||
private set
|
private set
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute(data: ResourceData): PatchResult {
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
data.xmlEditor["res/values/public.xml"].use { editor ->
|
data.xmlEditor["res/values/public.xml"].use { editor ->
|
||||||
resourceMappings = buildMap {
|
resourceMappings = buildList {
|
||||||
editor.file.documentElement.doRecursively { node ->
|
editor.file.documentElement.doRecursively { node ->
|
||||||
if (node !is Element) return@doRecursively
|
if (node !is Element) return@doRecursively
|
||||||
val nameAttribute = node.getAttribute("name")
|
val nameAttribute = node.getAttribute("name")
|
||||||
|
val typeAttribute = node.getAttribute("type")
|
||||||
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) return@doRecursively
|
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) return@doRecursively
|
||||||
this[nameAttribute] = node.getAttribute("id").substring(2).toLong(16)
|
val id = node.getAttribute("id").substring(2).toLong(16)
|
||||||
|
add(ResourceElement(typeAttribute, nameAttribute, id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ResourceElement(val type: String, val name: String, val id: Long)
|
||||||
|
|||||||
Reference in New Issue
Block a user