RSCanvas stuff, mostly from runeloader. I assume this breaks resizable. I can't tell whether or not this defers all rendering by 1 frame. Fix various injectreplace bugs. I need to make the unused parameter annotate methods with ObfuscatedSignature or I can't export methods that had an unused parameter. This is going to all break if I ever need to export something with an unused parameter that isn't at the end. Should consider only removing unused parameters that I can detect as being garbage.
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -39,6 +39,11 @@
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.runelite</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
|
||||
public class ClassFile
|
||||
{
|
||||
@@ -246,6 +247,11 @@ public class ClassFile
|
||||
{
|
||||
return methods.findMethod(name);
|
||||
}
|
||||
|
||||
public Method findMethod(String name, Signature signature)
|
||||
{
|
||||
return methods.findMethod(new NameAndType(name, signature));
|
||||
}
|
||||
|
||||
public Method findMethodDeep(String name)
|
||||
{
|
||||
@@ -300,4 +306,9 @@ public class ClassFile
|
||||
{
|
||||
this.access_flags &= ~ACC_FINAL;
|
||||
}
|
||||
|
||||
public void clearAbstract()
|
||||
{
|
||||
this.access_flags &= ~ACC_ABSTRACT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,14 @@ public class Method
|
||||
return (accessFlags & ACC_FINAL) != 0;
|
||||
}
|
||||
|
||||
public void setFinal(boolean f)
|
||||
{
|
||||
if (f)
|
||||
accessFlags |= ACC_FINAL;
|
||||
else
|
||||
accessFlags &= ~ACC_FINAL;
|
||||
}
|
||||
|
||||
public boolean isPrivate()
|
||||
{
|
||||
return (accessFlags & ACC_PRIVATE) != 0;
|
||||
@@ -152,6 +160,11 @@ public class Method
|
||||
{
|
||||
accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PRIVATE);
|
||||
}
|
||||
|
||||
public void setPublic()
|
||||
{
|
||||
accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PUBLIC);
|
||||
}
|
||||
|
||||
public Exceptions getExceptions()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,11 @@ public class AConstNull extends Instruction
|
||||
super(instructions, type, pc);
|
||||
}
|
||||
|
||||
public AConstNull(Instructions instructions)
|
||||
{
|
||||
super(instructions, InstructionType.ACONST_NULL, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstructionContext execute(Frame frame)
|
||||
{
|
||||
|
||||
@@ -68,16 +68,6 @@ public class InvokeInterface extends Instruction implements InvokeInstruction
|
||||
{
|
||||
return myMethods != null ? myMethods : Arrays.asList();
|
||||
}
|
||||
|
||||
private void findMethodFromClass(List<net.runelite.asm.Method> list, ClassFile clazz)
|
||||
{
|
||||
net.runelite.asm.Method m = clazz.findMethodDeep(method.getNameAndType());
|
||||
if (m != null && !list.contains(m))
|
||||
list.add(m);
|
||||
|
||||
for (ClassFile cf : clazz.getChildren())
|
||||
findMethodFromClass(list, cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstructionContext execute(Frame frame)
|
||||
|
||||
@@ -141,11 +141,13 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction
|
||||
public void setMethod(Method method)
|
||||
{
|
||||
this.method = method;
|
||||
lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lookup()
|
||||
{
|
||||
myMethods = null;
|
||||
ClassGroup group = this.getInstructions().getCode().getAttributes().getClassFile().getGroup();
|
||||
|
||||
ClassFile otherClass = group.findClass(method.getClassEntry().getName());
|
||||
|
||||
@@ -62,6 +62,7 @@ public class New extends Instruction
|
||||
public void setNewClass(Class clazz)
|
||||
{
|
||||
this.clazz = clazz;
|
||||
lookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.Attributes;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
@@ -172,7 +173,12 @@ public class Inject
|
||||
assert !m.isStatic();
|
||||
|
||||
String exportedName = an.find(EXPORT).getElement().getString();
|
||||
obfuscatedName = an.find(OBFUSCATED_NAME).getElement().getString();
|
||||
|
||||
Annotation obAn = an.find(OBFUSCATED_NAME);
|
||||
if (obAn != null)
|
||||
obfuscatedName = obAn.getElement().getString();
|
||||
else
|
||||
obfuscatedName = m.getName();
|
||||
|
||||
Method otherm;
|
||||
|
||||
@@ -181,8 +187,11 @@ public class Inject
|
||||
String garbage = null;
|
||||
if (obfuscatedSignature != null)
|
||||
{
|
||||
String signatureString = obfuscatedSignature.getElements().get(0).getString();
|
||||
garbage = obfuscatedSignature.getElements().get(1).getString();
|
||||
List<Element> elements = obfuscatedSignature.getElements();
|
||||
|
||||
String signatureString = elements.get(0).getString();
|
||||
if (elements.size() == 2)
|
||||
garbage = obfuscatedSignature.getElements().get(1).getString();
|
||||
|
||||
Signature signature = new Signature(signatureString); // parse signature
|
||||
|
||||
@@ -349,7 +358,11 @@ public class Inject
|
||||
// deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature
|
||||
// invokeMethod = method to invoke, obfuscated
|
||||
|
||||
assert clazz.findMethod(method.getName()) == null;
|
||||
if (clazz.findMethod(method.getName()) != null)
|
||||
{
|
||||
return; // hmm. this might be due to an export/import of a non obfuscated method
|
||||
}
|
||||
|
||||
assert !invokeMethod.isStatic();
|
||||
assert invokeMethod.getMethods().getClassFile() == clazz;
|
||||
|
||||
@@ -423,6 +436,10 @@ public class Inject
|
||||
{
|
||||
// function requires garbage value
|
||||
|
||||
// if garbage is null here it might just be an unused parameter, not part of the obfuscation
|
||||
if (garbage == null)
|
||||
garbage = "0";
|
||||
|
||||
switch (lastGarbageArgumentType.getType())
|
||||
{
|
||||
case "Z":
|
||||
|
||||
@@ -14,24 +14,31 @@ import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.AConstNull;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.BiPush;
|
||||
import net.runelite.asm.attributes.code.instructions.DLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.FLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.ILoad;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC2_W;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC_W;
|
||||
import net.runelite.asm.attributes.code.instructions.LLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.New;
|
||||
import net.runelite.asm.attributes.code.instructions.Pop;
|
||||
import net.runelite.asm.attributes.code.instructions.Return;
|
||||
import net.runelite.asm.attributes.code.instructions.SiPush;
|
||||
import net.runelite.asm.pool.NameAndType;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.asm.signature.Type;
|
||||
|
||||
public class InjectReplace
|
||||
{
|
||||
private static final Type REPLACE = new Type("Lnet/runelite/mapping/Replace;");
|
||||
private static final Type OBFUSCATED_OVERRIDE = new Type("Lnet/runelite/mapping/ObfuscatedOverride;");
|
||||
//private static final Type OBFUSCATED_NAME = new Type("Lnet/runelite/mapping/ObfuscatedName;");
|
||||
private static final Type OBFUSCATED_NAME = new Type("Lnet/runelite/mapping/ObfuscatedName;");
|
||||
private static final Type OBFUSCATED_SIGNATURE = new Type("Lnet/runelite/mapping/ObfuscatedSignature;");
|
||||
private static final Type EXPORT = new Type("Lnet/runelite/mapping/Export;");
|
||||
|
||||
private ClassFile cf, vanilla;
|
||||
@@ -79,6 +86,7 @@ public class InjectReplace
|
||||
// set parent
|
||||
classToInject.setParentClass(vanilla.getPoolClass());
|
||||
vanilla.clearFinal(); // can't be final anymore now that we inherit from it
|
||||
classToInject.clearAbstract(); // this is being instantiated now, so is no longer abstract
|
||||
|
||||
injectConstructors(classToInject);
|
||||
|
||||
@@ -94,7 +102,7 @@ public class InjectReplace
|
||||
|
||||
private void injectConstructors(ClassFile classToInject)
|
||||
{
|
||||
// Delete compiler generate constructors
|
||||
// Delete compiler generated constructors
|
||||
Methods methods = classToInject.getMethods();
|
||||
Methods vanillaMethods = vanilla.getMethods();
|
||||
|
||||
@@ -139,6 +147,9 @@ public class InjectReplace
|
||||
Method constructor = new Method(methods, "<init>", m.getDescriptor());
|
||||
constructor.setAccessFlags(Method.ACC_PUBLIC);
|
||||
|
||||
// ensure vanilla ctor is public too
|
||||
m.setAccessFlags(Method.ACC_PUBLIC);
|
||||
|
||||
Attributes methodAttributes = constructor.getAttributes();
|
||||
|
||||
// create code attribute
|
||||
@@ -222,41 +233,102 @@ public class InjectReplace
|
||||
String overridenMethod = annotation.getElement().getString(); // name of @Exported method to override
|
||||
|
||||
// Find method with exported name on 'cf'
|
||||
Method obfuscatedMethodToOverride = findMethodByExportedName(overridenMethod);
|
||||
Method obfuscatedMethodToOverride = findMethodByExportedName(overridenMethod); // this is from the deobfuscated jar
|
||||
Method vanillaMethodToOverride = findVanillaMethodFromDeobfuscatedMethod(obfuscatedMethodToOverride);
|
||||
NameAndType deobfuscatedNat = m.getNameAndType();
|
||||
|
||||
assert obfuscatedMethodToOverride != null;
|
||||
assert !obfuscatedMethodToOverride.isFinal();
|
||||
assert !obfuscatedMethodToOverride.isPrivate();
|
||||
assert vanillaMethodToOverride != null;
|
||||
|
||||
vanillaMethodToOverride.setFinal(false);
|
||||
vanillaMethodToOverride.setPublic();
|
||||
|
||||
assert !vanillaMethodToOverride.isFinal();
|
||||
assert !vanillaMethodToOverride.isPrivate();
|
||||
|
||||
// Rename method to override
|
||||
m.setName(obfuscatedMethodToOverride.getName());
|
||||
m.setName(vanillaMethodToOverride.getName());
|
||||
|
||||
assert false;
|
||||
if (!m.getDescriptor().equals(obfuscatedMethodToOverride.getDescriptor()))
|
||||
String garbageValue = null;
|
||||
Signature originalSignature = null;
|
||||
if (!m.getDescriptor().equals(vanillaMethodToOverride.getDescriptor()))
|
||||
{
|
||||
// Obfuscation can add garbage parameter.
|
||||
assert m.getDescriptor().size() + 1 == obfuscatedMethodToOverride.getDescriptor().size();
|
||||
assert m.getDescriptor().size() + 1 == vanillaMethodToOverride.getDescriptor().size();
|
||||
|
||||
// Either we have to modify the bytecode when it is copied over to include this,
|
||||
// or maybe can inject overloaded function into superclass if it doesn't cause a signature collision
|
||||
assert false;
|
||||
originalSignature = m.getDescriptor();
|
||||
|
||||
m.arguments = vanillaMethodToOverride.getDescriptor(); // is this right?
|
||||
|
||||
garbageValue = this.getGarbage(obfuscatedMethodToOverride);
|
||||
}
|
||||
|
||||
// This means method is overriden. It is possible that the return value is a child class
|
||||
// of the parents overriden method, and it will still override the method however the signatures won't match,
|
||||
// but we don't do that.
|
||||
assert m.getDescriptor().equals(obfuscatedMethodToOverride.getDescriptor());
|
||||
assert m.getDescriptor().equals(vanillaMethodToOverride.getDescriptor());
|
||||
|
||||
// Now that the function is overriden, when the invoke injector is called, it turns around and invokevirtuals
|
||||
// the parent method, which hits ours.
|
||||
|
||||
// locate super.method() calls and modify...
|
||||
for (Instruction i : m.getCode().getInstructions().getInstructions())
|
||||
for (Instruction i : new ArrayList<>(m.getCode().getInstructions().getInstructions()))
|
||||
{
|
||||
if (!(i instanceof InvokeSpecial))
|
||||
continue;
|
||||
|
||||
if (originalSignature != null)
|
||||
{
|
||||
assert originalSignature.size() + 1 == m.getDescriptor().size();
|
||||
|
||||
Instructions instructions = m.getCode().getInstructions();
|
||||
List<Instruction> ins = instructions.getInstructions();
|
||||
Type type = m.getDescriptor().getTypeOfArg(m.getDescriptor().size() - 1);
|
||||
int offset = ins.indexOf(i);
|
||||
|
||||
assert offset != -1;
|
||||
|
||||
// XXX we could maybe just pull the variable off of the lvt here, instead
|
||||
// if we know we haven't overwritten it?
|
||||
if (type.getArrayDims() > 0 || !type.isPrimitive())
|
||||
{
|
||||
ins.add(offset, new AConstNull(instructions));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (garbageValue == null)
|
||||
{
|
||||
garbageValue = "0";
|
||||
}
|
||||
|
||||
switch (type.getType())
|
||||
{
|
||||
case "Z":
|
||||
case "B":
|
||||
case "C":
|
||||
ins.add(offset, new BiPush(instructions, Byte.parseByte(garbageValue)));
|
||||
break;
|
||||
case "S":
|
||||
ins.add(offset, new SiPush(instructions, Short.parseShort(garbageValue)));
|
||||
break;
|
||||
case "I":
|
||||
ins.add(offset, new LDC_W(instructions, Integer.parseInt(garbageValue)));
|
||||
break;
|
||||
case "D":
|
||||
ins.add(offset, new LDC2_W(instructions, Double.parseDouble(garbageValue)));
|
||||
break;
|
||||
case "F":
|
||||
ins.add(offset, new LDC_W(instructions, Float.parseFloat(garbageValue)));
|
||||
break;
|
||||
case "J":
|
||||
ins.add(offset, new LDC2_W(instructions, Long.parseLong(garbageValue)));
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InvokeSpecial is = (InvokeSpecial) i;
|
||||
|
||||
net.runelite.asm.pool.Method invokedMethod = (net.runelite.asm.pool.Method) is.getMethod();
|
||||
@@ -293,6 +365,60 @@ public class InjectReplace
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method findVanillaMethodFromDeobfuscatedMethod(Method method)
|
||||
{
|
||||
String name = getObfuscatedName(method);
|
||||
Signature sig = getObfuscatedSignature(method);
|
||||
return vanilla.findMethod(name, sig);
|
||||
}
|
||||
|
||||
private String getObfuscatedName(Method method)
|
||||
{
|
||||
Annotations an = method.getAttributes().getAnnotations();
|
||||
if (an == null)
|
||||
return method.getName();
|
||||
|
||||
Annotation a = an.find(OBFUSCATED_NAME);
|
||||
if (a == null)
|
||||
return method.getName();
|
||||
|
||||
return a.getElement().getString();
|
||||
}
|
||||
|
||||
private Signature getObfuscatedSignature(Method method)
|
||||
{
|
||||
Annotations an = method.getAttributes().getAnnotations();
|
||||
if (an == null)
|
||||
{
|
||||
return method.getDescriptor();
|
||||
}
|
||||
|
||||
Annotation obSig = an.find(OBFUSCATED_SIGNATURE);
|
||||
if (obSig == null)
|
||||
{
|
||||
return method.getDescriptor();
|
||||
}
|
||||
|
||||
return new Signature(obSig.getElement().getString());
|
||||
}
|
||||
|
||||
private String getGarbage(Method method)
|
||||
{
|
||||
Annotations an = method.getAttributes().getAnnotations();
|
||||
if (an == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Annotation obSig = an.find(OBFUSCATED_SIGNATURE);
|
||||
if (obSig == null || obSig.getElements().size() < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return obSig.getElements().get(1).getString();
|
||||
}
|
||||
|
||||
private void replaceSuperclass(ClassFile classToInject)
|
||||
{
|
||||
for (ClassFile cf : vanilla.getGroup().getClasses())
|
||||
@@ -382,6 +508,9 @@ public class InjectReplace
|
||||
InvokeSpecial is = (InvokeSpecial) i;
|
||||
net.runelite.asm.pool.Method method = (net.runelite.asm.pool.Method) is.getMethod();
|
||||
|
||||
if (!method.getNameAndType().getName().equals("<init>") || !method.getClassEntry().equals(vanilla.getPoolClass()))
|
||||
continue;
|
||||
|
||||
is.setMethod(new net.runelite.asm.pool.Method(
|
||||
classToInject.getPoolClass(),
|
||||
method.getNameAndType()
|
||||
|
||||
27
src/main/java/net/runelite/inject/RSCanvas.java
Normal file
27
src/main/java/net/runelite/inject/RSCanvas.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.runelite.inject;
|
||||
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public abstract class RSCanvas extends Canvas
|
||||
{
|
||||
private final BufferedImage clientBuffer = new BufferedImage(756, 503, BufferedImage.TYPE_INT_RGB);
|
||||
private final BufferedImage gameBuffer = new BufferedImage(756, 503, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
@Override
|
||||
public Graphics getGraphics()
|
||||
{
|
||||
Graphics clientGraphics = clientBuffer.getGraphics();
|
||||
clientGraphics.drawImage(gameBuffer, 0, 0, null);
|
||||
//clientGraphics.dispose();
|
||||
|
||||
//clientGraphics = clientBuffer.getGraphics();
|
||||
clientGraphics.drawString("something, something", 42, 42);
|
||||
|
||||
Graphics superGraphics = super.getGraphics();
|
||||
superGraphics.drawImage(clientBuffer, 0, 0, null);
|
||||
|
||||
return gameBuffer.getGraphics();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user