Skip to content

Commit

Permalink
copied hex patch from upstream. Dont know how to depend it abd use on…
Browse files Browse the repository at this point in the history
… manager
  • Loading branch information
indrastorms committed May 5, 2024
1 parent 0e8b697 commit e83b8e7
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 18 deletions.
18 changes: 17 additions & 1 deletion api/dropped-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,23 @@ public final class dropped/patches/nova/prime/patch/UnlockPrimePatch : app/revan
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class dropped/patches/spotify/premium/UnlockPremiumPatch : app/revanced/patches/shared/misc/hex/BaseHexPatch {
public abstract class dropped/patches/shared/misc/hex/BaseHexPatch : app/revanced/patcher/patch/RawResourcePatch {
public fun <init> ()V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
public abstract fun getReplacements ()Ljava/util/List;
}

public final class dropped/patches/shared/misc/hex/BaseHexPatch$Replacement {
public static final field Companion Ldropped/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public final fun replacePattern ([B)V
}

public final class dropped/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion {
}

public final class dropped/patches/spotify/premium/UnlockPremiumPatch : dropped/patches/shared/misc/hex/BaseHexPatch {
public fun <init> ()V
public fun getReplacements ()Ljava/util/List;
}
Expand Down
14 changes: 0 additions & 14 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import org.gradle.kotlin.dsl.support.listFilesOrdered
plugins {
alias(libs.plugins.kotlin)
alias(libs.plugins.binary.compatibility.validator)
alias(libs.plugins.shadow)
`maven-publish`
signing
}
Expand Down Expand Up @@ -50,24 +49,11 @@ tasks {
}
}

shadowJar {
manifest {
exclude("META-INF/versions/**")
}

dependencies {
include(dependency("app.revanced:revanced.patches.*"))
relocate("app.revanced:revanced.patches", "shadow.app.revanced:revanced.patches")
}
minimize()
}

register("buildDexJar") {
description = "Build and add a DEX to the JAR file"
group = "build"

dependsOn(build)
dependsOn(shadowJar)

doLast {
val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools")
Expand Down
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ revanced-patches = "4.8.0-dev.8"
smali = "3.0.5"
binary-compatibility-validator = "0.14.0"
kotlin = "1.9.22"
shadow = "8.1.1"

[libraries]
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
Expand All @@ -14,4 +13,3 @@ smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
[plugins]
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
120 changes: 120 additions & 0 deletions src/main/kotlin/dropped/patches/shared/misc/hex/BaseHexPatch.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package dropped.patches.shared.misc.hex

import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.RawResourcePatch
import kotlin.math.max

abstract class BaseHexPatch : RawResourcePatch() {
abstract val replacements: List<Replacement>

override fun execute(context: ResourceContext) {
replacements.groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) ->
val targetFile = try {
context[targetFilePath, true]
} catch (e: Exception) {
throw PatchException("Could not find target file: $targetFilePath")
}

// TODO: Use a file channel to read and write the file instead of reading the whole file into memory,
// in order to reduce memory usage.
val targetFileBytes = targetFile.readBytes()

replacements.forEach { replacement ->
replacement.replacePattern(targetFileBytes)
}

targetFile.writeBytes(targetFileBytes)
}
}

/**
* Represents a pattern to search for and its replacement pattern.
*
* @property pattern The pattern to search for.
* @property replacementPattern The pattern to replace the [pattern] with.
* @property targetFilePath The path to the file to make the changes in relative to the APK root.
*/
class Replacement(
private val pattern: String,
replacementPattern: String,
internal val targetFilePath: String,
) {
private val patternBytes = pattern.toByteArrayPattern()
private val replacementPattern = replacementPattern.toByteArrayPattern()

init {
if (this.patternBytes.size != this.replacementPattern.size) {
throw PatchException("Pattern and replacement pattern must have the same length: $pattern")
}
}

/**
* Replaces the [patternBytes] with the [replacementPattern] in the [targetFileBytes].
*
* @param targetFileBytes The bytes of the file to make the changes in.
*/
fun replacePattern(targetFileBytes: ByteArray) {
val startIndex = indexOfPatternIn(targetFileBytes)

if (startIndex == -1) {
throw PatchException("Pattern not found in target file: $pattern")
}

replacementPattern.copyInto(targetFileBytes, startIndex)
}

// TODO: Allow searching in a file channel instead of a byte array to reduce memory usage.
/**
* Returns the index of the first occurrence of [patternBytes] in the haystack
* using the Boyer-Moore algorithm.
*
* @param haystack The array to search in.
*
* @return The index of the first occurrence of the [patternBytes] in the haystack or -1
* if the [patternBytes] is not found.
*/
private fun indexOfPatternIn(haystack: ByteArray): Int {
val needle = patternBytes

val haystackLength = haystack.size - 1
val needleLength = needle.size - 1
val right = IntArray(256) { -1 }

for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i

var skip: Int
for (i in 0..haystackLength - needleLength) {
skip = 0

for (j in needleLength - 1 downTo 0)
if (needle[j] != haystack[i + j]) {
skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])

break
}

if (skip == 0) return i
}
return -1
}

companion object {
/**
* Convert a string representing a pattern of hexadecimal bytes to a byte array.
*
* @return The byte array representing the pattern.
* @throws PatchException If the pattern is invalid.
*/
private fun String.toByteArrayPattern() = try {
split(" ").map { it.toInt(16).toByte() }.toByteArray()
} catch (e: NumberFormatException) {
throw PatchException(
"Could not parse pattern: $this. A pattern is a sequence of case insensitive strings " +
"representing hexadecimal bytes separated by spaces",
e,
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dropped.patches.spotify.premium

import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.hex.BaseHexPatch
import dropped.patches.shared.misc.hex.BaseHexPatch

@Patch(
name = "Unlock Spotify Premium",
Expand Down

0 comments on commit e83b8e7

Please sign in to comment.