diff --git a/injector-plugin/injector-plugin.gradle.kts b/injector-plugin/injector-plugin.gradle.kts deleted file mode 100644 index 97d3974575..0000000000 --- a/injector-plugin/injector-plugin.gradle.kts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2019 Owain van Brakel - * 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. - */ - -group = "com.openosrs.rs" -description = "Injector" - -val deobfuscatedJar = "${project.extra["rootPath"]}/runescape-client/build/libs/runescape-client-${ProjectVersions.rlVersion}.jar" -val vanillaJar = "${buildDir}/vanilla-${ProjectVersions.rsversion}.jar" - -val vanilla = configurations.create("vanilla") - -dependencies { - annotationProcessor(Libraries.sisu) - - compileOnly(Libraries.mavenPluginAnnotations) - - implementation(Libraries.guava) - implementation(Libraries.mavenPluginApi) - implementation(Libraries.asmAll) - implementation(Libraries.asmUtil) - implementation(project(":deobfuscator")) - implementation(project(":runelite-mixins")) - implementation(project(":runelite-api")) - implementation(project(":runescape-api")) - - testImplementation(Libraries.junit) - testImplementation(Libraries.mockitoCore) - testImplementation(project(":deobfuscator")) - testImplementation(project(path = ":deobfuscator", configuration = "testArchives")) - - vanilla(Libraries.vanilla) -} - -tasks { - register("copyVanilla") { - copy { - from(configurations.get("vanilla")) - into("$buildDir") - } - } - - register("inject") { - dependsOn("copyVanilla") - - classpath = project.sourceSets.main.get().runtimeClasspath - main = "net.runelite.injector.Injector" - args(listOf(deobfuscatedJar, vanillaJar, project.extra["injectedClassesPath"])) - } - - compileJava { - dependsOn(":runescape-client:build") - - inputs.dir(project(":runescape-client").projectDir.absolutePath) - inputs.dir(project(":runescape-api").projectDir.absolutePath) - inputs.dir(project(":runelite-mixins").projectDir.absolutePath) - } - - jar { - dependsOn("inject") - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/Inject.java b/injector-plugin/src/main/java/net/runelite/injector/Inject.java deleted file mode 100644 index 4a87466311..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/Inject.java +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.util.HashMap; -import java.util.Map; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.Field; -import net.runelite.asm.Interfaces; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Annotations; -import net.runelite.asm.attributes.annotation.Annotation; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.instructions.ALoad; -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.LLoad; -import net.runelite.asm.pool.Class; -import net.runelite.asm.signature.Signature; -import net.runelite.deob.DeobAnnotations; -import net.runelite.deob.deobfuscators.arithmetic.DMath; -import static net.runelite.injector.InjectUtil.getFieldType; -import net.runelite.injector.raw.ClearColorBuffer; -import net.runelite.injector.raw.DrawAfterWidgets; -import net.runelite.injector.raw.Occluder; -import net.runelite.injector.raw.RasterizerAlpha; -import net.runelite.injector.raw.RenderDraw; -import net.runelite.injector.raw.ScriptVM; -import net.runelite.mapping.Import; -import net.runelite.rs.api.RSClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import net.runelite.injector.raw.HidePlayerAttacks; - -public class Inject -{ - public static final java.lang.Class CLIENT_CLASS = RSClient.class; - public static final String API_PACKAGE_BASE = "net.runelite.rs.api.RS"; - public static final String RL_API_PACKAGE_BASE = "net.runelite.api."; - private static final Logger logger = LoggerFactory.getLogger(Inject.class); - private final InjectHookMethod hookMethod = new InjectHookMethod(this); - - private final InjectGetter getters = new InjectGetter(this); - private final InjectSetter setters = new InjectSetter(this); - private final InjectInvoker invokes = new InjectInvoker(this); - private final InjectConstruct construct = new InjectConstruct(this); - - private final MixinInjector mixinInjector = new MixinInjector(this); - - // deobfuscated contains exports etc to apply to vanilla - private final ClassGroup deobfuscated, vanilla; - - public Inject(ClassGroup deobfuscated, ClassGroup vanilla) - { - this.deobfuscated = deobfuscated; - this.vanilla = vanilla; - } - - /** - * Convert a java.lang.Class to a Type - * - * @param c - * @return - */ - public static Type classToType(java.lang.Class c) - { - int dimms = 0; - while (c.isArray()) - { - c = c.getComponentType(); - ++dimms; - } - - if (c.isPrimitive()) - { - String s; - - switch (c.getName()) - { - case "int": - s = "I"; - break; - case "long": - s = "J"; - break; - case "boolean": - s = "Z"; - break; - case "char": - s = "C"; - break; - case "short": - s = "S"; - break; - case "float": - s = "F"; - break; - case "double": - s = "D"; - break; - case "byte": - s = "B"; - break; - case "void": - s = "V"; - break; - default: - throw new RuntimeException("unknown primitive type " + c.getName()); - } - - return Type.getType(s, dimms); - } - - return Type.getType("L" + c.getName().replace('.', '/') + ";", dimms); - } - - public Signature getMethodSignature(Method m) - { - Signature signature = m.getDescriptor(); - - Annotation obfSignature = m.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE); - if (obfSignature != null) - { - //Annotation exists. Signature was updated by us during deobfuscation - signature = DeobAnnotations.getObfuscatedSignature(m); - } - - return signature; - } - - /** - * Build a Signature from a java method - * - * @param method - * @return - */ - public Signature javaMethodToSignature(java.lang.reflect.Method method) - { - Signature.Builder builder = new Signature.Builder() - .setReturnType(classToType(method.getReturnType())); - for (java.lang.Class clazz : method.getParameterTypes()) - { - builder.addArgument(classToType(clazz)); - } - return builder.build(); - } - - public void run() throws InjectionException - { - Map implemented = new HashMap<>(); - - // inject interfaces first, so the validateTypeIsConvertibleTo - // check below works - for (ClassFile cf : deobfuscated.getClasses()) - { - Annotations an = cf.getAnnotations(); - - if (an == null || an.size() == 0) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(an); - if (obfuscatedName == null) - { - obfuscatedName = cf.getName(); - } - - ClassFile other = vanilla.findClass(obfuscatedName); - assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName; - - java.lang.Class implementingClass = injectInterface(cf, other); - // it can not implement an interface but still have exported static fields, which are - // moved to client - - implemented.put(cf, implementingClass); - } - - // Has to be done before mixins - // well, can be done after really - // but why do that when you can do it before - new RasterizerAlpha(this).inject(); - - // requires interfaces to be injected - mixinInjector.inject(); - construct.inject(implemented); - - for (ClassFile cf : deobfuscated.getClasses()) - { - java.lang.Class implementingClass = implemented.get(cf); - Annotations an = cf.getAnnotations(); - - if (an == null || an.size() == 0) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(an); - if (obfuscatedName == null) - { - obfuscatedName = cf.getName(); - } - - ClassFile other = vanilla.findClass(obfuscatedName); - assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName; - - for (Field f : cf.getFields()) - { - an = f.getAnnotations(); - - if (an == null || an.find(DeobAnnotations.EXPORT) == null) - { - continue; // not an exported field - } - - Annotation exportAnnotation = an.find(DeobAnnotations.EXPORT); - String exportedName = exportAnnotation.getElement().getString(); - - obfuscatedName = DeobAnnotations.getObfuscatedName(an); - - Annotation getterAnnotation = an.find(DeobAnnotations.OBFUSCATED_GETTER); - Number getter = null; - if (getterAnnotation != null) - { - getter = (Number) getterAnnotation.getElement().getValue(); - } - // the ob jar is the same as the vanilla so this field must exist in this class. - - Type obType = getFieldType(f); - Field otherf = other.findField(obfuscatedName, obType); - assert otherf != null; - - assert f.isStatic() == otherf.isStatic(); - - ClassFile targetClass = f.isStatic() ? vanilla.findClass("client") : other; // target class for getter - java.lang.Class targetApiClass = f.isStatic() ? CLIENT_CLASS : implementingClass; // target api class for getter - if (targetApiClass == null) - { - assert !f.isStatic(); - - // non static field exported on non exported interface - // logger.debug("Non static exported field {} on non exported interface", exportedName); - continue; - } - - java.lang.reflect.Method apiMethod = findImportMethodOnApi(targetApiClass, exportedName, true); - if (apiMethod != null) - { - Number setter = null; - if (getter != null) - { - setter = DMath.modInverse(getter); // inverse getter to get the setter - } - - setters.injectSetter(targetClass, targetApiClass, otherf, exportedName, setter); - } - - apiMethod = findImportMethodOnApi(targetApiClass, exportedName, false); - if (apiMethod == null) - { - //logger.debug("Unable to find import method on api class {} with imported name {}, not injecting getter", targetApiClass, exportedName); - continue; - } - - // check that otherf is converable to apiMethod's - // return type - Type fieldType = otherf.getType(); - Type returnType = classToType(apiMethod.getReturnType()); - if (!validateTypeIsConvertibleTo(fieldType, returnType)) - { - throw new InjectionException("Type " + fieldType + " is not convertable to " + returnType + " for getter " + apiMethod); - } - - getters.injectGetter(targetClass, apiMethod, otherf, getter); - } - - for (Method m : cf.getMethods()) - { - hookMethod.process(m); - invokes.process(m, other, implementingClass); - } - } - - logger.info("Injected {} getters, {} setters, {} invokers", - getters.getInjectedGetters(), - setters.getInjectedSetters(), invokes.getInjectedInvokers()); - - new DrawAfterWidgets(this).inject(); - new ScriptVM(this).inject(); - new ClearColorBuffer(this).inject(); - new RenderDraw(this).inject(); - // new DrawMenu(this).inject(); - new Occluder(this).inject(); - new HidePlayerAttacks(this).inject(); - } - - private java.lang.Class injectInterface(ClassFile cf, ClassFile other) - { - Annotations an = cf.getAnnotations(); - if (an == null) - { - return null; - } - - Annotation a = an.find(DeobAnnotations.IMPLEMENTS); - if (a == null) - { - return null; - } - - String ifaceName = API_PACKAGE_BASE + a.getElement().getString(); - java.lang.Class apiClass; - - try - { - apiClass = java.lang.Class.forName(ifaceName); - } - catch (ClassNotFoundException ex) - { - logger.trace("Class {} implements nonexistent interface {}, skipping interface injection", - cf.getName(), - ifaceName); - return null; - } - - String ifaceNameInternal = ifaceName.replace('.', '/'); // to internal name - Class clazz = new Class(ifaceNameInternal); - - Interfaces interfaces = other.getInterfaces(); - interfaces.addInterface(clazz); - - return apiClass; - } - - public java.lang.reflect.Method findImportMethodOnApi(java.lang.Class clazz, String name, Boolean setter) - { - for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) - { - if (method.isSynthetic()) - { - /* - * If you override an interface method in another interface - * with a return type that is a child of the overriden methods - * return type, both methods end up in the interface, and both - * are *annotated*. But the base one is synthetic. - */ - continue; - } - - Import i = method.getAnnotation(Import.class); - - if (i == null || !name.equals(i.value()) || (setter != null && (method.getParameterCount() > 0) != setter)) - { - continue; - } - - return method; - } - - return null; - } - - /** - * create a load instruction for a variable of type from a given index - * - * @param instructions - * @param type - * @param index - * @return - */ - public Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index) - { - if (type.getDimensions() > 0 || !type.isPrimitive()) - { - return new ALoad(instructions, index); - } - - switch (type.toString()) - { - case "B": - case "C": - case "I": - case "S": - case "Z": - return new ILoad(instructions, index); - case "D": - return new DLoad(instructions, index); - case "F": - return new FLoad(instructions, index); - case "J": - return new LLoad(instructions, index); - default: - throw new RuntimeException("Unknown type"); - } - } - - ClassFile toDeobClass(ClassFile obClass) - { - for (ClassFile cf : deobfuscated.getClasses()) - { - String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations()); - - if (obClass.getName().equalsIgnoreCase(obfuscatedName)) - { - return cf; - } - } - - return null; - } - - Type deobfuscatedTypeToApiType(Type type) throws InjectionException - { - if (type.isPrimitive()) - { - return type; - } - - ClassFile cf = deobfuscated.findClass(type.getInternalName()); - if (cf == null) - { - return type; // not my type - } - - java.lang.Class rsApiType; - try - { - rsApiType = java.lang.Class.forName(API_PACKAGE_BASE + cf.getName().replace("/", ".")); - } - catch (ClassNotFoundException ex) - { - throw new InjectionException("Deobfuscated type " + type.getInternalName() + " has no API type", ex); - } - - java.lang.Class rlApiType = null; - - for (java.lang.Class inter : rsApiType.getInterfaces()) - { - if (inter.getName().startsWith(RL_API_PACKAGE_BASE)) - { - rlApiType = inter; - } - } - - // if (rlApiType == null) - // { - // throw new InjectionException("RS API type " + rsApiType + " does not extend RL API interface"); - // } - - final java.lang.Class finalType = rlApiType == null ? rsApiType : rlApiType; - - return Type.getType("L" + finalType.getName().replace('.', '/') + ";", type.getDimensions()); - } - - Type apiTypeToDeobfuscatedType(Type type) - { - if (type.isPrimitive()) - { - return type; - } - - String internalName = type.getInternalName().replace('/', '.'); - if (!internalName.startsWith(API_PACKAGE_BASE)) - { - return type; // not an rs api type - } - - return Type.getType("L" + type.getInternalName().substring(API_PACKAGE_BASE.length()) + ";", type.getDimensions()); - } - - ClassFile findVanillaForInterface(java.lang.Class clazz) - { - String className = clazz.getName().replace('.', '/'); - for (ClassFile cf : getVanilla().getClasses()) - { - for (net.runelite.asm.pool.Class cl : cf.getInterfaces().getInterfaces()) - { - if (cl.getName().equals(className)) - { - return cf; - } - } - } - return null; - } - - private boolean validateTypeIsConvertibleTo(Type from, Type to) throws InjectionException - { - if (from.getDimensions() != to.getDimensions()) - { - throw new InjectionException("Array dimension mismatch"); - } - - if (from.isPrimitive()) - { - return true; - } - - ClassFile vanillaClass = vanilla.findClass(from.getInternalName()); - if (vanillaClass == null) - { - return true; - } - - boolean okay = false; - for (Class inter : vanillaClass.getInterfaces().getInterfaces()) - { - java.lang.Class c; - - try - { - c = java.lang.Class.forName(inter.getName().replace('/', '.')); - } - catch (ClassNotFoundException ex) - { - continue; - } - - okay |= check(c, to); - } - - return okay; - } - - private boolean check(java.lang.Class c, Type type) - { - String s = type.getInternalName() - .replace('/', '.'); - - if (c.getName().equals(s)) - { - return true; - } - - for (java.lang.Class c2 : c.getInterfaces()) - { - if (check(c2, type)) - { - return true; - } - } - return false; - } - - public final ClassGroup getDeobfuscated() - { - return deobfuscated; - } - - public final ClassGroup getVanilla() - { - return vanilla; - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectConstruct.java b/injector-plugin/src/main/java/net/runelite/injector/InjectConstruct.java deleted file mode 100644 index 8a87451da6..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectConstruct.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Code; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.instructions.CheckCast; -import net.runelite.asm.attributes.code.instructions.Dup; -import net.runelite.asm.attributes.code.instructions.InvokeSpecial; -import net.runelite.asm.attributes.code.instructions.New; -import net.runelite.asm.attributes.code.instructions.Return; -import net.runelite.asm.signature.Signature; -import net.runelite.deob.DeobAnnotations; -import net.runelite.mapping.Construct; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InjectConstruct -{ - private static final Logger logger = LoggerFactory.getLogger(InjectConstruct.class); - - private final Inject inject; - - InjectConstruct(Inject inject) - { - this.inject = inject; - } - - public void inject(Map implemented) throws InjectionException - { - for (Entry entry : implemented.entrySet()) - { - Class clazz = entry.getValue(); - ClassFile cf = entry.getKey(); - - if (clazz == null) - { - continue; - } - - for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) - { - if (method.isSynthetic()) - { - continue; - } - - Construct construct = method.getAnnotation(Construct.class); - if (construct == null) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations()); - if (obfuscatedName == null) - { - obfuscatedName = cf.getName(); - } - - ClassGroup vanilla = inject.getVanilla(); - ClassFile other = vanilla.findClass(obfuscatedName); - assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName; - - injectConstruct(other, method); - } - } - } - - void injectConstruct(ClassFile targetClass, java.lang.reflect.Method apiMethod) throws InjectionException - { - logger.info("Injecting construct for {}", apiMethod); - - assert targetClass.findMethod(apiMethod.getName()) == null; - - Class typeToConstruct = apiMethod.getReturnType(); - ClassFile vanillaClass = inject.findVanillaForInterface(typeToConstruct); - if (vanillaClass == null) - { - throw new InjectionException("Unable to find vanilla class which implements interface " + typeToConstruct); - } - - Signature sig = inject.javaMethodToSignature(apiMethod); - - Signature constructorSig = new Signature.Builder() - .addArguments(Stream.of(apiMethod.getParameterTypes()) - .map(arg -> - { - ClassFile vanilla = inject.findVanillaForInterface(arg); - if (vanilla != null) - { - return new Type("L" + vanilla.getName() + ";"); - } - return Inject.classToType(arg); - }) - .collect(Collectors.toList())) - .setReturnType(Type.VOID) - .build(); - Method vanillaConstructor = vanillaClass.findMethod("", constructorSig); - if (vanillaConstructor == null) - { - throw new InjectionException("Unable to find constructor for " + vanillaClass.getName() + "." + constructorSig); - } - - Method setterMethod = new Method(targetClass, apiMethod.getName(), sig); - setterMethod.setAccessFlags(ACC_PUBLIC); - targetClass.addMethod(setterMethod); - - Code code = new Code(setterMethod); - setterMethod.setCode(code); - - Instructions instructions = code.getInstructions(); - List ins = instructions.getInstructions(); - - ins.add(new New(instructions, vanillaClass.getPoolClass())); - ins.add(new Dup(instructions)); - int idx = 1; - int parameter = 0; - for (Type type : vanillaConstructor.getDescriptor().getArguments()) - { - Instruction load = inject.createLoadForTypeIndex(instructions, type, idx); - idx += type.getSize(); - ins.add(load); - - Type paramType = sig.getTypeOfArg(parameter); - if (!type.equals(paramType)) - { - CheckCast checkCast = new CheckCast(instructions); - checkCast.setType(type); - ins.add(checkCast); - } - - ++parameter; - } - ins.add(new InvokeSpecial(instructions, vanillaConstructor.getPoolMethod())); - ins.add(new Return(instructions)); - - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectGetter.java b/injector-plugin/src/main/java/net/runelite/injector/InjectGetter.java deleted file mode 100644 index cd94ee80d8..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectGetter.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.util.List; -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.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.GetField; -import net.runelite.asm.attributes.code.instructions.GetStatic; -import net.runelite.asm.attributes.code.instructions.IMul; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.attributes.code.instructions.LMul; -import net.runelite.asm.attributes.code.instructions.Return; -import net.runelite.asm.signature.Signature; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class InjectGetter -{ - private static final Logger logger = LoggerFactory.getLogger(InjectGetter.class); - - private final Inject inject; - - private int injectedGetters; - - InjectGetter(Inject inject) - { - this.inject = inject; - } - - void injectGetter(ClassFile clazz, java.lang.reflect.Method method, Field field, Number getter) - { - // clazz = class file we're injecting the method into. - // method = api method (java reflect) that we're overriding - // field = field we're getting. might not be in this class if static. - // getter = encryption getter - - assert clazz.findMethod(method.getName()) == null; - assert field.isStatic() || field.getClassFile() == clazz; - - Signature sig = new Signature.Builder() - .setReturnType(Inject.classToType(method.getReturnType())) - .build(); - Method getterMethod = new Method(clazz, method.getName(), sig); - getterMethod.setAccessFlags(ACC_PUBLIC); - - // create code - Code code = new Code(getterMethod); - getterMethod.setCode(code); - - Instructions instructions = code.getInstructions(); - List ins = instructions.getInstructions(); - - if (field.isStatic()) - { - code.setMaxStack(1); - - ins.add(new GetStatic(instructions, field.getPoolField())); - } - else - { - code.setMaxStack(2); - - ins.add(new ALoad(instructions, 0)); - ins.add(new GetField(instructions, field.getPoolField())); - } - - if (getter != null) - { - code.setMaxStack(2); - - assert getter instanceof Integer || getter instanceof Long; - - if (getter instanceof Integer) - { - ins.add(new LDC(instructions, (int) getter)); - ins.add(new IMul(instructions)); - } - else - { - ins.add(new LDC(instructions, (long) getter)); - ins.add(new LMul(instructions)); - } - } - - InstructionType returnType; - if (field.getType().isPrimitive() && field.getType().getDimensions() == 0) - { - switch (field.getType().toString()) - { - case "B": - case "C": - case "I": - case "S": - case "Z": - returnType = InstructionType.IRETURN; - break; - case "D": - returnType = InstructionType.DRETURN; - break; - case "F": - returnType = InstructionType.FRETURN; - break; - case "J": - returnType = InstructionType.LRETURN; - break; - default: - throw new RuntimeException("Unknown type"); - } - } - else - { - returnType = InstructionType.ARETURN; - } - - ins.add(new Return(instructions, returnType)); - - clazz.addMethod(getterMethod); - ++injectedGetters; - } - - int getInjectedGetters() - { - return injectedGetters; - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectHook.java b/injector-plugin/src/main/java/net/runelite/injector/InjectHook.java deleted file mode 100644 index 19ae3a3df6..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectHook.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -import com.google.common.collect.Lists; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import net.runelite.asm.Field; -import net.runelite.asm.Method; -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.DupInstruction; -import net.runelite.asm.attributes.code.instruction.types.SetFieldInstruction; -import net.runelite.asm.attributes.code.instructions.ArrayStore; -import net.runelite.asm.attributes.code.instructions.CheckCast; -import net.runelite.asm.attributes.code.instructions.Dup; -import net.runelite.asm.attributes.code.instructions.IMul; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.InvokeVirtual; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.attributes.code.instructions.LMul; -import net.runelite.asm.attributes.code.instructions.PutField; -import net.runelite.asm.attributes.code.instructions.Swap; -import net.runelite.asm.execution.Execution; -import net.runelite.asm.execution.InstructionContext; -import net.runelite.asm.execution.StackContext; -import net.runelite.asm.signature.Signature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class InjectHook -{ - private static final Logger logger = LoggerFactory.getLogger(InjectHook.class); - private static final String HOOK_METHOD_SIGNATURE = "(I)V"; - private static final String CLINIT = ""; - private final Inject inject; - private final Map hooked = new HashMap<>(); - private int injectedHooks; - - InjectHook(Inject inject) - { - this.inject = inject; - } - - void hook(Field field, HookInfo hookInfo) - { - hooked.put(field, hookInfo); - } - - void run() - { - Execution e = new Execution(inject.getVanilla()); - e.populateInitialMethods(); - - Set done = new HashSet<>(); - Set doneIh = new HashSet<>(); - - e.addExecutionVisitor((InstructionContext ic) -> - { - Instruction i = ic.getInstruction(); - Instructions ins = i.getInstructions(); - Code code = ins.getCode(); - Method method = code.getMethod(); - - if (method.getName().equals(CLINIT)) - { - return; - } - - if (!(i instanceof SetFieldInstruction)) - { - return; - } - - if (!done.add(i)) - { - return; - } - - SetFieldInstruction sfi = (SetFieldInstruction) i; - Field fieldBeingSet = sfi.getMyField(); - - if (fieldBeingSet == null) - { - return; - } - - HookInfo hookInfo = hooked.get(fieldBeingSet); - if (hookInfo == null) - { - return; - } - - String hookName = hookInfo.fieldName; - assert hookName != null; - - logger.trace("Found injection location for hook {} at instruction {}", hookName, sfi); - ++injectedHooks; - - StackContext value = ic.getPops().get(0); - - StackContext objectStackContext = null; - if (sfi instanceof PutField) - { - objectStackContext = ic.getPops().get(1); - } - - int idx = ins.getInstructions().indexOf(sfi); - assert idx != -1; - - try - { - if (hookInfo.before) - { - injectCallbackBefore(ins, idx, hookInfo, null, objectStackContext, value); - } - else - { - // idx + 1 to insert after the set - injectCallback(ins, idx + 1, hookInfo, null, objectStackContext); - } - } - catch (InjectionException ex) - { - throw new RuntimeException(ex); - } - }); - - // these look like: - // getfield - // iload_0 - // iconst_0 - // iastore - e.addExecutionVisitor((InstructionContext ic) -> - { - Instruction i = ic.getInstruction(); - Instructions ins = i.getInstructions(); - Code code = ins.getCode(); - Method method = code.getMethod(); - - if (method.getName().equals(CLINIT)) - { - return; - } - - if (!(i instanceof ArrayStore)) - { - return; - } - - if (!doneIh.add(i)) - { - return; - } - - ArrayStore as = (ArrayStore) i; - - Field fieldBeingSet = as.getMyField(ic); - if (fieldBeingSet == null) - { - return; - } - - HookInfo hookInfo = hooked.get(fieldBeingSet); - if (hookInfo == null) - { - return; - } - - String hookName = hookInfo.fieldName; - - StackContext value = ic.getPops().get(0); - StackContext index = ic.getPops().get(1); - - StackContext arrayReference = ic.getPops().get(2); - InstructionContext arrayReferencePushed = arrayReference.getPushed(); - - StackContext objectStackContext = null; - if (arrayReferencePushed.getInstruction().getType() == InstructionType.GETFIELD) - { - objectStackContext = arrayReferencePushed.getPops().get(0); - } - - // inject hook after 'i' - logger.info("Found array injection location for hook {} at instruction {}", hookName, i); - ++injectedHooks; - - int idx = ins.getInstructions().indexOf(i); - assert idx != -1; - - try - { - if (hookInfo.before) - { - injectCallbackBefore(ins, idx, hookInfo, index, objectStackContext, value); - } - else - { - injectCallback(ins, idx + 1, hookInfo, index, objectStackContext); - } - } - catch (InjectionException ex) - { - throw new RuntimeException(ex); - } - }); - - e.run(); - } - - private void injectCallbackBefore(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext object, StackContext value) throws InjectionException - { - Signature signature = hookInfo.method.getDescriptor(); - Type methodArgumentType = signature.getTypeOfArg(0); - - if (!hookInfo.method.isStatic()) - { - if (object == null) - { - throw new InjectionException("null object"); - } - - ins.getInstructions().add(idx++, new Dup(ins)); // dup value - idx = recursivelyPush(ins, idx, object); - ins.getInstructions().add(idx++, new Swap(ins)); - if (hookInfo.getter != null) - { - assert hookInfo.getter instanceof Integer || hookInfo.getter instanceof Long; - - if (hookInfo.getter instanceof Integer) - { - ins.getInstructions().add(idx++, new LDC(ins, (int) hookInfo.getter)); - ins.getInstructions().add(idx++, new IMul(ins)); - } - else - { - ins.getInstructions().add(idx++, new LDC(ins, (long) hookInfo.getter)); - ins.getInstructions().add(idx++, new LMul(ins)); - } - } - if (!value.type.equals(methodArgumentType)) - { - CheckCast checkCast = new CheckCast(ins); - checkCast.setType(methodArgumentType); - ins.getInstructions().add(idx++, checkCast); - } - if (index != null) - { - idx = recursivelyPush(ins, idx, index); - } - - InvokeVirtual invoke = new InvokeVirtual(ins, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(hookInfo.clazz), - hookInfo.method.getName(), - signature - ) - ); - ins.getInstructions().add(idx++, invoke); - } - else - { - ins.getInstructions().add(idx++, new Dup(ins)); // dup value - if (!value.type.equals(methodArgumentType)) - { - CheckCast checkCast = new CheckCast(ins); - checkCast.setType(methodArgumentType); - ins.getInstructions().add(idx++, checkCast); - } - if (index != null) - { - idx = recursivelyPush(ins, idx, index); - } - - InvokeStatic invoke = new InvokeStatic(ins, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(hookInfo.clazz), - hookInfo.method.getName(), - signature - ) - ); - ins.getInstructions().add(idx++, invoke); - } - } - - private int recursivelyPush(Instructions ins, int idx, StackContext sctx) - { - InstructionContext ctx = sctx.getPushed(); - if (ctx.getInstruction() instanceof DupInstruction) - { - DupInstruction dupInstruction = (DupInstruction) ctx.getInstruction(); - sctx = dupInstruction.getOriginal(sctx); - ctx = sctx.getPushed(); - } - - for (StackContext s : Lists.reverse(ctx.getPops())) - { - idx = recursivelyPush(ins, idx, s); - } - - ins.getInstructions().add(idx++, ctx.getInstruction().clone()); - return idx; - } - - private void injectCallback(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext objectPusher) throws InjectionException - { - if (!hookInfo.method.isStatic()) - { - if (objectPusher == null) - { - throw new InjectionException("Null object pusher"); - } - - idx = recursivelyPush(ins, idx, objectPusher); - if (index != null) - { - idx = recursivelyPush(ins, idx, index); - } - else - { - ins.getInstructions().add(idx++, new LDC(ins, -1)); - } - - InvokeVirtual invoke = new InvokeVirtual(ins, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(hookInfo.clazz), - hookInfo.method.getName(), - new Signature(HOOK_METHOD_SIGNATURE) - ) - ); - ins.getInstructions().add(idx++, invoke); - - } - else - { - if (index != null) - { - idx = recursivelyPush(ins, idx, index); - } - else - { - ins.getInstructions().add(idx++, new LDC(ins, -1)); - } - - InvokeStatic invoke = new InvokeStatic(ins, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(hookInfo.clazz), - hookInfo.method.getName(), - new Signature(HOOK_METHOD_SIGNATURE) - ) - ); - ins.getInstructions().add(idx++, invoke); - } - } - - int getInjectedHooks() - { - return injectedHooks; - } - - static class HookInfo - { - String fieldName; - String clazz; - Method method; - boolean before; - Number getter; - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectHookMethod.java b/injector-plugin/src/main/java/net/runelite/injector/InjectHookMethod.java deleted file mode 100644 index 3c304807be..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectHookMethod.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Annotations; -import net.runelite.asm.attributes.Code; -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.instruction.types.InvokeInstruction; -import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction; -import net.runelite.asm.attributes.code.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.InvokeVirtual; -import net.runelite.asm.signature.Signature; -import net.runelite.deob.DeobAnnotations; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InjectHookMethod -{ - public static final String HOOKS = "net/runelite/client/callback/Hooks"; - private static final Logger logger = LoggerFactory.getLogger(InjectHookMethod.class); - private final Inject inject; - - InjectHookMethod(Inject inject) - { - this.inject = inject; - } - - void process(Method method) throws InjectionException - { - Annotations an = method.getAnnotations(); - if (an == null) - { - return; - } - - Annotation a = an.find(DeobAnnotations.HOOK); - if (a == null) - { - return; - } - - String hookName = a.getElement().getString(); - boolean end = a.getElements().size() == 2 && a.getElements().get(1).getValue().equals(true); - - inject(null, method, hookName, end, true); - } - - public void inject(Method hookMethod, Method method, String name, boolean end, boolean useHooks) throws InjectionException - { - Annotations an = method.getAnnotations(); - - // Method is hooked - // Find equivalent method in vanilla, and insert callback at the beginning - ClassFile cf = method.getClassFile(); - String obfuscatedMethodName = DeobAnnotations.getObfuscatedName(an), - obfuscatedClassName = DeobAnnotations.getObfuscatedName(cf.getAnnotations()); - - // might be a constructor - if (obfuscatedMethodName == null) - { - obfuscatedMethodName = method.getName(); - } - - assert obfuscatedClassName != null : "hook on method in class with no obfuscated name"; - assert obfuscatedMethodName != null : "hook on method with no obfuscated name"; - - Signature obfuscatedSignature = inject.getMethodSignature(method); - - ClassGroup vanilla = inject.getVanilla(); - ClassFile vanillaClass = vanilla.findClass(obfuscatedClassName); - Method vanillaMethod = vanillaClass.findMethod(obfuscatedMethodName, obfuscatedSignature); - assert method.isStatic() == vanillaMethod.isStatic(); - - // Insert instructions at beginning of method - injectHookMethod(hookMethod, name, end, method, vanillaMethod, useHooks); - } - - private void injectHookMethod(Method hookMethod, String hookName, boolean end, Method deobMethod, Method vanillaMethod, boolean useHooks) throws InjectionException - { - Code code = vanillaMethod.getCode(); - if (code == null) - { - logger.warn(vanillaMethod + " code is null"); - } - Instructions instructions = code.getInstructions(); - - Signature.Builder builder = new Signature.Builder() - .setReturnType(Type.VOID); // Hooks always return void - - for (Type type : deobMethod.getDescriptor().getArguments()) - { - builder.addArgument(inject.deobfuscatedTypeToApiType(type)); - } - - assert deobMethod.isStatic() == vanillaMethod.isStatic(); - - boolean modifiedSignature = false; - if (!deobMethod.isStatic() && useHooks) - { - // Add variable to signature - builder.addArgument(0, inject.deobfuscatedTypeToApiType(new Type(deobMethod.getClassFile().getName()))); - modifiedSignature = true; - } - - Signature signature = builder.build(); - - List insertIndexes = findHookLocations(hookName, end, vanillaMethod); - insertIndexes.sort((a, b) -> Integer.compare(b, a)); - - for (int insertPos : insertIndexes) - { - if (!deobMethod.isStatic()) - { - instructions.addInstruction(insertPos++, new ALoad(instructions, 0)); - } - - int signatureStart = modifiedSignature ? 1 : 0; - int index = deobMethod.isStatic() ? 0 : 1; // current variable index - - for (int i = signatureStart; i < signature.size(); ++i) - { - Type type = signature.getTypeOfArg(i); - - Instruction load = inject.createLoadForTypeIndex(instructions, type, index); - instructions.addInstruction(insertPos++, load); - - index += type.getSize(); - } - - InvokeInstruction invoke; - - // use old Hooks callback - if (useHooks) - { - // Invoke callback - invoke = new InvokeStatic(instructions, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(HOOKS), - hookName, - signature - ) - ); - } - else - { - // Invoke methodhook - assert hookMethod != null; - - if (vanillaMethod.isStatic()) - { - invoke = new InvokeStatic(instructions, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class("client"), // Static methods are in client - hookMethod.getName(), - signature - ) - ); - } - else - { - // otherwise invoke member function - //instructions.addInstruction(insertPos++, new ALoad(instructions, 0)); - invoke = new InvokeVirtual(instructions, - new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(vanillaMethod.getClassFile().getName()), - hookMethod.getName(), - hookMethod.getDescriptor() - ) - ); - } - } - - instructions.addInstruction(insertPos++, (Instruction) invoke); - } - - logger.info("Injected method hook {} in {} with {} args: {}", - hookName, vanillaMethod, signature.size(), - signature.getArguments()); - } - - private List findHookLocations(String hookName, boolean end, Method vanillaMethod) throws InjectionException - { - Instructions instructions = vanillaMethod.getCode().getInstructions(); - - if (end) - { - // find return - List returns = instructions.getInstructions().stream() - .filter(i -> i instanceof ReturnInstruction) - .collect(Collectors.toList()); - List indexes = new ArrayList<>(); - - for (Instruction ret : returns) - { - int idx = instructions.getInstructions().indexOf(ret); - assert idx != -1; - indexes.add(idx); - } - - return indexes; - } - - if (!vanillaMethod.getName().equals("")) - { - return Arrays.asList(0); - } - - // Find index after invokespecial - for (int i = 0; i < instructions.getInstructions().size(); ++i) - { - Instruction in = instructions.getInstructions().get(i); - - if (in.getType() == InstructionType.INVOKESPECIAL) - { - return Arrays.asList(i + 1); // one after - } - } - - throw new IllegalStateException("constructor with no invokespecial"); - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectInvoker.java b/injector-plugin/src/main/java/net/runelite/injector/InjectInvoker.java deleted file mode 100644 index 3367b26c7a..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectInvoker.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.util.List; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Annotations; -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.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.BiPush; -import net.runelite.asm.attributes.code.instructions.CheckCast; -import net.runelite.asm.attributes.code.instructions.DLoad; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.InvokeVirtual; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.attributes.code.instructions.LLoad; -import net.runelite.asm.attributes.code.instructions.Return; -import net.runelite.asm.attributes.code.instructions.SiPush; -import net.runelite.asm.signature.Signature; -import net.runelite.deob.DeobAnnotations; -import static net.runelite.deob.DeobAnnotations.EXPORT; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class InjectInvoker -{ - private static final Logger logger = LoggerFactory.getLogger(InjectInvoker.class); - - private final Inject inject; - - private int injectedInvokers; - - InjectInvoker(Inject inject) - { - this.inject = inject; - } - - /** - * Inject an invoker for a method - * - * @param m Method in the deobfuscated client to inject an invoker for - * @param other Class in the vanilla client of the same class m is a - * member of - * @param implementingClass Java class for the API interface the class - * will implement - */ - void process(Method m, ClassFile other, java.lang.Class implementingClass) - { - Annotations an = m.getAnnotations(); - - if (an == null || an.find(EXPORT) == null) - { - return; // not an exported method - } - - String exportedName = DeobAnnotations.getExportedName(an); - String obfuscatedName = DeobAnnotations.getObfuscatedName(an); - - if (obfuscatedName == null) - { - obfuscatedName = m.getName(); - } - - String garbage = DeobAnnotations.getDecoder(m); - Method otherm = other.findMethod(obfuscatedName, inject.getMethodSignature(m)); - - assert otherm != null; - assert m.isStatic() == otherm.isStatic(); - - ClassGroup vanilla = inject.getVanilla(); - - ClassFile targetClass = m.isStatic() ? vanilla.findClass("client") : other; - - // Place into implementing class, unless the method is static - java.lang.Class targetClassJava = m.isStatic() ? Inject.CLIENT_CLASS : implementingClass; - - if (targetClassJava == null) - { - assert !m.isStatic(); - - // non static exported method on non exported interface, weird. - // logger.debug("Non static exported method {} on non exported interface", exportedName); - return; - } - - java.lang.reflect.Method apiMethod = inject.findImportMethodOnApi(targetClassJava, exportedName, null); // api method to invoke 'otherm' - if (apiMethod == null) - { - // logger.debug("Unable to find api method on {} with imported name {}, not injecting invoker", targetClassJava, exportedName); - return; - } - - injectInvoker(targetClass, apiMethod, m, otherm, garbage); - ++injectedInvokers; - } - - private void injectInvoker(ClassFile clazz, java.lang.reflect.Method method, Method deobfuscatedMethod, Method invokeMethod, String garbage) - { - // clazz = clazz to add invoker to - // method = api method to override - // deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature - // invokeMethod = method to invoke, obfuscated - - if (clazz.findMethod(method.getName(), deobfuscatedMethod.getDescriptor()) != null) - { - logger.warn("Not injecting method {} because it already exists!", method); - return; // this can happen from exporting a field and method with the same name - } - - assert invokeMethod.isStatic() == deobfuscatedMethod.isStatic(); - assert invokeMethod.isStatic() || invokeMethod.getClassFile() == clazz; - - Type lastGarbageArgumentType = null; - - if (deobfuscatedMethod.getDescriptor().getArguments().size() != invokeMethod.getDescriptor().getArguments().size()) - { - // allow for obfuscated method to have a single bogus signature at the end - assert deobfuscatedMethod.getDescriptor().size() + 1 == invokeMethod.getDescriptor().size(); - - List arguments = invokeMethod.getDescriptor().getArguments(); - lastGarbageArgumentType = arguments.get(arguments.size() - 1); - } - - // Injected method signature is always the same as the API - Signature apiSignature = inject.javaMethodToSignature(method); - Method invokerMethodSignature = new Method(clazz, method.getName(), apiSignature); - invokerMethodSignature.setAccessFlags(ACC_PUBLIC); - - // create code attribute - Code code = new Code(invokerMethodSignature); - invokerMethodSignature.setCode(code); - - Instructions instructions = code.getInstructions(); - List ins = instructions.getInstructions(); - - code.setMaxStack(1 + invokeMethod.getDescriptor().size()); // this + arguments - - // load function arguments onto the stack. - int index = 0; - if (!invokeMethod.isStatic()) - { - ins.add(new ALoad(instructions, index++)); // this - } - else - { - ++index; // this method is always non static - } - for (int i = 0; i < deobfuscatedMethod.getDescriptor().size(); ++i) - { - Type type = deobfuscatedMethod.getDescriptor().getTypeOfArg(i); - - Instruction loadInstruction = inject.createLoadForTypeIndex(instructions, type, index); - ins.add(loadInstruction); - - Signature invokeDesc = invokeMethod.getDescriptor(); - Type obType = invokeDesc.getTypeOfArg(i); - if (!type.equals(obType)) - { - CheckCast checkCast = new CheckCast(instructions); - checkCast.setType(obType); - ins.add(checkCast); - } - - if (loadInstruction instanceof DLoad || loadInstruction instanceof LLoad) - { - index += 2; - } - else - { - index += 1; - } - } - - if (lastGarbageArgumentType != null) - { - // 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.toString()) - { - case "Z": - case "B": - case "C": - ins.add(new BiPush(instructions, Byte.parseByte(garbage))); - break; - case "S": - ins.add(new SiPush(instructions, Short.parseShort(garbage))); - break; - case "I": - ins.add(new LDC(instructions, Integer.parseInt(garbage))); - break; - case "D": - ins.add(new LDC(instructions, Double.parseDouble(garbage))); - break; - case "F": - ins.add(new LDC(instructions, Float.parseFloat(garbage))); - break; - case "J": - ins.add(new LDC(instructions, Long.parseLong(garbage))); - break; - default: - throw new RuntimeException("Unknown type"); - } - } - - if (invokeMethod.isStatic()) - { - ins.add(new InvokeStatic(instructions, invokeMethod.getPoolMethod())); - } - else - { - ins.add(new InvokeVirtual(instructions, invokeMethod.getPoolMethod())); - } - - Type returnValue = invokeMethod.getDescriptor().getReturnValue(); - InstructionType returnType; - - if (returnValue.isPrimitive() && returnValue.getDimensions() == 0) - { - switch (returnValue.toString()) - { - case "Z": - case "I": - returnType = InstructionType.IRETURN; - break; - case "J": - returnType = InstructionType.LRETURN; - break; - case "F": - returnType = InstructionType.FRETURN; - break; - case "D": - returnType = InstructionType.DRETURN; - break; - case "V": - returnType = InstructionType.RETURN; - break; - default: - assert false; - return; - } - } - else - { - returnType = InstructionType.ARETURN; - } - - ins.add(new Return(instructions, returnType)); - - clazz.addMethod(invokerMethodSignature); - } - - int getInjectedInvokers() - { - return injectedInvokers; - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectMojo.java b/injector-plugin/src/main/java/net/runelite/injector/InjectMojo.java deleted file mode 100644 index b7c22194fe..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectMojo.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.deob.clientver.ClientVersion; -import net.runelite.deob.util.JarUtil; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; - -@Mojo( - name = "runelite-injector", - defaultPhase = LifecyclePhase.GENERATE_RESOURCES -) -public class InjectMojo extends AbstractMojo -{ - private final Log log = getLog(); - @Parameter(defaultValue = "${project.build.outputDirectory}") - private File outputDirectory; - @Parameter(defaultValue = "./runescape-client/target/rs-client-${project.version}.jar", readonly = true, required = true) - private String rsClientPath; - @Parameter(defaultValue = "${net.runelite.rs:vanilla:jar}", readonly = true, required = true) - private String vanillaPath; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException - { - ClientVersion ver = new ClientVersion(new File(vanillaPath)); - int version; - try - { - version = ver.getVersion(); - } - catch (IOException ex) - { - throw new MojoExecutionException("Unable to read vanilla client version", ex); - } - - log.info("Vanilla client version " + version); - - ClassGroup rs; - ClassGroup vanilla; - - try - { - rs = JarUtil.loadJar(new File(rsClientPath)); - vanilla = JarUtil.loadJar(new File(vanillaPath)); - } - catch (IOException ex) - { - throw new MojoExecutionException("Unable to load dependency jars", ex); - } - - Injector injector = new Injector(rs, vanilla); - try - { - injector.inject(); - } - catch (InjectionException ex) - { - throw new MojoExecutionException("Error injecting client", ex); - } - - InjectorValidator iv = new InjectorValidator(vanilla); - iv.validate(); - - if (iv.getError() > 0) - { - throw new MojoExecutionException("Error building injected jar"); - } - - if (iv.getMissing() > 0) - { - throw new MojoExecutionException("Unable to inject all methods"); - } - - try - { - writeClasses(vanilla, outputDirectory); - } - catch (IOException ex) - { - throw new MojoExecutionException("Unable to write classes", ex); - } - - log.info("Injector wrote " + vanilla.getClasses().size() + " classes, " + iv.getOkay() + " injected methods"); - } - - private void writeClasses(ClassGroup group, File outputDirectory) throws IOException - { - for (ClassFile cf : group.getClasses()) - { - File classFile = getClassFile(outputDirectory, cf); - byte[] classData = JarUtil.writeClass(group, cf); - - try (FileOutputStream fout = new FileOutputStream(classFile, false)) - { - fout.write(classData); - } - } - } - - private File getClassFile(File base, ClassFile cf) - { - File f = base; - - String[] parts = cf.getName().split("/"); - for (int i = 0; i < parts.length - 1; ++i) - { - String part = parts[i]; - - f = new File(f, part); - } - - f.mkdirs(); - f = new File(f, parts[parts.length - 1] + ".class"); - - return f; - } - -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectSetter.java b/injector-plugin/src/main/java/net/runelite/injector/InjectSetter.java deleted file mode 100644 index 58c225fa87..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectSetter.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -import java.util.List; -import net.runelite.asm.ClassFile; -import net.runelite.asm.Field; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Code; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.CheckCast; -import net.runelite.asm.attributes.code.instructions.IMul; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.attributes.code.instructions.LMul; -import net.runelite.asm.attributes.code.instructions.PutField; -import net.runelite.asm.attributes.code.instructions.PutStatic; -import net.runelite.asm.attributes.code.instructions.VReturn; -import net.runelite.asm.signature.Signature; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class InjectSetter -{ - private static final Logger logger = LoggerFactory.getLogger(InjectSetter.class); - - private final Inject inject; - - private int injectedSetters; - - InjectSetter(Inject inject) - { - this.inject = inject; - } - - /** - * inject a setter into the vanilla classgroup - * - * @param targetClass Class where to inject the setter (field's class, - * or client) - * @param targetApiClass API targetClass implements, which may have the - * setter declared - * @param field Field of vanilla that will be set - * @param exportedName exported name of field - */ - void injectSetter(ClassFile targetClass, Class targetApiClass, Field field, String exportedName, Number setter) - { - java.lang.reflect.Method method = inject.findImportMethodOnApi(targetApiClass, exportedName, true); - if (method == null) - { - logger.warn("Setter injection for field {} but an API method was not found on {}", exportedName, targetApiClass); - return; - } - - if (method.getParameterCount() != 1) - { - logger.warn("Setter {} with not parameter count != 1?", exportedName); - return; - } - - logger.info("Injecting setter for {} on {}", exportedName, targetApiClass); - - assert targetClass.findMethod(method.getName()) == null; - assert field.isStatic() || field.getClassFile() == targetClass; - - Signature sig = new Signature.Builder() - .setReturnType(Type.VOID) - .addArgument(Inject.classToType(method.getParameterTypes()[0])) - .build(); - - Method setterMethod = new Method(targetClass, method.getName(), sig); - setterMethod.setAccessFlags(ACC_PUBLIC); - targetClass.addMethod(setterMethod); - ++injectedSetters; - - Code code = new Code(setterMethod); - setterMethod.setCode(code); - - Instructions instructions = code.getInstructions(); - List ins = instructions.getInstructions(); - - // load this - if (!field.isStatic()) - { - ins.add(new ALoad(instructions, 0)); - } - - // load argument - Type argumentType = sig.getTypeOfArg(0); - ins.add(inject.createLoadForTypeIndex(instructions, argumentType, 1)); - - // cast argument to field type - Type fieldType = field.getType(); - if (!argumentType.equals(fieldType)) - { - CheckCast checkCast = new CheckCast(instructions); - checkCast.setType(fieldType); - ins.add(checkCast); - } - - if (setter != null) - { - assert setter instanceof Integer || setter instanceof Long; - - if (setter instanceof Integer) - { - ins.add(new LDC(instructions, (int) setter)); - ins.add(new IMul(instructions)); - } - else - { - ins.add(new LDC(instructions, (long) setter)); - ins.add(new LMul(instructions)); - } - } - - if (field.isStatic()) - { - ins.add(new PutStatic(instructions, field)); - } - else - { - ins.add(new PutField(instructions, field)); - } - - ins.add(new VReturn(instructions)); - } - - int getInjectedSetters() - { - return injectedSetters; - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectUtil.java b/injector-plugin/src/main/java/net/runelite/injector/InjectUtil.java deleted file mode 100644 index 2292b349ab..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectUtil.java +++ /dev/null @@ -1,281 +0,0 @@ -package net.runelite.injector; - -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.Field; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.annotation.Annotation; -import net.runelite.asm.signature.Signature; -import net.runelite.deob.DeobAnnotations; - -public class InjectUtil -{ - public static ClassFile toObClass(final ClassGroup vanilla, final ClassFile deobCf) throws InjectionException - { - final String obfuscatedName = DeobAnnotations.getObfuscatedName(deobCf.getAnnotations()); - final ClassFile obCf = vanilla.findClass(obfuscatedName); - - if (obCf == null) - { - throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedName)); - } - - return obCf; - } - - public static Field toObField(final ClassGroup vanilla, final Field field) throws InjectionException - { - String obfuscatedClassName = DeobAnnotations.getObfuscatedName(field.getClassFile().getAnnotations()); - String obfuscatedFieldName = DeobAnnotations.getObfuscatedName(field.getAnnotations()); // obfuscated name of field - Type type = getFieldType(field); - - ClassFile obfuscatedClass = vanilla.findClass(obfuscatedClassName); - if (obfuscatedClass == null) - { - throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedClassName)); - } - - Field obfuscatedField = obfuscatedClass.findFieldDeep(obfuscatedFieldName, type); - if (obfuscatedField == null) - { - throw new InjectionException(String.format("Field \"%s\" could not be found.", obfuscatedFieldName)); - } - - return obfuscatedField; - } - - public static ClassFile toDeobClass(final ClassFile obCf, final ClassGroup deob) throws InjectionException - { - final ClassFile wowThatWasQuick = deob.findObfuscatedName(obCf.getName()); - if (wowThatWasQuick == null) - { - throw new InjectionException("It wasn't obfscated enough, or a bit too much. Whatever it was it, wasn't in deob"); - } - return wowThatWasQuick; - } - - public static Type getFieldType(final Field f) - { - Type type = f.getType(); - - Annotation obfSignature = f.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE); - if (obfSignature != null) - { - //Annotation exists. Type was updated by us during deobfuscation - type = DeobAnnotations.getObfuscatedType(f); - } - - return type; - } - - /** - * Find a static method in ClassGroup group. Check the class with name hint first. - * (useful for static methods which are in the class they belong to) - */ - public static Method findStaticMethod(final ClassGroup group, final String name, final String hint) throws InjectionException - { - final ClassFile cf = group.findClass(hint); - - if (cf == null) - { - throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", hint)); - } - - Method m = cf.findStaticMethod(name); - - if (m == null) - { - m = group.findStaticMethod(name); - } - - return m; - } - - /** - * Find a static method in ClassGroup group. Throws exception if not found. - */ - public static Method findStaticMethod(final ClassGroup group, final String name) throws InjectionException - { - Method m = group.findStaticMethod(name); - - if (m == null) - { - throw new InjectionException(String.format("Static method \"%s\" could not be found.", name)); - } - - return m; - } - - /** - * Find a static method in ClassGroup group. Throws exception if not found. - */ - public static Method findStaticMethod(final ClassGroup group, final String name, Signature sig) throws InjectionException - { - Method m = group.findStaticMethod(name, sig); - - if (m == null) - { - throw new InjectionException(String.format("Static method \"%s\" could not be found.", name)); - } - - return m; - } - - public static Method findMethod(Inject inject, String name) throws InjectionException - { - return findMethod(inject, name, null); - } - - public static Method findMethod(Inject inject, String name, String hint) throws InjectionException - { - if (hint != null) - { - ClassFile c = inject.getDeobfuscated().findClass(hint); - - if (c == null) - { - throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)"); - } - - Method deob = c.findMethod(name); - - if (deob != null) - { - String obfuscatedName = DeobAnnotations.getObfuscatedName(deob.getAnnotations()); - Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(deob); - - ClassFile ob = toObClass(inject.getVanilla(), c); - - return ob.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : deob.getDescriptor()); - } - } - - for (ClassFile c : inject.getDeobfuscated().getClasses()) - { - for (Method m : c.getMethods()) - { - if (!m.getName().equals(name)) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations()); - Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m); - - ClassFile c2 = toObClass(inject.getVanilla(), c); - - return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor()); - } - } - - throw new InjectionException("Couldn't find method " + name); - } - - public static Method findStaticMethod(Inject inject, String name) throws InjectionException - { - for (ClassFile c : inject.getDeobfuscated().getClasses()) - { - for (Method m : c.getMethods()) - { - if (!m.isStatic() || !m.getName().equals(name)) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations()); - Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m); - - ClassFile c2 = toObClass(inject.getVanilla(), c); - - return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor()); - } - } - - throw new InjectionException("Couldn't find static method " + name); - } - - - public static Field findObField(Inject inject, String name) throws InjectionException - { - for (ClassFile c : inject.getVanilla().getClasses()) - { - for (Field f : c.getFields()) - { - if (!f.getName().equals(name)) - { - continue; - } - return f; - } - } - - throw new InjectionException(String.format("Field \"%s\" could not be found.", name)); - } - - public static Field findDeobField(Inject inject, String name) throws InjectionException - { - return findDeobField(inject, name, null); - } - - public static Field findDeobField(Inject inject, String name, String hint) throws InjectionException - { - if (hint != null) - { - ClassFile c = inject.getDeobfuscated().findClass(hint); - if (c == null) - { - throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)"); - } - - for (Field f : c.getFields()) - { - if (!f.getName().equals(name)) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations()); - - ClassFile c2 = toObClass(inject.getVanilla(), c); - return c2.findField(obfuscatedName); - } - } - - for (ClassFile c : inject.getDeobfuscated().getClasses()) - { - for (Field f : c.getFields()) - { - if (!f.getName().equals(name)) - { - continue; - } - - String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations()); - - ClassFile c2 = toObClass(inject.getVanilla(), c); - return c2.findField(obfuscatedName); - } - } - - throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name)); - } - - public static Field findDeobFieldButUseless(Inject inject, String name) throws InjectionException - { - for (ClassFile c : inject.getDeobfuscated().getClasses()) - { - for (Field f : c.getFields()) - { - if (!f.getName().equals(name)) - { - continue; - } - - return f; - } - } - - throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name)); - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectionException.java b/injector-plugin/src/main/java/net/runelite/injector/InjectionException.java deleted file mode 100644 index 7881ba739e..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectionException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -public class InjectionException extends Exception -{ - public InjectionException(String message) - { - super(message); - } - - public InjectionException(Throwable cause) - { - super(cause); - } - - public InjectionException(String message, Throwable cause) - { - super(message, cause); - } - -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/Injector.java b/injector-plugin/src/main/java/net/runelite/injector/Injector.java deleted file mode 100644 index 4543ed5b46..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/Injector.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2016-2017, Adam - * 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 net.runelite.injector; - -import java.io.File; -import java.io.IOException; - -import com.google.common.io.Files; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.deob.util.JarUtil; - -public class Injector -{ - private final ClassGroup deobfuscated, vanilla; - - public Injector(ClassGroup deobfuscated, ClassGroup vanilla) - { - this.deobfuscated = deobfuscated; - this.vanilla = vanilla; - } - - public static void main(String[] args) throws IOException, InjectionException - { - if (args.length < 3) - { - System.exit(-1); - } - - ClassGroup deobfuscated = JarUtil.loadJar(new File(args[0])); - ClassGroup vanilla = JarUtil.loadJar(new File(args[1])); - - Injector u = new Injector( - deobfuscated, - vanilla - ); - u.inject(); - - InjectorValidator iv = new InjectorValidator(vanilla); - iv.validate(); - - u.save(new File(args[2])); - } - - public void inject() throws InjectionException - { - Inject instance = new Inject(deobfuscated, vanilla); - instance.run(); - } - - private void save(File out) throws IOException - { - out.mkdirs(); - for (ClassFile cf : vanilla.getClasses()) - { - File f = new File(out, cf.getClassName() + ".class"); - byte[] data = JarUtil.writeClass(vanilla, cf); - Files.write(data, f); - } - } - - -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/InjectorValidator.java b/injector-plugin/src/main/java/net/runelite/injector/InjectorValidator.java deleted file mode 100644 index a49df63a3a..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/InjectorValidator.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import net.runelite.asm.ClassFile; -import net.runelite.asm.ClassGroup; -import net.runelite.asm.signature.Signature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Verifies the injected jar is valid - * - * @author Adam - */ -class InjectorValidator -{ - private static final Logger logger = LoggerFactory.getLogger(InjectorValidator.class); - - private static final String API_PACKAGE_BASE = "net/runelite/rs/api/"; - - private final ClassGroup group; - - private int error, missing, okay; - - InjectorValidator(ClassGroup group) - { - this.group = group; - } - - void validate() - { - for (ClassFile cf : group.getClasses()) - { - validate(cf); - } - - logger.info("{} overridden methods, {} missing", okay, missing); - } - - private void validate(ClassFile cf) - { - // find methods of the interface not implemented in the class - for (net.runelite.asm.pool.Class clazz : cf.getInterfaces().getInterfaces()) - { - if (!clazz.getName().startsWith(API_PACKAGE_BASE)) - { - continue; - } - - Class c; - try - { - c = Class.forName(clazz.getName().replace('/', '.')); - } - catch (ClassNotFoundException ex) - { - logger.warn(null, ex); - continue; - } - - if (cf.isAbstract()) - { - // Abstract classes don't have to implement anything - continue; - } - - for (Method method : c.getMethods()) - { - if (method.isSynthetic() || method.isDefault()) - { - continue; - } - - // could check method signature here too but it is - // annoying to deal with both runelite api and java - // reflection api - if (cf.findMethodDeep(method.getName()) == null) - { - logger.warn("Class {} implements interface {} but not does implement method {}", - cf.getName(), c.getSimpleName(), method); - ++missing; - } - else - { - ++okay; - } - } - } - - Set signatures = new HashSet<>(); - - for (net.runelite.asm.Method method : cf.getMethods()) - { - NameAndSignature nas = new NameAndSignature(method.getName(), method.getDescriptor()); - - if (signatures.contains(nas)) - { - logger.error("Class {} has duplicate method with same name and signature {} {}", - cf.getName(), method.getName(), method.getDescriptor()); - ++error; - } - - signatures.add(nas); - } - } - - int getError() - { - return error; - } - - int getMissing() - { - return missing; - } - - int getOkay() - { - return okay; - } - - static final class NameAndSignature - { - String name; - Signature signature; - - NameAndSignature(String name, Signature signature) - { - this.name = name; - this.signature = signature; - } - - @Override - public int hashCode() - { - int hash = 3; - hash = 67 * hash + Objects.hashCode(this.name); - hash = 67 * hash + Objects.hashCode(this.signature); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final NameAndSignature other = (NameAndSignature) obj; - if (!Objects.equals(this.name, other.name)) - { - return false; - } - if (!Objects.equals(this.signature, other.signature)) - { - return false; - } - return true; - } - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/MixinInjector.java b/injector-plugin/src/main/java/net/runelite/injector/MixinInjector.java deleted file mode 100644 index bf4627c785..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/MixinInjector.java +++ /dev/null @@ -1,998 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * 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 net.runelite.injector; - -import com.google.common.reflect.ClassPath; -import com.google.common.reflect.ClassPath.ClassInfo; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import net.runelite.api.mixins.Mixin; -import net.runelite.asm.ClassFile; -import net.runelite.asm.Field; -import net.runelite.asm.Method; -import net.runelite.asm.Type; -import net.runelite.asm.attributes.Code; -import net.runelite.asm.attributes.annotation.Annotation; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.instruction.types.FieldInstruction; -import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction; -import net.runelite.asm.attributes.code.instruction.types.LVTInstruction; -import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction; -import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction; -import net.runelite.asm.attributes.code.instructions.ALoad; -import net.runelite.asm.attributes.code.instructions.ANewArray; -import net.runelite.asm.attributes.code.instructions.CheckCast; -import net.runelite.asm.attributes.code.instructions.GetField; -import net.runelite.asm.attributes.code.instructions.ILoad; -import net.runelite.asm.attributes.code.instructions.InvokeDynamic; -import net.runelite.asm.attributes.code.instructions.InvokeSpecial; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.Pop; -import net.runelite.asm.attributes.code.instructions.PutField; -import net.runelite.asm.signature.Signature; -import net.runelite.asm.visitors.ClassFileVisitor; -import net.runelite.deob.DeobAnnotations; -import static net.runelite.injector.InjectUtil.findStaticMethod; -import static net.runelite.injector.InjectUtil.toDeobClass; -import static net.runelite.injector.InjectUtil.toObClass; -import static net.runelite.injector.InjectUtil.toObField; -import org.objectweb.asm.ClassReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MixinInjector -{ - private static final Logger logger = LoggerFactory.getLogger(MixinInjector.class); - - private static final Type INJECT = new Type("Lnet/runelite/api/mixins/Inject;"); - private static final Type SHADOW = new Type("Lnet/runelite/api/mixins/Shadow;"); - private static final Type COPY = new Type("Lnet/runelite/api/mixins/Copy;"); - private static final Type REPLACE = new Type("Lnet/runelite/api/mixins/Replace;"); - private static final Type FIELDHOOK = new Type("Lnet/runelite/api/mixins/FieldHook;"); - private static final Type METHODHOOK = new Type("Lnet/runelite/api/mixins/MethodHook;"); - private static final Type JAVAX_INJECT = new Type("Ljavax/inject/Inject;"); - private static final Type NAMED = new Type("Ljavax/inject/Named;"); - - private static final String MIXIN_BASE = "net.runelite.mixins"; - private static final String ASSERTION_FIELD = "$assertionsDisabled"; - - private final Inject inject; - - // field name -> Field of injected fields - private final Map injectedFields = new HashMap<>(); - // Use net.runelite.asm.pool.Field instead of Field because the pool version has hashcode implemented - private final Map shadowFields = new HashMap<>(); - - MixinInjector(Inject inject) - { - this.inject = inject; - } - - public void inject() throws InjectionException - { - ClassPath classPath; - - try - { - classPath = ClassPath.from(this.getClass().getClassLoader()); - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - - // key: mixin class - // value: mixin targets - Map, List> mixinClasses = new HashMap<>(); - - // Find mixins and populate mixinClasses - for (ClassInfo classInfo : classPath.getTopLevelClasses(MIXIN_BASE)) - { - Class mixinClass = classInfo.load(); - List mixinTargets = new ArrayList<>(); - - for (Mixin mixin : mixinClass.getAnnotationsByType(Mixin.class)) - { - Class implementInto = mixin.value(); - - ClassFile targetCf = inject.findVanillaForInterface(implementInto); - - if (targetCf == null) - { - throw new InjectionException("No class implements " + implementInto + " for mixin " + mixinClass); - } - - mixinTargets.add(targetCf); - } - - mixinClasses.put(mixinClass, mixinTargets); - } - - inject(mixinClasses); - } - - public void inject(Map, List> mixinClasses) throws InjectionException - { - injectFields(mixinClasses); - findShadowFields(mixinClasses); - - for (Class mixinClass : mixinClasses.keySet()) - { - try - { - for (ClassFile cf : mixinClasses.get(mixinClass)) - { - // Make a new mixin ClassFile copy every time, - // so they don't share Code references - ClassFile mixinCf = loadClass(mixinClass); - - injectMethods(mixinCf, cf, shadowFields); - } - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - } - - injectFieldHooks(mixinClasses); - injectMethodHooks(mixinClasses); - } - - /** - * Finds fields that are marked @Inject and inject them into the target - */ - private void injectFields(Map, List> mixinClasses) throws InjectionException - { - // Inject fields, and put them in injectedFields if they can be used by other mixins - for (Class mixinClass : mixinClasses.keySet()) - { - ClassFile mixinCf; - - try - { - mixinCf = loadClass(mixinClass); - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - - List targetCfs = mixinClasses.get(mixinClass); - - for (ClassFile cf : targetCfs) - { - for (Field field : mixinCf.getFields()) - { - // Always inject $assertionsEnabled if its missing. - if (ASSERTION_FIELD.equals(field.getName())) - { - if (cf.findField(ASSERTION_FIELD, Type.BOOLEAN) != null) - { - continue; - } - } - else - { - Annotation inject = field.getAnnotations().find(INJECT); - - if (inject == null) - { - continue; - } - } - - Field copy = new Field(cf, field.getName(), field.getType()); - copy.setAccessFlags(field.getAccessFlags()); - copy.setPublic(); - copy.setValue(field.getValue()); - - Annotation jInject = field.getAnnotations().find(JAVAX_INJECT); - if (jInject != null) - { - copy.getAnnotations().addAnnotation(jInject); - logger.info("Added javax inject to {}.{}", cf.getClassName(), copy.getName()); - - Annotation named = field.getAnnotations().find(NAMED); - if (named != null) - { - copy.getAnnotations().addAnnotation(named); - logger.info("Added javax named to {}.{}", cf.getClassName(), copy.getName()); - } - } - - cf.addField(copy); - - if (injectedFields.containsKey(field.getName()) && !field.getName().equals(ASSERTION_FIELD)) - { - java.util.logging.Logger.getAnonymousLogger().severe("Duplicate field : " + field.getName()); - throw new InjectionException("Injected field names must be globally unique"); - } - - injectedFields.put(field.getName(), copy); - } - } - - } - } - - /** - * Find fields which are marked @Shadow, and what they shadow - */ - private void findShadowFields(Map, List> mixinClasses) throws InjectionException - { - // Find shadow fields - // Injected static fields take precedence when looking up shadowed fields - for (Class mixinClass : mixinClasses.keySet()) - { - ClassFile mixinCf; - - try - { - mixinCf = loadClass(mixinClass); - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - - for (Field field : mixinCf.getFields()) - { - Annotation shadow = field.getAnnotations().find(SHADOW); - if (shadow != null) - { - if (!field.isStatic()) - { - throw new InjectionException("Can only shadow static fields"); - } - - String shadowName = shadow.getElement().getString(); // shadow this field - - Field injectedField = injectedFields.get(shadowName); - if (injectedField != null) - { - // Shadow a field injected by a mixin - shadowFields.put(field.getPoolField(), injectedField); - } - else - { - // Shadow a field already in the gamepack - Field shadowField = InjectUtil.findDeobFieldButUseless(inject, shadowName); - - if (shadowField == null) - { - throw new InjectionException("Shadow of nonexistent field " + shadowName); - } - - Field obShadow = toObField(inject.getVanilla(), shadowField); - assert obShadow != null; - shadowFields.put(field.getPoolField(), obShadow); - } - } - } - } - } - - private ClassFile loadClass(Class clazz) throws IOException - { - try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class")) - { - ClassReader reader = new ClassReader(is); - ClassFileVisitor cv = new ClassFileVisitor(); - - reader.accept(cv, 0); - - return cv.getClassFile(); - } - } - - private void injectMethods(ClassFile mixinCf, ClassFile cf, Map shadowFields) - throws InjectionException - { - // Keeps mappings between methods annotated with @Copy -> the copied method within the vanilla pack - Map copiedMethods = new HashMap<>(); - - // Handle the copy mixins first, so all other mixins know of the copies - for (Method method : mixinCf.getMethods()) - { - Annotation copyAnnotation = method.getAnnotations().find(COPY); - - if (copyAnnotation == null) - { - continue; - } - - String deobMethodName = (String) copyAnnotation.getElement().getValue(); - Method deobMethod; - if (method.isStatic()) - { - deobMethod = findStaticMethod(inject.getDeobfuscated(), deobMethodName, method.getDescriptor().rsApiToRsClient()); - } - else - { - ClassFile deobCf = toDeobClass(cf, inject.getDeobfuscated()); - deobMethod = deobCf.findMethod(deobMethodName, method.getDescriptor().rsApiToRsClient()); - } - - - if (deobMethod == null) - { - throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf); - } - - if (method.isStatic() != deobMethod.isStatic()) - { - throw new InjectionException("Mixin method " + method + " should be " + (deobMethod.isStatic() ? "static" : "non-static")); - } - - // Find the vanilla class where the method to copy is in - String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations()); - ClassFile obCf = inject.getVanilla().findClass(obClassName); - assert obCf != null : "unable to find vanilla class from obfuscated name " + obClassName; - - String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations()); - Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod); - - if (obMethodName == null) - { - obMethodName = deobMethod.getName(); - } - if (obMethodSignature == null) - { - obMethodSignature = deobMethod.getDescriptor(); - } - - Method obMethod = obCf.findMethod(obMethodName, obMethodSignature); - if (obMethod == null) - { - throw new InjectionException("Failed to find the ob method " + obMethodName + " for mixin " + mixinCf); - } - - if (method.getDescriptor().size() > obMethod.getDescriptor().size()) - { - throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method"); - } - - Method copy = new Method(cf, "copy$" + deobMethodName, obMethodSignature); - moveCode(copy, obMethod.getCode()); - copy.setAccessFlags(obMethod.getAccessFlags()); - copy.setPublic(); - copy.getExceptions().getExceptions().addAll(obMethod.getExceptions().getExceptions()); - copy.getAnnotations().getAnnotations().addAll(obMethod.getAnnotations().getAnnotations()); - cf.addMethod(copy); - - /* - If the desc for the mixin method and the desc for the ob method - are the same in length, assume that the mixin method is taking - care of the garbage parameter itself. - */ - boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size() - && deobMethod.getDescriptor().size() < obMethodSignature.size(); - copiedMethods.put(method.getPoolMethod(), new CopiedMethod(copy, hasGarbageValue)); - - logger.debug("Injected copy of {} to {}", obMethod, copy); - } - - // Handle the rest of the mixin types - for (Method method : mixinCf.getMethods()) - { - boolean isClinit = "".equals(method.getName()); - boolean isInit = "".equals(method.getName()); - boolean hasInject = method.getAnnotations().find(INJECT) != null; - - // You can't annotate clinit, so its always injected - if ((hasInject && isInit) || isClinit) - { - if (!"()V".equals(method.getDescriptor().toString())) - { - throw new InjectionException("Injected constructors cannot have arguments"); - } - - Method[] originalMethods = cf.getMethods().stream() - .filter(n -> n.getName().equals(method.getName())) - .toArray(Method[]::new); - // If there isn't a already just inject ours, otherwise rename it - // This is always true for - String name = method.getName(); - if (originalMethods.length > 0) - { - name = "rl$$" + (isInit ? "init" : "clinit"); - } - String numberlessName = name; - for (int i = 1; cf.findMethod(name, method.getDescriptor()) != null; i++) - { - name = numberlessName + i; - } - - Method copy = new Method(cf, name, method.getDescriptor()); - moveCode(copy, method.getCode()); - copy.setAccessFlags(method.getAccessFlags()); - copy.setPrivate(); - assert method.getExceptions().getExceptions().isEmpty(); - - // Remove the call to the superclass's ctor - if (isInit) - { - Instructions instructions = copy.getCode().getInstructions(); - ListIterator listIter = instructions.getInstructions().listIterator(); - for (; listIter.hasNext(); ) - { - Instruction instr = listIter.next(); - if (instr instanceof InvokeSpecial) - { - InvokeSpecial invoke = (InvokeSpecial) instr; - assert invoke.getMethod().getName().equals(""); - listIter.remove(); - int pops = invoke.getMethod().getType().getArguments().size() + 1; - for (int i = 0; i < pops; i++) - { - listIter.add(new Pop(instructions)); - } - break; - } - } - } - - setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods); - cf.addMethod(copy); - - // Call our method at the return point of the matching method(s) - for (Method om : originalMethods) - { - Instructions instructions = om.getCode().getInstructions(); - ListIterator listIter = instructions.getInstructions().listIterator(); - for (; listIter.hasNext(); ) - { - Instruction instr = listIter.next(); - if (instr instanceof ReturnInstruction) - { - listIter.previous(); - if (isInit) - { - listIter.add(new ALoad(instructions, 0)); - listIter.add(new InvokeSpecial(instructions, copy.getPoolMethod())); - } - else if (isClinit) - { - listIter.add(new InvokeStatic(instructions, copy.getPoolMethod())); - } - listIter.next(); - } - } - } - - logger.debug("Injected mixin method {} to {}", copy, cf); - } - else if (hasInject) - { - // Make sure the method doesn't invoke copied methods - for (Instruction i : method.getCode().getInstructions().getInstructions()) - { - if (i instanceof InvokeInstruction) - { - InvokeInstruction ii = (InvokeInstruction) i; - - if (copiedMethods.containsKey(ii.getMethod())) - { - throw new InjectionException("Injected methods cannot invoke copied methods"); - } - } - } - - Method copy = new Method(cf, method.getName(), method.getDescriptor()); - moveCode(copy, method.getCode()); - copy.setAccessFlags(method.getAccessFlags()); - copy.setPublic(); - assert method.getExceptions().getExceptions().isEmpty(); - - setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods); - - cf.addMethod(copy); - - logger.debug("Injected mixin method {} to {}", copy, cf); - } - else if (method.getAnnotations().find(REPLACE) != null) - { - Annotation replaceAnnotation = method.getAnnotations().find(REPLACE); - String deobMethodName = (String) replaceAnnotation.getElement().getValue(); - - ClassFile deobCf = inject.toDeobClass(cf); - Method deobMethod = findDeobMethod(deobCf, deobMethodName, method.getDescriptor()); - - if (deobMethod == null) - { - throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf); - } - - if (method.isStatic() != deobMethod.isStatic()) - { - throw new InjectionException("Mixin method " + method + " should be " - + (deobMethod.isStatic() ? "static" : "non-static")); - } - - String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations()); - Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod); - - // Deob signature is the same as ob signature - if (obMethodName == null) - { - obMethodName = deobMethod.getName(); - } - if (obMethodSignature == null) - { - obMethodSignature = deobMethod.getDescriptor(); - } - - // Find the vanilla class where the method to copy is in - String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations()); - ClassFile obCf = inject.getVanilla().findClass(obClassName); - - Method obMethod = obCf.findMethod(obMethodName, obMethodSignature); - assert obMethod != null : "obfuscated method " + obMethodName + obMethodSignature + " does not exist"; - - if (method.getDescriptor().size() > obMethod.getDescriptor().size()) - { - throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method"); - } - - Type returnType = method.getDescriptor().getReturnValue(); - Type deobReturnType = inject.apiTypeToDeobfuscatedType(returnType); - if (!returnType.equals(deobReturnType)) - { - ClassFile deobReturnTypeClassFile = inject.getDeobfuscated() - .findClass(deobReturnType.getInternalName()); - if (deobReturnTypeClassFile != null) - { - ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobReturnTypeClassFile); - - Instructions instructions = method.getCode().getInstructions(); - ListIterator listIter = instructions.getInstructions().listIterator(); - for (; listIter.hasNext(); ) - { - Instruction instr = listIter.next(); - if (instr instanceof ReturnInstruction) - { - listIter.previous(); - CheckCast checkCast = new CheckCast(instructions); - checkCast.setType(new Type(obReturnTypeClass.getName())); - listIter.add(checkCast); - listIter.next(); - } - } - } - } - - moveCode(obMethod, method.getCode()); - - boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size() - && deobMethod.getDescriptor().size() < obMethodSignature.size(); - - if (hasGarbageValue) - { - int garbageIndex = obMethod.isStatic() - ? obMethod.getDescriptor().size() - 1 - : obMethod.getDescriptor().size(); - - /* - If the mixin method doesn't have the garbage parameter, - the compiler will have produced code that uses the garbage - parameter's local variable index for other things, - so we'll have to add 1 to all loads/stores to indices - that are >= garbageIndex. - */ - shiftLocalIndices(obMethod.getCode().getInstructions(), garbageIndex); - } - - setOwnersToTargetClass(mixinCf, cf, obMethod, shadowFields, copiedMethods); - - logger.debug("Replaced method {} with mixin method {}", obMethod, method); - } - } - } - - private void moveCode(Method method, Code code) - { - Code newCode = new Code(method); - newCode.setMaxStack(code.getMaxStack()); - newCode.getInstructions().getInstructions().addAll(code.getInstructions().getInstructions()); - // Update instructions for each instruction - for (Instruction i : newCode.getInstructions().getInstructions()) - { - i.setInstructions(newCode.getInstructions()); - } - newCode.getExceptions().getExceptions().addAll(code.getExceptions().getExceptions()); - for (net.runelite.asm.attributes.code.Exception e : newCode.getExceptions().getExceptions()) - { - e.setExceptions(newCode.getExceptions()); - } - method.setCode(newCode); - } - - private void setOwnersToTargetClass(ClassFile mixinCf, ClassFile cf, Method method, - Map shadowFields, - Map copiedMethods) - throws InjectionException - { - ListIterator iterator = method.getCode().getInstructions().getInstructions().listIterator(); - - while (iterator.hasNext()) - { - Instruction i = iterator.next(); - - if (i instanceof ANewArray) - { - Type type = ((ANewArray) i).getType_(); - ClassFile deobCf = inject.getDeobfuscated().findClass(type.toString().replace("Lnet/runelite/rs/api/RS", "").replace(";", "")); - - if (deobCf != null) - { - ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobCf); - Type newType = new Type("L" + obReturnTypeClass.getName() + ";"); - - ((ANewArray) i).setType(newType); - logger.info("Replaced {} type {} with type {}", i, type, newType); - } - } - - if (i instanceof InvokeInstruction) - { - InvokeInstruction ii = (InvokeInstruction) i; - - CopiedMethod copiedMethod = copiedMethods.get(ii.getMethod()); - if (copiedMethod != null) - { - ii.setMethod(copiedMethod.obMethod.getPoolMethod()); - - // Pass through garbage value if the method has one - if (copiedMethod.hasGarbageValue) - { - int garbageIndex = copiedMethod.obMethod.isStatic() - ? copiedMethod.obMethod.getDescriptor().size() - 1 - : copiedMethod.obMethod.getDescriptor().size(); - - iterator.previous(); - iterator.add(new ILoad(method.getCode().getInstructions(), garbageIndex)); - iterator.next(); - } - } - else if (ii.getMethod().getClazz().getName().equals(mixinCf.getName())) - { - ii.setMethod(new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(cf.getName()), - ii.getMethod().getName(), - ii.getMethod().getType() - )); - } - } - else if (i instanceof FieldInstruction) - { - FieldInstruction fi = (FieldInstruction) i; - - Field shadowed = shadowFields.get(fi.getField()); - if (shadowed != null) - { - fi.setField(shadowed.getPoolField()); - } - else if (fi.getField().getClazz().getName().equals(mixinCf.getName())) - { - fi.setField(new net.runelite.asm.pool.Field( - new net.runelite.asm.pool.Class(cf.getName()), - fi.getField().getName(), - fi.getField().getType() - )); - } - } - else if (i instanceof PushConstantInstruction) - { - PushConstantInstruction pi = (PushConstantInstruction) i; - if (mixinCf.getPoolClass().equals(pi.getConstant())) - { - pi.setConstant(cf.getPoolClass()); - } - } - - verify(mixinCf, i); - } - } - - private void shiftLocalIndices(Instructions instructions, int startIdx) - { - for (Instruction i : instructions.getInstructions()) - { - if (i instanceof LVTInstruction) - { - LVTInstruction lvti = (LVTInstruction) i; - - if (lvti.getVariableIndex() >= startIdx) - { - lvti.setVariableIndex(lvti.getVariableIndex() + 1); - } - } - } - } - - private Method findDeobMethod(ClassFile deobCf, String deobMethodName, Signature descriptor) - throws InjectionException - { - List matchingMethods = new ArrayList<>(); - - for (Method m : deobCf.getMethods()) - { - if (!deobMethodName.equals(m.getName())) - { - continue; - } - - Type returnType = inject.apiTypeToDeobfuscatedType(descriptor.getReturnValue()); - Type returnType2 = m.getDescriptor().getReturnValue(); - - if (!returnType.equals(returnType2)) - { - continue; - } - - List args = descriptor.getArguments(); - List args2 = m.getDescriptor().getArguments(); - - if (args.size() > args2.size()) - { - continue; - } - - boolean matchingArgs = true; - - for (int i = 0; i < args.size(); i++) - { - Type type = inject.apiTypeToDeobfuscatedType(args.get(i)); - Type type2 = args2.get(i); - - if (!type.equals(type2)) - { - matchingArgs = false; - break; - } - } - - if (!matchingArgs) - { - continue; - } - - matchingMethods.add(m); - } - - if (matchingMethods.size() > 1) - { - // this happens when it has found several deob methods for some mixin method, - // to get rid of the error, refine your search by making your mixin method have more parameters - throw new InjectionException("There are several matching methods when there should only be one"); - } - else if (matchingMethods.size() == 1) - { - return matchingMethods.get(0); - } - - Method method = deobCf.findMethod(deobMethodName); - - if (method == null) - { - // Look for static methods if an instance method couldn't be found - for (ClassFile deobCf2 : inject.getDeobfuscated().getClasses()) - { - if (deobCf2 != deobCf) - { - method = deobCf2.findMethod(deobMethodName); - - if (method != null) - { - break; - } - } - } - } - - return method; - } - - private void verify(ClassFile mixinCf, Instruction i) throws InjectionException - { - if (i instanceof FieldInstruction) - { - FieldInstruction fi = (FieldInstruction) i; - - if (fi.getField().getClazz().getName().equals(mixinCf.getName())) - { - if (i instanceof PutField || i instanceof GetField) - { - throw new InjectionException("Access to non static member field of mixin"); - } - - Field field = fi.getMyField(); - if (field != null && !field.isPublic()) - { - throw new InjectionException("Static access to non public field " + field); - } - } - } - else if (i instanceof InvokeStatic) - { - InvokeStatic is = (InvokeStatic) i; - - if (is.getMethod().getClazz() != mixinCf.getPoolClass() - && is.getMethod().getClazz().getName().startsWith(MIXIN_BASE.replace(".", "/"))) - { - throw new InjectionException("Invoking static methods of other mixins is not supported"); - } - } - else if (i instanceof InvokeDynamic) - { - // RS classes don't verify under java 7+ due to the - // super() invokespecial being inside of a try{} - throw new InjectionException("Injected bytecode must be Java 6 compatible"); - } - } - - private void injectFieldHooks(Map, List> mixinClasses) throws InjectionException - { - InjectHook injectHook = new InjectHook(inject); - - for (Class mixinClass : mixinClasses.keySet()) - { - ClassFile mixinCf; - - try - { - mixinCf = loadClass(mixinClass); - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - - List targetCfs = mixinClasses.get(mixinClass); - - for (ClassFile cf : targetCfs) - { - for (Method method : mixinCf.getMethods()) - { - Annotation fieldHook = method.getAnnotations().find(FIELDHOOK); - if (fieldHook != null) - { - String hookName = fieldHook.getElement().getString(); - boolean before = fieldHook.getElements().size() == 2 && fieldHook.getElements().get(1).getValue().equals(true); - ClassFile deobCf = inject.toDeobClass(cf); - Field targetField = deobCf.findField(hookName); - if (targetField == null) - { - // first try non static fields, then static - targetField = InjectUtil.findDeobFieldButUseless(inject, hookName); - } - - if (targetField == null) - { - throw new InjectionException("Field hook for nonexistent field " + hookName + " on " + method); - } - - Annotation an = targetField.getAnnotations().find(DeobAnnotations.OBFUSCATED_GETTER); - Number getter = null; - if (an != null) - { - getter = (Number) an.getElement().getValue(); - } - - Field obField = toObField(inject.getVanilla(), targetField); - - if (method.isStatic() != targetField.isStatic()) - { - throw new InjectionException("Field hook method static flag must match target field"); - } - - // cf is the target class to invoke - InjectHook.HookInfo hookInfo = new InjectHook.HookInfo(); - hookInfo.clazz = cf.getName(); - hookInfo.fieldName = hookName; - hookInfo.method = method; - hookInfo.before = before; - hookInfo.getter = getter; - injectHook.hook(obField, hookInfo); - } - } - } - } - - injectHook.run(); - - logger.info("Injected {} field hooks", injectHook.getInjectedHooks()); - } - - private void injectMethodHooks(Map, List> mixinClasses) throws InjectionException - { - InjectHookMethod injectHookMethod = new InjectHookMethod(inject); - - for (Class mixinClass : mixinClasses.keySet()) - { - ClassFile mixinCf; - - try - { - mixinCf = loadClass(mixinClass); - } - catch (IOException ex) - { - throw new InjectionException(ex); - } - - List targetCfs = mixinClasses.get(mixinClass); - - for (ClassFile cf : targetCfs) - { - for (Method method : mixinCf.getMethods()) - { - Annotation methodHook = method.getAnnotations().find(METHODHOOK); - - if (methodHook == null) - { - continue; - } - - String hookName = methodHook.getElement().getString(); - boolean end = methodHook.getElements().size() == 2 && methodHook.getElements().get(1).getValue().equals(true); - ClassFile deobCf = inject.toDeobClass(cf); - Method targetMethod = findDeobMethod(deobCf, hookName, method.getDescriptor()); - - if (targetMethod == null) - { - throw new InjectionException("Method hook for nonexistent method " + hookName + " on " + method); - } - - if (method.isStatic() != targetMethod.isStatic()) - { - throw new InjectionException("Method hook static flag must match target - " + hookName); - } - - injectHookMethod.inject(method, targetMethod, hookName, end, false); - } - } - } - } - - private static class CopiedMethod - { - private Method obMethod; - private boolean hasGarbageValue; - - private CopiedMethod(Method obMethod, boolean hasGarbageValue) - { - this.obMethod = obMethod; - this.hasGarbageValue = hasGarbageValue; - } - } -} diff --git a/injector-plugin/src/main/java/net/runelite/injector/raw/ClearColorBuffer.java b/injector-plugin/src/main/java/net/runelite/injector/raw/ClearColorBuffer.java deleted file mode 100644 index f4de5d056e..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/raw/ClearColorBuffer.java +++ /dev/null @@ -1,124 +0,0 @@ -package net.runelite.injector.raw; - -import java.util.ListIterator; -import net.runelite.asm.ClassFile; -import net.runelite.asm.Method; -import net.runelite.asm.attributes.Code; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.instructions.ILoad; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.pool.Class; -import net.runelite.asm.signature.Signature; -import net.runelite.injector.Inject; -import net.runelite.injector.InjectUtil; -import net.runelite.injector.InjectionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ClearColorBuffer -{ - private static final Logger log = LoggerFactory.getLogger(ClearColorBuffer.class); - private static final net.runelite.asm.pool.Method clearBuffer = new net.runelite.asm.pool.Method( - new Class("net.runelite.client.callback.Hooks"), - "clearColorBuffer", - new Signature("(IIIII)V") - ); - private final Inject inject; - - public ClearColorBuffer(Inject inject) - { - this.inject = inject; - } - - public void inject() throws InjectionException - { - injectColorBufferHooks(); - } - - private void injectColorBufferHooks() throws InjectionException - { - net.runelite.asm.pool.Method fillRectangle = InjectUtil.findStaticMethod(inject, "Rasterizer2D_fillRectangle").getPoolMethod(); - - int count = 0; - int replaced = 0; - - for (ClassFile cf : inject.getVanilla().getClasses()) - { - for (Method m : cf.getMethods()) - { - if (!m.isStatic()) - { - continue; - } - - Code c = m.getCode(); - if (c == null) - { - continue; - } - - Instructions ins = c.getInstructions(); - ListIterator it = ins.getInstructions().listIterator(); - - for (; it.hasNext(); ) - { - Instruction i = it.next(); - if (!(i instanceof InvokeStatic)) - { - continue; - } - - if (!((InvokeStatic) i).getMethod().equals(fillRectangle)) - { - continue; - } - - int indexToReturnTo = it.nextIndex(); - count++; - it.previous(); - Instruction current = it.previous(); - if (current instanceof LDC && ((LDC) current).getConstantAsInt() == 0) - { - int varIdx = 0; - for (; ; ) - { - current = it.previous(); - if (current instanceof ILoad && ((ILoad) current).getVariableIndex() == 3 - varIdx) - { - varIdx++; - log.debug(varIdx + " we can count yay"); - continue; - } - - break; - } - - if (varIdx == 4) - { - for (; !(current instanceof InvokeStatic); ) - { - current = it.next(); - } - assert it.nextIndex() == indexToReturnTo; - - it.set(new InvokeStatic(ins, clearBuffer)); - replaced++; - log.debug("Found drawRectangle at {}. Found: {}, replaced {}", m.getName(), count, replaced); - } - else - { - log.debug("Welp, guess this wasn't it chief " + m); - } - } - - while (it.nextIndex() != indexToReturnTo) - { - it.next(); - } - } - } - } - } -} \ No newline at end of file diff --git a/injector-plugin/src/main/java/net/runelite/injector/raw/DrawAfterWidgets.java b/injector-plugin/src/main/java/net/runelite/injector/raw/DrawAfterWidgets.java deleted file mode 100644 index 5b6f6fbd50..0000000000 --- a/injector-plugin/src/main/java/net/runelite/injector/raw/DrawAfterWidgets.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2018, Lotto - * 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 net.runelite.injector.raw; - -import java.util.HashSet; -import java.util.ListIterator; -import java.util.Set; -import net.runelite.asm.ClassFile; -import net.runelite.asm.Method; -import net.runelite.asm.attributes.code.Instruction; -import net.runelite.asm.attributes.code.Instructions; -import net.runelite.asm.attributes.code.Label; -import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction; -import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction; -import net.runelite.asm.attributes.code.instructions.GetStatic; -import net.runelite.asm.attributes.code.instructions.IMul; -import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.signature.Signature; -import net.runelite.injector.Inject; -import static net.runelite.injector.InjectHookMethod.HOOKS; -import static net.runelite.injector.InjectUtil.findStaticMethod; -import net.runelite.injector.InjectionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DrawAfterWidgets -{ - private static final Logger logger = LoggerFactory.getLogger(DrawAfterWidgets.class); - - private final Inject inject; - - public DrawAfterWidgets(Inject inject) - { - this.inject = inject; - } - - public void inject() throws InjectionException - { - injectDrawAfterWidgets(); - } - - private void injectDrawAfterWidgets() throws InjectionException - { - /* - This call has to be injected using raw injection because the - drawWidgets method gets inlined in some revisions. If it wouldn't be, - mixins would be used to add the call to the end of drawWidgets. - - --> This hook depends on the positions of "if (535573958 * kl != -1)" and "jz.db();". - - - Revision 180 - client.gs(): - ______________________________________________________ - - @Export("drawLoggedIn") - final void drawLoggedIn() { - if(rootInterface != -1) { - ClientPreferences.method1809(rootInterface); - } - - int var1; - for(var1 = 0; var1 < rootWidgetCount; ++var1) { - if(__client_od[var1]) { - __client_ot[var1] = true; - } - - __client_oq[var1] = __client_od[var1]; - __client_od[var1] = false; - } - - __client_oo = cycle; - __client_lq = -1; - __client_ln = -1; - UserComparator6.__fg_jh = null; - if(rootInterface != -1) { - rootWidgetCount = 0; - Interpreter.method1977(rootInterface, 0, 0, SoundCache.canvasWidth, Huffman.canvasHeight, 0, 0, -1); - } - - < -- here appearantly - - Rasterizer2D.Rasterizer2D_resetClip(); - ______________________________________________________ - */ - - boolean injected = false; - - Method noClip = findStaticMethod(inject, "Rasterizer2D_resetClip"); // !!!!! - - if (noClip == null) - { - throw new InjectionException("Mapped method \"Rasterizer2D_resetClip\" could not be found."); - } - - net.runelite.asm.pool.Method poolNoClip = noClip.getPoolMethod(); - - for (ClassFile c : inject.getVanilla().getClasses()) - { - for (Method m : c.getMethods()) - { - if (m.getCode() == null) - { - continue; - } - - Instructions instructions = m.getCode().getInstructions(); - - Set