package codechicken.core.asm; import codechicken.lib.asm.ASMHelper; import codechicken.lib.asm.ClassHeirachyManager; import codechicken.lib.asm.ObfMapping; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.LaunchClassLoader; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; public class DefaultImplementationTransformer implements IClassTransformer { private static LaunchClassLoader cl = (LaunchClassLoader) ClassHeirachyManager.class.getClassLoader(); private static ClassNode getClassNode(String name) { try { return ASMHelper.createClassNode(cl.getClassBytes(name.replace('/', '.'))); } catch (IOException e) { throw new RuntimeException(e); } } static class InterfaceImpl { public final String iname; public ArrayList<MethodNode> impls = new ArrayList<MethodNode>(); public InterfaceImpl(String iname, String cname) { this.iname = iname; HashSet<String> names = new HashSet<String>(); ClassNode inode = getClassNode(iname); for (MethodNode method : inode.methods) { names.add(method.name + method.desc); } ClassNode cnode = getClassNode(cname); for (MethodNode method : cnode.methods) { if (names.contains(method.name + method.desc)) { impls.add(method); method.desc = new ObfMapping(cnode.name, method.name, method.desc).toRuntime().s_desc; } } } public boolean patch(ClassNode cnode) { LinkedList<String> names = new LinkedList<String>(); for (MethodNode method : cnode.methods) { ObfMapping m = new ObfMapping(cnode.name, method.name, method.desc).toRuntime(); names.add(m.s_name + m.s_desc); } boolean changed = false; for (MethodNode impl : impls) { if (names.contains(impl.name + impl.desc)) { continue; } MethodNode copy = new MethodNode(impl.access, impl.name, impl.desc, impl.signature, impl.exceptions == null ? null : impl.exceptions.toArray(new String[0])); ASMHelper.copy(impl, copy); cnode.methods.add(impl); changed = true; } return changed; } } private static HashMap<String, InterfaceImpl> impls = new HashMap<String, InterfaceImpl>(); public static void registerDefaultImpl(String iname, String cname) { impls.put(iname.replace('.', '/'), new InterfaceImpl(iname, cname)); } @Override public byte[] transform(String name, String transformedName, byte[] bytes) { if (transformedName.startsWith("net.minecraft") || impls.isEmpty()) { return bytes; } ClassNode cnode = ASMHelper.createClassNode(bytes); boolean changed = false; for (String iname : cnode.interfaces) { InterfaceImpl impl = impls.get(iname); if (impl != null) { changed |= impl.patch(cnode); } } return changed ? ASMHelper.createBytes(cnode, 0) : bytes; } }