diff --git a/bundler/src/main/java/dev/openrs2/bundler/transform/ResourceTransformer.kt b/bundler/src/main/java/dev/openrs2/bundler/transform/ResourceTransformer.kt index f7f52911d5..9419c92151 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/transform/ResourceTransformer.kt +++ b/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? = null, private val glResources: List> = Resource.compressGlResources(), private val miscResources: List = 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>, + messages: List + ) { + 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, + messages: List, + 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, + "", + "(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" + ) + } +}