You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-06 16:38:50 +02:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
368c61c1bf | ||
![]() |
bbd40bf2f6 | ||
![]() |
3de999a2d3 | ||
![]() |
83cbb2f110 | ||
![]() |
fcc7fa75d0 | ||
![]() |
495ebface8 | ||
![]() |
8c12f8d488 | ||
![]() |
523f67b238 | ||
![]() |
4813a8b48e | ||
![]() |
d5c66022c9 | ||
![]() |
1d4034b36c | ||
![]() |
dd2d696d00 | ||
![]() |
6326321b65 | ||
![]() |
f291a4ae3e |
60
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
60
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: 🐞 Bug report
|
||||
description: Report a very clearly broken issue.
|
||||
title: 'bug: <title>'
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced bug report
|
||||
|
||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Crash
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: How did you find the bug? Any additional details that might help?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Add the steps to reproduce this bug including your environment.
|
||||
placeholder: Step 1. Download some files. Step 2. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots or videos
|
||||
description: Add screenshots or videos that show the bug here.
|
||||
placeholder: Drag and drop the screenshots/videos into this box.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solution
|
||||
description: If applicable, add a possible solution.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
24
.github/ISSUE_TEMPLATE/bug-report.md
vendored
24
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report on the patcher. Do not submit suggestions for patches here.
|
||||
title: 'problem: some problem'
|
||||
labels: bug
|
||||
assignees: oSumAtrIX, Sculas
|
||||
|
||||
---
|
||||
|
||||
## 🐞 Issue
|
||||
|
||||
<!-- Describe your issue in detail here -->
|
||||
|
||||
## ⚙ Reproduce
|
||||
|
||||
<!-- Include your environment and steps to reproduce the issue as detailed as possible -->
|
||||
|
||||
## 🛠 Solution
|
||||
|
||||
<!-- If applicable, add a possible solution -->
|
||||
|
||||
## ⚠ Additional context
|
||||
|
||||
<!-- Add any other context about the problem here -->
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 📃 Documentation
|
||||
url: https://github.com/revanced/revanced-documentation/
|
||||
about: Don't know how or where to start? Check out our documentation!
|
||||
- name: 🗨 Discussions
|
||||
url: https://github.com/revanced/revanced-suggestions/discussions
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
46
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
Normal file
46
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed feature request.
|
||||
title: 'feat: <title>'
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# ReVanced feature request
|
||||
|
||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
options:
|
||||
- Functionality
|
||||
- Cosmetic
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue
|
||||
description: What is the current problem. Why does it require a feature request?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe your feature in detail. How does it solve the issue?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: Why should your feature should be considered?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a change for the patcher. Do not submit suggestions for patches here.
|
||||
title: 'feat: some feature'
|
||||
labels: feature-request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## 🐞 Issue
|
||||
|
||||
<!-- Explain here, what the current problem is and why it leads you to request a feature change -->
|
||||
|
||||
## ❗ Solution
|
||||
|
||||
<!-- Explain how your current issue can be solved -->
|
||||
|
||||
## ❓ Motivation
|
||||
|
||||
<!-- Explain why your feature should be considered -->
|
||||
|
||||
## ⚠ Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here -->
|
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,3 +1,18 @@
|
||||
# [2.7.0](https://github.com/revanced/revanced-patcher/compare/v2.6.0...v2.7.0) (2022-08-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `Closeable` patches ([bbd40bf](https://github.com/revanced/revanced-patcher/commit/bbd40bf2f6ff200705f2bcb272dd1680bb244e3f))
|
||||
|
||||
# [2.6.0](https://github.com/revanced/revanced-patcher/compare/v2.5.2...v2.6.0) (2022-07-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Patch#dependsOn extension ([523f67b](https://github.com/revanced/revanced-patcher/commit/523f67b238646caaa9b7676a0e238ce82adbdda4))
|
||||
* Soft Dependencies for Patches ([8c12f8d](https://github.com/revanced/revanced-patcher/commit/8c12f8d488f939cc932e826aad0b20876ae165b7))
|
||||
|
||||
## [2.5.2](https://github.com/revanced/revanced-patcher/compare/v2.5.1...v2.5.2) (2022-07-24)
|
||||
|
||||
## [2.5.1](https://github.com/revanced/revanced-patcher/compare/v2.5.0...v2.5.1) (2022-07-17)
|
||||
|
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 2.5.2
|
||||
version = 2.7.0
|
||||
|
@@ -11,6 +11,7 @@ import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultError
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependencyType
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||
import app.revanced.patcher.util.ListBackedSet
|
||||
@@ -31,6 +32,7 @@ import org.jf.dexlib2.Opcodes
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.DexFile
|
||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
@@ -246,13 +248,13 @@ class Patcher(private val options: PatcherOptions) {
|
||||
*/
|
||||
private fun applyPatch(
|
||||
patch: Class<out Patch<Data>>,
|
||||
appliedPatches: MutableMap<String, Boolean>
|
||||
appliedPatches: LinkedHashMap<String, AppliedPatch>
|
||||
): PatchResult {
|
||||
val patchName = patch.patchName
|
||||
|
||||
// if the patch has already applied silently skip it
|
||||
if (appliedPatches.contains(patchName)) {
|
||||
if (!appliedPatches[patchName]!!)
|
||||
if (!appliedPatches[patchName]!!.success)
|
||||
return PatchResultError("'$patchName' did not succeed previously")
|
||||
|
||||
logger.trace("Skipping '$patchName' because it has already been applied")
|
||||
@@ -261,15 +263,19 @@ class Patcher(private val options: PatcherOptions) {
|
||||
}
|
||||
|
||||
// recursively apply all dependency patches
|
||||
patch.dependencies?.forEach {
|
||||
val patchDependency = it.java
|
||||
|
||||
val result = applyPatch(patchDependency, appliedPatches)
|
||||
patch.dependencies.forEach {
|
||||
val dependency = it.patch.java
|
||||
if ( // soft dependencies must be included manually.
|
||||
it.type == DependencyType.SOFT && !data.patches.any { p ->
|
||||
p.patchName == dependency.patchName
|
||||
}
|
||||
) return@forEach
|
||||
|
||||
val result = applyPatch(dependency, appliedPatches)
|
||||
if (result.isSuccess()) return@forEach
|
||||
|
||||
val errorMessage = result.error()!!.cause
|
||||
return PatchResultError("'$patchName' depends on '${patchDependency.patchName}' but the following error was raised: $errorMessage")
|
||||
return PatchResultError("'$patchName' depends on '${dependency.patchName}' but the following error was raised: $errorMessage")
|
||||
}
|
||||
|
||||
val patchInstance = patch.getDeclaredConstructor().newInstance()
|
||||
@@ -293,10 +299,10 @@ class Patcher(private val options: PatcherOptions) {
|
||||
|
||||
return try {
|
||||
val result = patchInstance.execute(data)
|
||||
appliedPatches[patchName] = result.isSuccess()
|
||||
appliedPatches[patchName] = AppliedPatch(patchInstance, result.isSuccess())
|
||||
result
|
||||
} catch (e: Exception) {
|
||||
appliedPatches[patchName] = false
|
||||
appliedPatches[patchName] = AppliedPatch(patchInstance, false)
|
||||
PatchResultError(e)
|
||||
}
|
||||
}
|
||||
@@ -308,23 +314,41 @@ class Patcher(private val options: PatcherOptions) {
|
||||
*/
|
||||
fun applyPatches(stopOnError: Boolean = false) = sequence {
|
||||
logger.trace("Applying all patches")
|
||||
val appliedPatches = mutableMapOf<String, Boolean>() // first is success, second is name
|
||||
|
||||
for (patch in data.patches) {
|
||||
val patchResult = applyPatch(patch, appliedPatches)
|
||||
val appliedPatches = LinkedHashMap<String, AppliedPatch>() // first is name
|
||||
|
||||
val result = if (patchResult.isSuccess()) {
|
||||
Result.success(patchResult.success()!!)
|
||||
} else {
|
||||
Result.failure(patchResult.error()!!)
|
||||
try {
|
||||
for (patch in data.patches) {
|
||||
val patchResult = applyPatch(patch, appliedPatches)
|
||||
|
||||
val result = if (patchResult.isSuccess()) {
|
||||
Result.success(patchResult.success()!!)
|
||||
} else {
|
||||
Result.failure(patchResult.error()!!)
|
||||
}
|
||||
|
||||
yield(patch.patchName to result)
|
||||
if (stopOnError && patchResult.isError()) break
|
||||
}
|
||||
} finally {
|
||||
// close all closeable patches in order
|
||||
for ((patch, _) in appliedPatches.values.reversed()) {
|
||||
if (patch !is Closeable) continue
|
||||
|
||||
yield(patch.patchName to result)
|
||||
if (stopOnError && patchResult.isError()) break
|
||||
patch.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A result of applying a [Patch].
|
||||
*
|
||||
* @param patchInstance The instance of the [Patch] that was applied.
|
||||
* @param success The result of the [Patch].
|
||||
*/
|
||||
internal data class AppliedPatch(val patchInstance: Patch<Data>, val success: Boolean)
|
||||
|
||||
private fun BuildOptions.setBuildOptions(options: PatcherOptions) {
|
||||
this.aaptPath = options.aaptPath
|
||||
this.useAapt2 = true
|
||||
|
@@ -7,6 +7,9 @@ import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.annotations.Dependencies
|
||||
import app.revanced.patcher.patch.annotations.DependencyType
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@@ -35,14 +38,26 @@ private fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
||||
return null
|
||||
}
|
||||
|
||||
private typealias PatchClass = Class<out Patch<Data>>
|
||||
|
||||
object PatchExtensions {
|
||||
val Class<out Patch<Data>>.patchName: String
|
||||
get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
|
||||
val Class<out Patch<Data>>.version get() = recursiveAnnotation(Version::class)?.version
|
||||
val Class<out Patch<Data>>.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
||||
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
||||
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Dependencies::class)?.dependencies
|
||||
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
val PatchClass.patchName: String get() = recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
|
||||
val PatchClass.version get() = recursiveAnnotation(Version::class)?.version
|
||||
val PatchClass.include get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
||||
val PatchClass.description get() = recursiveAnnotation(Description::class)?.description
|
||||
val PatchClass.dependencies get() = buildList {
|
||||
recursiveAnnotation(DependsOn::class)?.let { add(PatchDependency(it.value, it.type)) }
|
||||
recursiveAnnotation(Dependencies::class)?.dependencies?.forEach { add(PatchDependency(it, DependencyType.HARD)) }
|
||||
}.toTypedArray()
|
||||
val PatchClass.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
|
||||
@JvmStatic
|
||||
fun PatchClass.dependsOn(patch: PatchClass): Boolean {
|
||||
if (this.patchName == patch.patchName) throw IllegalArgumentException("thisval and patch may not be the same")
|
||||
return this.dependencies.any { it.patch.java.patchName == this@dependsOn.patchName }
|
||||
}
|
||||
|
||||
class PatchDependency internal constructor(val patch: KClass<out Patch<Data>>, val type: DependencyType = DependencyType.HARD)
|
||||
}
|
||||
|
||||
object MethodFingerprintExtensions {
|
||||
|
@@ -3,14 +3,16 @@ package app.revanced.patcher.patch
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* A ReVanced patch.
|
||||
*
|
||||
* Can either be a [ResourcePatch] or a [BytecodePatch].
|
||||
* If it implements [Closeable], it will be closed after all patches have been executed.
|
||||
* Closing will be done in reverse execution order.
|
||||
*/
|
||||
abstract class Patch<out T : Data> {
|
||||
|
||||
/**
|
||||
* The main function of the [Patch] which the patcher will call.
|
||||
*/
|
||||
|
@@ -19,6 +19,35 @@ annotation class Patch(val include: Boolean = true)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MustBeDocumented
|
||||
@Deprecated(
|
||||
"Does not support new parameter 'type'",
|
||||
ReplaceWith("DependsOn")
|
||||
)
|
||||
annotation class Dependencies(
|
||||
val dependencies: Array<KClass<out Patch<Data>>> = []
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* Annotation for dependencies of [Patch]es .
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MustBeDocumented
|
||||
@Repeatable
|
||||
annotation class DependsOn(
|
||||
val value: KClass<out Patch<Data>>,
|
||||
val type: DependencyType = DependencyType.HARD
|
||||
)
|
||||
|
||||
enum class DependencyType {
|
||||
/**
|
||||
* Enforces that the dependency is applied, even if it was not selected.
|
||||
*/
|
||||
HARD,
|
||||
|
||||
/**
|
||||
* Applies the dependency only if it was selected.
|
||||
*/
|
||||
@Deprecated("Will be removed when Patch Options is implemented.")
|
||||
SOFT
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package app.revanced.patcher.usage.bytecode.annotation
|
||||
package app.revanced.patcher.usage.bytecode
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
@@ -1,4 +1,4 @@
|
||||
package app.revanced.patcher.usage.bytecode.patch
|
||||
package app.revanced.patcher.usage.bytecode
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
@@ -9,9 +9,10 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependencyType
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.usage.bytecode.fingerprints.ExampleFingerprint
|
||||
import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibility
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
@@ -36,6 +37,7 @@ import org.jf.dexlib2.util.Preconditions
|
||||
@Description("Example demonstration of a bytecode patch.")
|
||||
@ExampleResourceCompatibility
|
||||
@Version("0.0.1")
|
||||
@DependsOn(ExampleBytecodePatch::class, DependencyType.SOFT)
|
||||
class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
||||
// This function will be executed by the patcher.
|
||||
// You can treat it as a constructor
|
@@ -1,4 +1,4 @@
|
||||
package app.revanced.patcher.usage.bytecode.fingerprints
|
||||
package app.revanced.patcher.usage.bytecode
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
@@ -6,7 +6,7 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.usage.bytecode.annotation.ExampleBytecodeCompatibility
|
||||
import app.revanced.patcher.usage.bytecode.ExampleBytecodeCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
Reference in New Issue
Block a user