141 lines
4.5 KiB
Java
141 lines
4.5 KiB
Java
package mods.betterfoliage.loader;
|
|
|
|
import java.lang.annotation.ElementType;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.annotation.Target;
|
|
import java.lang.reflect.Method;
|
|
|
|
import net.minecraft.launchwrapper.IClassTransformer;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.objectweb.asm.ClassReader;
|
|
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.MethodInsnNode;
|
|
import org.objectweb.asm.tree.MethodNode;
|
|
|
|
public class EZTransformerBase implements IClassTransformer {
|
|
|
|
public static interface IInstructionMatch {
|
|
public boolean matches(AbstractInsnNode node);
|
|
}
|
|
|
|
@Target(ElementType.METHOD)
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
public static @interface MethodTransform {
|
|
public String className();
|
|
public String methodName();
|
|
public String signature();
|
|
public String log();
|
|
}
|
|
|
|
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
|
|
|
|
protected Boolean isObfuscated;
|
|
|
|
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
|
// ???
|
|
if (basicClass == null) return null;
|
|
|
|
// read class
|
|
ClassNode classNode = new ClassNode();
|
|
ClassReader classReader = new ClassReader(basicClass);
|
|
classReader.accept(classNode, 0);
|
|
boolean hasTransformed = false;
|
|
|
|
for (Method classMethod : getClass().getMethods()) {
|
|
// check for annotated method with correct signature
|
|
MethodTransform annot = classMethod.getAnnotation(MethodTransform.class);
|
|
if (annot == null) continue;
|
|
if (classMethod.getParameterTypes().length != 1) continue;
|
|
if (!classMethod.getParameterTypes()[0].equals(MethodNode.class)) continue;
|
|
|
|
// try to find specified method in class
|
|
if (!transformedName.equals(annot.className())) continue;
|
|
logger.debug(String.format("Found class: %s -> %s", name, transformedName));
|
|
for (MethodNode methodNode : classNode.methods) {
|
|
logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc));
|
|
isObfuscated = null;
|
|
if (methodNode.name.equals(DeobfHelper.transformElementName(annot.methodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(annot.signature()))) {
|
|
isObfuscated = true;
|
|
} else if (methodNode.name.equals(annot.methodName()) && methodNode.desc.equals(annot.signature())) {
|
|
isObfuscated = false;
|
|
}
|
|
|
|
if (isObfuscated != null) {
|
|
// transform
|
|
hasTransformed = true;
|
|
try {
|
|
classMethod.invoke(this, new Object[] {methodNode});
|
|
logger.info(String.format("%s: SUCCESS", annot.log()));
|
|
} catch (Exception e) {
|
|
logger.info(String.format("%s: FAILURE", annot.log()));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// return result
|
|
ClassWriter writer = new ClassWriter(0);
|
|
if (hasTransformed) classNode.accept(writer);
|
|
return !hasTransformed ? basicClass : writer.toByteArray();
|
|
}
|
|
|
|
protected String className(String className) {
|
|
return isObfuscated ? DeobfHelper.transformClassName(className) : className;
|
|
}
|
|
|
|
protected String element(String fieldName) {
|
|
return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName;
|
|
}
|
|
|
|
protected String signature(String signature) {
|
|
return isObfuscated ? DeobfHelper.transformSignature(signature) : signature;
|
|
}
|
|
|
|
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
|
|
AbstractInsnNode current = start;
|
|
while(current != null) {
|
|
if (match.matches(current)) break;
|
|
current = current.getNext();
|
|
}
|
|
return current;
|
|
}
|
|
|
|
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
|
|
AbstractInsnNode current = start;
|
|
while(current != null) {
|
|
if (match.matches(current)) break;
|
|
current = current.getPrevious();
|
|
}
|
|
return current;
|
|
}
|
|
|
|
protected static IInstructionMatch matchInvokeAny() {
|
|
return new IInstructionMatch() {
|
|
public boolean matches(AbstractInsnNode node) {
|
|
return node instanceof MethodInsnNode;
|
|
}
|
|
};
|
|
}
|
|
|
|
protected static IInstructionMatch matchOpcode(final int opcode) {
|
|
return new IInstructionMatch() {
|
|
public boolean matches(AbstractInsnNode node) {
|
|
return node.getOpcode() == opcode;
|
|
}
|
|
};
|
|
}
|
|
|
|
protected static void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
|
|
InsnList listAdd = new InsnList();
|
|
for (AbstractInsnNode inst : added) listAdd.add(inst);
|
|
insnList.insert(node, listAdd);
|
|
}
|
|
}
|