package net.scapeemulator.asm.bundler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import net.scapeemulator.asm.bundler.trans.Transformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
private final Map<String, ClassNode> classes = new HashMap<>();
public static Application merge(Application... applications) {
Application merged = new Application();
for (Application application : applications)
for (Map.Entry<String, ClassNode> entry : application.classes.entrySet())
merged.classes.put(entry.getKey(), entry.getValue());
return merged;
}
public static Application unpack200(File file) throws IOException {
logger.info("Unpacking pack200 file " + file + "...");
/* read the .pack200 file into memory and tack on the GZIP header */
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
out.write(31);
out.write(-117);
try (InputStream is = new FileInputStream(file)) {
for (;;) {
byte[] buf = new byte[4096];
int len = is.read(buf);
if (len == -1)
break;
out.write(buf, 0, len);
}
}
} finally {
out.close();
}
/* convert the pack200 file to a temporary jar file */
File tmp = File.createTempFile("tmp", ".jar");
JarOutputStream os = new JarOutputStream(new FileOutputStream(tmp));
try {
InputStream in = new GZIPInputStream(new ByteArrayInputStream(out.toByteArray()));
try {
Pack200.Unpacker unpacker = Pack200.newUnpacker();
unpacker.unpack(in, os);
} finally {
in.close();
}
} finally {
os.close();
}
/* unpack the temporary jar file */
return unpackJar(tmp);
}
public static Application unpackJar(File file) throws IOException {
logger.info("Unpacking jar file " + file + "...");
Application application = new Application();
JarFile jar = new JarFile(file);
try {
for (Enumeration<JarEntry> it = jar.entries(); it.hasMoreElements();) {
JarEntry entry = it.nextElement();
if (!entry.getName().endsWith(".class"))
continue;
InputStream is = jar.getInputStream(entry);
try {
ClassReader reader = new ClassReader(is);
ClassNode node = new ClassNode();
reader.accept(node, 0);
application.classes.put(entry.getName(), node);
} finally {
is.close();
}
}
} finally {
jar.close();
}
return application;
}
public void pack200(File file) throws IOException {
File tmp = File.createTempFile("tmp", ".jar");
packJar(tmp);
logger.info("Packing pack200 file " + file + "..");
JarFile jar = new JarFile(tmp);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
OutputStream os = new GZIPOutputStream(out);
try {
Pack200.Packer packer = Pack200.newPacker();
packer.pack(jar, os);
} finally {
os.close();
}
} finally {
out.close();
}
byte[] bytes = out.toByteArray();
byte[] strippedBytes = new byte[bytes.length - 2];
System.arraycopy(bytes, 2, strippedBytes, 0, strippedBytes.length);
OutputStream os = new FileOutputStream(file);
try {
os.write(strippedBytes);
} finally {
os.close();
}
} finally {
jar.close();
}
}
public void packJar(File file) throws IOException {
logger.info("Packing jar file " + file + "...");
JarOutputStream os = new JarOutputStream(new FileOutputStream(file));
try {
for (Map.Entry<String, ClassNode> entry : classes.entrySet()) {
os.putNextEntry(new JarEntry(entry.getKey()));
ClassWriter writer = new ClassWriter(0);
entry.getValue().accept(writer);
os.write(writer.toByteArray());
}
} finally {
os.close();
}
}
public void transform(Transformer transformer) {
for (ClassNode node : classes.values())
transformer.transform(node);
}
}