diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt index d7a9a3cb..9aee6005 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt +++ b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt @@ -1,30 +1,13 @@ package dev.openrs2.asm.classpath -import com.github.michaelbull.logging.InlineLogger -import dev.openrs2.asm.ClassVersionUtils -import dev.openrs2.asm.NopClassVisitor import dev.openrs2.asm.remap -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.ClassWriter -import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper 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.Path import java.util.TreeMap -import java.util.jar.JarEntry -import java.util.jar.JarInputStream -import java.util.jar.JarOutputStream -import java.util.jar.Manifest -import java.util.jar.Pack200 -class Library() : Iterable { - private var classes = TreeMap() +class Library(classes: Map = emptyMap()) : Iterable { + + private var classes = TreeMap(classes) constructor(library: Library) : this() { for (clazz in library.classes.values) { @@ -61,125 +44,4 @@ class Library() : Iterable { 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 { - private val logger = InlineLogger() - private const val CLASS_SUFFIX = ".class" - private const val TEMP_PREFIX = "tmp" - private const val JAR_SUFFIX = ".jar" - - fun readJar(path: Path): Library { - logger.info { "Reading jar $path" } - - val library = Library() - - JarInputStream(Files.newInputStream(path)).use { `in` -> - while (true) { - val entry = `in`.nextJarEntry ?: break - if (!entry.name.endsWith(CLASS_SUFFIX)) { - continue - } - - val clazz = ClassNode() - val reader = ClassReader(`in`) - reader.accept(JsrInliner(clazz), ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES) - - library.add(clazz) - } - } - - return library - } - - fun readPack(path: Path): Library { - logger.info { "Reading pack $path" } - - val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) - try { - Gzip.createHeaderlessInputStream(Files.newInputStream(path)).use { `in` -> - JarOutputStream(Files.newOutputStream(temp)).use { out -> - Pack200.newUnpacker().unpack(`in`, out) - } - } - - return readJar(temp) - } finally { - Files.deleteIfExists(temp) - } - } - } } diff --git a/asm/src/main/java/dev/openrs2/asm/io/JarLibraryReader.kt b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryReader.kt new file mode 100644 index 00000000..65b8d71a --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryReader.kt @@ -0,0 +1,35 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.JsrInliner +import dev.openrs2.asm.classpath.Library +import org.objectweb.asm.ClassReader +import org.objectweb.asm.tree.ClassNode +import java.util.jar.JarEntry +import java.util.jar.JarInputStream + +class JarLibraryReader(private val input: JarInputStream) : LibraryReader { + + override fun read(): Library { + val classes = generateSequence { input.nextJarEntry } + .filter(::isClass) + .map { input.readClass() } + .associateBy(ClassNode::name) + + return Library(classes) + } + + private fun isClass(entry: JarEntry): Boolean { + return entry.name.endsWith(CLASS_SUFFIX) + } + + private fun JarInputStream.readClass(): ClassNode { + val clazz = ClassNode() + val reader = ClassReader(this) + reader.accept(JsrInliner(clazz), ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES) + return clazz + } + + private companion object { + private const val CLASS_SUFFIX = ".class" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt new file mode 100644 index 00000000..63468157 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt @@ -0,0 +1,45 @@ +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" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt new file mode 100644 index 00000000..d5fe1867 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt @@ -0,0 +1,12 @@ +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 + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/LibraryReader.kt b/asm/src/main/java/dev/openrs2/asm/io/LibraryReader.kt new file mode 100644 index 00000000..05b9638b --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/LibraryReader.kt @@ -0,0 +1,7 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.Library + +interface LibraryReader { + fun read(): Library +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt new file mode 100644 index 00000000..b04ee79f --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt @@ -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) +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryReader.kt b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryReader.kt new file mode 100644 index 00000000..0919640e --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryReader.kt @@ -0,0 +1,35 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.Library +import dev.openrs2.compress.gzip.Gzip +import java.io.InputStream +import java.nio.file.Files +import java.util.jar.JarInputStream +import java.util.jar.JarOutputStream +import java.util.jar.Pack200 + +class Pack200LibraryReader(private val input: InputStream) : LibraryReader { + + override fun read(): Library { + val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) + + try { + Gzip.createHeaderlessInputStream(input).use { gzipInput -> + JarOutputStream(Files.newOutputStream(temp)).use { output -> + Pack200.newUnpacker().unpack(gzipInput, output) + } + } + + return JarInputStream(Files.newInputStream(temp)).use { tempInput -> + JarLibraryReader(tempInput).read() + } + } finally { + Files.deleteIfExists(temp) + } + } + + private companion object { + private const val TEMP_PREFIX = "tmp" + private const val JAR_SUFFIX = ".jar" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt new file mode 100644 index 00000000..a04573f5 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt @@ -0,0 +1,36 @@ +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" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt new file mode 100644 index 00000000..74beea12 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt @@ -0,0 +1,54 @@ +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" + } +} diff --git a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt index 7a664f15..264277b3 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt @@ -3,6 +3,10 @@ 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.Pack200LibraryReader +import dev.openrs2.asm.io.SignedJarLibraryWriter import dev.openrs2.bundler.transform.BufferSizeTransformer import dev.openrs2.bundler.transform.CachePathTransformer import dev.openrs2.bundler.transform.HostCheckTransformer @@ -14,9 +18,12 @@ import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.bundler.transform.RightClickTransformer import dev.openrs2.bundler.transform.TypoTransformer import dev.openrs2.crypto.Pkcs12KeyStore +import dev.openrs2.util.io.DeterministicJarOutputStream +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.JarInputStream import java.util.jar.Manifest import javax.inject.Inject import javax.inject.Singleton @@ -28,12 +35,12 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { fun run(input: Path, output: Path, keyStorePath: Path) { // read input jars/packs logger.info { "Reading input jars" } - val unpacker = Library.readJar(input.resolve("unpackclass.pack")) - val loader = Library.readJar(input.resolve("loader.jar")) - val glLoader = Library.readJar(input.resolve("loader_gl.jar")) - val gl = Library.readPack(input.resolve("jaggl.pack200")) - val client = Library.readJar(input.resolve("runescape.jar")) - val glClient = Library.readPack(input.resolve("runescape_gl.pack200")) + val unpacker = readJar(input.resolve("unpackclass.pack")) + val loader = readJar(input.resolve("loader.jar")) + val glLoader = readJar(input.resolve("loader_gl.jar")) + val gl = readPack(input.resolve("jaggl.pack200")) + val client = readJar(input.resolve("runescape.jar")) + val glClient = readPack(input.resolve("runescape_gl.pack200")) // bundle libraries together into a common classpath val runtime = ClassLoader.getPlatformClassLoader() @@ -109,11 +116,43 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { } // 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) - loader.writeSignedJar(classPath, output.resolve("loader.jar"), keyStore, signedManifest) - glLoader.writeSignedJar(glClassPath, output.resolve("loader_gl.jar"), keyStore, signedManifest) + writeSignedJar(classPath, loader, output.resolve("loader.jar"), keyStore) + writeSignedJar(glClassPath, glLoader, output.resolve("loader_gl.jar"), keyStore) + } + + private fun readJar(path: Path): Library { + logger.info { "Reading jar $path" } + + return JarInputStream(Files.newInputStream(path)).use { input -> + JarLibraryReader(input).read() + } + } + + private fun readPack(path: Path): Library { + logger.info { "Reading pack $path" } + + return Files.newInputStream(path).use { input -> + Pack200LibraryReader(input).read() + } + } + + 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 { diff --git a/bundler/src/main/java/dev/openrs2/bundler/Resource.kt b/bundler/src/main/java/dev/openrs2/bundler/Resource.kt index 4ed7e5d6..d49a3b94 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Resource.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Resource.kt @@ -3,6 +3,10 @@ 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.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.nio.file.Files import java.nio.file.Path @@ -66,23 +70,26 @@ class Resource( } fun compressJar(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writeJar(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + DeterministicJarOutputStream(output).use { jarOutput -> + JarLibraryWriter(jarOutput).write(classPath, library) + } + + return compress(source, destination, output.toByteArray()) } } fun compressPack(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writePack(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + Pack200LibraryWriter(output).write(classPath, library) + return compress(source, destination, output.toByteArray()) } } fun compressJs5(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writeJs5(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + Js5LibraryWriter(output).write(classPath, library) + return compress(source, destination, output.toByteArray()) } } diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt index 338793bd..c380c4e3 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt @@ -3,8 +3,9 @@ package dev.openrs2.deob import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library -import dev.openrs2.asm.classpath.Library.Companion.readJar -import dev.openrs2.asm.classpath.Library.Companion.readPack +import dev.openrs2.asm.io.JarLibraryReader +import dev.openrs2.asm.io.JarLibraryWriter +import dev.openrs2.asm.io.Pack200LibraryReader import dev.openrs2.bundler.Bundler import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.deob.SignedClassUtils.move @@ -36,8 +37,10 @@ import dev.openrs2.deob.transform.UnusedArgTransformer import dev.openrs2.deob.transform.UnusedLocalTransformer import dev.openrs2.deob.transform.UnusedMethodTransformer import dev.openrs2.deob.transform.VisibilityTransformer +import dev.openrs2.util.io.DeterministicJarOutputStream import java.nio.file.Files import java.nio.file.Path +import java.util.jar.JarInputStream class Deobfuscator(private val input: Path, private val output: Path) { fun run() { @@ -124,20 +127,44 @@ class Deobfuscator(private val input: Path, private val output: Path) { Files.createDirectories(output) - client.writeJar(classPath, output.resolve("runescape.jar")) - loader.writeJar(classPath, output.resolve("loader.jar")) - signLink.writeJar(classPath, output.resolve("signlink.jar")) - unpack.writeJar(classPath, output.resolve("unpack.jar")) - unpackClass.writeJar(classPath, output.resolve("unpackclass.jar")) + writeJar(classPath, client, output.resolve("runescape.jar")) + writeJar(classPath, loader, output.resolve("loader.jar")) + writeJar(classPath, signLink, output.resolve("signlink.jar")) + writeJar(classPath, unpack, output.resolve("unpack.jar")) + writeJar(classPath, unpackClass, output.resolve("unpackclass.jar")) - gl.writeJar(glClassPath, output.resolve("jaggl.jar")) - glClient.writeJar(glClassPath, output.resolve("runescape_gl.jar")) - glLoader.writeJar(glClassPath, output.resolve("loader_gl.jar")) - glSignLink.writeJar(glClassPath, output.resolve("signlink_gl.jar")) - glUnpack.writeJar(glClassPath, output.resolve("unpack_gl.jar")) - glUnpackClass.writeJar(glClassPath, output.resolve("unpackclass_gl.jar")) + writeJar(glClassPath, gl, output.resolve("jaggl.jar")) + writeJar(glClassPath, glClient, output.resolve("runescape_gl.jar")) + writeJar(glClassPath, glLoader, output.resolve("loader_gl.jar")) + writeJar(glClassPath, glSignLink, output.resolve("signlink_gl.jar")) + writeJar(glClassPath, glUnpack, output.resolve("unpack_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 readJar(path: Path): Library { + logger.info { "Reading jar $path" } + + return JarInputStream(Files.newInputStream(path)).use { input -> + JarLibraryReader(input).read() + } + } + + private fun readPack(path: Path): Library { + logger.info { "Reading pack $path" } + + return Files.newInputStream(path).use { input -> + Pack200LibraryReader(input).read() + } + } + + 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 { diff --git a/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt b/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt index 29b658f2..f128a67d 100644 --- a/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt +++ b/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt @@ -1,17 +1,15 @@ package dev.openrs2.util.io import java.io.OutputStream -import java.nio.file.Files -import java.nio.file.Path import java.nio.file.attribute.FileTime -import java.util.jar.JarInputStream import java.util.jar.JarOutputStream import java.util.jar.Manifest import java.util.zip.ZipEntry class DeterministicJarOutputStream : JarOutputStream { - private constructor(out: OutputStream) : super(out) - private constructor(out: OutputStream, man: Manifest) : super(out, man) + + constructor(out: OutputStream) : super(out) + constructor(out: OutputStream, man: Manifest) : super(out, man) override fun putNextEntry(ze: ZipEntry) { ze.creationTime = UNIX_EPOCH @@ -22,25 +20,5 @@ class DeterministicJarOutputStream : JarOutputStream { companion object { private val UNIX_EPOCH = FileTime.fromMillis(0) - - fun create(out: OutputStream, manifest: Manifest? = null): JarOutputStream { - return if (manifest != null) { - DeterministicJarOutputStream(out, manifest) - } else { - DeterministicJarOutputStream(out) - } - } - - fun repack(src: Path, dest: Path) { - JarInputStream(Files.newInputStream(src)).use { input -> - create(Files.newOutputStream(dest), input.manifest).use { output -> - while (true) { - val entry = input.nextJarEntry ?: break - output.putNextEntry(entry) - input.copyTo(output) - } - } - } - } } }