Move Library#write methods to dedicated classes

Signed-off-by: Scu11 <scu11@openrs2.dev>
pull/105/head
Scu11 4 years ago committed by Graham
parent b40eedfb3b
commit 26348b8a2e
  1. 84
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt
  2. 44
      asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt
  3. 11
      asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt
  4. 8
      asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt
  5. 34
      asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt
  6. 52
      asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt
  7. 26
      bundler/src/main/java/dev/openrs2/bundler/Bundler.kt
  8. 25
      bundler/src/main/java/dev/openrs2/bundler/Resource.kt
  9. 34
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt
  10. 4
      util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt

@ -1,26 +1,16 @@
package dev.openrs2.asm.classpath package dev.openrs2.asm.classpath
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.ClassVersionUtils
import dev.openrs2.asm.NopClassVisitor
import dev.openrs2.asm.remap import dev.openrs2.asm.remap
import dev.openrs2.compress.gzip.Gzip import dev.openrs2.compress.gzip.Gzip
import dev.openrs2.crypto.Pkcs12KeyStore
import dev.openrs2.util.io.DeterministicJarOutputStream
import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.Remapper import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.util.CheckClassAdapter
import java.io.OutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.TreeMap import java.util.TreeMap
import java.util.jar.JarEntry
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import java.util.jar.Pack200 import java.util.jar.Pack200
class Library() : Iterable<ClassNode> { class Library() : Iterable<ClassNode> {
@ -62,80 +52,6 @@ class Library() : Iterable<ClassNode> {
classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name } classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name }
} }
fun writeJar(classPath: ClassPath, path: Path, manifest: Manifest? = null) {
logger.info { "Writing jar $path" }
Files.newOutputStream(path).use {
writeJar(classPath, it, manifest)
}
}
fun writeJar(classPath: ClassPath, out: OutputStream, manifest: Manifest? = null) {
DeterministicJarOutputStream.create(out, manifest).use { jar ->
for (clazz in classes.values) {
val writer = if (ClassVersionUtils.gte(clazz.version, Opcodes.V1_7)) {
StackFrameClassWriter(classPath)
} else {
ClassWriter(ClassWriter.COMPUTE_MAXS)
}
clazz.accept(writer)
jar.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX))
jar.write(writer.toByteArray())
/*
* XXX(gpe): CheckClassAdapter breaks the Label offset
* calculation in the OriginalPcTable's write method, so we do
* a second pass without any attributes to check the class,
* feeding the callbacks into a no-op visitor.
*/
for (method in clazz.methods) {
method.attrs?.clear()
}
clazz.accept(CheckClassAdapter(NopClassVisitor, true))
}
}
}
fun writeSignedJar(classPath: ClassPath, path: Path, keyStore: Pkcs12KeyStore, manifest: Manifest? = null) {
logger.info { "Writing signed jar $path" }
val unsignedPath = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
writeJar(classPath, unsignedPath, manifest)
val signedPath = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
keyStore.signJar(unsignedPath, signedPath)
DeterministicJarOutputStream.repack(signedPath, path)
} finally {
Files.deleteIfExists(signedPath)
}
} finally {
Files.deleteIfExists(unsignedPath)
}
}
fun writePack(classPath: ClassPath, out: OutputStream) {
val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
writeJar(classPath, temp)
JarInputStream(Files.newInputStream(temp)).use { `in` ->
Gzip.createHeaderlessOutputStream(out).use { gzip ->
Pack200.newPacker().pack(`in`, gzip)
}
}
} finally {
Files.deleteIfExists(temp)
}
}
fun writeJs5(classPath: ClassPath, out: OutputStream) {
// TODO(gpe): implement
}
companion object { companion object {
private val logger = InlineLogger() private val logger = InlineLogger()
private const val CLASS_SUFFIX = ".class" private const val CLASS_SUFFIX = ".class"

@ -0,0 +1,44 @@
package dev.openrs2.asm.io
import dev.openrs2.asm.ClassVersionUtils
import dev.openrs2.asm.NopClassVisitor
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.classpath.StackFrameClassWriter
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import org.objectweb.asm.util.CheckClassAdapter
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
class JarLibraryWriter(private val output: JarOutputStream) : LibraryWriter {
override fun write(classPath: ClassPath, library: Library) {
for (clazz in library) {
val writer = if (ClassVersionUtils.gte(clazz.version, Opcodes.V1_7)) {
StackFrameClassWriter(classPath)
} else {
ClassWriter(ClassWriter.COMPUTE_MAXS)
}
clazz.accept(writer)
output.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX))
output.write(writer.toByteArray())
/*
* XXX(gpe): CheckClassAdapter breaks the Label offset
* calculation in the OriginalPcTable's write method, so we do
* a second pass without any attributes to check the class,
* feeding the callbacks into a no-op visitor.
*/
for (method in clazz.methods) {
method.attrs?.clear()
}
clazz.accept(CheckClassAdapter(NopClassVisitor, true))
}
}
private companion object {
private const val CLASS_SUFFIX = ".class"
}
}

