1
mirror of https://github.com/revanced/revanced-patcher synced 2025-09-06 16:38:50 +02:00

Compare commits

...

18 Commits

Author SHA1 Message Date
semantic-release-bot
070c09cf71 chore(release): 1.10.2 [skip ci]
## [1.10.2](https://github.com/revanced/revanced-patcher/compare/v1.10.1...v1.10.2) (2022-06-23)

### Bug Fixes

* dexlib must be propagated ([b738dcd](b738dcd7ea))
2022-06-23 10:21:25 +00:00
Sculas
b738dcd7ea fix: dexlib must be propagated 2022-06-23 12:20:04 +02:00
semantic-release-bot
8efcf329bb chore(release): 1.10.1 [skip ci]
## [1.10.1](https://github.com/revanced/revanced-patcher/compare/v1.10.0...v1.10.1) (2022-06-23)

### Bug Fixes

* callback only when inteded ([e3bf367](e3bf367ad6))
* mutability of local variable `modified` ([0e87ef5](0e87ef56c4))
2022-06-23 06:59:43 +00:00
oSumAtrIX
0e87ef56c4 fix: mutability of local variable modified 2022-06-23 08:57:41 +02:00
oSumAtrIX
e3bf367ad6 fix: callback only when inteded 2022-06-23 08:54:47 +02:00
semantic-release-bot
3d61dacbda chore(release): 1.10.0 [skip ci]
# [1.10.0](https://github.com/revanced/revanced-patcher/compare/v1.9.0...v1.10.0) (2022-06-23)

### Features

* improve logging ([c20dfe1](c20dfe12d5))
2022-06-23 00:12:04 +00:00
oSumAtrIX
c20dfe12d5 feat: improve logging 2022-06-23 02:10:43 +02:00
semantic-release-bot
78663cde88 chore(release): 1.9.0 [skip ci]
# [1.9.0](https://github.com/revanced/revanced-patcher/compare/v1.8.0...v1.9.0) (2022-06-22)

### Bug Fixes

* callback for each file instead of class ([930768d](930768dfb3))

### Features

* yield the patch result ([dde5385](dde5385232))
2022-06-22 23:43:17 +00:00
oSumAtrIX
dde5385232 feat: yield the patch result 2022-06-23 01:41:52 +02:00
oSumAtrIX
930768dfb3 fix: callback for each file instead of class 2022-06-23 01:41:52 +02:00
semantic-release-bot
1f4bc5079f chore(release): 1.8.0 [skip ci]
# [1.8.0](https://github.com/revanced/revanced-patcher/compare/v1.7.2...v1.8.0) (2022-06-22)

### Features

* logging class ([caf2745](caf2745805))
2022-06-22 23:25:29 +00:00
oSumAtrIX
caf2745805 feat: logging class 2022-06-23 01:23:35 +02:00
oSumAtrIX
a4529c3fee refactor: logging and exception strings 2022-06-22 16:47:58 +02:00
semantic-release-bot
835c0f9f7a chore(release): 1.7.2 [skip ci]
## [1.7.2](https://github.com/revanced/revanced-patcher/compare/v1.7.1...v1.7.2) (2022-06-22)

### Bug Fixes

