package codechicken.obfuscator; import com.google.common.base.Function; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.commons.RemappingClassAdapter; import org.objectweb.asm.tree.ClassNode; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.PrintStream; import java.text.DecimalFormat; import java.util.LinkedList; import java.util.List; import java.util.Map; public class ObfuscationRun implements ILogStreams { public final ObfDirection obfDir; public final ObfuscationMap obf; public final ObfRemapper obfMapper; public final ConstantObfuscator cstMappper; public File[] mappings; public Map<String, String> config; private PrintStream out = System.out; private PrintStream err = System.err; private PrintStream quietStream = new PrintStream(DummyOutputStream.instance); private boolean verbose; private boolean quiet; public boolean clean; private long startTime; private boolean finished; public ObfuscationRun(boolean obfuscate, File[] mappings, Map<String, String> config) { obfDir = new ObfDirection().setObfuscate(obfuscate); this.mappings = mappings; this.config = config; obf = new ObfuscationMap().setLog(this); obfMapper = new ObfRemapper(obf, obfDir); cstMappper = new ConstantObfuscator(obfMapper, config.get("classConstantCalls").split(","), config.get("descConstantCalls").split(",")); } public ObfuscationRun setClean() { clean = true; return this; } public ObfuscationRun setVerbose() { verbose = true; return this; } public ObfuscationRun setQuiet() { quiet = true; return this; } public ObfuscationRun setOut(PrintStream p) { out = p; return this; } public PrintStream out() { return quiet ? quietStream : out; } public PrintStream fine() { return verbose ? out : quietStream; } public ObfuscationRun setErr(PrintStream p) { err = p; return this; } public PrintStream err() { return quiet ? quietStream : err; } public ObfuscationRun setSearge() { obfDir.setSearge(true); return this; } public ObfuscationRun setSeargeConstants() { obfDir.setSeargeConstants(true); return this; } public void start() { startTime = System.currentTimeMillis(); } public static Map<String, String> fillDefaults(Map<String, String> config) { if (!config.containsKey("excludedPackages")) { config.put("excludedPackages", "java/;sun/;javax/;scala/;" + "argo/;org/lwjgl/;org/objectweb/;org/bouncycastle/;com/google/"); } if (!config.containsKey("ignore")) { config.put("ignore", "."); } if (!config.containsKey("classConstantCalls")) { config.put("classConstantCalls", "codechicken/lib/asm/ObfMapping.<init>(Ljava/lang/String;)V," + "codechicken/lib/asm/ObfMapping.subclass(Ljava/lang/String;)Lcodechicken/lib/asm/ObfMapping;," + "codechicken/lib/asm/ObfMapping.<init>(Lcodechicken/lib/asm/ObfMapping;Ljava/lang/String;)V"); } if (!config.containsKey("descConstantCalls")) { config.put("descConstantCalls", "codechicken/lib/asm/ObfMapping.<init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/MethodVisitor.visitFieldInsn(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/tree/MethodNode.visitFieldInsn(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/MethodVisitor.visitMethodInsn(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/tree/MethodNode.visitMethodInsn(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/tree/MethodInsnNode.<init>(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V," + "org/objectweb/asm/tree/FieldInsnNode.<init>(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); } return config; } public static void processLines(File file, Function<String, Void> function) { try { BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { function.apply(line); } reader.close(); } catch (Exception e) { throw new RuntimeException(e); } } public static void processFiles(File dir, Function<File, Void> function, boolean recursive) { for (File file : dir.listFiles()) { if (file.isDirectory() && recursive) { processFiles(file, function, recursive); } else { function.apply(file); } } } public static void deleteDir(File directory, boolean remove) { if (!directory.exists()) { if (!remove) { directory.mkdirs(); } return; } for (File file : directory.listFiles()) { if (file.isDirectory()) { deleteDir(file, true); } else { if (!file.delete()) { throw new RuntimeException("Delete Failed: " + file); } } } if (remove) { if (!directory.delete()) { throw new RuntimeException("Delete Failed: " + directory); } } } public static File[] parseConfDir(File confDir) { File srgDir = new File(confDir, "conf"); if (!srgDir.exists()) { srgDir = confDir; } File srgs = new File(srgDir, "packaged.srg"); if (!srgs.exists()) { srgs = new File(srgDir, "joined.srg"); } if (!srgs.exists()) { throw new RuntimeException("Could not find packaged.srg or joined.srg"); } File mapDir = new File(confDir, "mappings"); if (!mapDir.exists()) { mapDir = confDir; } File methods = new File(mapDir, "methods.csv"); if (!methods.exists()) { throw new RuntimeException("Could not find methods.csv"); } File fields = new File(mapDir, "fields.csv"); if (!fields.exists()) { throw new RuntimeException("Could not find fields.csv"); } return new File[] { srgs, methods, fields }; } public long startTime() { return startTime; } public void remap(ClassNode cnode, ClassVisitor cv) { cstMappper.transform(cnode); cnode.accept(new RemappingClassAdapter(cv, obfMapper)); } public static List<String> getParents(ClassNode cnode) { List<String> parents = new LinkedList<String>(); if (cnode.superName != null) { parents.add(cnode.superName); } for (String s_interface : cnode.interfaces) { parents.add(s_interface); } return parents; } public void finish(boolean errored) { long millis = System.currentTimeMillis() - startTime; out().println((errored ? "Errored after" : "Done in ") + new DecimalFormat("0.00").format(millis / 1000D) + "s"); finished = true; } public boolean finished() { return finished; } public void parseMappings() { obf.parseMappings(mappings); } }