@ -0,0 +1,11 @@
package dev.openrs2.asm.io
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import java.io.OutputStream
class Js5LibraryWriter(private val output: OutputStream) : LibraryWriter {
override fun write(classPath: ClassPath, library: Library) {
// TODO(gpe): implement
}
}

@ -0,0 +1,8 @@
package dev.openrs2.asm.io
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
interface LibraryWriter {
fun write(classPath: ClassPath, library: Library)
}

@ -0,0 +1,34 @@
package dev.openrs2.asm.io
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.compress.gzip.Gzip
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.io.OutputStream
import java.nio.file.Files
import java.util.jar.JarInputStream
import java.util.jar.Pack200
class Pack200LibraryWriter(private val output: OutputStream) : LibraryWriter {
override fun write(classPath: ClassPath, library: Library) {
val tempJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
DeterministicJarOutputStream(Files.newOutputStream(tempJar)).use { tempOutput ->
JarLibraryWriter(tempOutput).write(classPath, library)
}
JarInputStream(Files.newInputStream(tempJar)).use { input ->
Gzip.createHeaderlessOutputStream(output).use { gzip ->
Pack200.newPacker().pack(input, gzip)
}
}
} finally {
Files.deleteIfExists(tempJar)
}
}
private companion object {
private const val TEMP_PREFIX = "tmp"
private const val JAR_SUFFIX = ".jar"
}
}

@ -0,0 +1,52 @@
package dev.openrs2.asm.io
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.crypto.Pkcs12KeyStore
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.JarInputStream
import java.util.jar.Manifest
class SignedJarLibraryWriter(
private val output: OutputStream,
private val manifest: Manifest,
private val keyStore: Pkcs12KeyStore
) : LibraryWriter {
override fun write(classPath: ClassPath, library: Library) {
val unsignedJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
DeterministicJarOutputStream(Files.newOutputStream(unsignedJar), manifest).use { unsignedOutput ->
JarLibraryWriter(unsignedOutput).write(classPath, library)
}
val signedJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try {
keyStore.signJar(unsignedJar, signedJar)
repack(signedJar)
} finally {
Files.deleteIfExists(signedJar)
}
} finally {
Files.deleteIfExists(unsignedJar)
}
}
private fun repack(signedJar: Path) {
JarInputStream(Files.newInputStream(signedJar)).use { input ->
DeterministicJarOutputStream(output, input.manifest).use { output ->
generateSequence { input.nextJarEntry }.forEach {
output.putNextEntry(it)
input.copyTo(output)
}
}
}
}
private companion object {
private const val TEMP_PREFIX = "tmp"
private const val JAR_SUFFIX = ".jar"
}
}

