Add Library class for reading and writing .jar and .pack files

master
Graham 5 years ago
parent 1cb159ed64
commit d9d6ab27c1
  1. 20
      asm/pom.xml
  2. 98
      asm/src/main/java/dev/openrs2/asm/Library.java
  3. 28
      util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.java
  4. 37
      util/src/main/java/dev/openrs2/util/io/SkipOutputStream.java

@ -12,4 +12,24 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<name>OpenRS2 ASM Utilities</name> <name>OpenRS2 ASM Utilities</name>
<dependencies>
<dependency>
<groupId>dev.openrs2</groupId>
<artifactId>openrs2-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
</dependency>
</dependencies>
</project> </project>

@ -0,0 +1,98 @@
package dev.openrs2.asm;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import dev.openrs2.util.io.DeterministicJarOutputStream;
import dev.openrs2.util.io.SkipOutputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.CheckClassAdapter;
public final class Library {
private static final String CLASS_SUFFIX = ".class";
private static final String TEMP_PREFIX = "tmp";
private static final String JAR_SUFFIX = ".jar";
private static final byte[] GZIP_HEADER = { 0x1f, (byte) 0x8b };
public static Library readJar(Path path) throws IOException {
var library = new Library();
try (var in = new JarInputStream(Files.newInputStream(path))) {
JarEntry entry;
while ((entry = in.getNextJarEntry()) != null) {
if (!entry.getName().endsWith(CLASS_SUFFIX)) {
continue;
}
var clazz = new ClassNode();
var reader = new ClassReader(in);
reader.accept(clazz, ClassReader.SKIP_DEBUG);
library.classes.put(clazz.name, clazz);
}
}
return library;
}
public static Library readPack(Path path) throws IOException {
var temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX);
try {
try (var header = new ByteArrayInputStream(GZIP_HEADER);
var data = Files.newInputStream(path);
var in = new GZIPInputStream(new SequenceInputStream(header, data));
var out = new JarOutputStream(Files.newOutputStream(temp))) {
Pack200.newUnpacker().unpack(in, out);
return readJar(temp);
}
} finally {
Files.deleteIfExists(temp);
}
}
private final Map<String, ClassNode> classes = new TreeMap<>();
private Library() {
/* empty */
}
public void writeJar(Path path) throws IOException {
try (var out = new DeterministicJarOutputStream(Files.newOutputStream(path))) {
for (var entry : classes.entrySet()) {
var clazz = entry.getValue();
var writer = new ClassWriter(0);
clazz.accept(new CheckClassAdapter(writer, true));
out.putNextEntry(new JarEntry(clazz.name + CLASS_SUFFIX));
out.write(writer.toByteArray());
}
}
}
public void writePack(Path path) throws IOException {
var temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX);
try {
writeJar(temp);
try (var in = new JarInputStream(Files.newInputStream(temp));
var out = new GZIPOutputStream(new SkipOutputStream(Files.newOutputStream(path), 2))) {
Pack200.newPacker().pack(in, out);
}
} finally {
Files.deleteIfExists(temp);
}
}
}

@ -0,0 +1,28 @@
package dev.openrs2.util.io;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.attribute.FileTime;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
public final class DeterministicJarOutputStream extends JarOutputStream {
private static final FileTime UNIX_EPOCH = FileTime.fromMillis(0);
public DeterministicJarOutputStream(OutputStream out) throws IOException {
super(out);
}
public DeterministicJarOutputStream(OutputStream out, Manifest man) throws IOException {
super(out, man);
}
@Override
public void putNextEntry(ZipEntry ze) throws IOException {
ze.setCreationTime(UNIX_EPOCH);
ze.setLastAccessTime(UNIX_EPOCH);
ze.setLastModifiedTime(UNIX_EPOCH);
super.putNextEntry(ze);
}
}

@ -0,0 +1,37 @@
package dev.openrs2.util.io;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public final class SkipOutputStream extends FilterOutputStream {
private long skipBytes;
public SkipOutputStream(OutputStream out, long skipBytes) {
super(out);
this.skipBytes = skipBytes;
}
@Override
public void write(int b) throws IOException {
if (skipBytes == 0) {
super.write(b);
} else {
skipBytes--;
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (len >= skipBytes) {
off += skipBytes;
len -= skipBytes;
skipBytes = 0;
} else {
skipBytes -= len;
return;
}
super.write(b, off, len);
}
}
Loading…
Cancel
Save