package me.grax.jbytemod.decompiler; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.jar.Manifest; import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.struct.ContextUnit; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.objectweb.asm.tree.MethodNode; import me.grax.jbytemod.JByteMod; import me.grax.jbytemod.ui.DecompilerPanel; public class FernflowerDecompiler extends Decompiler implements IBytecodeProvider, IResultSaver { private byte[] bytes; private String returned; public static final HashMap<String, Boolean> options = new HashMap<>(); static { options.put("rbr", true); options.put("rsy", false); options.put("din", true); options.put("dc4", true); options.put("das", true); options.put("hes", true); options.put("hdc", true); options.put("dgs", false); options.put("ner", true); options.put("den", true); options.put("rgn", true); options.put("lit", false); options.put("asc", true); options.put("bto", true); options.put("nns", false); options.put("uto", true); options.put("udv", true); options.put("rer", true); options.put("fdi", true); options.put("ren", false); options.put("inn", true); options.put("lac", false); } public FernflowerDecompiler(JByteMod jbm, DecompilerPanel dp) { super(jbm, dp); } public String decompile(byte[] b, MethodNode mn) { try { //TODO decompile method only this.bytes = b; HashMap<String, Object> map = new HashMap<>(); for (String key : options.keySet()) { map.put(key, JByteMod.ops.get("ff_" + key).getBoolean() ? "1" : "0"); } Fernflower f = new Fernflower(this, this, map, new PrintStreamLogger(JByteMod.LOGGER)); StructContext sc = f.getStructContext(); StructClass cl = new StructClass(b, true, sc.getLoader()); sc.getClasses().put(cn.name, cl); //instead of loading a file use custom bridge, created a few getters String fakePath = new File("none.class").getAbsolutePath(); ContextUnit unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, fakePath, true, sc.getSaver(), sc.getDecompiledData()); sc.getUnits().put(fakePath, unit); unit.addClass(cl, "none.class"); sc.getLoader().addClassLink(cn.name, new LazyLoader.Link(LazyLoader.Link.CLASS, fakePath, null)); f.decompileContext(); return returned; } catch (Exception e) { e.printStackTrace(); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); return sw.toString(); } } @Override public byte[] getBytecode(String externalPath, String internalPath) throws IOException { return bytes; } //we can ignore most of those methods because we do not want to save the output as a file @Override public void saveFolder(String path) { } @Override public void copyFile(String source, String path, String entryName) { } @Override public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { this.returned = content; } @Override public void createArchive(String path, String archiveName, Manifest manifest) { } @Override public void saveDirEntry(String path, String archiveName, String entryName) { } @Override public void copyEntry(String source, String path, String archiveName, String entry) { } @Override public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { } @Override public void closeArchive(String path, String archiveName) { } }