package codechicken.core.asm; import codechicken.lib.asm.ASMHelper; import codechicken.lib.asm.CC_ClassWriter; import codechicken.lib.asm.ObfMapping; import codechicken.obfuscator.IHeirachyEvaluator; import codechicken.obfuscator.ObfuscationMap.ObfuscationEntry; import codechicken.obfuscator.ObfuscationRun; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; import net.minecraftforge.fml.common.asm.transformers.AccessTransformer; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.Map.Entry; public class MCPDeobfuscationTransformer implements IClassTransformer, Opcodes, IHeirachyEvaluator { public static class LoadPlugin implements IFMLLoadingPlugin { @Override public String[] getASMTransformerClass() { return new String[0]; } @Override public String getModContainerClass() { return null; } @Override public String getSetupClass() { return null; } @Override public void injectData(Map<String, Object> data) { load(); } @Override public String getAccessTransformerClass() { return null; } } private static ObfuscationRun run; private static List<String> excludedPackages = new LinkedList<String>(); private static MCPDeobfuscationTransformer instance = new MCPDeobfuscationTransformer(); private static boolean activated; private static Field f_transformers; private static Field f_modifiers; private static Field f_Modifier_name; private static Field f_Modifier_desc; static { try { f_transformers = LaunchClassLoader.class.getDeclaredField("transformers"); f_modifiers = AccessTransformer.class.getDeclaredField("modifiers"); Class<?> c_Modifier = Class.forName(AccessTransformer.class.getName() + "$Modifier", false, Launch.classLoader); f_Modifier_name = c_Modifier.getDeclaredField("name"); f_Modifier_desc = c_Modifier.getDeclaredField("desc"); f_transformers.setAccessible(true); f_modifiers.setAccessible(true); f_Modifier_name.setAccessible(true); f_Modifier_desc.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } } private static Object get(Field f, Object inst) { try { return f.get(inst); } catch (Exception e) { throw new RuntimeException(e); } } private static void set(Field f, Object inst, Object value) { try { f.set(inst, value); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private static List<IClassTransformer> getTransformers() { return (List<IClassTransformer>) get(f_transformers, Launch.classLoader); } public static void load() { CodeChickenCoreModContainer.loadConfig(); if (CodeChickenCoreModContainer.config.getTag("dev.deobfuscate").setComment("set to true to completely deobfuscate mcp names").getBooleanValue(!ObfMapping.obfuscated)) { run = new ObfuscationRun(false, ObfMapping.MCPRemapper.getConfFiles(), ObfuscationRun.fillDefaults(new HashMap<String, String>())); run.obf.setHeirachyEvaluator(instance); run.setQuiet().parseMappings(); Collections.addAll(excludedPackages, run.config.get("excludedPackages").split(";")); if (ObfMapping.obfuscated) { ObfMapping.loadMCPRemapper(); run.setSeargeConstants(); getTransformers().add(instance); } else { getTransformers().add(0, instance);//insert transformer as first. } } } @Override public byte[] transform(String name, String transformedName, byte[] bytes) { if (name.equals("net.minecraftforge.fml.common.Loader")) { bytes = injectCallback(bytes); activated = true; } if (!activated || bytes == null) { return bytes; } ClassNode cnode = ASMHelper.createClassNode(bytes, ClassReader.EXPAND_FRAMES); ClassWriter cw = new CC_ClassWriter(0, true); run.remap(cnode, cw); return cw.toByteArray(); } private byte[] injectCallback(byte[] bytes) { ClassNode cnode = ASMHelper.createClassNode(bytes); MethodNode mnode = ASMHelper.findMethod(new ObfMapping(cnode.name, "<clinit>", "()V"), cnode); mnode.instructions.insert(new MethodInsnNode(INVOKESTATIC, "codechicken/core/asm/MCPDeobfuscationTransformer", "loadCallback", "()V")); return ASMHelper.createBytes(cnode, 0); } public static void loadCallback() { if (ObfMapping.obfuscated) { //move ourselves to the end List<IClassTransformer> transformers = getTransformers(); transformers.remove(instance); transformers.add(instance); } else { //remap access transformers for (IClassTransformer t : getTransformers()) { if (t instanceof AccessTransformer) { remapAccessTransformer(t); } } } } @SuppressWarnings("unchecked") private static void remapAccessTransformer(IClassTransformer t) { Multimap<String, ?> modifiers = (Multimap<String, ?>) get(f_modifiers, t); Multimap<String, Object> t_modifiers = HashMultimap.create(); for (Entry<String, ?> entry : modifiers.entries()) { Object mod = entry.getValue(); String m_owner = entry.getKey().replace('.', '/'); String m_name = (String) get(f_Modifier_name, mod); String m_desc = ((String) get(f_Modifier_desc, mod)).replace('.', '/'); ObfMapping map; if (m_name.equals("*")) { map = new ObfMapping(m_owner); map.s_name = m_name; map.s_desc = m_desc; } else { try { map = new ObfMapping(m_owner, m_name, m_desc).toClassloading(); } catch (Exception e) { new Object(); continue; } } set(f_Modifier_name, mod, map.s_name); set(f_Modifier_desc, mod, map.s_desc); t_modifiers.put(map.javaClass(), mod); } set(f_modifiers, t, t_modifiers); } @Override public List<String> getParents(ObfuscationEntry desc) { try { String name = ObfMapping.obfuscated ? desc.obf.s_owner : desc.mcp.s_owner; name = name.replace('/', '.'); byte[] bytes = Launch.classLoader.getClassBytes(name); if (bytes != null) { return ObfuscationRun.getParents(ASMHelper.createClassNode(bytes)); } } catch (IOException e) { } return null; } @Override public boolean isLibClass(ObfuscationEntry desc) { String name = desc.srg.s_owner; for (String p : excludedPackages) { if (name.startsWith(p)) { return true; } } return false; } public static String unmap(String name) { if (run == null) { return null; } ObfuscationEntry e = run.obf.lookupMcpClass(name); if (e == null) { return null; } return e.obf.s_owner; } public static MCPDeobfuscationTransformer instance() { return instance; } }