Asm
package cbop.core.loader.asm;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
/**
* The Class Asm.
*
* @author cbOP_
*/
public class Asm {
public String owner;
public String name;
public String desc;
public Asm(String owner, String name, String desc) {
this.owner = owner;
this.name = name;
this.desc = desc;
if (owner.contains(".")) {
throw new IllegalArgumentException(owner);
}
}
public static Asm fromDesc(String s) {
int lastDot = s.lastIndexOf('.');
if (lastDot < 0) {
return new Asm(s, "", "");
}
int sep = s.indexOf('(');
int sep_end = sep;
if (sep < 0) {
sep = s.indexOf(' ');
sep_end = sep + 1;
}
if (sep < 0) {
sep = s.indexOf(':');
sep_end = sep + 1;
}
if (sep < 0) {
return new Asm(s.substring(0, lastDot),
s.substring(lastDot + 1), "");
}
return new Asm(s.substring(0, lastDot), s.substring(lastDot + 1,
sep), s.substring(sep_end));
}
public boolean matches(MethodNode node) {
return name.equals(node.name) && desc.equals(node.desc);
}
public boolean matches(MethodInsnNode node) {
return owner.equals(node.owner) && name.equals(node.name)
&& desc.equals(node.desc);
}
public AbstractInsnNode toInsn(int opcode) {
if (isClass()) {
return new TypeInsnNode(opcode, owner);
} else if (isMethod()) {
return new MethodInsnNode(opcode, owner, name, desc);
} else {
return new FieldInsnNode(opcode, owner, name, desc);
}
}
public boolean isClass(String name) {
return name.replace('.', '/').equals(owner);
}
public boolean matches(String name, String desc) {
return name.equals(name) && desc.equals(desc);
}
public boolean matches(FieldNode node) {
return name.equals(node.name) && desc.equals(node.desc);
}
public boolean matches(FieldInsnNode node) {
return owner.equals(node.owner) && name.equals(node.name)
&& desc.equals(node.desc);
}
public String javaClass() {
return owner.replace('/', '.');
}
public String methodDesc() {
return owner + "." + name + desc;
}
public String fieldDesc() {
return owner + "." + name + ":" + desc;
}
public boolean isClass() {
return name.length() == 0;
}
public boolean isMethod() {
return desc.contains("(");
}
public boolean isField() {
return !isClass() && !isMethod();
}
}
AsmClass, AsmMethod and AsmField just extend Asm with a constructor.
NAPITransformer
package cbop.core.napi.transform;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import cbop.core.loader.asm.AsmClass;
import cbop.core.loader.asm.AsmField;
import cbop.core.loader.asm.AsmMethod;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.relauncher.Side;
/**
* The Class CoreTransformer.
*
* @author cbOP_
*/
public class NAPITransformer implements IClassTransformer {
public AsmClass c_IIconRegister = new AsmClass("net/minecraft/client/renderer/texture/IIconRegister");
public AsmClass c_ITileEntityProvider = new AsmClass("net/minecraft/block/ITileEntityProvider");
public AsmClass c_World = new AsmClass("net/minecraft/world/World");
public AsmClass c_Block = new AsmClass("net/minecraft/block/Block");
public AsmClass c_TileEntity = new AsmClass("net/minecraft/tileentity/TileEntity");
public AsmMethod m_renderAsNormalBlock = new AsmMethod("net/minecraft/block/Block", "renderAsNormalBlock", "()Z");
public AsmMethod m_isOpaqueCube = new AsmMethod("net/minecraft/block/Block", "isOpaqueCube", "()Z");
public AsmMethod m_getRenderType = new AsmMethod("net/minecraft/block/Block", "getRenderType", "()I");
public AsmMethod m_registerBlockIcons = new AsmMethod("net/minecraft/block/Block", "registerBlockIcons", "(Lnet/minecraft/client/renderer/texture/IIconRegister;)V");
public AsmMethod m_createNewTileEntity = new AsmMethod("net/minecraft/block/ITileEntityProvider", "createNewTileEntity", "(Lnet/minecraft/world/World;I)Lnet/minecraft/tileentity/TileEntity;");
public AsmMethod m_breakBlock = new AsmMethod("net/minecraft/block/Block", "breakBlock", "(Lnet/minecraft/world/World;IIILnet/minecraft/block/Block;I)V");
public AsmMethod m_removeTileEntity = new AsmMethod("net/minecraft/world/World", "removeTileEntity", "(III)V");
public AsmMethod m_onBlockEventReceived = new AsmMethod("net/minecraft/block/Block", "onBlockEventReceived", "(Lnet/minecraft/world/World;IIIII)Z");
public AsmMethod m_getTileEntity = new AsmMethod("net/minecraft/world/World", "getTileEntity", "(III)Lnet/minecraft/tileentity/TileEntity;");
public AsmMethod m_registerIcons = new AsmMethod("net/minecraft/item/Item", "registerIcons", "(Lnet/minecraft/client/renderer/texture/IIconRegister;)V");
public AsmField f_isBlockContainer = new AsmField("net/minecraft/block/Block", "isBlockContainer", "Z");
private String annotation;
private ClassNode classNode;
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) {
return null;
}
classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
{
addAnnotation("BlockRenderer");
addAnnotation("ItemRenderer");
addAnnotation("TEProvider");
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
return writer.toByteArray();
}
private void addAnnotation(String name) {
AnnotationNode node = null;
if (classNode.visibleAnnotations != null) {
for (AnnotationNode an : classNode.visibleAnnotations) {
if (an.desc.equals("Lcbop/core/napi/" + name + ";")) {
node = an;
break;
}
}
}
if (node == null) {
return;
}
this.annotation = name;
debugAnnotation(classNode, name);
try {
Annotation a = (Annotation) Class.forName("cbop.core.napi.transform." + annotation).newInstance();
boolean client = a.apply(this);
if (client && FMLCommonHandler.instance().getSide() != Side.CLIENT) {
classNode.visibleAnnotations.remove(node);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void addInterface(AsmClass inf) {
if (!hasInterface(classNode, inf)) {
classNode.interfaces.add(inf.owner);
debugInterface(inf);
}
}
public void addMethod(AsmMethod method, boolean client) {
if (!hasMethod(classNode, method) &&
client ? FMLCommonHandler.instance().getSide() == Side.CLIENT : true) {
MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC, method.name, method.desc, null, null);
try {
Method m = (Method) Class.forName("cbop.core.napi.transform."
+ annotation + "_" + method.name).newInstance();
m.apply(this, classNode, mn);
} catch (Exception e) {
e.printStackTrace();
}
classNode.methods.add(mn);
debugMethod(method);
}
}
public void addConstructor() {
try {
Constructor c = (Constructor) Class.forName("cbop.core.napi.transform."
+ annotation + "_constructor").newInstance();
for (MethodNode mn : classNode.methods) {
if (mn.name.equals("<init>")) {
c.apply(this, classNode, mn);
debugContructor(mn);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean hasInterface(ClassNode classNode, AsmClass c) {
for (String i : classNode.interfaces) {
if (c.isClass(i)) {
return true;
}
}
return false;
}
private boolean hasMethod(ClassNode classNode, AsmMethod m) {
for (MethodNode mn : classNode.methods) {
if (m.matches(mn)) {
return true;
}
}
return false;
}
private void debugAnnotation(ClassNode classNode, String name) {
System.out.println("[cbOP_ Core] Transforming @" + name + " in " + classNode.name);
}
private void debugInterface(AsmClass inf) {
System.out.println("[cbOP_ Core] => Added interface " + inf.owner);
}
private void debugMethod(AsmMethod method) {
System.out.println("[cbOP_ Core] => Generated " + method.name);
}
private void debugContructor(MethodNode mn) {
System.out.println("[cbOP_ Core] => Modified constructor " + mn.desc);
}
}
BlockRenderer
package cbop.core.napi.transform;
public class BlockRenderer implements Annotation {
@Override
public boolean apply(NAPITransformer napi) {
napi.addMethod(napi.m_renderAsNormalBlock, false);
napi.addMethod(napi.m_isOpaqueCube, false);
napi.addMethod(napi.m_getRenderType, true);
napi.addMethod(napi.m_registerBlockIcons, true);
return true;
}
}
BlockRenderer_getRenderType
package cbop.core.napi.transform;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
public class BlockRenderer_getRenderType implements Method {
@Override
public void apply(NAPITransformer napi, ClassNode cn, MethodNode mn) {
mn.maxLocals = 1;
mn.maxStack = 1;
mn.localVariables.add(new LocalVariableNode("this", "L" + cn.name + ";",
null, new LabelNode(), new LabelNode(), 0));
mn.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"cbop/core/napi/NAPI", "getBlockRenderer", "()I"));
mn.instructions.add(new InsnNode(Opcodes.IRETURN));
}
}