* add execute permission to `./gradlew` file ([#46](https://github.com/revanced/revanced-patcher/issues/46)) ([34f607a](34f607aa24))
2022-06-22 14:17:16 +00:00
Oskar
34f607aa24 fix: add execute permission to ./gradlew file (#46) 2022-06-22 16:16:00 +02:00
Lucaskyy
0f38b94701 Merge remote-tracking branch 'origin/main' into main 2022-06-22 16:13:34 +02:00
Lucaskyy
39bb1b25dc refactor: add logging 2022-06-22 16:13:23 +02:00
Lucaskyy
4fc63a4d8a refactor: add callbacks for applyPatches & addFiles 2022-06-22 15:37:33 +02:00
9 changed files with 137 additions and 49 deletions

View File

@@ -28,8 +28,6 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: "lts/*"
- name: Make gradlew executable
run: chmod +x gradlew
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,3 +1,51 @@
## [1.10.2](https://github.com/revanced/revanced-patcher/compare/v1.10.1...v1.10.2) (2022-06-23)
### Bug Fixes
* dexlib must be propagated ([b738dcd](https://github.com/revanced/revanced-patcher/commit/b738dcd7ea04f5fe56e66af46fb11541fe54f6af))
## [1.10.1](https://github.com/revanced/revanced-patcher/compare/v1.10.0...v1.10.1) (2022-06-23)
### Bug Fixes
* callback only when inteded ([e3bf367](https://github.com/revanced/revanced-patcher/commit/e3bf367ad6615b30b06027d65f906b2588567a7f))
* mutability of local variable `modified` ([0e87ef5](https://github.com/revanced/revanced-patcher/commit/0e87ef56c418d5c37d58abb9b27f85e25fd44f81))
# [1.10.0](https://github.com/revanced/revanced-patcher/compare/v1.9.0...v1.10.0) (2022-06-23)
### Features
* improve logging ([c20dfe1](https://github.com/revanced/revanced-patcher/commit/c20dfe12d5c737264b844e6634de11bf1e1629f0))
# [1.9.0](https://github.com/revanced/revanced-patcher/compare/v1.8.0...v1.9.0) (2022-06-22)
### Bug Fixes
* callback for each file instead of class ([930768d](https://github.com/revanced/revanced-patcher/commit/930768dfb31dc5fa6c248050b08ac117c40ee0a3))
### Features
* yield the patch result ([dde5385](https://github.com/revanced/revanced-patcher/commit/dde5385232abddc8a85d6e9a939549b71dd9130e))
# [1.8.0](https://github.com/revanced/revanced-patcher/compare/v1.7.2...v1.8.0) (2022-06-22)
### Features
* logging class ([caf2745](https://github.com/revanced/revanced-patcher/commit/caf2745805ffd4b59fa81e79cc489b1a1a5c5d89))
## [1.7.2](https://github.com/revanced/revanced-patcher/compare/v1.7.1...v1.7.2) (2022-06-22)
### Bug Fixes
* add execute permission to `./gradlew` file ([#46](https://github.com/revanced/revanced-patcher/issues/46)) ([34f607a](https://github.com/revanced/revanced-patcher/commit/34f607aa24d89a777d906cc887203f343ce3fd07))
## [1.7.1](https://github.com/revanced/revanced-patcher/compare/v1.7.0...v1.7.1) (2022-06-22)

View File

@@ -21,8 +21,9 @@ repositories {
}
dependencies {
api("app.revanced:multidexlib2:2.5.2.r2")
implementation("xpp3:xpp3:1.1.4c")
implementation("app.revanced:multidexlib2:2.5.2.r2")
implementation("org.smali:smali:2.5.2")
implementation("org.apktool:apktool-lib:2.6.5-SNAPSHOT")

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 1.7.1
version = 1.10.2

0
gradlew vendored Normal file → Executable file
View File

View File

@@ -42,13 +42,18 @@ val NAMER = BasicDexFileNamer()
* @param options The options for the patcher.
*/
class Patcher(private val options: PatcherOptions) {
val data: PatcherData
private val logger = options.logger
private val opcodes: Opcodes
val data: PatcherData
init {
val extInputFile = ExtFile(options.inputFile)
val outDir = File(options.resourceCacheDirectory)
if (outDir.exists()) outDir.deleteRecursively()
if (outDir.exists()) {
logger.info("Deleting existing resource cache directory")
outDir.deleteRecursively()
}
outDir.mkdirs()
val androlib = Androlib(BuildOptions().also { it.setBuildOptions(options) })
@@ -57,6 +62,8 @@ class Patcher(private val options: PatcherOptions) {
val packageMetadata = PackageMetadata()
if (options.patchResources) {
logger.info("Decoding resources")
// decode resources to cache directory
androlib.decodeManifestWithResources(extInputFile, outDir, resourceTable)
androlib.decodeResourcesFull(extInputFile, outDir, resourceTable)
@@ -71,6 +78,8 @@ class Patcher(private val options: PatcherOptions) {
}
} else {
logger.info("Only decoding AndroidManifest.xml because resource patching is disabled")
// create decoder for the resource table
val decoder = ResAttrDecoder()
decoder.currentPackage = ResPackage(resourceTable, 0, null)
@@ -93,6 +102,8 @@ class Patcher(private val options: PatcherOptions) {
packageMetadata.metaInfo.versionInfo = resourceTable.versionInfo
packageMetadata.metaInfo.sdkInfo = resourceTable.sdkInfo
logger.info("Reading dex files")
// read dex files
val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null)
// get the opcodes
@@ -111,23 +122,36 @@ class Patcher(private val options: PatcherOptions) {
* @param throwOnDuplicates If this is set to true, the patcher will throw an exception if a duplicate class has been found.
*/
fun addFiles(
files: List<File>, allowedOverwrites: Iterable<String> = emptyList(), throwOnDuplicates: Boolean = false
files: List<File>,
allowedOverwrites: Iterable<String> = emptyList(),
throwOnDuplicates: Boolean = false,
callback: (File) -> Unit
) {
for (file in files) {
var modified = false
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
val e = data.bytecodeData.classes.internalClasses.findIndexed { it.type == classDef.type }
if (e != null) {
if (throwOnDuplicates) {
throw Exception("Class ${classDef.type} has already been added to the patcher.")
}
val (_, idx) = e
if (allowedOverwrites.contains(classDef.type)) {
data.bytecodeData.classes.internalClasses[idx] = classDef
}
val type = classDef.type
val existingClass = data.bytecodeData.classes.internalClasses.findIndexed { it.type == type }
if (existingClass == null) {
if (throwOnDuplicates) throw Exception("Class $type has already been added to the patcher")
logger.trace("Merging $type")
data.bytecodeData.classes.internalClasses.add(classDef)
modified = true
continue
}
data.bytecodeData.classes.internalClasses.add(classDef)
if (!allowedOverwrites.contains(type)) continue
logger.trace("Overwriting $type")
val index = existingClass.second
data.bytecodeData.classes.internalClasses[index] = classDef
modified = true
}
if (modified) callback(file)
}
}
@@ -168,19 +192,19 @@ class Patcher(private val options: PatcherOptions) {
val resDirectory = cacheDirectory.resolve("res")
val includedFiles = metaInfo.usesFramework.ids.map { id ->
androlibResources.getFrameworkApk(
id,
metaInfo.usesFramework.tag
id, metaInfo.usesFramework.tag
)
}.toTypedArray()
logger.info("Compiling resources")
androlibResources.aaptPackage(
aaptFile, manifestFile, resDirectory, null,
null, includedFiles
aaptFile, manifestFile, resDirectory, null, null, includedFiles
)
resourceFile = aaptFile
}
logger.trace("Creating new dex file")
val newDexFile = object : DexFile {
override fun getClasses(): Set<ClassDef> {
data.bytecodeData.classes.applyProxies()
@@ -192,7 +216,8 @@ class Patcher(private val options: PatcherOptions) {
}
}
// write dex modified files
// write modified dex files
logger.info("Writing modified dex files")
val dexFiles = mutableMapOf<String, MemoryDataStore>()
MultiDexIO.writeDexFile(
true, -1, // core count
@@ -202,9 +227,7 @@ class Patcher(private val options: PatcherOptions) {
return PatcherResult(
dexFiles.map {
app.revanced.patcher.util.dex.DexFile(it.key, it.value.readAt(0))
},
metaInfo.doNotCompress.toList(),
resourceFile
}, metaInfo.doNotCompress.toList(), resourceFile
)
}
@@ -228,7 +251,11 @@ class Patcher(private val options: PatcherOptions) {
val patchName = patch.patchName
// if the patch has already applied silently skip it
if (appliedPatches.contains(patchName)) return PatchResultSuccess()
if (appliedPatches.contains(patchName)) {
logger.trace("Skipping patch $patchName because it has already been applied")
return PatchResultSuccess()
}
appliedPatches.add(patchName)
// recursively apply all dependency patches
@@ -246,7 +273,9 @@ class Patcher(private val options: PatcherOptions) {
// if the current patch is a resource patch but resource patching is disabled, return an error
val isResourcePatch = patchInstance is ResourcePatch
if (!options.patchResources && isResourcePatch) return PatchResultError("$patchName is a resource patch, but resource patching is disabled.")
if (!options.patchResources && isResourcePatch) {
return PatchResultError("$patchName is a resource patch, but resource patching is disabled.")
}
// TODO: find a solution for this
val data = if (isResourcePatch) {
@@ -258,6 +287,8 @@ class Patcher(private val options: PatcherOptions) {
data.bytecodeData
}
logger.trace("Executing patch $patchName of type: ${if (isResourcePatch) "resource" else "bytecode"}")
return try {
patchInstance.execute(data)
} catch (e: Exception) {
@@ -268,30 +299,23 @@ class Patcher(private val options: PatcherOptions) {
/**
* Apply patches loaded into the patcher.
* @param stopOnError If true, the patches will stop on the first error.
* @return A map of [PatchResultSuccess]. If the [Patch] was successfully applied,
* [PatchResultSuccess] will always be returned to the wrapping Result object.
* If the [Patch] failed to apply, an Exception will always be returned to the wrapping Result object.
* @return A pair of the name of the [Patch] and its [PatchResult].
*/
fun applyPatches(
stopOnError: Boolean = false, callback: (String) -> Unit = {}
): Map<String, Result<PatchResultSuccess>> {
fun applyPatches(stopOnError: Boolean = false) = sequence {
logger.trace("Applying all patches")
val appliedPatches = mutableListOf<String>()
return buildMap {
for (patch in data.patches) {
val result = applyPatch(patch, appliedPatches)
for (patch in data.patches) {
val patchResult = applyPatch(patch, appliedPatches)
val name = patch.patchName
callback(name)
this[name] = if (result.isSuccess()) {
Result.success(result.success()!!)
} else {
Result.failure(result.error()!!)
}
if (stopOnError && result.isError()) break
val result = if (patchResult.isSuccess()) {
Result.success(patchResult.success()!!)
} else {
Result.failure(patchResult.error()!!)
}
yield(patch.patchName to result)
if (stopOnError && patchResult.isError()) break
}
}
}
@@ -300,4 +324,4 @@ private fun BuildOptions.setBuildOptions(options: PatcherOptions) {
this.aaptPath = options.aaptPath
this.useAapt2 = true
this.frameworkFolderLocation = options.frameworkFolderLocation
}
}

View File

@@ -1,19 +1,23 @@
package app.revanced.patcher
import app.revanced.patcher.logging.impl.NopLogger
import app.revanced.patcher.logging.Logger
import java.io.File
/**
* Options for a patcher.
* Options for the [Patcher].
* @param inputFile The input file (usually an apk file).
* @param resourceCacheDirectory Directory to cache resources.
* @param patchResources Weather to use the resource patcher. Resources will still need to be decoded.
* @param aaptPath Optional path to a custom aapt binary.
* @param frameworkFolderLocation Optional path to a custom framework folder.
* @param logger Custom logger implementation for the [Patcher].
*/
data class PatcherOptions(
internal val inputFile: File,
internal val resourceCacheDirectory: String,
internal val patchResources: Boolean = false,
internal val aaptPath: String = "",
internal val frameworkFolderLocation: String? = null
internal val frameworkFolderLocation: String? = null,
internal val logger: Logger = NopLogger
)

View File

@@ -0,0 +1,8 @@
package app.revanced.patcher.logging
interface Logger {
fun error(msg: String) {}
fun warn(msg: String) {}
fun info(msg: String) {}
fun trace(msg: String) {}
}

View File

@@ -0,0 +1,5 @@
package app.revanced.patcher.logging.impl
import app.revanced.patcher.logging.Logger
object NopLogger : Logger