diff --git a/pom.xml b/pom.xml
index efb5165e6e..985ec73a4d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,21 +24,16 @@
commons-compress
1.10
-
com.google.code.gson
gson
2.4
-
+
+ org.ow2.asm
+ asm-all
+ 5.0.4
+
org.slf4j
diff --git a/src/main/java/net/runelite/deob/inject/AddFieldInstruction.java b/src/main/java/net/runelite/deob/inject/AddFieldInstruction.java
new file mode 100644
index 0000000000..79d7eca2fb
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/AddFieldInstruction.java
@@ -0,0 +1,7 @@
+package net.runelite.deob.inject;
+
+public class AddFieldInstruction {
+ public static String owner;
+ public static String name;
+ public static String desc;
+}
diff --git a/src/main/java/net/runelite/deob/inject/AddInterfaceInstruction.java b/src/main/java/net/runelite/deob/inject/AddInterfaceInstruction.java
new file mode 100644
index 0000000000..376f7ea799
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/AddInterfaceInstruction.java
@@ -0,0 +1,29 @@
+package net.runelite.deob.inject;
+
+import org.objectweb.asm.tree.ClassNode;
+
+public class AddInterfaceInstruction {
+ private final String clientClass;
+ private final String interfaceClass;
+
+ public AddInterfaceInstruction(String var1, String var2) {
+ this.clientClass = var1;
+ this.interfaceClass = var2;
+ }
+
+ public String getClientClass() {
+ return this.clientClass;
+ }
+
+ public String getInterfaceClass() {
+ return this.interfaceClass;
+ }
+
+ public boolean valid(ClassNode var1) {
+ return var1.name.equalsIgnoreCase(this.clientClass);
+ }
+
+ public void inject(ClassNode var1) {
+ var1.interfaces.add(this.interfaceClass);
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/AddMethodInstruction.java b/src/main/java/net/runelite/deob/inject/AddMethodInstruction.java
new file mode 100644
index 0000000000..e356158a13
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/AddMethodInstruction.java
@@ -0,0 +1,37 @@
+package net.runelite.deob.inject;
+
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class AddMethodInstruction {
+ private final String clientClass;
+ private final String methodName;
+ private final String methodDesc;
+ private final AbstractInsnNode[] instructions;
+
+ public AddMethodInstruction(String var1, String var2, String var3, AbstractInsnNode[] var4) {
+ this.clientClass = var1;
+ this.methodName = var2;
+ this.methodDesc = var3;
+ this.instructions = var4;
+ }
+
+ public boolean valid(ClassNode var1) {
+ return var1.name.equalsIgnoreCase(this.clientClass);
+ }
+
+ public void inject(ClassNode var1) {
+ MethodNode var2 = new MethodNode(1, this.methodName, this.methodDesc, (String)null, (String[])null);
+ AbstractInsnNode[] var6 = this.instructions;
+ int var5 = this.instructions.length;
+
+ for(int var4 = 0; var4 < var5; ++var4) {
+ AbstractInsnNode var3 = var6[var4];
+ var2.instructions.add(var3);
+ }
+
+ var2.visitMaxs(0, 0);
+ var1.methods.add(var2);
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/GetterInjectInstruction.java b/src/main/java/net/runelite/deob/inject/GetterInjectInstruction.java
new file mode 100644
index 0000000000..923f494b7e
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/GetterInjectInstruction.java
@@ -0,0 +1,149 @@
+package net.runelite.deob.inject;
+
+import java.util.Iterator;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.FieldNode;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+public class GetterInjectInstruction {
+ private final String className;
+ private final String getterMethodDesc;
+ private final String getterName;
+ private final String getterClassName;
+ private final String getterFieldName;
+ private final Integer multiplier;
+ private final boolean staticField;
+
+ public GetterInjectInstruction(String var1, String var2, String var3, String var4, String var5, Integer var6, boolean var7) {
+ this.className = var1;
+ this.getterMethodDesc = "()" + var3;
+ this.getterName = var2;
+ this.getterClassName = var4;
+ this.getterFieldName = var5;
+ this.multiplier = var6;
+ this.staticField = var7;
+ }
+
+ public String getClassName() {
+ return this.className;
+ }
+
+ public String getGetterMethodDesc() {
+ return this.getterMethodDesc;
+ }
+
+ public String getGetterName() {
+ return this.getterName;
+ }
+
+ public String getGetterClassName() {
+ return this.getterClassName;
+ }
+
+ public String getGetterFieldName() {
+ return this.getterFieldName;
+ }
+
+ public Integer getMultiplier() {
+ return this.multiplier;
+ }
+
+ public boolean isStaticField() {
+ return this.staticField;
+ }
+//
+// public boolean valid(ClassNode var1) {
+// return var1.name.equalsIgnoreCase(this.className);
+// }
+//
+// private FieldNode get(qelKbskphK[] var1) {
+// qelKbskphK[] var5 = var1;
+// int var4 = var1.length;
+//
+// for(int var3 = 0; var3 < var4; ++var3) {
+// qelKbskphK var2 = var5[var3];
+// ClassNode var6 = var2.KxdZMCzVTn();
+// if(var6.name.equalsIgnoreCase(this.getterClassName)) {
+// Iterator var8 = var6.fields.iterator();
+//
+// while(var8.hasNext()) {
+// FieldNode var7 = (FieldNode)var8.next();
+// if(var7.name.equalsIgnoreCase(this.getterFieldName)) {
+// return var7;
+// }
+// }
+// }
+// }
+//
+// return null;
+// }
+//
+// public void inject(ClassNode var1, qelKbskphK[] var2) {
+// String var3 = this.getGetterMethodDesc().substring(2);
+// MethodNode var4 = new MethodNode(1, this.getGetterName(), this.getGetterMethodDesc(), (String)null, (String[])null);
+// if(!this.isStaticField()) {
+// var4.instructions.add(new VarInsnNode(25, 0));
+// }
+//
+// int var5 = this.isStaticField()?178:180;
+// var4.instructions.add(new FieldInsnNode(var5, this.getterClassName, this.getterFieldName, this.get(var2).desc));
+// if(this.getMultiplier() != null) {
+// var4.instructions.add(new LdcInsnNode(this.getMultiplier()));
+// var4.instructions.add(new InsnNode(104));
+// }
+//
+// var4.instructions.add(new InsnNode(getReturn(Type.getReturnType(var3).getSort())));
+// var1.methods.add(var4);
+// }
+//
+// public static int getReturn(int var0) {
+// return var0 == 0?177:(var0 >= 1 && var0 <= 5?172:(var0 == 6?174:(var0 == 7?173:(var0 == 8?175:176))));
+// }
+//
+// private int getReturnOpcode(String var1) {
+// var1 = var1.substring(var1.indexOf(")") + 1);
+// if(var1.length() > 1) {
+// return 176;
+// } else {
+// char var2 = var1.charAt(0);
+// switch(var2) {
+// case 'B':
+// case 'C':
+// case 'I':
+// case 'S':
+// case 'Z':
+// return 172;
+// case 'D':
+// return 175;
+// case 'E':
+// case 'G':
+// case 'H':
+// case 'K':
+// case 'L':
+// case 'M':
+// case 'N':
+// case 'O':
+// case 'P':
+// case 'Q':
+// case 'R':
+// case 'T':
+// case 'U':
+// case 'V':
+// case 'W':
+// case 'X':
+// case 'Y':
+// default:
+// throw new RuntimeException("bad_return");
+// case 'F':
+// return 174;
+// case 'J':
+// return 173;
+// }
+// }
+// }
+}
diff --git a/src/main/java/net/runelite/deob/inject/InjectionModscript.java b/src/main/java/net/runelite/deob/inject/InjectionModscript.java
new file mode 100644
index 0000000000..e1694f5e00
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/InjectionModscript.java
@@ -0,0 +1,60 @@
+package net.runelite.deob.inject;
+
+import com.google.common.io.Files;
+import com.google.gson.GsonBuilder;
+import com.runeloader.RuneLoader;
+import com.runeloader.api.inject.InstructionDeserializer;
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import org.objectweb.asm.tree.AbstractInsnNode;
+
+public class InjectionModscript {
+ private List getterInjects = new LinkedList();
+ private List superChangeInjects = new LinkedList();
+ private List addInterfaceInjects = new LinkedList();
+ private List methodMods = new LinkedList();
+ private List addMethods = new LinkedList();
+ private List newMethodMods = new LinkedList();
+
+ public void save() {
+ byte[] var1 = (new GsonBuilder()).setPrettyPrinting().create().toJson(this).getBytes();
+
+ try {
+ Files.write(var1, new File("injection.json"));
+ } catch (IOException var3) {
+ var3.printStackTrace();
+ }
+
+ }
+
+ public static InjectionModscript load(File var0) throws IOException {
+ byte[] var1 = Files.toByteArray(var0);
+ return (InjectionModscript)(new GsonBuilder()).registerTypeAdapter(AbstractInsnNode.class, new InstructionDeserializer()).create().fromJson((new String(var1)).replaceAll("runecore", "runeloader"), InjectionModscript.class);
+ }
+
+ public List getGetterInjects() {
+ return this.getterInjects;
+ }
+
+ public List getSuperChangeInjects() {
+ return this.superChangeInjects;
+ }
+
+ public List getAddInterfaceInjects() {
+ return this.addInterfaceInjects;
+ }
+
+ public List getMethodMods() {
+ return this.methodMods;
+ }
+
+ public List getAddMethods() {
+ return this.addMethods;
+ }
+
+ public List getNewMethodMods() {
+ return this.newMethodMods;
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/InstructionDeserializer.java b/src/main/java/net/runelite/deob/inject/InstructionDeserializer.java
new file mode 100644
index 0000000000..7d8ff3f03a
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/InstructionDeserializer.java
@@ -0,0 +1,57 @@
+package net.runelite.deob.inject;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.lang.reflect.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+public class InstructionDeserializer implements JsonDeserializer {
+ public AbstractInsnNode deserialize(JsonElement var1, Type var2, JsonDeserializationContext var3) {
+ JsonObject var4 = (JsonObject)var1;
+ int var5 = var4.get("opcode").getAsInt();
+ if(var5 != 21 && var5 != 25 && var5 != 58 && var5 != 54 && var5 != 22) {
+ String var7;
+ String var8;
+ String var10;
+ if(var5 != 184 && var5 != 182 && var5 != 183) {
+ if(var5 == 18) {
+ try {
+ return new LdcInsnNode(Integer.valueOf(var4.get("cst").getAsInt()));
+ } catch (Exception var9) {
+ return new LdcInsnNode(var4.get("cst").getAsString());
+ }
+ } else if(var5 == 187) {
+ return new TypeInsnNode(var5, var4.get("desc").getAsString());
+ } else if(var5 != 16 && var5 != 17) {
+ if(var5 != 179 && var5 != 178 && var5 != 180 && var5 != 181) {
+ return new InsnNode(var5);
+ } else {
+ var10 = var4.get("owner").getAsString();
+ var7 = var4.get("name").getAsString();
+ var8 = var4.get("desc").getAsString();
+ return new FieldInsnNode(var5, var10, var7, var8);
+ }
+ } else {
+ return new IntInsnNode(var5, var4.get("operand").getAsInt());
+ }
+ } else {
+ var10 = var4.get("owner").getAsString();
+ var7 = var4.get("name").getAsString();
+ var8 = var4.get("desc").getAsString();
+ return new MethodInsnNode(var5, var10, var7, var8);
+ }
+ } else {
+ int var6 = var4.get("var").getAsInt();
+ return new VarInsnNode(var5, var6);
+ }
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/MethodModInstruction.java b/src/main/java/net/runelite/deob/inject/MethodModInstruction.java
new file mode 100644
index 0000000000..a2a0edc658
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/MethodModInstruction.java
@@ -0,0 +1,64 @@
+package net.runelite.deob.inject;
+
+import java.util.Iterator;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.MethodNode;
+
+public class MethodModInstruction {
+ private final int startIndex;
+ private final AbstractInsnNode[] nodes;
+ public final String owner;
+ public final String method;
+ public final String desc;
+
+ public MethodModInstruction(int var1, AbstractInsnNode[] var2, String var3, String var4, String var5) {
+ this.startIndex = var1;
+ this.nodes = var2;
+ this.owner = var3;
+ this.method = var4;
+ this.desc = var5;
+ }
+
+ public boolean valid(ClassNode var1) {
+ return var1.name.equalsIgnoreCase(this.owner);
+ }
+
+ public void inject(ClassNode var1) {
+ Iterator var3 = var1.methods.iterator();
+
+ while(true) {
+ MethodNode var2;
+ do {
+ do {
+ if(!var3.hasNext()) {
+ return;
+ }
+
+ var2 = (MethodNode)var3.next();
+ } while(!var2.name.equalsIgnoreCase(this.method));
+ } while(!var2.desc.equalsIgnoreCase(this.desc));
+
+ InsnList var4 = var2.instructions;
+
+ try {
+ AbstractInsnNode var5 = var4.get(this.startIndex);
+ AbstractInsnNode var6 = null;
+
+ for(int var7 = 0; var7 < this.nodes.length; ++var7) {
+ if(var6 == null) {
+ var4.insertBefore(var5, this.nodes[var7]);
+ } else {
+ var4.insert(var6, this.nodes[var7]);
+ }
+
+ var6 = this.nodes[var7];
+ }
+ } catch (Exception var8) {
+ System.err.println("Failed on " + this.startIndex + " @ " + this.owner + "." + this.method + " " + this.desc);
+ var8.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/SuperChangeInstruction.java b/src/main/java/net/runelite/deob/inject/SuperChangeInstruction.java
new file mode 100644
index 0000000000..76754bd9fe
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/SuperChangeInstruction.java
@@ -0,0 +1,29 @@
+package net.runelite.deob.inject;
+
+import org.objectweb.asm.tree.ClassNode;
+
+public class SuperChangeInstruction {
+ private final String clientName;
+ private final String superName;
+
+ public SuperChangeInstruction(String var1, String var2) {
+ this.clientName = var1;
+ this.superName = var2;
+ }
+
+ public String getClientName() {
+ return this.clientName;
+ }
+
+ public String getSuperName() {
+ return this.superName;
+ }
+
+ public boolean valid(ClassNode var1) {
+ return var1.name.equalsIgnoreCase(this.clientName);
+ }
+
+ public void inject(ClassNode var1) {
+ var1.superName = this.superName;
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/TableJumpInstruction$TableJump.java b/src/main/java/net/runelite/deob/inject/TableJumpInstruction$TableJump.java
new file mode 100644
index 0000000000..96dcea7c8a
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/TableJumpInstruction$TableJump.java
@@ -0,0 +1,16 @@
+package net.runelite.deob.inject;
+
+import org.objectweb.asm.Label;
+
+public class TableJumpInstruction$TableJump {
+ public Label label;
+ public final int instructionIndex;
+ public final int labelArrayIndex;
+ public final int opcode;
+
+ public TableJumpInstruction$TableJump(int var1, int var2, int var3) {
+ this.instructionIndex = var1;
+ this.opcode = var3;
+ this.labelArrayIndex = var2;
+ }
+}
diff --git a/src/main/java/net/runelite/deob/inject/TableJumpInstruction.java b/src/main/java/net/runelite/deob/inject/TableJumpInstruction.java
new file mode 100644
index 0000000000..bd4289c5b4
--- /dev/null
+++ b/src/main/java/net/runelite/deob/inject/TableJumpInstruction.java
@@ -0,0 +1,105 @@
+package net.runelite.deob.inject;
+
+import java.util.Iterator;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class TableJumpInstruction {
+ private final TableJumpInstruction$TableJump[] tableJumps;
+ private final int[] labels;
+ private final int start;
+ private final AbstractInsnNode[] instructions;
+ private final String owner;
+ private final String name;
+ private final String desc;
+
+ public TableJumpInstruction(TableJumpInstruction$TableJump[] var1, AbstractInsnNode[] var2, int[] var3, int var4, String var5, String var6, String var7) {
+ this.tableJumps = var1;
+ this.instructions = var2;
+ this.labels = var3;
+ this.owner = var5;
+ this.name = var6;
+ this.desc = var7;
+ this.start = var4;
+ }
+
+ public boolean valid(ClassNode var1) {
+ return var1.name.equalsIgnoreCase(this.owner);
+ }
+
+ public void inject(ClassNode var1) {
+ Iterator var3 = var1.methods.iterator();
+
+ MethodNode var2;
+ do {
+ if(!var3.hasNext()) {
+ return;
+ }
+
+ var2 = (MethodNode)var3.next();
+ } while(!var2.name.equalsIgnoreCase(this.name) || !var2.desc.equalsIgnoreCase(this.desc));
+
+ this.insert(var2);
+ }
+
+ private void insert(MethodNode var1) {
+ AbstractInsnNode[] var2 = new AbstractInsnNode[this.instructions.length];
+ Label[] var3 = new Label[this.labels.length];
+ InsnList var4 = var1.instructions;
+
+ int var5;
+ for(var5 = 0; var5 < this.labels.length; ++var5) {
+ int var6 = this.labels[var5];
+ if(var6 == -1) {
+ var3[var5] = new Label();
+ } else {
+ JumpInsnNode var7 = (JumpInsnNode)var4.get(var6);
+ var3[var5] = var7.label.getLabel();
+ }
+ }
+
+ for(var5 = 0; var5 < this.instructions.length; ++var5) {
+ Object var11 = this.instructions[var5];
+ if(var11 == null) {
+ TableJumpInstruction$TableJump var12 = this.getJump(var5);
+ LabelNode var8 = new LabelNode(var3[var12.labelArrayIndex]);
+ if(var12.opcode != 9000) {
+ var11 = new JumpInsnNode(var12.opcode, var8);
+ } else {
+ var11 = var8;
+ }
+ }
+
+ var2[var5] = (AbstractInsnNode)var11;
+ }
+
+ AbstractInsnNode var10 = var4.get(this.start);
+ AbstractInsnNode[] var9 = var2;
+ int var15 = var2.length;
+
+ for(int var13 = 0; var13 < var15; ++var13) {
+ AbstractInsnNode var14 = var9[var13];
+ var4.insertBefore(var10, var14);
+ }
+
+ }
+
+ private TableJumpInstruction$TableJump getJump(int var1) {
+ TableJumpInstruction$TableJump[] var5 = this.tableJumps;
+ int var4 = this.tableJumps.length;
+
+ for(int var3 = 0; var3 < var4; ++var3) {
+ TableJumpInstruction$TableJump var2 = var5[var3];
+ if(var2.instructionIndex == var1) {
+ return var2;
+ }
+ }
+
+ return null;
+ }
+}