diff --git a/src/main/java/com/openosrs/injector/InjectUtil.java b/src/main/java/com/openosrs/injector/InjectUtil.java index 870e2bb..b20bb05 100644 --- a/src/main/java/com/openosrs/injector/InjectUtil.java +++ b/src/main/java/com/openosrs/injector/InjectUtil.java @@ -30,6 +30,7 @@ import net.runelite.asm.attributes.code.instructions.VReturn; import net.runelite.asm.pool.Class; import net.runelite.asm.signature.Signature; import net.runelite.deob.DeobAnnotations; +import org.jetbrains.annotations.Nullable; public interface InjectUtil { @@ -40,9 +41,9 @@ public interface InjectUtil * @param name The name of the method you want to find * @return The obfuscated version of the found method */ - static Method findStaticMethod(InjectData data, String name) throws Injexception + static Method findMethod(InjectData data, String name) throws Injexception { - return findStaticMethod(data, name, null, null); + return findMethod(data, name, null, null); } /** @@ -55,44 +56,84 @@ public interface InjectUtil * * @return The obfuscated version of the found method */ - static Method findStaticMethod(InjectData data, String name, String classHint, Signature sig) throws Injexception + static Method findMethod( + InjectData data, + String name, + String classHint, + Signature sig) + throws Injexception + { + return findMethod(data, name, classHint, sig, false, false); + } + + /** + * Finds a method in injectData's deobfuscated classes. + * + * @param data InjectData instance + * @param name (Exported) method name + * @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure + * @throws Injexception If the hint class couldn't be found, or no method matching the settings was found + */ + static Method findMethod( + InjectData data, + String name, + @Nullable String classHint) + throws Injexception + { + return findMethod(data, name, classHint, null, false, false); + } + + /** + * Finds a method in injectData's deobfuscated classes. + * + * @param data InjectData instance + * @param name (Exported) method name + * @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure + * @param sig The deobfuscated methods' signature, or null, if you're unsure + * @param notStatic If this is true, only check non-static methods. If classHint isn't null, check only subclasses + * @param returnDeob If this is true, this method will return the deobfuscated method, instead of turning it into vanilla first + * + * @throws Injexception If the hint class couldn't be found, or no method matching the settings was found + */ + static Method findMethod( + InjectData data, + String name, + @Nullable String classHint, + @Nullable Signature sig, + boolean notStatic, + boolean returnDeob) + throws Injexception { final ClassGroup deob = data.getDeobfuscated(); - Method method; - if (classHint != null) { - ClassFile clazz = findClassOrThrow(deob, classHint); - - if (sig == null) + ClassFile cf; + Method m; + cf = findClassOrThrow(deob, classHint); + if (notStatic) { - method = clazz.findStaticMethod(name); + if (sig == null) + m = cf.findMethodDeep(name); + else + m = cf.findMethodDeep(name, sig); + if (m == null) + throw new Injexception(String.format("Couldn't find %s in subclasses of %s", name, classHint)); } else - { - method = clazz.findStaticMethod(name, sig); - } + m = cf.findMethod(name); - if (method != null) - return data.toVanilla(method); + if (m != null) + return returnDeob ? m : data.toVanilla(m); } - for (ClassFile clazz : deob) - { - if (sig == null) - { - method = clazz.findStaticMethod(name); - } - else - { - method = clazz.findStaticMethod(name, sig); - } + for (ClassFile cf : deob) + for (Method m : cf.getMethods()) + if (m.getName().equals(name)) + if (!notStatic || !m.isStatic()) + if (sig == null || sig.equals(m.getDescriptor())) + return returnDeob ? m : data.toVanilla(m); - if (method != null) - return data.toVanilla(method); - } - - throw new Injexception("Static method " + name + " doesn't exist"); + throw new Injexception(String.format("Couldn't find %s", name)); } static ClassFile findClassOrThrow(ClassGroup group, String name) throws Injexception @@ -104,19 +145,6 @@ public interface InjectUtil return clazz; } - /** - * Finds a static method in deob and converts it to ob - * - * @param data InjectData instance - * @param pool Pool method of the method you want - * - * @return The obfuscated version of the found method - */ - static Method findStaticMethod(InjectData data, net.runelite.asm.pool.Method pool) throws Injexception - { - return findStaticMethod(data, pool.getName(), pool.getClazz().getName(), pool.getType()); - } - static Method findMethodWithArgs(InjectData data, String name, String hintClass, Signature sig) throws Injexception { final ClassGroup deob = data.getDeobfuscated(); @@ -151,13 +179,12 @@ public interface InjectUtil /** * Fail-fast implementation of ClassGroup.findStaticMethod */ - static Method findStaticMethod(ClassGroup group, String name, Signature type) throws Injexception + static Method findMethod(ClassGroup group, String name, Signature type) throws Injexception { Method m = group.findStaticMethod(name, type); if (m == null) - { throw new Injexception(String.format("Method %s couldn't be found", name + type.toString())); - } + return m; } @@ -168,9 +195,8 @@ public interface InjectUtil { Method m = clazz.findMethodDeep(name, type); if (m == null) - { throw new Injexception(String.format("Method %s couldn't be found", name + type.toString())); - } + return m; } @@ -185,10 +211,9 @@ public interface InjectUtil { Field f = clazz.findField(name); if (f != null && f.isStatic()) - { return f; - } } + throw new Injexception("Couldn't find static field " + name); } @@ -212,67 +237,40 @@ public interface InjectUtil ClassFile clazz = findClassOrThrow(deob, classHint); if (type == null) - { field = clazz.findField(name); - } else - { field = clazz.findField(name, type); - } if (field != null) - { return data.toVanilla(field); - } } for (ClassFile clazz : deob) { if (type == null) - { field = clazz.findField(name); - } else - { field = clazz.findField(name, type); - } if (field != null) - { return data.toVanilla(field); - } } throw new Injexception(String.format("Static field %s doesn't exist", (type != null ? type + " " : "") + name)); } - /** - * Finds a static field in deob and converts it to ob - * - * @param data InjectData instance - * @param pool Pool field of the field you want - * - * @return The obfuscated version of the found field - */ - static Field findStaticField(InjectData data, net.runelite.asm.pool.Field pool) throws Injexception - { - return findStaticField(data, pool.getName(), pool.getClazz().getName(), pool.getType()); - } - /** * Fail-fast implementation of ClassGroup.findFieldDeep */ static Field findFieldDeep(ClassFile clazz, String name) throws Injexception { + Field f; + do - { - Field f = clazz.findField(name); - if (f != null) - { + if ((f = clazz.findField(name)) != null) return f; - } - } while ((clazz = clazz.getParent()) != null); + throw new Injexception("Couldn't find field " + name); } @@ -291,19 +289,12 @@ public interface InjectUtil field = clazz.findField(name); if (field != null) - { return field; - } } for (ClassFile clazz : group) - { - field = clazz.findField(name); - if (field != null) - { + if ((field = clazz.findField(name)) != null) return field; - } - } throw new Injexception("Field " + name + " doesn't exist"); } @@ -327,15 +318,11 @@ public interface InjectUtil static Type apiToDeob(InjectData data, Type api) { if (api.isPrimitive()) - { return api; - } final String internalName = api.getInternalName(); if (internalName.startsWith(API_BASE)) - { return Type.getType("L" + api.getInternalName().substring(API_BASE.length()) + ";", api.getDimensions()); - } else if (internalName.startsWith(RL_API_BASE)) { Class rlApiC = new Class(internalName); @@ -370,15 +357,11 @@ public interface InjectUtil static Type deobToVanilla(InjectData data, Type deobT) { if (deobT.isPrimitive()) - { return deobT; - } final ClassFile deobClass = data.getDeobfuscated().findClass(deobT.getInternalName()); if (deobClass == null) - { return deobT; - } return Type.getType("L" + data.toVanilla(deobClass).getName() + ";", deobT.getDimensions()); } @@ -394,17 +377,11 @@ public interface InjectUtil List bb = b.getArguments(); if (aa.size() != bb.size()) - { return false; - } for (int i = 0; i < aa.size(); i++) - { if (!aa.get(i).equals(bb.get(i))) - { return false; - } - } return true; } @@ -435,9 +412,7 @@ public interface InjectUtil static Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index) { if (type.getDimensions() > 0 || !type.isPrimitive()) - { return new ALoad(instructions, index); - } switch (type.toString()) { @@ -464,9 +439,7 @@ public interface InjectUtil static Instruction createReturnForType(Instructions instructions, Type type) { if (!type.isPrimitive()) - { return new Return(instructions, InstructionType.ARETURN); - } switch (type.toString()) { @@ -492,13 +465,9 @@ public interface InjectUtil static Instruction createInvokeFor(Instructions instructions, net.runelite.asm.pool.Method method, boolean isStatic) { if (isStatic) - { return new InvokeStatic(instructions, method); - } else - { return new InvokeVirtual(instructions, method); - } } /** diff --git a/src/main/java/com/openosrs/injector/Injection.java b/src/main/java/com/openosrs/injector/Injection.java index 77070d7..3e3f3c0 100644 --- a/src/main/java/com/openosrs/injector/Injection.java +++ b/src/main/java/com/openosrs/injector/Injection.java @@ -10,7 +10,7 @@ import com.openosrs.injector.injectors.RSApiInjector; import com.openosrs.injector.injectors.raw.ClearColorBuffer; import com.openosrs.injector.injectors.raw.DrawAfterWidgets; import com.openosrs.injector.injectors.raw.Occluder; -import com.openosrs.injector.injectors.raw.RasterizerHook; +import com.openosrs.injector.injectors.raw.RasterizerAlpha; import com.openosrs.injector.injectors.raw.RenderDraw; import com.openosrs.injector.injectors.raw.ScriptVM; import com.openosrs.injector.rsapi.RSApi; @@ -41,7 +41,7 @@ public class Injection extends InjectData implements InjectTaskHandler inject(new InterfaceInjector(this)); - inject(new RasterizerHook(this)); + inject(new RasterizerAlpha(this)); inject(new MixinInjector(this)); diff --git a/src/main/java/com/openosrs/injector/injectors/MixinInjector.java b/src/main/java/com/openosrs/injector/injectors/MixinInjector.java index ed4ecbe..b108d8d 100644 --- a/src/main/java/com/openosrs/injector/injectors/MixinInjector.java +++ b/src/main/java/com/openosrs/injector/injectors/MixinInjector.java @@ -248,7 +248,7 @@ public class MixinInjector extends AbstractInjector if (mixinMethod.isStatic()) { - deobSourceMethod = InjectUtil.findStaticMethod(inject.getDeobfuscated(), copiedName, mixinMethod.getDescriptor().rsApiToRsClient()); + deobSourceMethod = InjectUtil.findMethod(inject.getDeobfuscated(), copiedName, mixinMethod.getDescriptor().rsApiToRsClient()); } else { diff --git a/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java b/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java index f2522c5..28b1370 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java @@ -39,8 +39,8 @@ public class ClearColorBuffer extends AbstractInjector */ final Execution exec = new Execution(inject.getVanilla()); - final net.runelite.asm.pool.Method fillRectPool = InjectUtil.findStaticMethod(inject, "Rasterizer2D_fillRectangle", "Rasterizer2D", null).getPoolMethod(); - final Method drawEntities = InjectUtil.findStaticMethod(inject, "drawEntities"); // XXX: should prob be called drawViewport? + final net.runelite.asm.pool.Method fillRectPool = InjectUtil.findMethod(inject, "Rasterizer2D_fillRectangle", "Rasterizer2D", null).getPoolMethod(); + final Method drawEntities = InjectUtil.findMethod(inject, "drawEntities"); // XXX: should prob be called drawViewport? exec.addMethod(drawEntities); exec.noInvoke = true; diff --git a/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java b/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java index 315f543..75b4794 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java @@ -73,7 +73,7 @@ public class DrawAfterWidgets extends AbstractInjector boolean injected = false; - Method noClip = InjectUtil.findStaticMethod(inject, "Rasterizer2D_resetClip", "Rasterizer2D", null); // !!!!! + Method noClip = InjectUtil.findMethod(inject, "Rasterizer2D_resetClip", "Rasterizer2D", null); // !!!!! if (noClip == null) { diff --git a/src/main/java/com/openosrs/injector/injectors/raw/HidePlayerAttacks.java b/src/main/java/com/openosrs/injector/injectors/raw/HidePlayerAttacks.java index 7a6bd6f..6238c58 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/HidePlayerAttacks.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/HidePlayerAttacks.java @@ -1,11 +1,9 @@ package com.openosrs.injector.injectors.raw; import com.openosrs.injector.InjectUtil; -import com.openosrs.injector.Injection; import com.openosrs.injector.Injexception; import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.injectors.AbstractInjector; -import com.openosrs.injector.injectors.Injector; import java.util.Iterator; import java.util.ListIterator; import net.runelite.asm.Method; @@ -34,7 +32,7 @@ public class HidePlayerAttacks extends AbstractInjector public void inject() throws Injexception { - final Method addPlayerOptions = InjectUtil.findStaticMethod(inject, "addPlayerToMenu"); + final Method addPlayerOptions = InjectUtil.findMethod(inject, "addPlayerToMenu"); final net.runelite.asm.pool.Method shouldHideAttackOptionFor = inject.getVanilla().findClass("client").findMethod("shouldHideAttackOptionFor").getPoolMethod(); injectHideAttack(addPlayerOptions, shouldHideAttackOptionFor); diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java b/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java new file mode 100644 index 0000000..618cbd5 --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2018, Lucas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.openosrs.injector.injectors.raw; + +import com.google.common.collect.Lists; +import com.openosrs.injector.InjectUtil; +import com.openosrs.injector.Injexception; +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import net.runelite.asm.ClassFile; +import net.runelite.asm.Field; +import net.runelite.asm.Method; +import net.runelite.asm.attributes.Code; +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.instruction.types.FieldInstruction; +import net.runelite.asm.attributes.code.instruction.types.GetFieldInstruction; +import net.runelite.asm.attributes.code.instruction.types.LVTInstruction; +import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction; +import net.runelite.asm.attributes.code.instructions.GetStatic; +import net.runelite.asm.attributes.code.instructions.IALoad; +import net.runelite.asm.attributes.code.instructions.IAStore; +import net.runelite.asm.attributes.code.instructions.IAdd; +import net.runelite.asm.attributes.code.instructions.ILoad; +import net.runelite.asm.attributes.code.instructions.IOr; +import net.runelite.asm.attributes.code.instructions.IShR; +import net.runelite.asm.attributes.code.instructions.ISub; +import net.runelite.asm.attributes.code.instructions.IUShR; +import net.runelite.asm.attributes.code.instructions.InvokeStatic; +import net.runelite.asm.attributes.code.instructions.LDC; +import net.runelite.asm.attributes.code.instructions.SiPush; +import net.runelite.asm.execution.Execution; +import net.runelite.asm.execution.InstructionContext; +import net.runelite.asm.execution.MethodContext; +import net.runelite.asm.execution.StackContext; +import net.runelite.asm.execution.VariableContext; +import net.runelite.asm.pool.Class; +import net.runelite.asm.signature.Signature; + +public class RasterizerAlpha extends AbstractInjector +{ + private static final net.runelite.asm.pool.Method DRAWALPHA = new net.runelite.asm.pool.Method( + new Class("client"), + "drawAlpha", + new Signature("([IIII)V") + ); + private static final int ALPHA = 0xff000000; + + public RasterizerAlpha(InjectData inject) + { + super(inject); + } + + /* + * This class exists to allow transparency in overlays + */ + public void inject() throws Injexception + { + final Field r2dPx = InjectUtil.findField(inject, "Rasterizer2D_pixels", "Rasterizer2D"); + final Method draw = InjectUtil.findMethod(inject, "draw", "Client"); + final ClassFile rasterizer2D = r2dPx.getClassFile(); + final Execution ex = new Execution(rasterizer2D.getGroup()); + ex.staticStep = false; + ex.step = false; + ex.addMethod(draw); + + int[] counts = new int[2]; + + ex.addMethodContextVisitor((MethodContext mc) -> + { + Instructions instrs = getInstrs(mc); + if (instrs == null) + return; + + int count = 0; + int orCount = 0; + + for (InstructionContext ic : mc.getInstructionContexts()) + { + Instruction instruction = ic.getInstruction(); + if (!(instruction instanceof IAStore)) + continue; + + // Field field = astore.getMyField(ic); + // doesn't track into methods so doing it here + StackContext array = ic.getPops().get(2); + + if (!isSameField(r2dPx, array)) + continue; + + // This is the colour that's being set + StackContext colour = ic.getPops().get(0); + + // resolve gets the original value pusher + InstructionContext colPusher = colour.getPushed().resolve(colour); + Instruction colPushI = colPusher.getInstruction(); + + // If it's not a >> or a | we're not interested + if (colPushI instanceof LVTInstruction // when called from a method we didn't execute + || colPushI instanceof PushConstantInstruction && + !((PushConstantInstruction) colPushI).getConstant().equals(0) + || colPushI instanceof IALoad) + { + // OR with 0xFF000000, unless 0 + int storeIdx = instrs.getInstructions().indexOf(instruction); + + instrs.addInstruction(storeIdx++, new LDC(instrs, ALPHA)); + instrs.addInstruction(storeIdx, new IOr(instrs, InstructionType.IOR)); + ++orCount; + continue; + } + else if (!( + colPushI instanceof IShR || + colPushI instanceof IUShR || + colPushI instanceof IAdd)) + { + continue; + } + + // So we know we're dealing with alpha here, now we need the alpha value + // earlier on in the method there's been a 256 - XXX, where xxx is alpha + + for (InstructionContext ins : mc.getInstructionContexts()) + { + if (!(ins.getInstruction() instanceof SiPush)) + continue; + + SiPush pci = (SiPush) ins.getInstruction(); + if ((short) pci.getConstant() != (short) 256) + continue; + + InstructionContext isub = ins.getPushes().get(0).getPopped().get(0); + if (!(isub.getInstruction() instanceof ISub)) + continue; + + StackContext alphaPop = isub.getPops().get(0); + InstructionContext alphaPusher = alphaPop.getPushed().resolve(alphaPop); + InstructionContext isubResult = isub.getPushes().get(0).getPopped().get(0); + + if (pushesToSameField(isubResult, alphaPusher)) + { + alphaPusher = resolveFieldThroughInvokes(alphaPop); + + if (alphaPusher == null) + throw new RuntimeException("Alpha var is overwritten and we don't know what pushed it"); // cheeky unchecked + } + + int storeIdx = instrs.getInstructions().indexOf(instruction); + + Instruction alphaPushI = alphaPusher.getInstruction(); + if (alphaPushI instanceof GetStatic) + { + instrs.addInstruction(storeIdx++, new LDC(instrs, 255)); + instrs.addInstruction(storeIdx++, new GetStatic(instrs, ((GetStatic) alphaPushI).getField())); + instrs.addInstruction(storeIdx++, new ISub(instrs, InstructionType.ISUB)); + } + else if (alphaPushI instanceof LVTInstruction) + { + instrs.addInstruction(storeIdx++, new ILoad(instrs, ((LVTInstruction) alphaPushI).getVariableIndex())); + } + + instrs.getInstructions().set(storeIdx, new InvokeStatic(instrs, DRAWALPHA)); + ++count; + break; + } + } + + if (orCount != 0) + { + counts[0] += orCount; + log.info("Added {} OR's into {}", orCount, mc.getMethod()); + } + if (count != 0) + { + counts[1] += count; + log.info("Injected {} DrawAlpha invokes into {}", count, mc.getMethod()); + } + }); + + ex.run(); + log.info("Injected {} DrawAlpha invokes and {} ors", counts[1], counts[0]); + } + + private static boolean pushesToSameField(InstructionContext cA, InstructionContext cB) + { + if (cA.getInstruction() instanceof FieldInstruction && cB instanceof FieldInstruction) + { + Field a = ((FieldInstruction) cA.getInstruction()).getMyField(); + Field b = ((FieldInstruction) cB.getInstruction()).getMyField(); + + return a == b; + } + + return false; + } + + private static Instructions getInstrs(MethodContext mc) + { + Code c = mc.getMethod().getCode(); + if (c == null) + return null; + + return c.getInstructions(); + } + + private static InstructionContext resolveFieldThroughInvokes(StackContext stackContext) + { + InstructionContext pusher = stackContext.getPushed().resolve(stackContext); + if (pusher.getInstruction() instanceof GetFieldInstruction) + return pusher; + + // No field I wanna trace, rn at least + if (!(pusher.getInstruction() instanceof LVTInstruction)) + return null; + + int vidx = ((LVTInstruction) pusher.getInstruction()).getVariableIndex(); + + VariableContext vc = pusher.getVariables().get(vidx); + stackContext = Lists.reverse(vc.getInstructionWhichStored().getPops()).get(vidx); + + return resolveFieldThroughInvokes(stackContext); + } + + private static boolean isSameField(Field f, StackContext array) + { + InstructionContext ic = resolveFieldThroughInvokes(array); + + if (ic == null) + return false; + + return ((GetFieldInstruction) ic.getInstruction()).getMyField() == f; + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RasterizerHook.java b/src/main/java/com/openosrs/injector/injectors/raw/RasterizerHook.java deleted file mode 100644 index 6b24ef1..0000000 --- a/src/main/java/com/openosrs/injector/injectors/raw/RasterizerHook.java +++ /dev/null @@ -1,383 +0,0 @@ -package com.openosrs.injector.injectors.raw; - -import com.openosrs.injector.InjectUtil; -import com.openosrs.injector.Injexception; -import com.openosrs.injector.injection.InjectData; -import com.openosrs.injector.injectors.AbstractInjector; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import net.runelite.asm.Field; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Code; -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.instruction.types.LVTInstruction; -import net.runelite.asm.attributes.code.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.ArrayStore; -import net.runelite.asm.attributes.code.instructions.GetField; -import net.runelite.asm.attributes.code.instructions.GetStatic; -import net.runelite.asm.attributes.code.instructions.IALoad; -import net.runelite.asm.attributes.code.instructions.IAStore; -import net.runelite.asm.attributes.code.instructions.ILoad; -import net.runelite.asm.attributes.code.instructions.IOr; -import net.runelite.asm.attributes.code.instructions.ISub; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.execution.Execution; -import net.runelite.asm.execution.InstructionContext; -import net.runelite.asm.pool.Class; -import net.runelite.asm.pool.Method; -import net.runelite.asm.signature.Signature; - -public class RasterizerHook extends AbstractInjector -{ - // TODO: Should probably make this better - - private static final int val = 0xff000000; - private static final Class R3D = new Class("Rasterizer3D"); - private static final Class FONT = new Class("AbstractFont"); - private static final Class R2D = new Class("Rasterizer2D"); - private static final Class SPRITE = new Class("Sprite"); - private net.runelite.asm.pool.Field R2D_PIXELS; - - private static final Method r3d_vert = new Method(R3D, "Rasterizer3D_vertAlpha", new Signature("([IIIIIIII)V")); - private static final Method r3d_horiz = new Method(R3D, "Rasterizer3D_horizAlpha", new Signature("([IIIIII)V")); - private static final net.runelite.asm.pool.Field r3d_field = new net.runelite.asm.pool.Field(R3D, "Rasterizer3D_alpha", Type.INT); - - private static final Method font_alpha = new Method(FONT, "AbstractFont_placeGlyphAlpha", new Signature("([I[BIIIIIIII)V")); - - private static final Method circle_alpha = new Method(R2D, "Rasterizer2D_drawCircleAlpha", new Signature("(IIIII)V")); - private static final Method line_alpha = new Method(R2D, "Rasterizer2D_drawHorizontalLineAlpha", new Signature("(IIIII)V")); - private static final Method line_alpha2 = new Method(R2D, "Rasterizer2D_drawVerticalLineAlpha", new Signature("(IIIII)V")); - private static final Method fill_rect_alpha = new Method(R2D, "Rasterizer2D_fillRectangleAlpha", new Signature("(IIIIII)V")); - - private static final Method sprite_alpha1 = new Method(SPRITE, "Sprite_drawTransparent", new Signature("([I[IIIIIIIII)V")); - private static final Method sprite_alpha2 = new Method(SPRITE, "Sprite_drawTransScaled", new Signature("([I[IIIIIIIIIIII)V")); - - private static final String font = "AbstractFont_placeGlyph"; - private static final String rast3D = "Rasterizer3D_iDontKnow"; - private static final String rast3D2 = "Rasterizer3D_textureAlpha"; - private static final String sprite = "Sprite_draw"; - private static final String sprite2 = "Sprite_drawScaled"; - private static final String sprite3 = "Sprite_drawTransOverlay"; - private static final String sprite4 = "Sprite_drawTransBg"; - private static final String indexedSprite = "IndexedSprite_something"; - private static final String indexedSprite2 = "IndexedSprite_two"; - - private static final net.runelite.asm.pool.Method drawAlpha = new net.runelite.asm.pool.Method( - new Class("client"), - "drawAlpha", - new Signature("([IIII)V") - ); - - private int drawAlphaCount = 0; - private int orCount = 0; - - public RasterizerHook(InjectData inject) throws Injexception - { - super(inject); - } - - public void inject() throws Injexception - { - R2D_PIXELS = InjectUtil.findStaticField(inject, "Rasterizer2D_pixels", R2D.getName(), new Type("[I")).getPoolField(); - - injectDrawAlpha(); - - runVars(); - - run(); - } - - private void injectDrawAlpha() throws Injexception - { - final Field field = InjectUtil.findStaticField(inject, r3d_field); - runR3DAlpha(r3d_horiz, field, 15); - runR3DAlpha(r3d_vert, field, 12); - runFontAlpha(font_alpha, 1, 9); - - runAlpha(circle_alpha, 2, 4); - runAlpha(line_alpha, 1, 4); - runAlpha(line_alpha2, 1, 4); - runAlpha(fill_rect_alpha, 1, 5); - runAlpha(sprite_alpha1, 1, 9, 0); - runAlpha(sprite_alpha2, 1, 12, 0); - } - - private void runR3DAlpha(Method pool, net.runelite.asm.Field field, int req) throws Injexception - { - net.runelite.asm.Method method = InjectUtil.findStaticMethod(inject, pool); - Instructions ins = method.getCode().getInstructions(); - int varIdx = 0; // This is obviously dumb but I cba making this better - int added = 0; - - List indices = new ArrayList<>(); - for (Instruction i : method.findLVTInstructionsForVariable(varIdx)) - { - indices.add(ins.getInstructions().indexOf(i)); - } - - if (indices.isEmpty()) - { - throw new Injexception("Couldn't find hook location in " + pool.toString()); - } - - for (int i : indices) - { - for (int codeIndex = i + added; codeIndex < ins.getInstructions().size(); codeIndex++) - { - if (ins.getInstructions().get(codeIndex) instanceof IAStore) - { - ins.getInstructions().set(codeIndex, new InvokeStatic(ins, drawAlpha)); - - ins.getInstructions().add(codeIndex, new ISub(ins, InstructionType.ISUB)); - ins.getInstructions().add(codeIndex, new GetStatic(ins, field.getPoolField())); - ins.getInstructions().add(codeIndex, new LDC(ins, 255)); - added++; - drawAlphaCount++; - break; - } - } - } - - if (added != req) - { - throw new Injexception("Not enough drawAlphas injected into " + pool.toString() + " (" + added + "/" + req + ")"); - } - } - - private void runFontAlpha(Method pool, int req, int extraArg) throws Injexception - { - net.runelite.asm.Method meth = InjectUtil.findStaticMethod(inject, pool); - Instructions ins = meth.getCode().getInstructions(); - int varIdx = 0; // This is obviously dumb but I cba making this better - int added = 0; - - List indices = new ArrayList<>(); - for (Instruction i : meth.findLVTInstructionsForVariable(varIdx)) - { - indices.add(ins.getInstructions().indexOf(i)); - } - - if (indices.isEmpty()) - { - throw new Injexception("Couldn't find hook location in " + pool.toString()); - } - - for (int i : indices) - { - for (int codeIndex = i + added; codeIndex < ins.getInstructions().size(); codeIndex++) - { - if (ins.getInstructions().get(codeIndex) instanceof IAStore) - { - ins.getInstructions().set(codeIndex, new InvokeStatic(ins, drawAlpha)); - - ins.getInstructions().add(codeIndex, new ISub(ins, InstructionType.ISUB)); - ins.getInstructions().add(codeIndex, new ILoad(ins, extraArg)); - ins.getInstructions().add(codeIndex, new LDC(ins, 255)); - added++; - drawAlphaCount++; - break; - } - } - } - - if (added != req) - { - throw new Injexception("Not enough drawAlphas injected into " + pool.toString() + " (" + added + "/" + req + ")"); - } - } - - private void runAlpha(Method pool, int req, int extraArg) throws Injexception - { - runAlpha(pool, req, extraArg, -1); - } - - private void runAlpha(Method pool, int req, int extraArg, int varIndex) throws Injexception - { - net.runelite.asm.Method meth = InjectUtil.findStaticMethod(inject, pool); - - Code code = meth.getCode(); - Instructions ins = code.getInstructions(); - int added = 0; - - List indices = new ArrayList<>(); - for (Instruction i : ins.getInstructions()) - { - if (!(i instanceof IALoad) && !(i instanceof GetField) && !(i instanceof ALoad)) - { - continue; - } - - if (i instanceof GetField) - { - if (((GetField) i).getField().equals(R2D_PIXELS)) - { - indices.add(ins.getInstructions().indexOf(i)); - } - } - else if ((i instanceof ALoad) && varIndex >= 0 && ((LVTInstruction) i).getVariableIndex() == varIndex) - { - indices.add(ins.getInstructions().indexOf(i)); - } - else if (varIndex == -1) - { - indices.add(ins.getInstructions().indexOf(i)); - } - } - - if (indices.isEmpty()) - { - throw new Injexception("Couldn't find hook location in " + pool.toString()); - } - - final int oldCount = drawAlphaCount; - - for (int i : indices) - { - for (int codeIndex = i + added; codeIndex < ins.getInstructions().size(); codeIndex++) - { - if (ins.getInstructions().get(codeIndex) instanceof IAStore) - { - ins.getInstructions().set(codeIndex, new InvokeStatic(ins, drawAlpha)); - if (extraArg != -1) - { - ins.getInstructions().add(codeIndex, new ILoad(ins, extraArg)); - added++; - } - drawAlphaCount++; - break; - } - } - } - - if (drawAlphaCount - oldCount > req) - { - throw new Injexception("Too many drawAlpha's were injected into " + pool.toString()); - } - } - - private void runVars() throws Injexception - { - runOnMethodWithVar(rast3D, 0, 36); - runOnMethodWithVar(rast3D2, 0, 36); - // 36 expected - runOnMethodWithVar(font, 0, 5); - // 5 expected - runOnMethodWithVar(sprite, 1, 5); - runOnMethodWithVar(sprite2, 0, 1); - runOnMethodWithVar(sprite3, 0, 1); - runOnMethodWithVar(sprite4, 0, 5); - // 12 expected - runOnMethodWithVar(indexedSprite, 0, 1); - runOnMethodWithVar(indexedSprite2, 0, 5); - // 6 expected - } - - private void run() throws Injexception - { - final int startCount = orCount; // Cause you can't just count shit ty - - Execution ex = new Execution(inject.getVanilla()); - ex.populateInitialMethods(); - - Set done = new HashSet<>(); - ex.addExecutionVisitor((InstructionContext ic) -> - { - Instruction i = ic.getInstruction(); - Instructions ins = i.getInstructions(); - Code code = ins.getCode(); - net.runelite.asm.Method method = code.getMethod(); - - if (!(i instanceof IAStore)) - { - return; - } - - if (!done.add(i)) - { - return; - } - - ArrayStore as = (ArrayStore) i; - Field fieldBeingSet = as.getMyField(ic); - - if (fieldBeingSet == null) - { - return; - } - - if (!fieldBeingSet.getPoolField().equals(R2D_PIXELS)) - { - return; - } - - int index = ins.getInstructions().indexOf(i); - - if (!(ins.getInstructions().get(index - 1) instanceof ILoad) && !ic.getPops().get(0).getValue().isUnknownOrNull()) - { - if ((int) ic.getPops().get(0).getValue().getValue() == 0) - { - log.debug("Didn't add hook in method {}.{}. {} added, {} total, value 0", method.getClassFile().getClassName(), method.getName(), orCount - startCount, orCount); - return; - } - } - - ins.getInstructions().add(index, new IOr(ins, InstructionType.IOR)); // Add instructions backwards - ins.getInstructions().add(index, new LDC(ins, val)); - orCount++; - log.debug("Added hook in method {}.{}. {} added, {} total", method.getClassFile().getClassName(), method.getName(), orCount - startCount, orCount); - }); - - ex.run(); - } - - private void runOnMethodWithVar(String meth, int varIndex, int req) throws Injexception - { - net.runelite.asm.Method method = InjectUtil.findStaticMethod(inject, meth); - - Instructions ins = method.getCode().getInstructions(); - List indices = new ArrayList<>(); - - for (Instruction i : method.findLVTInstructionsForVariable(varIndex)) - { - int index = ins.getInstructions().indexOf(i); - - assert index != -1; - assert ins.getInstructions().get(index + 1) instanceof ILoad; // Index in the array - - indices.add(index); - } - - int added = 0; - for (int i : indices) - { - for (int codeIndex = i + added; codeIndex < ins.getInstructions().size(); codeIndex++) - { - if (ins.getInstructions().get(codeIndex) instanceof IAStore) - { - added += 2; - orCount++; - ins.addInstruction(codeIndex, new IOr(ins, InstructionType.IOR)); // Add instructions backwards - ins.addInstruction(codeIndex, new LDC(ins, val)); - break; - } - } - } - - if (added / 2 != req) - { - throw new Injexception("Didn't inject the right amount of ors into " + meth); - } - - log.info("Added {} instructions in {}. {} total", added / 2, meth, orCount); - } - - public boolean validate() - { - return drawAlphaCount + 1 == 35 && orCount + 1 == 125; - } -}