package dev.openrs2.bundler import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.io.JarLibraryReader import dev.openrs2.asm.io.JarLibraryWriter import dev.openrs2.asm.io.Js5LibraryWriter import dev.openrs2.asm.io.ManifestJarLibraryWriter import dev.openrs2.asm.io.Pack200LibraryReader import dev.openrs2.asm.io.Pack200LibraryWriter import dev.openrs2.asm.io.SignedJarLibraryWriter import dev.openrs2.asm.transform.Transformer import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.conf.Config import dev.openrs2.crypto.Pkcs12KeyStore import java.nio.file.Files import java.nio.file.Path import java.util.jar.Attributes import java.util.jar.Attributes.Name.MANIFEST_VERSION import java.util.jar.Manifest import javax.inject.Inject import javax.inject.Singleton @Singleton class Bundler @Inject constructor( @BundlerQualifier private val transformers: Set<@JvmSuppressWildcards Transformer>, private val config: Config ) { private val unsignedManifest = Manifest().apply { mainAttributes[MANIFEST_VERSION] = "1.0" mainAttributes[APPLICATION_NAME] = config.game mainAttributes[PERMISSIONS] = "sandbox" } private val signedManifest = Manifest().apply { mainAttributes[MANIFEST_VERSION] = "1.0" mainAttributes[APPLICATION_NAME] = config.game mainAttributes[PERMISSIONS] = "all-permissions" } fun run(input: Path, output: Path, keyStorePath: Path) { // read input jars/packs logger.info { "Reading input jars" } val unpacker = Library.read(input.resolve("unpackclass.pack"), JarLibraryReader) val loader = Library.read(input.resolve("loader.jar"), JarLibraryReader) val glLoader = Library.read(input.resolve("loader_gl.jar"), JarLibraryReader) val gl = Library.read(input.resolve("jaggl.pack200"), Pack200LibraryReader) val client = Library.read(input.resolve("runescape.jar"), JarLibraryReader) val glClient = Library.read(input.resolve("runescape_gl.pack200"), Pack200LibraryReader) // bundle libraries together into a common classpath val runtime = ClassLoader.getPlatformClassLoader() val classPath = ClassPath( runtime, dependencies = listOf(unpacker), libraries = listOf(client, loader) ) val glClassPath = ClassPath( runtime, dependencies = listOf(gl, unpacker), libraries = listOf(glClient, glLoader) ) // run simple transformers logger.info { "Transforming client" } for (transformer in transformers) { logger.info { "Running transformer ${transformer.javaClass.simpleName}" } transformer.transform(classPath) } logger.info { "Transforming client_gl" } for (transformer in transformers) { logger.info { "Running transformer ${transformer.javaClass.simpleName}" } transformer.transform(glClassPath) } // compress resources logger.info { "Compressing resources" } val unpackerJar = Resource.compressLibrary( "unpackclass.pack", "game_unpacker.dat", classPath, unpacker, JarLibraryWriter ) val clientPack = Resource.compressLibrary( "runescape.pack200", "main_file_cache.dat0", classPath, client, Pack200LibraryWriter ) val clientJs5 = Resource.compressLibrary( "runescape.js5", "main_file_cache.dat1", classPath, client, Js5LibraryWriter ) val glClientPack = Resource.compressLibrary( "runescape_gl.pack200", "main_file_cache.dat3", glClassPath, glClient, Pack200LibraryWriter ) val glClientJs5 = Resource.compressLibrary( "runescape_gl.js5", "main_file_cache.dat4", glClassPath, glClient, Js5LibraryWriter ) val glPack = Resource.compressLibrary( "jaggl.pack200", "main_file_cache.dat5", glClassPath, gl, Pack200LibraryWriter ) val glJs5 = Resource.compressLibrary( "jaggl.js5", "main_file_cache.dat6", glClassPath, gl, Js5LibraryWriter ) val glNatives = Resource.compressGlNatives() val miscNatives = Resource.compressMiscNatives() // update checksums in the loader logger.info { "Updating checksums" } val resourceTransformer = ResourceTransformer( resources = listOf(unpackerJar, clientPack, clientJs5), glResources = glNatives, miscResources = miscNatives ) resourceTransformer.transform(classPath) val glResourceTransformer = ResourceTransformer( resources = listOf(unpackerJar, glClientPack, glClientJs5, glPack, glJs5), glResources = glNatives, miscResources = miscNatives ) glResourceTransformer.transform(glClassPath) // write all resources to disk logger.info { "Writing resources" } Files.createDirectories(output) val resources = listOf( unpackerJar, clientPack, clientJs5, glClientPack, glClientJs5, glPack, glJs5 ) + glNatives.flatten() + miscNatives for (resource in resources) { resource.write(output) } // write unsigned client and loaders client.write(output.resolve("runescape.jar"), ManifestJarLibraryWriter(unsignedManifest), classPath) val keyStore = Pkcs12KeyStore.open(keyStorePath, config.game) loader.write(output.resolve("loader.jar"), SignedJarLibraryWriter(signedManifest, keyStore), classPath) glLoader.write(output.resolve("loader_gl.jar"), SignedJarLibraryWriter(signedManifest, keyStore), glClassPath) } private companion object { private val logger = InlineLogger() private val APPLICATION_NAME = Attributes.Name("Application-Name") private val PERMISSIONS = Attributes.Name("Permissions") } }