Add ArtifactLink support for newer loaders

Signed-off-by: Graham <gpe@openrs2.org>
master
Graham 6 months ago
parent 1f79a47839
commit 566a49f6a7
  1. 313
      archive/src/main/kotlin/org/openrs2/archive/client/ClientImporter.kt

@ -38,6 +38,7 @@ import org.openrs2.asm.io.LibraryReader
import org.openrs2.asm.io.Pack200LibraryReader
import org.openrs2.asm.io.PackClassLibraryReader
import org.openrs2.asm.nextReal
import org.openrs2.asm.previousReal
import org.openrs2.buffer.use
import org.openrs2.compress.gzip.Gzip
import org.openrs2.db.Database
@ -806,147 +807,259 @@ public class ClientImporter @Inject constructor(
val loader = library["loader"]
if (loader != null) {
val links = mutableListOf<ArtifactLink>()
val paths = mutableSetOf<String>()
val links = parseLoaderLinks(loader)
if (links.isNotEmpty()) {
return links
}
return parseResourceLinks(library)
}
// TODO(gpe)
return emptyList()
}
private fun parseLoaderLinks(loader: ClassNode): List<ArtifactLink> {
val links = mutableListOf<ArtifactLink>()
val paths = mutableSetOf<String>()
for (method in loader.methods) {
if (method.name != "run" || method.desc != "()V") {
for (method in loader.methods) {
if (method.name != "run" || method.desc != "()V") {
continue
}
for (insn in method.instructions) {
if (insn !is MethodInsnNode || insn.owner != loader.name || !insn.desc.endsWith(")[B")) {
continue
}
for (insn in method.instructions) {
if (insn !is MethodInsnNode || insn.owner != loader.name || !insn.desc.endsWith(")[B")) {
// TODO(gpe): extract file size too (tricky due to dummy arguments)
val exprs = getArgumentExpressions(insn) ?: continue
for (expr in exprs) {
val single = expr.singleOrNull() ?: continue
if (single !is LdcInsnNode) {
continue
}
// TODO(gpe): extract file size too (tricky due to dummy arguments)
val exprs = getArgumentExpressions(insn) ?: continue
for (expr in exprs) {
val single = expr.singleOrNull() ?: continue
if (single !is LdcInsnNode) {
continue
}
val cst = single.cst
if (cst is String && FILE_NAME_REGEX.matches(cst)) {
paths += cst
}
val cst = single.cst
if (cst is String && FILE_NAME_REGEX.matches(cst)) {
paths += cst
}
}
}
}
val hashes = mutableMapOf<AbstractInsnNode, ByteArray>()
for (method in loader.methods) {
for (match in SHA1_CMP_MATCHER.match(method)) {
val sha1 = ByteArray(SHA1_BYTES)
var i = 0
val hashes = mutableMapOf<AbstractInsnNode, ByteArray>()
while (i < match.size) {
var n = match[i++].intConstant
if (n != null) {
i++ // ALOAD
}
for (method in loader.methods) {
for (match in SHA1_CMP_MATCHER.match(method)) {
val sha1 = ByteArray(SHA1_BYTES)
var i = 0
val index = match[i++].intConstant!!
i++ // BALOAD
while (i < match.size) {
var n = match[i++].intConstant
if (n != null) {
i++ // ALOAD
}
var xor = false
if (i + 1 < match.size && match[i + 1].opcode == Opcodes.IXOR) {
i += 2 // ICONST_M1, IXOR
xor = true
}
val index = match[i++].intConstant!!
i++ // BALOAD
if (match[i].opcode == Opcodes.IFNE) {
n = 0
i++
} else {
if (n == null) {
n = match[i++].intConstant!!
}
var xor = false
if (i + 1 < match.size && match[i + 1].opcode == Opcodes.IXOR) {
i += 2 // ICONST_M1, IXOR
xor = true
}
i++ // ICMP_IFNE
if (match[i].opcode == Opcodes.IFNE) {
n = 0
i++
} else {
if (n == null) {
n = match[i++].intConstant!!
}
if (xor) {
n = n.inv()
}
i++ // ICMP_IFNE
}
sha1[index] = n.toByte()
if (xor) {
n = n.inv()
}
hashes[match[0]] = sha1
sha1[index] = n.toByte()
}
}
for (method in loader.methods) {
for (match in PATH_CMP_MATCHER.match(method)) {
val first = match[0]
val ldc = if (first is LdcInsnNode) {
first
} else {
match[1] as LdcInsnNode
}
hashes[match[0]] = sha1
}
}
val path = ldc.cst
if (path !is String) {
continue
}
for (method in loader.methods) {
for (match in PATH_CMP_MATCHER.match(method)) {
val first = match[0]
val ldc = if (first is LdcInsnNode) {
first
} else {
match[1] as LdcInsnNode
}
val acmp = match[2] as JumpInsnNode
val target = if (acmp.opcode == Opcodes.IF_ACMPNE) {
acmp.nextReal
} else {
acmp.label.nextReal
}
val path = ldc.cst
if (path !is String) {
continue
}
val hash = hashes.remove(target) ?: continue
if (!paths.remove(path)) {
logger.warn { "Adding link for unused file $path" }
}
val acmp = match[2] as JumpInsnNode
val target = if (acmp.opcode == Opcodes.IF_ACMPNE) {
acmp.nextReal
} else {
acmp.label.nextReal
}
links += parseLink(path, hash)
val hash = hashes.remove(target) ?: continue
if (!paths.remove(path)) {
logger.warn { "Adding link for unused file $path" }
}
}
if (paths.size != hashes.size || paths.size > 1) {
throw IllegalArgumentException()
} else if (paths.size == 1) {
links += parseLink(paths.single(), hashes.values.single())
links += parseLink(path, hash, null)
}
}
return links
if (paths.size != hashes.size || paths.size > 1) {
throw IllegalArgumentException()
} else if (paths.size == 1) {
links += parseLink(paths.single(), hashes.values.single(), null)
}
// TODO(gpe)
return emptyList()
return links
}
private fun parseLink(path: String, sha1: ByteArray): ArtifactLink {
val m = FILE_NAME_REGEX.matchEntire(path) ?: throw IllegalArgumentException()
val (name, crc1, ext, crc2) = m.destructured
private fun parseResourceLinks(library: Library): List<ArtifactLink> {
val links = mutableListOf<ArtifactLink>()
val type = when (name) {
// TODO(gpe): funorb loaders
"runescape", "client" -> ArtifactType.CLIENT
"unpackclass" -> ArtifactType.UNPACKCLASS
"jogl", "jogltrimmed" -> ArtifactType.JOGL
"jogl_awt" -> ArtifactType.JOGL_AWT
else -> throw IllegalArgumentException()
for (clazz in library) {
val clinit = clazz.methods.firstOrNull { it.name == "<clinit>" && it.desc == "()V" }
if (clinit != null) {
for (match in RESOURCE_CTOR_MATCHER.match(clinit)) {
val srcLdc = match[1] as LdcInsnNode
val src = srcLdc.cst
if (src !is String) {
continue
}
val newArray = match.single { it.opcode == Opcodes.NEWARRAY }
val off = match.indexOf(newArray) + 3
val sha1 = ByteArray(SHA1_BYTES) { i ->
val insn = match[off + i * 4]
insn.intConstant!!.toByte()
}
val size = newArray.previousReal!!.previousReal!!.previousReal!!.intConstant!!
links += parseLink(src, sha1, size)
}
}
}
return links
}
private fun parseLink(path: String, sha1: ByteArray, size: Int?): ArtifactLink {
val m = FILE_NAME_REGEX.matchEntire(path) ?: throw IllegalArgumentException(path)
val (name, namePrefix, crc1, ext, crc2) = m.destructured
val format = when (ext) {
"pack200" -> ArtifactFormat.PACK200
"js5" -> ArtifactFormat.PACKCLASS
"jar", "pack" -> ArtifactFormat.JAR
"dll" -> ArtifactFormat.NATIVE
else -> throw IllegalArgumentException()
"dll", "lib" -> ArtifactFormat.NATIVE
else -> throw IllegalArgumentException(ext)
}
val os = if (format == ArtifactFormat.NATIVE) OperatingSystem.WINDOWS else OperatingSystem.INDEPENDENT
val arch = if (format == ArtifactFormat.NATIVE) Architecture.X86 else Architecture.INDEPENDENT
val jvm = if (format == ArtifactFormat.NATIVE) Jvm.SUN else Jvm.INDEPENDENT
val type: ArtifactType
val os: OperatingSystem
val arch: Architecture
val jvm: Jvm
if (format == ArtifactFormat.NATIVE) {
data class Tuple(
val type: ArtifactType,
val os: OperatingSystem,
val arch: Architecture,
val jvm: Jvm
)
val tuple = when (name) {
"browsercontrol" -> Tuple(ArtifactType.BROWSERCONTROL, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jaggl_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jaggl_1" -> Tuple(ArtifactType.JAGGL, OperatingSystem.WINDOWS, Architecture.AMD64, Jvm.SUN)
"jaggl_2" -> Tuple(ArtifactType.JAGGL, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jaggl_3" -> Tuple(ArtifactType.JAGGL, OperatingSystem.LINUX, Architecture.AMD64, Jvm.SUN)
"jaggl_4" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.POWERPC, Jvm.SUN)
// TODO: is jaggl_5 correct? the loader doesn't use it
"jaggl_5" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.X86, Jvm.SUN)
"jaggl_6" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.AMD64, Jvm.SUN)
"jaggl_7" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.UNIVERSAL, Jvm.SUN)
"jaggl_0_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jaggl_1_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jaggl_1_1" -> Tuple(ArtifactType.JAGGL_DRI, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jaggl_2_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.POWERPC, Jvm.SUN)
"jaggl_3_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.X86, Jvm.SUN)
"jaggl_4_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.WINDOWS, Architecture.AMD64, Jvm.SUN)
"jaggl_5_0" -> Tuple(ArtifactType.JAGGL, OperatingSystem.MACOS, Architecture.AMD64, Jvm.SUN)
"jagmisc_0" -> Tuple(ArtifactType.JAGMISC, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jagmisc_1" -> Tuple(ArtifactType.JAGMISC, OperatingSystem.WINDOWS, Architecture.X86, Jvm.MICROSOFT)
"jagmisc_2" -> Tuple(ArtifactType.JAGMISC, OperatingSystem.WINDOWS, Architecture.AMD64, Jvm.SUN)
"jogl" -> Tuple(ArtifactType.JOGL, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jogl_awt" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jogl_0_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jogl_0_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
"jogl_1_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.WINDOWS, Architecture.AMD64, Jvm.SUN)
"jogl_1_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.WINDOWS, Architecture.AMD64, Jvm.SUN)
"jogl_2_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jogl_2_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jogl_2_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.LINUX, Architecture.X86, Jvm.SUN)
"jogl_3_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.LINUX, Architecture.AMD64, Jvm.SUN)
"jogl_3_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.LINUX, Architecture.AMD64, Jvm.SUN)
"jogl_3_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.LINUX, Architecture.AMD64, Jvm.SUN)
"jogl_4_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.MACOS, Architecture.POWERPC, Jvm.SUN)
"jogl_4_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.MACOS, Architecture.POWERPC, Jvm.SUN)
"jogl_5_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.MACOS, Architecture.UNIVERSAL, Jvm.SUN)
"jogl_5_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.MACOS, Architecture.UNIVERSAL, Jvm.SUN)
"jogl_6_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.SOLARIS, Architecture.AMD64, Jvm.SUN)
"jogl_6_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.SOLARIS, Architecture.AMD64, Jvm.SUN)
"jogl_6_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.SOLARIS, Architecture.AMD64, Jvm.SUN)
"jogl_7_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.SOLARIS, Architecture.X86, Jvm.SUN)
"jogl_7_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.SOLARIS, Architecture.X86, Jvm.SUN)
"jogl_7_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.SOLARIS, Architecture.X86, Jvm.SUN)
"jogl_8_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.SOLARIS, Architecture.SPARC, Jvm.SUN)
"jogl_8_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.SOLARIS, Architecture.SPARC, Jvm.SUN)
"jogl_8_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.SOLARIS, Architecture.SPARC, Jvm.SUN)
"jogl_9_0" -> Tuple(ArtifactType.JOGL, OperatingSystem.SOLARIS, Architecture.SPARCV9, Jvm.SUN)
"jogl_9_1" -> Tuple(ArtifactType.JOGL_AWT, OperatingSystem.SOLARIS, Architecture.SPARCV9, Jvm.SUN)
"jogl_9_2" -> Tuple(ArtifactType.GLUEGEN_RT, OperatingSystem.SOLARIS, Architecture.SPARCV9, Jvm.SUN)
"sw3d_0" -> Tuple(ArtifactType.SW3D, OperatingSystem.WINDOWS, Architecture.X86, Jvm.SUN)
else -> throw IllegalArgumentException(name)
}
type = tuple.type
os = tuple.os
arch = tuple.arch
jvm = tuple.jvm
} else {
// TODO(gpe): funorb loaders
type = when (namePrefix) {
"runescape", "client" -> ArtifactType.CLIENT
"runescape_gl" -> ArtifactType.CLIENT_GL
"unpackclass" -> ArtifactType.UNPACKCLASS
"jogl", "jogltrimmed" -> ArtifactType.JOGL
"jaggl" -> ArtifactType.JAGGL
else -> throw IllegalArgumentException(namePrefix)
}
os = OperatingSystem.INDEPENDENT
arch = Architecture.INDEPENDENT
jvm = Jvm.INDEPENDENT
}
val crc = crc1.toIntOrNull() ?: crc2.toIntOrNull() ?: throw IllegalArgumentException()
@ -958,7 +1071,7 @@ public class ClientImporter @Inject constructor(
jvm,
crc,
sha1,
null
size
)
}
@ -1009,9 +1122,11 @@ public class ClientImporter @Inject constructor(
private val SHA1_MATCHER =
InsnMatcher.compile("BIPUSH NEWARRAY (DUP (ICONST | BIPUSH) (ICONST | BIPUSH | SIPUSH) IASTORE)+")
private val FILE_NAME_REGEX = Regex("([a-z_]+)(?:_(-?[0-9]+))?[.]([a-z0-9]+)(?:\\?crc=(-?[0-9]+))?")
private val FILE_NAME_REGEX = Regex("(([a-z0-9_]+?)(?:_[0-9]){0,2})(?:_(-?[0-9]+))?[.]([a-z0-9]+)(?:\\?crc=(-?[0-9]+))?")
private val SHA1_CMP_MATCHER =
InsnMatcher.compile("((ICONST | BIPUSH)? ALOAD (ICONST | BIPUSH) BALOAD (ICONST IXOR)? (ICONST | BIPUSH)? (IF_ICMPEQ | IF_ICMPNE | IFEQ | IFNE))+")
private val PATH_CMP_MATCHER = InsnMatcher.compile("(LDC ALOAD | ALOAD LDC) (IF_ACMPEQ | IF_ACMPNE)")
private val RESOURCE_CTOR_MATCHER = InsnMatcher.compile("LDC LDC (LDC+ | ICONST ANEWARRAY (DUP ICONST LDC AASTORE)+) (ICONST | BIPUSH | SIPUSH | LDC) (ICONST | BIPUSH | SIPUSH | LDC) BIPUSH NEWARRAY (DUP (ICONST | BIPUSH) (ICONST | BIPUSH) IASTORE)+ INVOKESPECIAL")
}
}

Loading…
Cancel
Save