mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-29 13:20:19 +02:00
181 lines
5.5 KiB
Kotlin
181 lines
5.5 KiB
Kotlin
@file:Suppress("DEPRECATION", "MemberVisibilityCanBePrivate", "SpellCheckingInspection")
|
|
|
|
package app.revanced.util
|
|
|
|
import app.revanced.patcher.data.ResourceContext
|
|
import app.revanced.patcher.patch.PatchException
|
|
import app.revanced.patcher.patch.options.PatchOption
|
|
import app.revanced.patcher.util.DomFileEditor
|
|
import org.w3c.dom.Element
|
|
import org.w3c.dom.Node
|
|
import java.io.File
|
|
import java.io.InputStream
|
|
import java.nio.file.Files
|
|
import java.nio.file.StandardCopyOption
|
|
|
|
val classLoader: ClassLoader = object {}.javaClass.classLoader
|
|
|
|
fun PatchOption<String>.valueOrThrow() = value
|
|
?: throw PatchException("Invalid patch option: $title.")
|
|
|
|
fun PatchOption<Int?>.valueOrThrow() = value
|
|
?: throw PatchException("Invalid patch option: $title.")
|
|
|
|
fun PatchOption<String>.lowerCaseOrThrow() = valueOrThrow()
|
|
.lowercase()
|
|
|
|
fun PatchOption<String>.underBarOrThrow() = lowerCaseOrThrow()
|
|
.replace(" ", "_")
|
|
|
|
fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
|
|
val child = ownerDocument.createElement(tagName)
|
|
child.block()
|
|
appendChild(child)
|
|
}
|
|
|
|
fun Node.cloneNodes(parent: Node) {
|
|
val node = cloneNode(true)
|
|
parent.appendChild(node)
|
|
parent.removeChild(this)
|
|
}
|
|
|
|
/**
|
|
* Recursively traverse the DOM tree starting from the given root node.
|
|
*
|
|
* @param action function that is called for every node in the tree.
|
|
*/
|
|
fun Node.doRecursively(action: (Node) -> Unit) {
|
|
action(this)
|
|
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
|
}
|
|
|
|
fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) {
|
|
val child = ownerDocument.createElement(tagName)
|
|
child.block()
|
|
parentNode.insertBefore(child, targetNode)
|
|
}
|
|
|
|
fun String.startsWithAny(vararg prefixes: String): Boolean {
|
|
for (prefix in prefixes)
|
|
if (this.startsWith(prefix))
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
fun ResourceContext.copyFile(
|
|
resourceGroup: List<ResourceGroup>,
|
|
path: String,
|
|
warning: String
|
|
): Boolean {
|
|
resourceGroup.let { resourceGroups ->
|
|
try {
|
|
val filePath = File(path)
|
|
val resourceDirectory = this["res"]
|
|
|
|
resourceGroups.forEach { group ->
|
|
val fromDirectory = filePath.resolve(group.resourceDirectoryName)
|
|
val toDirectory = resourceDirectory.resolve(group.resourceDirectoryName)
|
|
|
|
group.resources.forEach { iconFileName ->
|
|
Files.write(
|
|
toDirectory.resolve(iconFileName).toPath(),
|
|
fromDirectory.resolve(iconFileName).readBytes()
|
|
)
|
|
}
|
|
}
|
|
|
|
return true
|
|
} catch (_: Exception) {
|
|
println(warning)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Copy resources from the current class loader to the resource directory.
|
|
*
|
|
* @param sourceResourceDirectory The source resource directory name.
|
|
* @param resources The resources to copy.
|
|
*/
|
|
fun ResourceContext.copyResources(
|
|
sourceResourceDirectory: String,
|
|
vararg resources: ResourceGroup,
|
|
) {
|
|
val targetResourceDirectory = this["res"]
|
|
|
|
for (resourceGroup in resources) {
|
|
resourceGroup.resources.forEach { resource ->
|
|
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
|
|
|
inputStreamFromBundledResource(
|
|
sourceResourceDirectory,
|
|
resourceFile
|
|
)?.let { inputStream ->
|
|
Files.copy(
|
|
inputStream,
|
|
targetResourceDirectory.resolve(resourceFile).toPath(),
|
|
StandardCopyOption.REPLACE_EXISTING,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun inputStreamFromBundledResource(
|
|
sourceResourceDirectory: String,
|
|
resourceFile: String,
|
|
): InputStream? = classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")
|
|
|
|
/**
|
|
* Resource names mapped to their corresponding resource data.
|
|
* @param resourceDirectoryName The name of the directory of the resource.
|
|
* @param resources A list of resource names.
|
|
*/
|
|
class ResourceGroup(val resourceDirectoryName: String, vararg val resources: String)
|
|
|
|
/**
|
|
* Copy resources from the current class loader to the resource directory.
|
|
* @param resourceDirectory The directory of the resource.
|
|
* @param targetResource The target resource.
|
|
* @param elementTag The element to copy.
|
|
*/
|
|
fun ResourceContext.copyXmlNode(
|
|
resourceDirectory: String,
|
|
targetResource: String,
|
|
elementTag: String
|
|
) = inputStreamFromBundledResource(
|
|
resourceDirectory,
|
|
targetResource
|
|
)?.let { inputStream ->
|
|
// Copy nodes from the resources node to the real resource node
|
|
elementTag.copyXmlNode(
|
|
this.xmlEditor[inputStream],
|
|
this.xmlEditor["res/$targetResource"]
|
|
).close()
|
|
}
|
|
|
|
/**
|
|
* Copies the specified node of the source [DomFileEditor] to the target [DomFileEditor].
|
|
* @param source the source [DomFileEditor].
|
|
* @param target the target [DomFileEditor]-
|
|
* @return AutoCloseable that closes the target [DomFileEditor]s.
|
|
*/
|
|
fun String.copyXmlNode(source: DomFileEditor, target: DomFileEditor): AutoCloseable {
|
|
val hostNodes = source.file.getElementsByTagName(this).item(0).childNodes
|
|
|
|
val destinationResourceFile = target.file
|
|
val destinationNode = destinationResourceFile.getElementsByTagName(this).item(0)
|
|
|
|
for (index in 0 until hostNodes.length) {
|
|
val node = hostNodes.item(index).cloneNode(true)
|
|
destinationResourceFile.adoptNode(node)
|
|
destinationNode.appendChild(node)
|
|
}
|
|
|
|
return AutoCloseable {
|
|
source.close()
|
|
target.close()
|
|
}
|
|
} |