package codechicken.obfuscator; import codechicken.lib.asm.ObfMapping; import com.google.common.base.Function; import com.google.common.collect.ArrayListMultimap; import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class ObfuscationMap { public class ObfuscationEntry { public final ObfMapping obf; public final ObfMapping srg; public final ObfMapping mcp; public ObfuscationEntry(ObfMapping obf, ObfMapping srg, ObfMapping mcp) { this.obf = obf; this.srg = srg; this.mcp = mcp; } } private class ClassEntry extends ObfuscationEntry { public Map<String, ObfuscationEntry> mcpMap = new HashMap<String, ObfuscationEntry>(); public Map<String, ObfuscationEntry> srgMap = new HashMap<String, ObfuscationEntry>(); public Map<String, ObfuscationEntry> obfMap = new HashMap<String, ObfuscationEntry>(); public ClassEntry(String obf, String srg) { super(new ObfMapping(obf, "", ""), new ObfMapping(srg, "", ""), new ObfMapping(srg, "", "")); } public ObfuscationEntry addEntry(ObfMapping obf_desc, ObfMapping srg_desc) { ObfuscationEntry entry = new ObfuscationEntry(obf_desc, srg_desc, srg_desc.copy()); obfMap.put(obf_desc.s_name.concat(obf_desc.s_desc), entry); srgMap.put(srg_desc.s_name, entry); if (srg_desc.s_name.startsWith("field_") || srg_desc.s_name.startsWith("func_")) { srgMemberMap.put(srg_desc.s_name, entry); } return entry; } public void inheritFrom(ClassEntry p) { inherit(obfMap, p.obfMap); inherit(srgMap, p.srgMap); inherit(mcpMap, p.mcpMap); } private void inherit(Map<String, ObfuscationEntry> child, Map<String, ObfuscationEntry> parent) { for (Entry<String, ObfuscationEntry> e : parent.entrySet()) { if (!child.containsKey(e.getKey())) { child.put(e.getKey(), e.getValue()); } } } } private Map<String, ClassEntry> srgMap = new HashMap<String, ClassEntry>(); private Map<String, ClassEntry> obfMap = new HashMap<String, ClassEntry>(); private ArrayListMultimap<String, ObfuscationEntry> srgMemberMap = ArrayListMultimap.create(); private IHeirachyEvaluator heirachyEvaluator; private HashSet<String> mappedClasses = new HashSet<String>(); public ILogStreams log = SystemLogStreams.inst; public ObfuscationMap setHeirachyEvaluator(IHeirachyEvaluator eval) { heirachyEvaluator = eval; return this; } public ObfuscationMap setLog(ILogStreams log) { this.log = log; return this; } public ObfuscationEntry addClass(String obf, String srg) { return addEntry(new ObfMapping(obf, "", ""), new ObfMapping(srg, "", "")); } public ObfuscationEntry addField(String obf_owner, String obf_name, String srg_owner, String srg_name) { return addEntry(new ObfMapping(obf_owner, obf_name, ""), new ObfMapping(srg_owner, srg_name, "")); } public ObfuscationEntry addMethod(String obf_owner, String obf_name, String obf_desc, String srg_owner, String srg_name, String srg_desc) { return addEntry(new ObfMapping(obf_owner, obf_name, obf_desc), new ObfMapping(srg_owner, srg_name, srg_desc)); } public ObfuscationEntry addEntry(ObfMapping obf_desc, ObfMapping srg_desc) { ClassEntry e = srgMap.get(srg_desc.s_owner); if (e == null) { e = new ClassEntry(obf_desc.s_owner, srg_desc.s_owner); obfMap.put(obf_desc.s_owner, e); srgMap.put(srg_desc.s_owner, e); } if (obf_desc.s_name.length() > 0) { return e.addEntry(obf_desc, srg_desc); } return e; } public void addMcpName(String srg_name, String mcp_name) { List<ObfuscationEntry> entries = srgMemberMap.get(srg_name); if (entries.isEmpty()) { log.err().println("Tried to add mcp name (" + mcp_name + ") for unknown srg key (" + srg_name + ")"); return; } for (ObfuscationEntry entry : entries) { entry.mcp.s_name = mcp_name; srgMap.get(entry.srg.s_owner).mcpMap.put(entry.mcp.s_name.concat(entry.mcp.s_desc), entry); } } public ObfuscationEntry lookupSrg(String srg_key) { List<ObfuscationEntry> list = srgMemberMap.get(srg_key); return list.isEmpty() ? null : list.get(0); } public ObfuscationEntry lookupMcpClass(String name) { return srgMap.get(name); } public ObfuscationEntry lookupObfClass(String name) { return obfMap.get(name); } public ObfuscationEntry lookupMcpField(String owner, String name) { return lookupMcpMethod(owner, name, ""); } public ObfuscationEntry lookupSrgField(String owner, String name) { if (name.startsWith("field_")) { ObfuscationEntry e = lookupSrg(name); if (e != null) { return e; } } evaluateHeirachy(owner); ClassEntry e = srgMap.get(owner); return e == null ? null : e.srgMap.get(name); } public ObfuscationEntry lookupObfField(String owner, String name) { return lookupObfMethod(owner, name, ""); } public ObfuscationEntry lookupMcpMethod(String owner, String name, String desc) { evaluateHeirachy(owner); ClassEntry e = srgMap.get(owner); return e == null ? null : e.mcpMap.get(name.concat(desc)); } public ObfuscationEntry lookupObfMethod(String owner, String name, String desc) { evaluateHeirachy(owner); ClassEntry e = obfMap.get(owner); return e == null ? null : e.obfMap.get(name.concat(desc)); } private boolean isMapped(ObfuscationEntry desc) { return mappedClasses.contains(desc.srg.s_owner); } private ObfuscationEntry getOrCreateClassEntry(String name) { ObfuscationEntry e = lookupObfClass(name); if (e == null) { e = lookupMcpClass(name); } if (e == null) { e = addClass(name, name);//if the class isn't in obf or srg maps, it must be a custom mod class with no name change. } return e; } public ObfuscationEntry evaluateHeirachy(String name) { ObfuscationEntry desc = getOrCreateClassEntry(name); if (isMapped(desc)) { return desc; } mappedClasses.add(desc.srg.s_owner); if (heirachyEvaluator == null) { throw new IllegalArgumentException("Cannot call method/field mappings if a heirachy evaluator is not set."); } if (!heirachyEvaluator.isLibClass(desc)) { List<String> parents = heirachyEvaluator.getParents(desc); if (parents == null) { log.err().println("Could not find class: " + desc.srg.s_owner); } else { for (String parent : parents) { inherit(desc, evaluateHeirachy(parent)); } } } return desc; } public void inherit(ObfuscationEntry desc, ObfuscationEntry p_desc) { inherit(desc.srg.s_owner, p_desc.srg.s_owner); } public void inherit(String name, String parent) { ClassEntry e = srgMap.get(name); if (e == null) { throw new IllegalStateException("Tried to inerit to an undefined class: " + name + " extends " + parent); } ClassEntry p = srgMap.get(parent); if (p == null) { throw new IllegalStateException("Tried to inerit from undefired parent: " + name + " extends " + parent); } e.inheritFrom(p); } public void parseMappings(File[] mappings) { parseSRGS(mappings[0]); parseCSV(mappings[1]); parseCSV(mappings[2]); } public static String[] splitLast(String s, char c) { int i = s.lastIndexOf(c); return new String[] { s.substring(0, i), s.substring(i + 1) }; } public static String[] split4(String s, char c) { String[] split = new String[4]; int i2 = s.indexOf(c); split[0] = s.substring(0, i2); int i = i2 + 1; i2 = s.indexOf(c, i); split[1] = s.substring(i, i2); i = i2 + 1; i2 = s.indexOf(c, i); split[2] = s.substring(i, i2); i = i2 + 1; i2 = s.indexOf(c, i); split[3] = s.substring(i); return split; } private void parseSRGS(File srgs) { log.out().println("Parsing " + srgs.getName()); Function<String, Void> function = new Function<String, Void>() { @Override public Void apply(String line) { int hpos = line.indexOf('#'); if (hpos > 0) { line = line.substring(0, hpos).trim(); } if (line.startsWith("CL: ")) { String[] params = splitLast(line.substring(4), ' '); addClass(params[0], params[1]); } else if (line.startsWith("FD: ")) { String[] params = splitLast(line.substring(4), ' '); String[] p1 = splitLast(params[0], '/'); String[] p2 = splitLast(params[1], '/'); addField(p1[0], p1[1], p2[0], p2[1]); return null; } else if (line.startsWith("MD: ")) { String[] params = split4(line.substring(4), ' '); String[] p1 = splitLast(params[0], '/'); String[] p2 = splitLast(params[2], '/'); addMethod(p1[0], p1[1], params[1], p2[0], p2[1], params[3]); return null; } return null; } }; ObfuscationRun.processLines(srgs, function); } private void parseCSV(File csv) { log.out().println("Parsing " + csv.getName()); Function<String, Void> function = new Function<String, Void>() { @Override public Void apply(String line) { if (line.startsWith("func_") || line.startsWith("field_")) { int i = line.indexOf(','); String srg = line.substring(0, i); int i2 = i + 1; i = line.indexOf(',', i2); String mcp = line.substring(i2, i); addMcpName(srg, mcp); } return null; } }; ObfuscationRun.processLines(csv, function); } }