forked from openrs2/openrs2
parent
1cb159ed64
commit
d9d6ab27c1
@ -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…
Reference in new issue