package joshie.progression.asm.helpers; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import joshie.progression.asm.ASMConfig; import joshie.progression.asm.AbstractASM; import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.tuple.Pair; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodNode; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import static joshie.progression.lib.PInfo.MODID; public class ASMHelper { public enum ObfType { NOTCH, FUNC, NAMED; } public static ASMConfig getASMConfig() { Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); File file = new File("config/" + MODID + "/asm.json"); if (!file.exists()) { try { ASMConfig config = new ASMConfig(); Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); writer.write(gson.toJson(config)); writer.close(); //Write the default json to file return config; } catch (Exception ignored) {} } else { try { return gson.fromJson(FileUtils.readFileToString(file), ASMConfig.class); } catch (Exception ignored) {} } return new ASMConfig(); } public static Pair<MethodNode, ObfType> getMethodAndObfType(ClassNode node, String... methodAndDescription) { for (int i = 0; i < methodAndDescription.length; i+= 2) { String name = methodAndDescription[i]; String desc = methodAndDescription[i + 1]; for (MethodNode method : node.methods) { if (method.name.equals(name) && method.desc.equals(desc)) { if (i == 0) return Pair.of(method, ObfType.NAMED); else if (i == 2) return Pair.of(method, ObfType.FUNC); else if (i == 4) return Pair.of(method, ObfType.NOTCH); } } } return null; } public static AbstractInsnNode getFirstInstruction(AbstractInsnNode insn) { for (AbstractInsnNode instruction = insn; instruction != null; instruction = instruction.getNext()) { if (instruction.getType() != AbstractInsnNode.LABEL && instruction.getType() != AbstractInsnNode.LINE) { return instruction; } } return null; } public static String getClassFor(String name) { return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace("/", ".")).replace('.', '/'); } public static byte[] override(AbstractASM asm, byte[] data) { ClassNode node = new ClassNode(); ClassReader cr = new ClassReader(data); cr.accept(node, 0); //Get the Result Pair<MethodNode, ObfType> result = getMethodAndObfType(node, asm.getMethodNameAndDescription()); MethodNode m = result.getKey(); InsnList list = new InsnList(); asm.addInstructions(result.getValue(), list); m.instructions.insertBefore(getFirstInstruction(m.instructions.getFirst()), list); //Write everything ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { @Override protected String getCommonSuperClass(final String original1, final String original2) { return super.getCommonSuperClass(ASMHelper.getClassFor(original1), ASMHelper.getClassFor(original2)); } }; node.accept(cw); return cw.toByteArray(); } public static byte[] visit(String name, AbstractASM a, byte[] data) { ClassReader cr = new ClassReader(data); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor cv = a.newInstance(name, cw); cr.accept(cv, ClassReader.EXPAND_FRAMES); return cw.toByteArray(); } }