@ -4,9 +4,13 @@ import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.transform.Transformer import dev.openrs2.asm.transform.Transformer
import dev.openrs2.asm.io.JarLibraryWriter
import dev.openrs2.asm.io.SignedJarLibraryWriter
import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.bundler.transform.ResourceTransformer
import dev.openrs2.conf.Config import dev.openrs2.conf.Config
import dev.openrs2.crypto.Pkcs12KeyStore import dev.openrs2.crypto.Pkcs12KeyStore
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.jar.Attributes import java.util.jar.Attributes
import java.util.jar.Attributes.Name.MANIFEST_VERSION import java.util.jar.Attributes.Name.MANIFEST_VERSION
@ -115,11 +119,27 @@ class Bundler @Inject constructor(
} }
// write unsigned client and loaders // write unsigned client and loaders
client.writeJar(classPath, output.resolve("runescape.jar"), unsignedManifest) writeJar(classPath, client, output.resolve("runescape.jar"))
val keyStore = Pkcs12KeyStore.open(keyStorePath, config.game) val keyStore = Pkcs12KeyStore.open(keyStorePath, config.game)
loader.writeSignedJar(classPath, output.resolve("loader.jar"), keyStore, signedManifest) writeSignedJar(classPath, loader, output.resolve("loader.jar"), keyStore)
glLoader.writeSignedJar(glClassPath, output.resolve("loader_gl.jar"), keyStore, signedManifest) writeSignedJar(glClassPath, glLoader, output.resolve("loader_gl.jar"), keyStore)
}
private fun writeJar(classPath: ClassPath, library: Library, path: Path) {
logger.info { "Writing jar $path" }
DeterministicJarOutputStream(Files.newOutputStream(path), unsignedManifest).use { output ->
JarLibraryWriter(output).write(classPath, library)
}
}
private fun writeSignedJar(classPath: ClassPath, library: Library, path: Path, keyStore: Pkcs12KeyStore) {
logger.info { "Writing signed jar $path" }
Files.newOutputStream(path).use {
SignedJarLibraryWriter(it, signedManifest, keyStore).write(classPath, library)
}
} }
companion object { companion object {

@ -3,6 +3,10 @@ package dev.openrs2.bundler
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.io.JarLibraryWriter
import dev.openrs2.asm.io.Js5LibraryWriter
import dev.openrs2.asm.io.Pack200LibraryWriter
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@ -66,23 +70,26 @@ class Resource(
} }
fun compressJar(source: String, destination: String, classPath: ClassPath, library: Library): Resource { fun compressJar(source: String, destination: String, classPath: ClassPath, library: Library): Resource {
ByteArrayOutputStream().use { out -> ByteArrayOutputStream().use { output ->
library.writeJar(classPath, out) DeterministicJarOutputStream(output).use { jarOutput ->
return compress(source, destination, out.toByteArray()) JarLibraryWriter(jarOutput).write(classPath, library)
}
return compress(source, destination, output.toByteArray())
} }
} }
fun compressPack(source: String, destination: String, classPath: ClassPath, library: Library): Resource { fun compressPack(source: String, destination: String, classPath: ClassPath, library: Library): Resource {
ByteArrayOutputStream().use { out -> ByteArrayOutputStream().use { output ->
library.writePack(classPath, out) Pack200LibraryWriter(output).write(classPath, library)
return compress(source, destination, out.toByteArray()) return compress(source, destination, output.toByteArray())
} }
} }
fun compressJs5(source: String, destination: String, classPath: ClassPath, library: Library): Resource { fun compressJs5(source: String, destination: String, classPath: ClassPath, library: Library): Resource {
ByteArrayOutputStream().use { out -> ByteArrayOutputStream().use { output ->
library.writeJs5(classPath, out) Js5LibraryWriter(output).write(classPath, library)
return compress(source, destination, out.toByteArray()) return compress(source, destination, output.toByteArray())
} }
} }

@ -3,8 +3,10 @@ package dev.openrs2.deob
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.io.JarLibraryWriter
import dev.openrs2.asm.transform.Transformer import dev.openrs2.asm.transform.Transformer
import dev.openrs2.deob.remap.PrefixRemapper import dev.openrs2.deob.remap.PrefixRemapper
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import javax.inject.Inject import javax.inject.Inject
@ -100,20 +102,28 @@ class Deobfuscator @Inject constructor(
Files.createDirectories(output) Files.createDirectories(output)
client.writeJar(classPath, output.resolve("runescape.jar")) writeJar(classPath, client, output.resolve("runescape.jar"))
loader.writeJar(classPath, output.resolve("loader.jar")) writeJar(classPath, loader, output.resolve("loader.jar"))
signLink.writeJar(classPath, output.resolve("signlink.jar")) writeJar(classPath, signLink, output.resolve("signlink.jar"))
unpack.writeJar(classPath, output.resolve("unpack.jar")) writeJar(classPath, unpack, output.resolve("unpack.jar"))
unpackClass.writeJar(classPath, output.resolve("unpackclass.jar")) writeJar(classPath, unpackClass, output.resolve("unpackclass.jar"))
gl.writeJar(glClassPath, output.resolve("jaggl.jar")) writeJar(glClassPath, gl, output.resolve("jaggl.jar"))
glClient.writeJar(glClassPath, output.resolve("runescape_gl.jar")) writeJar(glClassPath, glClient, output.resolve("runescape_gl.jar"))
glLoader.writeJar(glClassPath, output.resolve("loader_gl.jar")) writeJar(glClassPath, glLoader, output.resolve("loader_gl.jar"))
glSignLink.writeJar(glClassPath, output.resolve("signlink_gl.jar")) writeJar(glClassPath, glSignLink, output.resolve("signlink_gl.jar"))
glUnpack.writeJar(glClassPath, output.resolve("unpack_gl.jar")) writeJar(glClassPath, glUnpack, output.resolve("unpack_gl.jar"))
glUnpackClass.writeJar(glClassPath, output.resolve("unpackclass_gl.jar")) writeJar(glClassPath, glUnpackClass, output.resolve("unpackclass_gl.jar"))
unsignedClient.writeJar(unsignedClassPath, output.resolve("runescape_unsigned.jar")) writeJar(unsignedClassPath, unsignedClient, output.resolve("runescape_unsigned.jar"))
}
private fun writeJar(classPath: ClassPath, library: Library, path: Path) {
logger.info { "Writing jar $path" }
DeterministicJarOutputStream(Files.newOutputStream(path)).use { output ->
JarLibraryWriter(output).write(classPath, library)
}
} }
companion object { companion object {

@ -10,8 +10,8 @@ import java.util.jar.Manifest
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
class DeterministicJarOutputStream : JarOutputStream { class DeterministicJarOutputStream : JarOutputStream {
private constructor(out: OutputStream) : super(out) constructor(out: OutputStream) : super(out)
private constructor(out: OutputStream, man: Manifest) : super(out, man) constructor(out: OutputStream, man: Manifest) : super(out, man)
override fun putNextEntry(ze: ZipEntry) { override fun putNextEntry(ze: ZipEntry) {
ze.creationTime = UNIX_EPOCH ze.creationTime = UNIX_EPOCH

Loading…
Cancel
Save