import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.nio.file.Files; import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; /** * Baut die Agent-Datei: eigene Klassen + ASM, dabei org/objectweb/asm nach * de/aeromc/capes/asm umbenannt. So kann das ASM von Fabric/Forge (das auf * demselben Klassenpfad liegt) nie mit unserem verwechselt werden. * * Aufruf: java Shade <ziel.jar> <klassenordner> <asm.jar> */ public class Shade { static final Remapper UMBENENNEN = new Remapper() { @Override public String map(String name) { return name.startsWith("org/objectweb/asm/") ? "de/aeromc/capes/asm/" + name.substring(18) : name; } }; static byte[] umbenennen(byte[] klasse) { ClassReader r = new ClassReader(klasse); ClassWriter w = new ClassWriter(0); r.accept(new ClassRemapper(w, UMBENENNEN), 0); return w.toByteArray(); } static byte[] lies(InputStream in) throws Exception { ByteArrayOutputStream b = new ByteArrayOutputStream(); byte[] p = new byte[8192]; int n; while ((n = in.read(p)) > 0) b.write(p, 0, n); return b.toByteArray(); } public static void main(String[] a) throws Exception { Manifest mf = new Manifest(); Attributes at = mf.getMainAttributes(); at.put(Attributes.Name.MANIFEST_VERSION, "1.0"); at.putValue("Premain-Class", "de.aeromc.capes.Agent"); at.putValue("Agent-Class", "de.aeromc.capes.Agent"); at.putValue("Can-Retransform-Classes", "false"); at.putValue("Implementation-Title", "AeroMC Umhang-Agent"); try (JarOutputStream out = new JarOutputStream(new FileOutputStream(a[0]), mf)) { // eigene Klassen File basis = new File(a[1]); for (File f : Files.walk(basis.toPath()).map(p -> p.toFile()).filter(f -> f.getName().endsWith(".class")).toArray(File[]::new)) { String name = basis.toPath().relativize(f.toPath()).toString().replace('\\', '/'); out.putNextEntry(new JarEntry(name)); out.write(umbenennen(Files.readAllBytes(f.toPath()))); out.closeEntry(); } // ASM (umbenannt), ohne Modul-Infos und Signaturen try (JarFile asm = new JarFile(a[2])) { Enumeration e = asm.entries(); while (e.hasMoreElements()) { JarEntry je = e.nextElement(); String n = je.getName(); if (!n.startsWith("org/objectweb/asm/") || !n.endsWith(".class")) continue; byte[] neu = umbenennen(lies(asm.getInputStream(je))); out.putNextEntry(new JarEntry("de/aeromc/capes/asm/" + n.substring(18))); out.write(neu); out.closeEntry(); } } } } }