Implement jaggl and jagmisc resource transformation

master
Graham 4 years ago
parent 2b82451b96
commit a79dfd6f32
  1. 197
      bundler/src/main/java/dev/openrs2/bundler/transform/ResourceTransformer.kt

@ -1,10 +1,205 @@
package dev.openrs2.bundler.transform
import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.InsnMatcher
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.createIntConstant
import dev.openrs2.asm.transform.Transformer
import dev.openrs2.bundler.Resource
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.IntInsnNode
import org.objectweb.asm.tree.LdcInsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.TypeInsnNode
class ResourceTransformer(
private val resources: List<Resource>? = null,
private val glResources: List<List<Resource>> = Resource.compressGlResources(),
private val miscResources: List<Resource> = Resource.compressMiscResources()
) : Transformer()
) : Transformer() {
private var glBlocks = 0
private var miscBlocks = 0
override fun preTransform(classPath: ClassPath) {
glBlocks = 0
miscBlocks = 0
}
override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean {
val match = GL_RESOURCES_MATCHER.match(method).singleOrNull()
if (match != null) {
// remove everything but the PUTSTATIC
for (i in 0 until match.size - 1) {
method.instructions.remove(match[i])
}
// find the type of the resource class
val new = match[8] as TypeInsnNode
val type = new.desc
// create our own resource array
val list = InsnList()
create2dResourceArray(list, type, glResources, GL_LOADING_MESSAGES)
// insert our own resource array before the PUTSTATIC
method.instructions.insertBefore(match[match.size - 1], list)
glBlocks++
}
val miscMatch = MISC_RESOURCES_MATCHER.match(method).singleOrNull()
if (miscMatch != null) {
// remove everything but the PUTSTATIC
for (i in 0 until miscMatch.size - 1) {
method.instructions.remove(miscMatch[i])
}
// find the type of the resource class
val new = miscMatch[4] as TypeInsnNode
val type = new.desc
// create our own resource array
val list = InsnList()
createResourceArray(list, type, miscResources, MISC_LOADING_MESSAGES)
// insert our own resource array before the PUTSTATIC
method.instructions.insertBefore(miscMatch[miscMatch.size - 1], list)
miscBlocks++
}
return false
}
private fun create2dResourceArray(
list: InsnList,
type: String,
resources: List<List<Resource>>,
messages: List<String>
) {
list.add(createIntConstant(resources.size))
list.add(TypeInsnNode(Opcodes.ANEWARRAY, "[L$type;"))
for ((i, innerResources) in resources.withIndex()) {
list.add(InsnNode(Opcodes.DUP))
list.add(createIntConstant(i))
createResourceArray(list, type, innerResources, messages, progressSufix = true)
list.add(InsnNode(Opcodes.AASTORE))
}
}
private fun createResourceArray(
list: InsnList,
type: String,
resources: List<Resource>,
messages: List<String>,
progressSufix: Boolean = false
) {
list.add(createIntConstant(resources.size))
list.add(TypeInsnNode(Opcodes.ANEWARRAY, type))
for ((i, resource) in resources.withIndex()) {
list.add(InsnNode(Opcodes.DUP))
list.add(createIntConstant(i))
list.add(TypeInsnNode(Opcodes.NEW, type))
list.add(InsnNode(Opcodes.DUP))
list.add(LdcInsnNode(resource.destination))
list.add(LdcInsnNode(resource.source))
list.add(createIntConstant(messages.size))
list.add(TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/String"))
for ((j, message) in messages.withIndex()) {
val messageWithSuffix = if (progressSufix && resources.size > 1) {
"$message (${i + 1}/${resources.size})"
} else {
message
}
list.add(InsnNode(Opcodes.DUP))
list.add(createIntConstant(j))
list.add(LdcInsnNode(messageWithSuffix))
list.add(InsnNode(Opcodes.AASTORE))
}
list.add(createIntConstant(resource.uncompressedSize))
list.add(createIntConstant(resource.compressedSize))
list.add(createIntConstant(resource.digest.size))
list.add(IntInsnNode(Opcodes.NEWARRAY, Opcodes.T_INT))
for ((j, byte) in resource.digest.withIndex()) {
list.add(InsnNode(Opcodes.DUP))
list.add(createIntConstant(j))
list.add(createIntConstant(byte.toInt()))
list.add(InsnNode(Opcodes.IASTORE))
}
list.add(
MethodInsnNode(
Opcodes.INVOKESPECIAL,
type,
"<init>",
"(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;II[I)V"
)
)
list.add(InsnNode(Opcodes.AASTORE))
}
}
override fun postTransform(classPath: ClassPath) {
logger.info { "Replaced $glBlocks jaggl and $miscBlocks jagmisc resource constructor blocks" }
}
companion object {
private val logger = InlineLogger()
private const val RESOURCE_CONSTRUCTOR = """
NEW DUP
LDC
LDC
ICONST_4 ANEWARRAY (DUP ICONST LDC AASTORE)+
(ICONST | BIPUSH | SIPUSH | LDC)
(ICONST | BIPUSH | SIPUSH | LDC)
BIPUSH
NEWARRAY
(DUP (ICONST | BIPUSH) (ICONST | BIPUSH) IASTORE)+
INVOKESPECIAL
"""
private const val RESOURCE_ARRAY_CONSTRUCTOR = "ICONST ANEWARRAY (DUP ICONST $RESOURCE_CONSTRUCTOR AASTORE)+"
private val GL_RESOURCES_MATCHER = InsnMatcher.compile(
"""
ICONST ANEWARRAY
(
DUP ICONST
$RESOURCE_ARRAY_CONSTRUCTOR
AASTORE
)+
PUTSTATIC
"""
)
private val MISC_RESOURCES_MATCHER = InsnMatcher.compile("$RESOURCE_ARRAY_CONSTRUCTOR PUTSTATIC")
private val GL_LOADING_MESSAGES = listOf(
"Loading 3D library",
"Lade 3D-Softwarebibliothek",
"Chargement de la librairie 3D",
"Carregando biblioteca 3D"
)
private val MISC_LOADING_MESSAGES = listOf(
"Loading utility library",
"Lade Utility-Softwarebibliothek",
"Chargement de la librairie utilitaire",
"Carregando biblioteca de ferramentas"
)
}
}

Loading…
Cancel
Save