build: Bump ReVanced Patcher
BREAKING CHANGE: Various APIs have been changed or removed.
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package app.revanced.util
|
||||
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.util.Document
|
||||
import app.revanced.util.resource.BaseResource
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
private val classLoader = object {}.javaClass.classLoader
|
||||
|
||||
/**
|
||||
* Returns a sequence for all child nodes.
|
||||
*/
|
||||
fun NodeList.asSequence() = (0 until this.length).asSequence().map { this.item(it) }
|
||||
|
||||
/**
|
||||
* Returns a sequence for all child nodes.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun Node.childElementsSequence() = this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } as Sequence<Element>
|
||||
|
||||
/**
|
||||
* Performs the given [action] on each child element.
|
||||
*/
|
||||
inline fun Node.forEachChildElement(action: (Element) -> Unit) =
|
||||
childElementsSequence().forEach {
|
||||
action(it)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.insertFirst(node: Node) {
|
||||
if (hasChildNodes()) {
|
||||
insertBefore(node, firstChild)
|
||||
} else {
|
||||
appendChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ResourcePatchContext.copyResources(
|
||||
sourceResourceDirectory: String,
|
||||
vararg resources: ResourceGroup,
|
||||
) {
|
||||
val targetResourceDirectory = this["res", false]
|
||||
|
||||
for (resourceGroup in resources) {
|
||||
resourceGroup.resources.forEach { resource ->
|
||||
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
||||
Files.copy(
|
||||
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
|
||||
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)
|
||||
|
||||
/**
|
||||
* Iterate through the children of a node by its tag.
|
||||
* @param resource The xml resource.
|
||||
* @param targetTag The target xml node.
|
||||
* @param callback The callback to call when iterating over the nodes.
|
||||
*/
|
||||
fun ResourcePatchContext.iterateXmlNodeChildren(
|
||||
resource: String,
|
||||
targetTag: String,
|
||||
callback: (node: Node) -> Unit,
|
||||
) = document[classLoader.getResourceAsStream(resource)!!].use { document ->
|
||||
val stringsNode = document.getElementsByTagName(targetTag).item(0).childNodes
|
||||
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified node of the source [Document] to the target [Document].
|
||||
* @param source the source [Document].
|
||||
* @param target the target [Document]-
|
||||
* @return AutoCloseable that closes the [Document]s.
|
||||
*/
|
||||
fun String.copyXmlNode(
|
||||
source: Document,
|
||||
target: Document,
|
||||
): AutoCloseable {
|
||||
val hostNodes = source.getElementsByTagName(this).item(0).childNodes
|
||||
|
||||
val destinationNode = target.getElementsByTagName(this).item(0)
|
||||
|
||||
for (index in 0 until hostNodes.length) {
|
||||
val node = hostNodes.item(index).cloneNode(true)
|
||||
target.adoptNode(node)
|
||||
destinationNode.appendChild(node)
|
||||
}
|
||||
|
||||
return AutoCloseable {
|
||||
source.close()
|
||||
target.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource node child.
|
||||
*
|
||||
* @param resource The resource to add.
|
||||
* @param resourceCallback Called when a resource has been processed.
|
||||
*/
|
||||
internal fun Node.addResource(
|
||||
resource: BaseResource,
|
||||
resourceCallback: (BaseResource) -> Unit = { },
|
||||
) {
|
||||
appendChild(resource.serialize(ownerDocument, resourceCallback))
|
||||
}
|
||||
|
||||
internal fun org.w3c.dom.Document.getNode(tagName: String) = this.getElementsByTagName(tagName).item(0)
|
||||
|
||||
internal fun NodeList.findElementByAttributeValue(attributeName: String, value: String): Element? {
|
||||
for (i in 0 until length) {
|
||||
val node = item(i)
|
||||
if (node.nodeType == Node.ELEMENT_NODE) {
|
||||
val element = node as Element
|
||||
|
||||
if (element.getAttribute(attributeName) == value) {
|
||||
return element
|
||||
}
|
||||
|
||||
// Recursively search.
|
||||
val found = element.childNodes.findElementByAttributeValue(attributeName, value)
|
||||
if (found != null) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String): Element {
|
||||
return findElementByAttributeValue(attributeName, value) ?: throw PatchException("Could not find: $attributeName $value")
|
||||
}
|
||||
|
||||
internal fun Element.copyAttributesFrom(oldContainer: Element) {
|
||||
// Copy attributes from the old element to the new element
|
||||
val attributes = oldContainer.attributes
|
||||
for (i in 0 until attributes.length) {
|
||||
val attr = attributes.item(i) as Attr
|
||||
setAttribute(attr.name, attr.value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user