diff --git a/build.gradle.kts b/build.gradle.kts index 456eb9e..0d5745e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Lucas + * 2022, Owain van Brakel * All rights reserved. * * This code is licensed under GPL3, see the complete license in @@ -9,16 +10,14 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("java-gradle-plugin") - kotlin("jvm") version "1.3.72" + kotlin("jvm") version "1.7.0" `maven-publish` - id("com.github.ben-manes.versions") version "0.28.0" - id("se.patrikerdes.use-latest-versions") version "0.2.14" } -val oprsver = "3.5.1" +val oprsver = "4.31.1" group = "com.openosrs" -version = "1.1.7" +version = "2.0.0" repositories { mavenCentral() @@ -31,19 +30,24 @@ repositories { } dependencies { - annotationProcessor("org.projectlombok:lombok:1.18.12") - compileOnly("org.projectlombok:lombok:1.18.12") + annotationProcessor(group = "org.projectlombok", name = "lombok", version = "1.18.20") + compileOnly(group = "org.projectlombok", name = "lombok", version = "1.18.20") - implementation("org.ow2.asm:asm:8.0.1") - implementation("org.ow2.asm:asm-util:8.0.1") - implementation("org.jetbrains:annotations:19.0.0") - implementation("com.google.guava:guava:29.0-jre") + implementation(group = "org.ow2.asm", name = "asm", version = "9.0") + implementation(group = "org.ow2.asm", name = "asm-util", version = "9.0") + implementation(group = "org.jetbrains", name = "annotations", version = "22.0.0") + implementation(group = "com.google.guava", name = "guava", version = "30.1.1-jre") { + exclude(group = "com.google.code.findbugs", module = "jsr305") + exclude(group = "com.google.errorprone", module = "error_prone_annotations") + exclude(group = "com.google.j2objc", module = "j2objc-annotations") + exclude(group = "org.codehaus.mojo", module = "animal-sniffer-annotations") + } implementation("com.openosrs:deobfuscator:${oprsver}") { isTransitive = false } - testCompileOnly("com.openosrs:injection-annotations:1.0") - testImplementation("junit:junit:4.13") + testCompileOnly(group = "com.openosrs", name = "injection-annotations", version = "1.1") + testImplementation(group = "junit", name = "junit", version = "4.12") } gradlePlugin { @@ -55,7 +59,7 @@ gradlePlugin { } } -configure { +configure { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 622ab64..92f06b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/com/openosrs/injector/InjectUtil.java b/src/main/java/com/openosrs/injector/InjectUtil.java index b924d03..22c0f00 100644 --- a/src/main/java/com/openosrs/injector/InjectUtil.java +++ b/src/main/java/com/openosrs/injector/InjectUtil.java @@ -3,14 +3,17 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; import com.openosrs.injector.injection.InjectData; +import static com.openosrs.injector.rsapi.RSApi.API_BASE; +import static com.openosrs.injector.rsapi.RSApi.RL_API_BASE; import com.openosrs.injector.rsapi.RSApiClass; import com.openosrs.injector.rsapi.RSApiMethod; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -42,8 +45,6 @@ import net.runelite.asm.signature.Signature; import net.runelite.deob.DeobAnnotations; import net.runelite.deob.deobfuscators.arithmetic.DMath; import org.jetbrains.annotations.Nullable; -import static com.openosrs.injector.rsapi.RSApi.API_BASE; -import static com.openosrs.injector.rsapi.RSApi.RL_API_BASE; public interface InjectUtil { @@ -62,11 +63,10 @@ public interface InjectUtil /** * Finds a static method in deob and converts it to ob * - * @param data InjectData instance - * @param name The name of the method you want to find + * @param data InjectData instance + * @param name The name of the method you want to find * @param classHint The name of the class you expect the method to be in, or null - * @param sig The signature the method has in deob, or null - * + * @param sig The signature the method has in deob, or null * @return The obfuscated version of the found method */ static Method findMethod( @@ -81,8 +81,8 @@ public interface InjectUtil /** * Finds a method in injectData's deobfuscated classes. * - * @param data InjectData instance - * @param name (Exported) method name + * @param data InjectData instance + * @param name (Exported) method name * @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure * @throws InjectException If the hint class couldn't be found, or no method matching the settings was found */ @@ -90,7 +90,7 @@ public interface InjectUtil InjectData data, String name, @Nullable String classHint) - throws InjectException + throws InjectException { return findMethod(data, name, classHint, null, false, false); } @@ -98,13 +98,12 @@ public interface InjectUtil /** * Finds a method in injectData's deobfuscated classes. * - * @param data InjectData instance - * @param name (Exported) method name - * @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure - * @param sig The deobfuscated methods' signature, or null, if you're unsure - * @param notStatic If this is true, only check non-static methods. If classHint isn't null, check only subclasses + * @param data InjectData instance + * @param name (Exported) method name + * @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure + * @param sig The deobfuscated methods' signature, or null, if you're unsure + * @param notStatic If this is true, only check non-static methods. If classHint isn't null, check only subclasses * @param returnDeob If this is true, this method will return the deobfuscated method, instead of turning it into vanilla first - * * @throws InjectException If the hint class couldn't be found, or no method matching the settings was found */ static Method findMethod( @@ -114,7 +113,7 @@ public interface InjectUtil @Nullable Predicate sig, boolean notStatic, boolean returnDeob) - throws InjectException + throws InjectException { final ClassGroup deob = data.getDeobfuscated(); if (classHint != null) @@ -125,23 +124,41 @@ public interface InjectUtil if (notStatic) { if (sig == null) + { m = InjectUtil.findMethodDeep(cf, name, s -> true); + } else + { m = InjectUtil.findMethodDeep(cf, name, sig); + } } else + { m = cf.findMethod(name); + } if (m != null) + { return returnDeob ? m : data.toVanilla(m); + } } for (ClassFile cf : deob) + { for (Method m : cf.getMethods()) + { if (m.getName().equals(name)) + { if (!notStatic || !m.isStatic()) + { if (sig == null || sig.test(m.getDescriptor())) + { return returnDeob ? m : data.toVanilla(m); + } + } + } + } + } throw new InjectException(String.format("Couldn't find %s", name)); } @@ -150,7 +167,9 @@ public interface InjectUtil { ClassFile clazz = group.findClass(name); if (clazz == null) + { throw new InjectException("Hint class " + name + " doesn't exist"); + } return clazz; } @@ -161,10 +180,18 @@ public interface InjectUtil static Method findMethodDeep(ClassFile clazz, String name, Predicate type) { do + { for (Method method : clazz.getMethods()) + { if (method.getName().equals(name)) + { if (type.test(method.getDescriptor())) + { return method; + } + } + } + } while ((clazz = clazz.getParent()) != null); throw new InjectException(String.format("Method %s couldn't be found", name + type.toString())); @@ -172,7 +199,7 @@ public interface InjectUtil /** * Fail-fast implementation of ClassGroup.findStaticField - * + *

* well... */ static Field findStaticField(ClassGroup group, String name) @@ -181,7 +208,9 @@ public interface InjectUtil { Field f = clazz.findField(name); if (f != null && f.isStatic()) + { return f; + } } throw new InjectException("Couldn't find static field " + name); @@ -190,11 +219,10 @@ public interface InjectUtil /** * Finds a static field in deob and converts it to ob * - * @param data InjectData instance - * @param name The name of the field you want to find + * @param data InjectData instance + * @param name The name of the field you want to find * @param classHint The name of the class you expect the field to be in, or null - * @param type The type the method has in deob, or null - * + * @param type The type the method has in deob, or null * @return The obfuscated version of the found field */ static Field findStaticField(InjectData data, String name, String classHint, Type type) @@ -207,23 +235,35 @@ public interface InjectUtil ClassFile clazz = findClassOrThrow(deob, classHint); if (type == null) + { field = clazz.findField(name); + } else + { field = clazz.findField(name, type); + } if (field != null) + { return field; + } } for (ClassFile clazz : deob) { if (type == null) + { field = clazz.findField(name); + } else + { field = clazz.findField(name, type); + } if (field != null) + { return field; + } } throw new InjectException(String.format("Static field %s doesn't exist", (type != null ? type + " " : "") + name)); @@ -237,8 +277,12 @@ public interface InjectUtil Field f; do + { if ((f = clazz.findField(name)) != null) + { return f; + } + } while ((clazz = clazz.getParent()) != null); throw new InjectException("Couldn't find field " + name); @@ -259,12 +303,18 @@ public interface InjectUtil field = clazz.findField(name); if (field != null) + { return field; + } } for (ClassFile clazz : group) + { if ((field = clazz.findField(name)) != null) + { return field; + } + } throw new InjectException("Field " + name + " doesn't exist"); } @@ -291,39 +341,62 @@ public interface InjectUtil } static Type apiToDeob(InjectData data, Type api) + { + return apiToDeob(data, api, null); + } + + static Type apiToDeob(InjectData data, Type api, Type deobType) { if (api.isPrimitive()) + { return api; + } final String internalName = api.getInternalName(); if (internalName.startsWith(API_BASE)) + { return Type.getType("L" + api.getInternalName().substring(API_BASE.length()) + ";", api.getDimensions()); + } else if (internalName.startsWith(RL_API_BASE)) { Class rlApiC = new Class(internalName); - RSApiClass highestKnown = data.getRsApi().withInterface(rlApiC); + Set allClasses = data.getRsApi().withInterface(rlApiC); // Cheeky unchecked exception - assert highestKnown != null : "No rs api class implements rl api class " + rlApiC.toString(); + assert allClasses.size() > 0 : "No rs api class implements rl api class " + rlApiC; - boolean changed; - do + if (allClasses.size() == 1) { - changed = false; - - for (RSApiClass interf : highestKnown.getApiInterfaces()) + RSApiClass highestKnown = allClasses.stream().findFirst().get(); + boolean changed; + do { - if (interf.getInterfaces().contains(rlApiC)) + changed = false; + + for (RSApiClass interf : highestKnown.getApiInterfaces()) { - highestKnown = interf; - changed = true; - break; + if (interf.getInterfaces().contains(rlApiC)) + { + highestKnown = interf; + changed = true; + break; + } + } + } + while (changed); + + return apiToDeob(data, Type.getType(highestKnown.getName(), api.getDimensions())); + } + else + { + for (RSApiClass rsApiClass : allClasses) + { + if (rsApiClass.getName().contains(deobType.getInternalName())) + { + return apiToDeob(data, Type.getType(rsApiClass.getName(), api.getDimensions())); } } } - while (changed); - - return apiToDeob(data, Type.getType(highestKnown.getName(), api.getDimensions())); } return api; @@ -332,11 +405,15 @@ public interface InjectUtil static Type deobToVanilla(InjectData data, Type deobT) { if (deobT.isPrimitive()) + { return deobT; + } final ClassFile deobClass = data.getDeobfuscated().findClass(deobT.getInternalName()); if (deobClass == null) + { return deobT; + } return Type.getType("L" + data.toVanilla(deobClass).getName() + ";", deobT.getDimensions()); } @@ -352,18 +429,24 @@ public interface InjectUtil List bb = b.getArguments(); if (aa.size() != bb.size()) + { return false; + } for (int i = 0; i < aa.size(); i++) + { if (!aa.get(i).equals(bb.get(i))) + { return false; + } + } return true; } /** * Gets the obfuscated name from something's annotations. - * + *

* If the annotation doesn't exist return the current name instead. */ static String getObfuscatedName(T from) @@ -387,7 +470,9 @@ public interface InjectUtil static Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index) { if (type.getDimensions() > 0 || !type.isPrimitive()) + { return new ALoad(instructions, index); + } switch (type.toString()) { @@ -414,7 +499,9 @@ public interface InjectUtil static Instruction createReturnForType(Instructions instructions, Type type) { if (!type.isPrimitive()) + { return new Return(instructions, InstructionType.ARETURN); + } switch (type.toString()) { @@ -440,9 +527,13 @@ public interface InjectUtil static Instruction createInvokeFor(Instructions instructions, net.runelite.asm.pool.Method method, boolean isStatic) { if (isStatic) + { return new InvokeStatic(instructions, method); + } else + { return new InvokeVirtual(instructions, method); + } } /** @@ -463,9 +554,13 @@ public interface InjectUtil into.accept(new LDC(instrs, getter)); if (getter instanceof Integer) + { into.accept(new IMul(instrs)); + } else if (getter instanceof Long) + { into.accept(new LMul(instrs)); + } } /** @@ -477,4 +572,27 @@ public interface InjectUtil { injectObfuscatedGetter(DMath.modInverse(getter), instrs, into); } + + private static List findArgs(final String str, final List ret, final int from, final int to) + { + if (from >= to) + { + return ret; + } + + int i = from; + while (str.charAt(i) == '[') + { + ++i; + } + + if (str.charAt(i) == 'L') + { + i = str.indexOf(';', i); + } + + ret.add(new Type(str.substring(from, ++i))); + + return findArgs(str, ret, i, to); + } } diff --git a/src/main/java/com/openosrs/injector/Injection.java b/src/main/java/com/openosrs/injector/Injection.java index f78bb5c..414a2d1 100644 --- a/src/main/java/com/openosrs/injector/Injection.java +++ b/src/main/java/com/openosrs/injector/Injection.java @@ -7,8 +7,10 @@ */ package com.openosrs.injector; +import com.google.common.hash.Hashing; import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.injection.InjectTaskHandler; +import com.openosrs.injector.injectors.CreateAnnotations; import com.openosrs.injector.injectors.InjectConstruct; import com.openosrs.injector.injectors.Injector; import com.openosrs.injector.injectors.InterfaceInjector; @@ -16,18 +18,27 @@ import com.openosrs.injector.injectors.MixinInjector; import com.openosrs.injector.injectors.RSApiInjector; import com.openosrs.injector.injectors.raw.AddPlayerToMenu; import com.openosrs.injector.injectors.raw.ClearColorBuffer; -import com.openosrs.injector.injectors.raw.DrawAfterWidgets; +import com.openosrs.injector.injectors.raw.CopyRuneLiteClasses; import com.openosrs.injector.injectors.raw.DrawMenu; +import com.openosrs.injector.injectors.raw.GameDrawingMode; +import com.openosrs.injector.injectors.raw.GraphicsObject; import com.openosrs.injector.injectors.raw.Occluder; import com.openosrs.injector.injectors.raw.RasterizerAlpha; import com.openosrs.injector.injectors.raw.RenderDraw; +import com.openosrs.injector.injectors.raw.RuneLiteIterables; +import com.openosrs.injector.injectors.raw.RuneliteMenuEntry; +import com.openosrs.injector.injectors.raw.RuneliteObject; import com.openosrs.injector.injectors.raw.ScriptVM; import com.openosrs.injector.rsapi.RSApi; import com.openosrs.injector.transformers.InjectTransformer; +import com.openosrs.injector.transformers.Java8Ifier; import com.openosrs.injector.transformers.SourceChanger; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import net.runelite.deob.util.JarUtil; +import static net.runelite.deob.util.JarUtil.load; import org.gradle.api.file.FileTree; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; @@ -35,21 +46,36 @@ import org.gradle.api.logging.Logging; public class Injection extends InjectData implements InjectTaskHandler { private static final Logger log = Logging.getLogger(Injection.class); + public static boolean development = true; - public Injection(File vanilla, File rsclient, File mixins, FileTree rsapi) throws IOException + public Injection(File vanilla, File rsclient, File mixins, FileTree rsapi, boolean development) { super( - JarUtil.loadJar(vanilla), - JarUtil.loadJar(rsclient), - JarUtil.loadJar(mixins), + load(vanilla), + load(rsclient), + load(mixins), new RSApi(rsapi) ); + + Injection.development = development; } public void inject() { log.debug("[DEBUG] Starting injection"); + transform(new Java8Ifier(this)); + + inject(new CreateAnnotations(this)); + + inject(new GraphicsObject(this)); + + inject(new CopyRuneLiteClasses(this)); + + inject(new RuneLiteIterables(this)); + + inject(new RuneliteObject(this)); + inject(new InterfaceInjector(this)); inject(new RasterizerAlpha(this)); @@ -64,7 +90,7 @@ public class Injection extends InjectData implements InjectTaskHandler inject(new RSApiInjector(this)); - inject(new DrawAfterWidgets(this)); + //inject(new DrawAfterWidgets(this)); inject(new ScriptVM(this)); @@ -77,34 +103,60 @@ public class Injection extends InjectData implements InjectTaskHandler inject(new DrawMenu(this)); + inject(new GameDrawingMode(this)); + inject(new AddPlayerToMenu(this)); + inject(new RuneliteMenuEntry(this)); + validate(new InjectorValidator(this)); transform(new SourceChanger(this)); } - public void save(File outputJar) throws IOException + public void save(File outputJar) { log.info("[INFO] Saving jar to {}", outputJar.toString()); - JarUtil.saveJar(this.getVanilla(), outputJar); + JarUtil.save(this.getVanilla(), outputJar); + } + + public void hash(File output, File vanilla) + { + log.info("[INFO] Saving hash to {}", output.toString()); + + try + { + String hash = com.google.common.io.Files.asByteSource(vanilla).hash(Hashing.sha256()).toString(); + log.lifecycle("Writing vanilla hash: {}", hash); + Files.write(output.toPath(), hash.getBytes(StandardCharsets.UTF_8)); + } + catch (IOException ex) + { + log.lifecycle("Failed to write vanilla hash file"); + throw new RuntimeException(ex); + } } private void inject(Injector injector) { final String name = injector.getName(); - log.info("[INFO] Starting {}", name); - injector.start(); injector.inject(); - log.lifecycle("{} {}", name, injector.getCompletionMsg()); + String completionMsg = injector.getCompletionMsg(); + + if (completionMsg != null) + { + log.lifecycle("{} {}", name, completionMsg); + } if (injector instanceof Validator) + { validate((Validator) injector); + } } private void validate(Validator validator) @@ -121,8 +173,6 @@ public class Injection extends InjectData implements InjectTaskHandler { final String name = transformer.getName(); - log.info("[INFO] Starting {}", name); - transformer.transform(); log.lifecycle("{} {}", name, transformer.getCompletionMsg()); diff --git a/src/main/java/com/openosrs/injector/InjectorValidator.java b/src/main/java/com/openosrs/injector/InjectorValidator.java index 4a606a4..a80e9d5 100644 --- a/src/main/java/com/openosrs/injector/InjectorValidator.java +++ b/src/main/java/com/openosrs/injector/InjectorValidator.java @@ -3,12 +3,13 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.rsapi.RSApi; +import static com.openosrs.injector.rsapi.RSApi.API_BASE; import com.openosrs.injector.rsapi.RSApiClass; import com.openosrs.injector.rsapi.RSApiMethod; import lombok.RequiredArgsConstructor; @@ -16,7 +17,6 @@ import net.runelite.asm.ClassFile; import net.runelite.asm.pool.Class; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; -import static com.openosrs.injector.rsapi.RSApi.API_BASE; @RequiredArgsConstructor public class InjectorValidator implements Validator @@ -35,7 +35,9 @@ public class InjectorValidator implements Validator for (Class intf : cf.getInterfaces()) { if (!intf.getName().startsWith(API_BASE)) + { continue; + } RSApiClass apiC = rsApi.findClass(intf.getName()); if (apiC == null) @@ -61,11 +63,13 @@ public class InjectorValidator implements Validator for (RSApiMethod apiMethod : apiClass) { if (apiMethod.isSynthetic() || apiMethod.isDefault()) + { continue; + } if (clazz.findMethodDeep(apiMethod.getName(), apiMethod.getSignature()) == null) { - log.warn("[WARN] Class {} implements interface {} but doesn't implement {}", + log.error("[WARN] Class {} implements interface {} but doesn't implement {}", clazz.getPoolClass(), apiClass.getClazz(), apiMethod.getMethod()); ++missing; } diff --git a/src/main/java/com/openosrs/injector/Validator.java b/src/main/java/com/openosrs/injector/Validator.java index ba3b7ef..d4679c7 100644 --- a/src/main/java/com/openosrs/injector/Validator.java +++ b/src/main/java/com/openosrs/injector/Validator.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/src/main/java/com/openosrs/injector/injection/InjectData.java b/src/main/java/com/openosrs/injector/injection/InjectData.java index 3f47bf1..824d0c3 100644 --- a/src/main/java/com/openosrs/injector/injection/InjectData.java +++ b/src/main/java/com/openosrs/injector/injection/InjectData.java @@ -3,11 +3,10 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injection; -import com.google.common.collect.ImmutableMap; import com.openosrs.injector.InjectUtil; import com.openosrs.injector.injectors.Injector; import com.openosrs.injector.rsapi.RSApi; @@ -27,7 +26,7 @@ import net.runelite.asm.signature.Signature; */ public abstract class InjectData { - public static final String HOOKS = "net/runelite/client/callback/Hooks"; + public static final String CALLBACKS = "net/runelite/api/hooks/Callbacks"; @Getter private final ClassGroup vanilla; @@ -41,10 +40,21 @@ public abstract class InjectData @Getter private final RSApi rsApi; + public InjectData(ClassGroup vanilla, ClassGroup deobfuscated, ClassGroup mixins, RSApi rsApi) + { + this.vanilla = vanilla; + this.deobfuscated = deobfuscated; + this.rsApi = rsApi; + this.mixins = mixins; + + initToVanilla(); + } + /** * Deobfuscated ClassFiles -> Vanilla ClassFiles */ - private final Map toVanilla; + @Getter + private final Map toVanilla = new HashMap<>(); /** * Strings -> Deobfuscated ClassFiles @@ -52,26 +62,15 @@ public abstract class InjectData * - Obfuscated name * - RSApi implementing name */ - private final Map toDeob = new HashMap<>(); - - public InjectData(ClassGroup vanilla, ClassGroup deobfuscated, ClassGroup mixins, RSApi rsApi) - { - this.vanilla = vanilla; - this.deobfuscated = deobfuscated; - this.rsApi = rsApi; - this.mixins = mixins; - this.toVanilla = initToVanilla(); - } + public final Map toDeob = new HashMap<>(); public abstract void runChildInjector(Injector injector); - private Map initToVanilla() + public void initToVanilla() { - ImmutableMap.Builder toVanillaB = ImmutableMap.builder(); - for (final ClassFile deobClass : deobfuscated) { - if (deobClass.getName().startsWith("net/runelite/")) + if (deobClass.getName().startsWith("net/runelite/") || deobClass.getName().startsWith("netscape")) { continue; } @@ -81,13 +80,14 @@ public abstract class InjectData { toDeob.put(obName, deobClass); - // Can't be null final ClassFile obClass = this.vanilla.findClass(obName); - toVanillaB.put(deobClass, obClass); + + if (obClass != null) + { + toVanilla.put(deobClass, obClass); + } } } - - return toVanillaB.build(); } /** @@ -95,6 +95,7 @@ public abstract class InjectData */ public ClassFile toVanilla(ClassFile deobClass) { + return toVanilla.get(deobClass); } @@ -148,7 +149,7 @@ public abstract class InjectData /** * Do something with all paired classes. - * + *

* Key = deobfuscated, Value = vanilla */ public void forEachPair(BiConsumer action) diff --git a/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java b/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java index 4f7fad4..170ca48 100644 --- a/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java +++ b/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java @@ -8,7 +8,6 @@ package com.openosrs.injector.injection; import java.io.File; -import java.io.IOException; /** * Interface containing all the methods gradle needs to know about @@ -23,5 +22,10 @@ public interface InjectTaskHandler /** * Call this to save the injected jar to outputJar */ - void save(File outputJar) throws IOException; + void save(File outputJar); + + /** + * Call this to save the vanilla hash + */ + void hash(File outputFile, File vanilla); } diff --git a/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java b/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java index b1c645c..baa9824 100644 --- a/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java +++ b/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; @@ -25,7 +25,7 @@ public abstract class AbstractInjector implements Injector stopwatch = Stopwatch.createStarted(); } - public final String getCompletionMsg() + public String getCompletionMsg() { return "finished in " + stopwatch.toString(); } diff --git a/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java b/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java new file mode 100644 index 0000000..f29ff93 --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019, Lucas + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + * + * 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 com.openosrs.injector.injectors; + +import com.openosrs.injector.injection.InjectData; +import net.runelite.asm.ClassFile; +import net.runelite.asm.Field; +import net.runelite.asm.Method; +import net.runelite.deob.DeobAnnotations; + +/* + * This handles creating "virtual" annotations to clean up rs-client in the main project + */ +public class CreateAnnotations extends AbstractInjector +{ + + public CreateAnnotations(InjectData inject) + { + super(inject); + } + + public void inject() + { + for (final ClassFile deobClass : inject.getDeobfuscated()) + { + injectFields(deobClass); + injectMethods(deobClass); + + if (deobClass.getName().startsWith("class")) + { + continue; + } + + deobClass.addAnnotation(DeobAnnotations.IMPLEMENTS, deobClass.getName()); + } + } + + private void injectFields(ClassFile deobClass) + { + for (Field deobField : deobClass.getFields()) + { + deobField.addAnnotation(DeobAnnotations.EXPORT, deobField.getName()); + } + } + + private void injectMethods(ClassFile deobClass) + { + for (Method deobMethod : deobClass.getMethods()) + { + deobMethod.addAnnotation(DeobAnnotations.EXPORT, deobMethod.getName()); + } + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java b/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java index ef1b8e7..49fea35 100644 --- a/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java +++ b/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. @@ -33,6 +33,7 @@ package com.openosrs.injector.injectors; import com.openosrs.injector.InjectException; import com.openosrs.injector.InjectUtil; import com.openosrs.injector.injection.InjectData; +import static com.openosrs.injector.rsapi.RSApi.CONSTRUCT; import com.openosrs.injector.rsapi.RSApiMethod; import java.util.List; import java.util.stream.Collectors; @@ -50,7 +51,6 @@ import net.runelite.asm.attributes.code.instructions.Return; import net.runelite.asm.pool.Class; import net.runelite.asm.pool.Method; import net.runelite.asm.signature.Signature; -import static com.openosrs.injector.rsapi.RSApi.CONSTRUCT; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; public class InjectConstruct extends AbstractInjector @@ -69,7 +69,9 @@ public class InjectConstruct extends AbstractInjector { Annotation construct = apiMethod.findAnnotation(CONSTRUCT); if (construct == null) + { continue; + } final Method method = apiMethod.getMethod(); final Class clazz = method.getClazz(); @@ -103,7 +105,9 @@ public class InjectConstruct extends AbstractInjector final net.runelite.asm.Method constructor = classToConstruct.findMethod("", constr); if (constructor == null) + { throw new InjectException("Unable to find constructor for " + classToConstruct.getName() + "." + constr); + } net.runelite.asm.Method setterMethod = new net.runelite.asm.Method(targetClass, apiMethod.getName(), apiMethod.getType()); diff --git a/src/main/java/com/openosrs/injector/injectors/InjectHook.java b/src/main/java/com/openosrs/injector/injectors/InjectHook.java index c2d8cde..9641b23 100644 --- a/src/main/java/com/openosrs/injector/injectors/InjectHook.java +++ b/src/main/java/com/openosrs/injector/injectors/InjectHook.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. * @@ -86,7 +86,9 @@ public class InjectHook extends AbstractInjector public void inject() { for (Map.Entry, List> entry : mixinTargets.entrySet()) + { injectMethods(entry.getKey(), entry.getValue()); + } injectHooks(); @@ -103,10 +105,17 @@ public class InjectHook extends AbstractInjector { final Annotation fieldHook = mixinMethod.findAnnotation(FIELDHOOK); if (fieldHook == null) + { continue; + } + + final boolean before = isBefore(fieldHook); + if (!before && !mixinMethod.getDescriptor().toString().equals("(I)V")) + { + throw new InjectException(String.format("FieldHook method `%s` has a bad signature `%s` (expected `(I)V`)", mixinMethod.getName(), mixinMethod.getDescriptor().rsApiToRsClient())); + } final String hookName = fieldHook.getValueString(); - final boolean before = isBefore(fieldHook); final ClassFile deobTarget = inject.toDeob(targetClass.getName()); final Field deobField; @@ -141,7 +150,7 @@ public class InjectHook extends AbstractInjector Set doneIh = new HashSet<>(); e.addExecutionVisitor(( - InstructionContext ic) -> + InstructionContext ic) -> { Instruction i = ic.getInstruction(); Instructions ins = i.getInstructions(); @@ -149,23 +158,33 @@ public class InjectHook extends AbstractInjector 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; + } log.trace("Found injection location for hook {} at instruction {}", hookInfo.method.getName(), sfi); ++injectedHooks; @@ -174,7 +193,9 @@ public class InjectHook extends AbstractInjector StackContext objectStackContext = null; if (sfi instanceof PutField) + { objectStackContext = ic.getPops().get(1); + } int idx = ins.getInstructions().indexOf(sfi); assert idx != -1; @@ -182,10 +203,14 @@ public class InjectHook extends AbstractInjector try { if (hookInfo.before) + { injectCallbackBefore(ins, idx, hookInfo, null, objectStackContext, value); + } else - // idx + 1 to insert after the set + // idx + 1 to insert after the set + { injectCallback(ins, idx + 1, hookInfo, null, objectStackContext); + } } catch (InjectException ex) { @@ -206,23 +231,33 @@ public class InjectHook extends AbstractInjector 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; + } StackContext value = ic.getPops().get(0); StackContext index = ic.getPops().get(1); @@ -232,7 +267,9 @@ public class InjectHook extends AbstractInjector StackContext objectStackContext = null; if (arrayReferencePushed.getInstruction().getType() == InstructionType.GETFIELD) + { objectStackContext = arrayReferencePushed.getPops().get(0); + } // inject hook after 'i' log.debug("[DEBUG] Found array injection location for hook {} at instruction {}", hookInfo.method.getName(), i); @@ -244,9 +281,13 @@ public class InjectHook extends AbstractInjector try { if (hookInfo.before) + { injectCallbackBefore(ins, idx, hookInfo, index, objectStackContext, value); + } else + { injectCallback(ins, idx + 1, hookInfo, index, objectStackContext); + } } catch (InjectException ex) { @@ -265,7 +306,9 @@ public class InjectHook extends AbstractInjector if (!hookInfo.method.isStatic()) { if (object == null) + { throw new InjectException("null object"); + } ins.getInstructions().add(idx++, new Dup(ins)); // dup value idx = recursivelyPush(ins, idx, object); @@ -313,7 +356,9 @@ public class InjectHook extends AbstractInjector } for (StackContext s : Lists.reverse(ctx.getPops())) + { idx = recursivelyPush(ins, idx, s); + } ins.getInstructions().add(idx++, ctx.getInstruction().clone()); return idx; @@ -324,15 +369,21 @@ public class InjectHook extends AbstractInjector if (!hookInfo.method.isStatic()) { if (objectPusher == null) + { throw new InjectException("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)); + } Instruction invoke = hookInfo.getInvoke(ins); ins.getInstructions().add(idx, invoke); diff --git a/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java b/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java index 1ca49ac..0023b4d 100644 --- a/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java +++ b/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. @@ -65,7 +65,9 @@ public class InjectHookMethod extends AbstractInjector public void inject() { for (Map.Entry, List> entry : mixinTargets.entrySet()) + { injectMethods(entry.getKey(), entry.getValue()); + } log.info("[INFO] Injected {} method hooks", injected); } @@ -80,10 +82,14 @@ public class InjectHookMethod extends AbstractInjector { final Annotation methodHook = mixinMethod.findAnnotation(METHODHOOK); if (methodHook == null) + { continue; + } if (!mixinMethod.getDescriptor().isVoid()) + { throw new InjectException("Method hook " + mixinMethod.getPoolMethod() + " doesn't have void return type"); + } final String hookName = methodHook.getValueString(); final boolean end = isEnd(methodHook); @@ -116,8 +122,12 @@ public class InjectHookMethod extends AbstractInjector { it = ins.listIterator(ins.size()); while (it.hasPrevious()) + { if (it.previous() instanceof ReturnInstruction) + { insertVoke(method, hookMethod, it); + } + } return; } @@ -126,8 +136,12 @@ public class InjectHookMethod extends AbstractInjector if (method.getName().equals("")) { while (it.hasNext()) + { if (it.next() instanceof InvokeSpecial) + { break; + } + } assert it.hasNext() : "Constructor without invokespecial"; } @@ -141,7 +155,9 @@ public class InjectHookMethod extends AbstractInjector int varIdx = 0; if (!method.isStatic()) + { iterator.add(new ALoad(instructions, varIdx++)); + } for (Type type : hookMethod.getType().getArguments()) { diff --git a/src/main/java/com/openosrs/injector/injectors/Injector.java b/src/main/java/com/openosrs/injector/injectors/Injector.java index a3d9048..e1e497b 100644 --- a/src/main/java/com/openosrs/injector/injectors/Injector.java +++ b/src/main/java/com/openosrs/injector/injectors/Injector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; diff --git a/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java b/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java index e96c039..008978f 100644 --- a/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java +++ b/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java @@ -3,16 +3,16 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; import com.openosrs.injector.injection.InjectData; +import static com.openosrs.injector.rsapi.RSApi.API_BASE; import net.runelite.asm.ClassFile; import net.runelite.asm.Interfaces; import net.runelite.asm.pool.Class; import net.runelite.deob.DeobAnnotations; -import static com.openosrs.injector.rsapi.RSApi.API_BASE; public class InterfaceInjector extends AbstractInjector { @@ -36,12 +36,14 @@ public class InterfaceInjector extends AbstractInjector final String impls = DeobAnnotations.getImplements(deobCf); if (impls == null) + { return; + } final String fullName = API_BASE + impls; if (!inject.getRsApi().hasClass(fullName)) { - log.debug("[DEBUG] Class {} implements nonexistent interface {}, skipping interface injection", + log.error("[DEBUG] Class {} implements nonexistent interface {}, skipping interface injection", deobCf.getName(), fullName ); diff --git a/src/main/java/com/openosrs/injector/injectors/MixinInjector.java b/src/main/java/com/openosrs/injector/injectors/MixinInjector.java index 4bce870..3b4e867 100644 --- a/src/main/java/com/openosrs/injector/injectors/MixinInjector.java +++ b/src/main/java/com/openosrs/injector/injectors/MixinInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. @@ -87,6 +87,7 @@ public class MixinInjector extends AbstractInjector private static final String ASSERTION_FIELD = "$assertionsDisabled"; private static final String MIXIN_BASE = "net/runelite/mixins/"; + private int injectedInterfaces = 0; private final Map injectedFields = new HashMap<>(); private final Map shadowFields = new HashMap<>(); private int copied = 0, replaced = 0, injected = 0; @@ -106,6 +107,13 @@ public class MixinInjector extends AbstractInjector @VisibleForTesting void inject(Map, List> mixinTargets) { + for (Map.Entry, List> entry : mixinTargets.entrySet()) + { + injectInterfaces(entry.getKey(), entry.getValue()); + } + + log.info("[INFO] Injected {} interfaces", injectedInterfaces); + for (Map.Entry, List> entry : mixinTargets.entrySet()) { injectFields(entry.getKey(), entry.getValue()); @@ -136,49 +144,103 @@ public class MixinInjector extends AbstractInjector { ImmutableMap.Builder, List> builder = ImmutableMap.builder(); - for (ClassFile mixinClass : inject.getMixins()) + try { - final List ret = getMixins(mixinClass); - builder.put( - (ret.size() > 1 ? mixinProvider(mixinClass) : () -> mixinClass), - ret - ); + for (ClassFile mixinClass : inject.getMixins()) + { + final List ret = getMixins(mixinClass); + builder.put( + (ret.size() > 1 ? mixinProvider(mixinClass) : () -> mixinClass), + ret + ); + } + } + catch (Exception e) + { + e.printStackTrace(); } return builder.build(); } - private void injectFields(Provider mixinProvider, List targetClasses) + private void injectInterfaces(Provider mixinProvider, List targetClasses) { - final ClassFile mixinClass = mixinProvider.get(); - - for (final ClassFile targetClass : targetClasses) + try { - for (Field field : mixinClass.getFields()) + final ClassFile mixinClass = mixinProvider.get(); + + for (final ClassFile targetClass : targetClasses) { - if (field.findAnnotation(INJECT) == null && - (!ASSERTION_FIELD.equals(field.getName()) || targetClass.findField(ASSERTION_FIELD, Type.BOOLEAN) != null)) + mixinClass.getInterfaces().getInterfaces().forEach((itf) -> { - continue; - } - - Field copy = new Field(targetClass, field.getName(), field.getType()); - copy.setAccessFlags(field.getAccessFlags()); - copy.setPublic(); - copy.setValue(field.getValue()); - - for (Map.Entry e : field.getAnnotations().entrySet()) - if (!e.getKey().toString().startsWith("Lnet/runelite/api/mixins")) - copy.addAnnotation(e.getValue()); - - targetClass.addField(copy); - - if (injectedFields.containsKey(field.getName()) && !ASSERTION_FIELD.equals(field.getName())) - throw new InjectException("Duplicate field: " + field.getName()); - - injectedFields.put(field.getName(), copy); + if (targetClass.getInterfaces().addInterface(itf)) + { + injectedInterfaces++; + } + }); } } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private void injectFields(Provider mixinProvider, List targetClasses) + { + try + { + final ClassFile mixinClass = mixinProvider.get(); + + for (final ClassFile targetClass : targetClasses) + { + for (Field field : mixinClass.getFields()) + { + if (field.findAnnotation(INJECT) == null && + (!ASSERTION_FIELD.equals(field.getName()) || targetClass.findField(ASSERTION_FIELD, Type.BOOLEAN) != null)) + { + continue; + } + + Field copy = new Field(targetClass, field.getName(), field.getType()); + copy.setAccessFlags(field.getAccessFlags()); + copy.setPublic(); + copy.setValue(field.getValue()); + + for (Map.Entry e : field.getAnnotations().entrySet()) + { + if (!e.getKey().toString().startsWith("Lnet/runelite/api/mixins")) + { + copy.addAnnotation(e.getValue()); + } + } + + if (targetClass.findField(field.getName(), field.getType()) != null && !ASSERTION_FIELD.equals(field.getName())) + { + throw new InjectException("Duplicate field: " + field.getName()); + } + + targetClass.addField(copy); + + // We only need to save static fields in injected fields, cause only static fields can be shadowed + if (!field.isStatic()) + { + continue; + } + if (injectedFields.containsKey(field.getName()) && !ASSERTION_FIELD.equals(field.getName())) + { + throw new InjectException("Duplicate static field: " + field.getName()); + } + + injectedFields.put(field.getName(), copy); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } private void findShadowFields(Provider mixinProvider) @@ -190,10 +252,14 @@ public class MixinInjector extends AbstractInjector Annotation shadow = field.findAnnotation(SHADOW); if (shadow == null) + { continue; + } if (!field.isStatic()) + { throw new InjectException("Shadowed fields must be static"); + } String shadowed = shadow.getValueString(); @@ -209,7 +275,9 @@ public class MixinInjector extends AbstractInjector } if ((targetField.getAccessFlags() & Opcodes.ACC_PRIVATE) != 0) + { throw new InjectException("Shadowed fields can't be private"); + } shadowFields.put( field.getPoolField(), @@ -232,7 +300,9 @@ public class MixinInjector extends AbstractInjector { Annotation copyA = mixinMethod.findAnnotation(COPY); if (copyA == null) + { continue; + } String copiedName = copyA.getValueString(); Signature deobSig = InjectUtil.apiToDeob(inject, mixinMethod.getDescriptor()); @@ -241,13 +311,17 @@ public class MixinInjector extends AbstractInjector Method deobSourceMethod = InjectUtil.findMethod(inject, copiedName, inject.toDeob(targetClass.getName()).getName(), deobSig::equals, notStat, true); if (mixinMethod.isStatic() != deobSourceMethod.isStatic()) + { throw new InjectException("Mixin method " + mixinMethod + " should be " + (deobSourceMethod.isStatic() ? "static" : "non-static")); + } // The actual method we're copying, including code etc Method sourceMethod = inject.toVanilla(deobSourceMethod); if (mixinMethod.getDescriptor().size() > sourceMethod.getDescriptor().size()) + { throw new InjectException("Mixin methods cannot have more parameters than their corresponding ob method"); + } Method copy = new Method(targetClass, "copy$" + copiedName, sourceMethod.getDescriptor()); moveCode(copy, sourceMethod.getCode()); @@ -255,7 +329,9 @@ public class MixinInjector extends AbstractInjector copy.setPublic(); copy.getExceptions().getExceptions().addAll(sourceMethod.getExceptions().getExceptions()); for (var a : sourceMethod.getAnnotations().values()) + { copy.addAnnotation(a); + } targetClass.addMethod(copy); ++copied; @@ -263,7 +339,7 @@ public class MixinInjector extends AbstractInjector * 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 = mixinMethod.getDescriptor().size() != sourceMethod.getDescriptor().size() && deobSourceMethod.getDescriptor().size() < copy.getDescriptor().size(); @@ -281,7 +357,9 @@ public class MixinInjector extends AbstractInjector if ((hasInject && isInit) || isClinit) { if (!"()V".equals(mixinMethod.getDescriptor().toString())) + { throw new InjectException("Injected constructors cannot have arguments"); + } Method[] originalMethods = targetClass.getMethods().stream() .filter(m -> m.getName().equals(mixinMethod.getName())) @@ -292,11 +370,15 @@ public class MixinInjector extends AbstractInjector // If there isn't a already just inject ours, otherwise rename it // This is always true for if (originalMethods.length > 0) + { name = "rl$$" + (isInit ? "init" : "clinit"); + } String numberlessName = name; for (int i = 1; targetClass.findMethod(name, mixinMethod.getDescriptor()) != null; i++) + { name = numberlessName + i; + } Method copy = new Method(targetClass, name, mixinMethod.getDescriptor()); moveCode(copy, mixinMethod.getCode()); @@ -320,7 +402,9 @@ public class MixinInjector extends AbstractInjector int pops = invoke.getMethod().getType().getArguments().size() + 1; for (int i = 0; i < pops; i++) + { listIter.add(new Pop(instructions)); + } break; } @@ -362,7 +446,7 @@ public class MixinInjector extends AbstractInjector else if (hasInject) { // Make sure the method doesn't invoke copied methods - for (Instruction i : mixinMethod.getCode().getInstructions()) + /*for (Instruction i : mixinMethod.getCode().getInstructions()) { if (i instanceof InvokeInstruction) { @@ -370,22 +454,27 @@ public class MixinInjector extends AbstractInjector if (copiedMethods.containsKey(ii.getMethod())) { - throw new InjectException("Injected methods cannot invoke copied methods"); + throw new InjectException("Injected methods cannot invoke copied methods " + ii.toString()); } } + }*/ + + Method method = targetClass.findMethod(mixinMethod.getName(), mixinMethod.getDescriptor()); + + if (method == null) + { + method = new Method(targetClass, mixinMethod.getName(), mixinMethod.getDescriptor()); + targetClass.addMethod(method); } - Method copy = new Method(targetClass, mixinMethod.getName(), mixinMethod.getDescriptor()); - moveCode(copy, mixinMethod.getCode()); - copy.setAccessFlags(mixinMethod.getAccessFlags()); - copy.setPublic(); + moveCode(method, mixinMethod.getCode()); + method.setAccessFlags(mixinMethod.getAccessFlags()); + method.setPublic(); assert mixinMethod.getExceptions().getExceptions().isEmpty(); - setOwnersToTargetClass(mixinClass, targetClass, copy, copiedMethods); + setOwnersToTargetClass(mixinClass, targetClass, method, copiedMethods); - targetClass.addMethod(copy); - - log.debug("[DEBUG] Injected mixin method {} to {}", copy, targetClass); + log.debug("[DEBUG] Injected mixin method {} to {}", method, targetClass); ++injected; } else if (mixinMethod.findAnnotation(REPLACE) != null) @@ -486,11 +575,15 @@ public class MixinInjector extends AbstractInjector // Update instructions for each instruction for (Instruction i : newCode.getInstructions()) + { i.setInstructions(newCode.getInstructions()); + } newCode.getExceptions().getExceptions().addAll(sourceCode.getExceptions().getExceptions()); for (net.runelite.asm.attributes.code.Exception e : newCode.getExceptions().getExceptions()) + { e.setExceptions(newCode.getExceptions()); + } targetMethod.setCode(newCode); } @@ -581,7 +674,9 @@ public class MixinInjector extends AbstractInjector PushConstantInstruction pi = (PushConstantInstruction) i; if (mixinCf.getPoolClass().equals(pi.getConstant())) + { pi.setConstant(cf.getPoolClass()); + } } verify(mixinCf, i); @@ -597,11 +692,15 @@ public class MixinInjector extends AbstractInjector if (fi.getField().getClazz().getName().equals(mixinCf.getName())) { if (i instanceof PutField || i instanceof GetField) + { throw new InjectException("Access to non static member field of mixin"); + } Field field = fi.getMyField(); if (field != null && !field.isPublic()) + { throw new InjectException("Static access to non public field " + field); + } } } else if (i instanceof InvokeStatic) @@ -610,12 +709,16 @@ public class MixinInjector extends AbstractInjector if (is.getMethod().getClazz() != mixinCf.getPoolClass() && is.getMethod().getClazz().getName().startsWith(MIXIN_BASE)) + { throw new InjectException("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{} + // RS classes don't verify under java 7+ due to the + // super() invokespecial being inside of a try{} + { throw new InjectException("Injected bytecode must be Java 6 compatible"); + } } private Method findDeobMatching(ClassFile deobClass, Method mixinMethod, String deobName) @@ -625,18 +728,26 @@ public class MixinInjector extends AbstractInjector for (Method method : deobClass.getMethods()) { if (!deobName.equals(method.getName())) + { continue; + } if (InjectUtil.apiToDeobSigEquals(inject, method.getDescriptor(), mixinMethod.getDescriptor())) + { matching.add(method); + } } if (matching.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 + // 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 InjectException("There are several matching methods when there should only be one"); + } else if (matching.size() == 1) + { return matching.get(0); + } return inject.getDeobfuscated().findStaticMethod(deobName); } @@ -672,21 +783,23 @@ public class MixinInjector extends AbstractInjector .map(mix -> InjectUtil.getVanillaClassFromAnnotationString(inject, mix)) .collect(Collectors.toUnmodifiableList()); } - throw new IllegalArgumentException("No MIXIN or MIXINS found on " + from.toString()); + throw new IllegalArgumentException("No MIXIN or MIXINS found on " + from); } @Value private static class CopiedMethod { - @Nonnull Method copy; - @Nullable Integer garbage; + @Nonnull + Method copy; + @Nullable Integer garbage; } @Value private static class ShadowField { - @Nonnull Field targetField; - @Nullable Number obfuscatedGetter; + @Nonnull + Field targetField; + @Nullable Number obfuscatedGetter; } private static Provider mixinProvider(ClassFile mixin) @@ -699,7 +812,9 @@ public class MixinInjector extends AbstractInjector public ClassFile get() { if (bytes != null) + { return JarUtil.loadClass(bytes); + } bytes = JarUtil.writeClass(mixin.getGroup(), mixin); return mixin; diff --git a/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java b/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java index e5a9c4e..3232a68 100644 --- a/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java +++ b/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. @@ -36,6 +36,7 @@ import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.injectors.rsapi.InjectGetter; import com.openosrs.injector.injectors.rsapi.InjectInvoke; import com.openosrs.injector.injectors.rsapi.InjectSetter; +import static com.openosrs.injector.rsapi.RSApi.API_BASE; import com.openosrs.injector.rsapi.RSApiClass; import com.openosrs.injector.rsapi.RSApiMethod; import java.util.ArrayList; @@ -50,7 +51,6 @@ import net.runelite.asm.Type; import net.runelite.asm.attributes.Annotated; import net.runelite.asm.signature.Signature; import net.runelite.deob.DeobAnnotations; -import static com.openosrs.injector.rsapi.RSApi.API_BASE; public class RSApiInjector extends AbstractInjector { @@ -84,7 +84,9 @@ public class RSApiInjector extends AbstractInjector final List matching = findImportsFor(deobField, deobField.isStatic(), implementingClass); if (matching == null) + { continue; + } final Type deobType = deobField.getType(); @@ -116,9 +118,9 @@ public class RSApiInjector extends AbstractInjector final Signature sig = apiMethod.getSignature(); - if (sig.isVoid()) + if (sig.size() == 1) { - if (sig.size() == 1) + if (sig.isVoid() || sig.getReturnValue().equals(Type.fromAsmString(apiMethod.getClazz().getName()))) { Type type = InjectUtil.apiToDeob(inject, sig.getTypeOfArg(0)); if (deobType.equals(type)) @@ -129,7 +131,7 @@ public class RSApiInjector extends AbstractInjector } else if (sig.size() == 0) { - Type type = InjectUtil.apiToDeob(inject, sig.getReturnValue()); + Type type = InjectUtil.apiToDeob(inject, sig.getReturnValue(), deobType); if (deobType.equals(type)) { continue; @@ -153,7 +155,9 @@ public class RSApiInjector extends AbstractInjector final Number getter = DeobAnnotations.getObfuscatedGetter(deobField); if (deobField.isStatic() != vanillaField.isStatic()) // Can this even happen + { throw new InjectException("Something went horribly wrong, and this should honestly never happen, but you never know. Btw it's the static-ness"); + } inject(matching, deobField, vanillaField, getter); } @@ -166,7 +170,9 @@ public class RSApiInjector extends AbstractInjector final List matching = findImportsFor(deobMethod, deobMethod.isStatic(), implementingClass); if (matching == null) + { continue; + } final Signature deobSig = deobMethod.getDescriptor(); @@ -211,7 +217,9 @@ public class RSApiInjector extends AbstractInjector apiMethod.setInjected(true); } else if (matching.size() != 0) + { throw new InjectException("Multiple api imports matching method " + deobMethod.getPoolMethod()); + } } } @@ -224,8 +232,10 @@ public class RSApiInjector extends AbstractInjector matched.removeIf(RSApiMethod::isInjected); - if (matched.size() > 2) + /*if (matched.size() > 2) + { throw new InjectException("More than 2 imported api methods for field " + deobField.getPoolField()); + }*/ final Field vanillaField = inject.toVanilla(deobField); final Number getter = DeobAnnotations.getObfuscatedGetter(deobField); @@ -238,7 +248,9 @@ public class RSApiInjector extends AbstractInjector { final String exportedName = InjectUtil.getExportedName(object); if (exportedName == null) + { return null; + } final List matching = new ArrayList<>(); @@ -264,7 +276,7 @@ public class RSApiInjector extends AbstractInjector final ClassFile targetClass = InjectUtil.vanillaFromApiMethod(inject, apiMethod); apiMethod.setInjected(true); - if (apiMethod.getSignature().isVoid()) + if (apiMethod.getSignature().getArguments().size() == 1) { ++set; log.debug("[DEBUG] Injecting setter {} for {} into {}", apiMethod.getMethod(), field.getPoolField(), targetClass.getPoolClass()); diff --git a/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java b/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java index 578b30b..e7293b0 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java @@ -4,7 +4,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -12,13 +12,10 @@ import com.openosrs.injector.InjectException; import com.openosrs.injector.InjectUtil; import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.injectors.AbstractInjector; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; import java.util.ListIterator; import net.runelite.asm.Method; 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.Label; import net.runelite.asm.attributes.code.instruction.types.ComparisonInstruction; @@ -32,8 +29,6 @@ import net.runelite.asm.attributes.code.instructions.IfACmpNe; import net.runelite.asm.attributes.code.instructions.IfICmpNe; import net.runelite.asm.attributes.code.instructions.IfNe; import net.runelite.asm.attributes.code.instructions.InvokeStatic; -import net.runelite.asm.attributes.code.instructions.LDC; -import net.runelite.asm.attributes.code.instructions.Return; import net.runelite.asm.pool.Field; public class AddPlayerToMenu extends AbstractInjector @@ -45,15 +40,15 @@ public class AddPlayerToMenu extends AbstractInjector public void inject() { - final Method addPlayerOptions = InjectUtil.findMethod(inject, "addPlayerToMenu"); + final Method addPlayerOptions = inject.toVanilla(inject.getDeobfuscated().findClass("Scene")).findMethod("copy$addPlayerToMenu"); final net.runelite.asm.pool.Method shouldHideAttackOptionFor = inject.getVanilla().findClass("client").findMethod("shouldHideAttackOptionFor").getPoolMethod(); - final net.runelite.asm.pool.Method shouldDrawMethod = - inject.getVanilla().findStaticMethod("shouldDraw").getPoolMethod(); +// final net.runelite.asm.pool.Method shouldDrawMethod = +// inject.getVanilla().findStaticMethod("shouldDraw").getPoolMethod(); try { - injectSameTileFix(addPlayerOptions, shouldDrawMethod); + // injectSameTileFix(addPlayerOptions, shouldDrawMethod); injectHideAttack(addPlayerOptions, shouldHideAttackOptionFor); injectHideCast(addPlayerOptions, shouldHideAttackOptionFor); } @@ -63,32 +58,32 @@ public class AddPlayerToMenu extends AbstractInjector } } - private void injectSameTileFix(Method addPlayerOptions, net.runelite.asm.pool.Method shouldDrawMethod) - { - // ALOAD 0 - // ICONST_0 - // INVOKESTATIC Scene.shouldDraw - // IFNE CONTINUE_LABEL if returned true then jump to continue - // RETURN - // CONTINUE_LABEL - // REST OF METHOD GOES HERE - Instructions insns = addPlayerOptions.getCode().getInstructions(); - Label CONTINUE_LABEL = new Label(insns); - List prependList = new ArrayList<>() - {{ - add(new ALoad(insns, 0)); - add(new LDC(insns, 0)); - add(new InvokeStatic(insns, shouldDrawMethod)); - add(new IfNe(insns, CONTINUE_LABEL)); - add(new Return(insns, InstructionType.RETURN)); - add(CONTINUE_LABEL); - }}; - int i = 0; - for (Instruction instruction : prependList) - { - insns.addInstruction(i++, instruction); - } - } +// private void injectSameTileFix(Method addPlayerOptions, net.runelite.asm.pool.Method shouldDrawMethod) +// { +// // ALOAD 0 +// // ICONST_0 +// // INVOKESTATIC Scene.shouldDraw +// // IFNE CONTINUE_LABEL if returned true then jump to continue +// // RETURN +// // CONTINUE_LABEL +// // REST OF METHOD GOES HERE +// Instructions insns = addPlayerOptions.getCode().getInstructions(); +// Label CONTINUE_LABEL = new Label(insns); +// List prependList = new ArrayList<>() +// {{ +// add(new ALoad(insns, 0)); +// add(new LDC(insns, 0)); +// add(new InvokeStatic(insns, shouldDrawMethod)); +// add(new IfNe(insns, CONTINUE_LABEL)); +// add(new Return(insns, InstructionType.RETURN)); +// add(CONTINUE_LABEL); +// }}; +// int i = 0; +// for (Instruction instruction : prependList) +// { +// insns.addInstruction(i++, instruction); +// } +// } private void injectHideAttack(Method addPlayerOptions, net.runelite.asm.pool.Method shouldHideAttackOptionFor) { diff --git a/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java b/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java index 762e269..c64499d 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -22,17 +22,9 @@ import net.runelite.asm.execution.Execution; import net.runelite.asm.execution.InstructionContext; import net.runelite.asm.execution.MethodContext; import net.runelite.asm.execution.StackContext; -import net.runelite.asm.signature.Signature; -import static com.openosrs.injector.injection.InjectData.HOOKS; public class ClearColorBuffer extends AbstractInjector { - private static final net.runelite.asm.pool.Method CLEARBUFFER = new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(HOOKS), - "clearColorBuffer", - new Signature("(IIIII)V") - ); - public ClearColorBuffer(InjectData inject) { super(inject); @@ -40,11 +32,13 @@ public class ClearColorBuffer extends AbstractInjector public void inject() { + /* * This class stops the client from basically painting everything black before the scene is drawn */ final Execution exec = new Execution(inject.getVanilla()); + final net.runelite.asm.pool.Method clearBuffer = inject.getVanilla().findClass("client").findMethod("clearColorBuffer").getPoolMethod(); final net.runelite.asm.pool.Method fillRectPool = InjectUtil.findMethod(inject, "Rasterizer2D_fillRectangle", "Rasterizer2D", null).getPoolMethod(); final Method drawEntities = InjectUtil.findMethod(inject, "drawEntities"); // XXX: should prob be called drawViewport? @@ -83,7 +77,7 @@ public class ClearColorBuffer extends AbstractInjector } Instructions ins = instr.getInstructions(); - ins.replace(instr, new InvokeStatic(ins, CLEARBUFFER)); + ins.replace(instr, new InvokeStatic(ins, clearBuffer)); log.debug("[DEBUG] Injected drawRectangle at {}", methodContext.getMethod().getPoolMethod()); } } diff --git a/src/main/java/com/openosrs/injector/injectors/raw/CopyRuneLiteClasses.java b/src/main/java/com/openosrs/injector/injectors/raw/CopyRuneLiteClasses.java new file mode 100644 index 0000000..aebb792 --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/CopyRuneLiteClasses.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2021, Owain van Brakel + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.injectors.raw; + +import com.openosrs.injector.InjectUtil; +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +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.CheckCast; +import net.runelite.asm.attributes.code.instructions.GetField; +import net.runelite.asm.attributes.code.instructions.GetStatic; +import net.runelite.asm.attributes.code.instructions.InvokeSpecial; +import net.runelite.asm.attributes.code.instructions.InvokeStatic; +import net.runelite.asm.attributes.code.instructions.InvokeVirtual; +import net.runelite.asm.attributes.code.instructions.New; +import net.runelite.asm.attributes.code.instructions.PutField; +import net.runelite.asm.attributes.code.instructions.PutStatic; +import net.runelite.asm.pool.Class; +import net.runelite.asm.signature.Signature; +import org.objectweb.asm.Opcodes; + +public class CopyRuneLiteClasses extends AbstractInjector +{ + private static final List RUNELITE_OBJECTS = List.of( + "RuneLiteClanMember", + "RuneLiteIterableLinkDeque", + "RuneLiteIterableNodeDeque", + "RuneLiteIterableNodeHashTable", + "RuneLiteMenuEntry", + "RuneLiteObject" + ); + + + private final Set shadowFields = new HashSet<>(); + + public CopyRuneLiteClasses(InjectData inject) + { + super(inject); + } + + public void inject() + { + for (String className : RUNELITE_OBJECTS) + { + shadowFields.clear(); + + ClassFile runeliteObjectVanilla = inject.getVanilla().findClass(className); + + final ClassFile runeLiteDeob = inject.getDeobfuscated() + .findClass(className); + + if (runeliteObjectVanilla == null) + { + runeliteObjectVanilla = new ClassFile(inject.getVanilla()); + runeliteObjectVanilla.setVersion(Opcodes.V1_8); + runeliteObjectVanilla.setName(className); + runeliteObjectVanilla.setAccess(runeLiteDeob.getAccess()); + + if (runeLiteDeob.getParentClass() != null) + { + ClassFile deobClass = inject.getDeobfuscated().findClass(runeLiteDeob.getParentClass().getName()); + + if (deobClass != null) + { + runeliteObjectVanilla.setParentClass(inject.toVanilla(deobClass).getPoolClass()); + } + else + { + runeliteObjectVanilla.setParentClass(runeLiteDeob.getParentClass()); + } + } + + inject.getToVanilla().put(runeLiteDeob, runeliteObjectVanilla); + + for (Class interfaze : runeLiteDeob.getInterfaces()) + { + runeliteObjectVanilla.getInterfaces().addInterface(interfaze); + } + + for (Method method : runeLiteDeob.getMethods()) + { + transformMethod(method); + runeliteObjectVanilla.addMethod(method); + } + + for (Field field : runeLiteDeob.getFields()) + { + if (shadowFields.contains(field.getType())) + { + continue; + } + + field.setType(InjectUtil.deobToVanilla(inject, field.getType())); + runeliteObjectVanilla.addField(field); + } + + inject.getVanilla().addClass(runeliteObjectVanilla); + } + } + } + + private void transformMethod(Method method) + { + method.setDescriptor(getObfuscatedSignature(method.getDescriptor())); + + Code code = method.getCode(); + + if (code != null) + { + Instructions ins = code.getInstructions(); + for (ListIterator iterator = ins.listIterator(); iterator.hasNext(); ) + { + Instruction i = iterator.next(); + + if (i instanceof PutField) + { + net.runelite.asm.pool.Field field = ((PutField) i).getField(); + Field vanilla = findField(field); + + if (vanilla != null) + { + iterator.set(new PutField(ins, vanilla)); + } + else + { + field.setType(getObfuscatedSignature(field.getType())); + iterator.set(new PutField(ins, field)); + } + } + else if (i instanceof GetField) + { + net.runelite.asm.pool.Field field = ((GetField) i).getField(); + Field vanilla = findField(field); + + if (vanilla != null) + { + iterator.set(new GetField(ins, vanilla)); + } + else + { + field.setType(getObfuscatedSignature(field.getType())); + iterator.set(new GetField(ins, field)); + } + } + else if (i instanceof PutStatic) + { + net.runelite.asm.pool.Field field = ((PutStatic) i).getField(); + Field vanilla = findField(field); + + if (vanilla != null) + { + iterator.set(new PutStatic(ins, vanilla)); + } + else + { + field.setType(getObfuscatedSignature(field.getType())); + iterator.set(new PutStatic(ins, field)); + } + } + else if (i instanceof GetStatic) + { + net.runelite.asm.pool.Field field = ((GetStatic) i).getField(); + Field vanilla = findField(field); + + if (method.getClassFile().getName().equals(field.getClazz().getName()) && field.getType().toString().contains("Lnet/runelite/rs/api/RS")) + { + shadowFields.add(field.getType()); + + String fieldName = field.getType().toString().replace("Lnet/runelite/rs/api/RS", "").replace(";", ""); + final Field deobTargetField = InjectUtil.findStaticField(inject, fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1), null, InjectUtil.apiToDeob(inject, field.getType())); + + iterator.set(new GetStatic(ins, inject.toVanilla(deobTargetField))); + } + else if (vanilla != null) + { + iterator.set(new GetStatic(ins, vanilla)); + } + else + { + field.setType(getObfuscatedSignature(field.getType())); + iterator.set(new GetStatic(ins, field)); + } + } + else if (i instanceof InvokeSpecial) + { + net.runelite.asm.pool.Method meth = ((InvokeSpecial) i).getMethod(); + Method vanilla = findMethod(meth, true); + + if (vanilla != null) + { + iterator.set(new InvokeSpecial(ins, vanilla)); + } + else + { + meth.setType(getObfuscatedSignature(meth.getType())); + iterator.set(new InvokeSpecial(ins, meth)); + } + } + else if (i instanceof InvokeStatic) + { + net.runelite.asm.pool.Method meth = ((InvokeStatic) i).getMethod(); + Method vanilla = findMethod(meth, false); + + if (vanilla != null) + { + iterator.set(new InvokeStatic(ins, vanilla)); + } + else + { + meth.setType(getObfuscatedSignature(meth.getType())); + iterator.set(new InvokeStatic(ins, meth)); + } + } + else if (i instanceof InvokeVirtual) + { + net.runelite.asm.pool.Method meth = ((InvokeVirtual) i).getMethod(); + Method vanilla = findMethod(meth, true); + + if (vanilla != null) + { + iterator.set(new InvokeVirtual(ins, vanilla)); + } + else + { + meth.setType(getObfuscatedSignature(meth.getType())); + iterator.set(new InvokeVirtual(ins, meth)); + } + } + else if (i instanceof New) + { + + Class clazz = ((New) i).getNewClass(); + ClassFile deobClass = inject.getDeobfuscated().findClass(clazz.getName()); + + if (deobClass != null) + { + iterator.set(new New(ins, inject.toVanilla(deobClass))); + } + } + else if (i instanceof CheckCast) + { + CheckCast clazz = ((CheckCast) i); + iterator.set(new CheckCast(ins, getObfuscatedSignature(clazz.getType_()))); + } + } + } + } + + private Signature getObfuscatedSignature(Signature signature) + { + Signature.Builder builder = new Signature.Builder(); + + for (int j = 0; j < signature.size(); ++j) + { + Type type = signature.getTypeOfArg(j); + builder.addArgument(InjectUtil.deobToVanilla(inject, type)); + } + + builder.setReturnType(InjectUtil.deobToVanilla(inject, signature.getReturnValue())); + + return builder.build(); + } + + private Type getObfuscatedSignature(Type type) + { + return InjectUtil.deobToVanilla(inject, type); + } + + private Method findMethod(net.runelite.asm.pool.Method meth, boolean notStatic) + { + try + { + return InjectUtil.findMethod(inject, meth.getName(), meth.getClazz().getName(), meth.getType()::equals, notStatic, false); + } + catch (Exception e) + { + return null; + } + } + + private Field findField(net.runelite.asm.pool.Field field) + { + try + { + return InjectUtil.findField(inject, field.getName(), field.getClazz().getName()); + } + catch (Exception e) + { + return null; + } + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java b/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java index 218b618..60a2a90 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. @@ -33,22 +33,25 @@ package com.openosrs.injector.injectors.raw; import com.openosrs.injector.InjectException; import com.openosrs.injector.InjectUtil; import com.openosrs.injector.injection.InjectData; +import static com.openosrs.injector.injection.InjectData.CALLBACKS; import com.openosrs.injector.injectors.AbstractInjector; import java.util.HashSet; import java.util.ListIterator; import java.util.Set; import net.runelite.asm.ClassFile; +import net.runelite.asm.Field; 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.GetField; import net.runelite.asm.attributes.code.instructions.GetStatic; import net.runelite.asm.attributes.code.instructions.IMul; +import net.runelite.asm.attributes.code.instructions.InvokeInterface; import net.runelite.asm.attributes.code.instructions.InvokeStatic; import net.runelite.asm.signature.Signature; -import static com.openosrs.injector.injection.InjectData.HOOKS; public class DrawAfterWidgets extends AbstractInjector { @@ -103,6 +106,8 @@ public class DrawAfterWidgets extends AbstractInjector boolean injected = false; + Field client = getVanillaStaticFieldFromDeob("client"); + Field callbacks = getObfuscatedField("callbacks"); Method noClip = InjectUtil.findMethod(inject, "Rasterizer2D_resetClip", "Rasterizer2D", null); // !!!!! if (noClip == null) @@ -235,16 +240,17 @@ public class DrawAfterWidgets extends AbstractInjector for (Label l : labelsToInjectAfter) { - InvokeStatic invoke = new InvokeStatic(instructions, + InvokeInterface invoke = new InvokeInterface(instructions, new net.runelite.asm.pool.Method( - new net.runelite.asm.pool.Class(HOOKS), + new net.runelite.asm.pool.Class(CALLBACKS), "drawAfterWidgets", new Signature("()V") ) ); instructions.addInstruction(instructions.getInstructions().indexOf(l) + 1, invoke); - + instructions.addInstruction(instructions.getInstructions().indexOf(l) + 1, new GetField(instructions, callbacks.getPoolField())); + instructions.addInstruction(instructions.getInstructions().indexOf(l) + 1, new GetStatic(instructions, client.getPoolField())); log.debug("[DEBUG] injectDrawAfterWidgets injected a call after " + l); injected = true; @@ -257,4 +263,40 @@ public class DrawAfterWidgets extends AbstractInjector throw new InjectException("injectDrawAfterWidgets failed to inject!"); } } + + public Field getVanillaStaticFieldFromDeob(String s) + { + for (ClassFile c : inject.getDeobfuscated()) + { + for (Field f : c.getFields()) + { + if (f.isStatic()) + { + if (f.getName().equals(s)) + { + return inject.toVanilla(f); + } + } + } + } + return null; + } + + public Field getObfuscatedField(String s) + { + for (ClassFile c : inject.getVanilla()) + { + for (Field f : c.getFields()) + { + if (!f.isStatic()) + { + if (f.getName().equals(s)) + { + return f; + } + } + } + } + return null; + } } diff --git a/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java b/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java index b229376..bdb004d 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -24,19 +24,10 @@ import net.runelite.asm.attributes.code.instructions.InvokeStatic; import net.runelite.asm.execution.Execution; import net.runelite.asm.execution.InstructionContext; import net.runelite.asm.execution.MethodContext; -import net.runelite.asm.pool.Class; import net.runelite.asm.pool.Field; -import net.runelite.asm.signature.Signature; -import static com.openosrs.injector.injection.InjectData.HOOKS; public class DrawMenu extends AbstractInjector { - private static final net.runelite.asm.pool.Method DRAWMENU = new net.runelite.asm.pool.Method( - new Class(HOOKS), - "drawMenu", - new Signature("()Z") - ); - public DrawMenu(InjectData inject) { super(inject); @@ -66,6 +57,7 @@ public class DrawMenu extends AbstractInjector * -------- */ + final net.runelite.asm.pool.Method drawMenu = inject.getVanilla().findClass("client").findMethod("drawMenu").getPoolMethod(); final Method drawLoggedIn = InjectUtil.findMethod(inject, "drawLoggedIn", "Client", null, true, false); final Field gameDrawMode = InjectUtil.findField(inject, "gameDrawingMode", "Client").getPoolField(); final Field isMenuOpen = InjectUtil.findField(inject, "isMenuOpen", "Client").getPoolField(); @@ -87,7 +79,9 @@ public class DrawMenu extends AbstractInjector { Instruction instruction = ic.getInstruction(); if (!(instruction instanceof GetStatic)) + { continue; + } if (((GetStatic) instruction).getField().equals(isMenuOpen)) { @@ -100,30 +94,40 @@ public class DrawMenu extends AbstractInjector // If the popper is a IfNe the label it's pointing to is the drawMenu one and topLeft is directly after it // else it's the other way around, obviously if (isMenuOpenPopI instanceof IfNe) + { injectInvokeAfter = ((IfNe) isMenuOpenPopI).getTo(); + } else + { injectInvokeAfter = isMenuOpenPopI; + } } else if (((GetStatic) instruction).getField().equals(gameDrawMode)) { List instrL = instruction.getInstructions().getInstructions(); for (int i = instrL.indexOf(instruction); !(instruction instanceof Label); i--) + { instruction = instrL.get(i); + } labelToJumpTo = (Label) instruction; } if (injectInvokeAfter != null && labelToJumpTo != null) + { break; + } } if (injectInvokeAfter == null || labelToJumpTo == null) + { throw new InjectException("Couldn't find the right location for DrawMenu to inject"); + } final Instructions instrs = mc.getMethod().getCode().getInstructions(); int idx = instrs.getInstructions().indexOf(injectInvokeAfter); - instrs.addInstruction(++idx, new InvokeStatic(instrs, DRAWMENU)); + instrs.addInstruction(++idx, new InvokeStatic(instrs, drawMenu)); instrs.addInstruction(++idx, new IfNe(instrs, labelToJumpTo)); log.info("[INFO] DrawMenu injected a method call at index {} in method {}. With a comparison jumping to {}", idx, drawLoggedIn, labelToJumpTo); diff --git a/src/main/java/com/openosrs/injector/injectors/raw/GameDrawingMode.java b/src/main/java/com/openosrs/injector/injectors/raw/GameDrawingMode.java new file mode 100644 index 0000000..977d304 --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/GameDrawingMode.java @@ -0,0 +1,50 @@ +package com.openosrs.injector.injectors.raw; + +import com.openosrs.injector.InjectUtil; +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import java.util.ListIterator; +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.instructions.LDC; +import net.runelite.asm.attributes.code.instructions.PutStatic; +import net.runelite.asm.pool.Field; + +public class GameDrawingMode extends AbstractInjector +{ + public GameDrawingMode(InjectData inject) + { + super(inject); + } + + public void inject() + { + final ClassFile clientVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("Client") + ); + final Field gameDrawingMode = InjectUtil.findField(inject, "gameDrawingMode", "Client").getPoolField(); + + Method clinit = clientVanilla.findMethod(""); + + Instructions ins = clinit.getCode().getInstructions(); + ListIterator iterator = ins.getInstructions().listIterator(); + while (iterator.hasNext()) + { + Instruction i = iterator.next(); + + if (i instanceof PutStatic) + { + Field field = ((PutStatic) i).getField(); + + if (field.getName().equals(gameDrawingMode.getName())) + { + iterator.add(new LDC(ins, 2)); + iterator.add(new PutStatic(ins, gameDrawingMode)); + } + } + } + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/GraphicsObject.java b/src/main/java/com/openosrs/injector/injectors/raw/GraphicsObject.java new file mode 100644 index 0000000..7b3e74d --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/GraphicsObject.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Owain van Brakel + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.injectors.raw; + +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import net.runelite.asm.ClassFile; +import net.runelite.asm.Method; +import net.runelite.asm.attributes.Code; +import net.runelite.asm.attributes.code.Instructions; +import net.runelite.asm.attributes.code.instructions.ALoad; +import net.runelite.asm.attributes.code.instructions.InvokeSpecial; +import net.runelite.asm.attributes.code.instructions.VReturn; +import net.runelite.asm.signature.Signature; + +public class GraphicsObject extends AbstractInjector +{ + public GraphicsObject(InjectData inject) + { + super(inject); + } + + public void inject() + { + final ClassFile graphicsObjectVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("GraphicsObject") + ); + + final ClassFile renderableVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("Renderable") + ); + + graphicsObjectVanilla.clearFinal(); + + Method initGraphicsObject = new Method(graphicsObjectVanilla, "", new Signature("()V")); + initGraphicsObject.setPublic(); + + final Code code = new Code(initGraphicsObject); + code.setMaxStack(1); + initGraphicsObject.setCode(code); + graphicsObjectVanilla.addMethod(initGraphicsObject); + + Instructions ins = code.getInstructions(); + + ins.addInstruction(new ALoad(ins, 0)); + ins.addInstruction(new InvokeSpecial(ins, new net.runelite.asm.pool.Method(renderableVanilla.getPoolClass(), "", new Signature("()V")))); + ins.addInstruction(new VReturn(ins)); + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java b/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java index f1ed0d6..0425462 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -49,12 +49,16 @@ public class Occluder extends AbstractInjector Instruction i = it.next(); if (!(i instanceof BiPush)) + { continue; + } boolean shouldChange = (byte) ((BiPush) i).getConstant() == OLDVALUE; if (!shouldChange) + { continue; + } replaced++; @@ -64,6 +68,8 @@ public class Occluder extends AbstractInjector } if (replaced != 10) + { throw new InjectException("Only found " + replaced + " 25's to replace in occlude instead of expected 10"); + } } } diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java b/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java index f769605..96e5bb4 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -74,7 +74,9 @@ public class RasterizerAlpha extends AbstractInjector { Instructions instrs = getInstrs(mc); if (instrs == null) + { return; + } int count = 0; int orCount = 0; @@ -84,14 +86,18 @@ public class RasterizerAlpha extends AbstractInjector { Instruction instruction = ic.getInstruction(); if (!(instruction instanceof IAStore)) + { continue; + } // Field field = astore.getMyField(ic); // doesn't track into methods so doing it here StackContext array = ic.getPops().get(2); if (!isSameField(r2dPx, array)) + { continue; + } // This is the colour that's being set StackContext colour = ic.getPops().get(0); @@ -111,15 +117,21 @@ public class RasterizerAlpha extends AbstractInjector for (InstructionContext ins : mc.getInstructionContexts()) { if (!(ins.getInstruction() instanceof SiPush)) + { continue; + } SiPush pci = (SiPush) ins.getInstruction(); if ((short) pci.getConstant() != (short) 256) + { continue; + } InstructionContext isub = ins.getPushes().get(0).getPopped().get(0); if (!(isub.getInstruction() instanceof ISub)) + { continue; + } StackContext alphaPop = isub.getPops().get(0); InstructionContext alphaPusher = alphaPop.getPushed(); @@ -168,12 +180,16 @@ public class RasterizerAlpha extends AbstractInjector // If we're copying from the same field we don't have to apply extra alpha again if (colPushI instanceof IALoad && isSameField(r2dPx, colPusher.getPops().get(1))) + { continue; + } // If the value is 0, it's supposed to be transparent, not black if (colPushI instanceof PushConstantInstruction && ((PushConstantInstruction) colPushI).getConstant().equals(0)) + { continue; + } // rasterPx[idx] = color | 0xff000000 (the | 0xff000000 is what's added) int storeIdx = instrs.getInstructions().indexOf(instruction); @@ -217,7 +233,9 @@ public class RasterizerAlpha extends AbstractInjector { Code c = mc.getMethod().getCode(); if (c == null) + { return null; + } return c.getInstructions(); } @@ -226,11 +244,15 @@ public class RasterizerAlpha extends AbstractInjector { InstructionContext pusher = stackContext.getPushed().resolve(stackContext); if (pusher.getInstruction() instanceof GetFieldInstruction) + { return pusher; + } // No field I wanna trace, rn at least if (!(pusher.getInstruction() instanceof LVTInstruction)) + { return null; + } int vidx = ((LVTInstruction) pusher.getInstruction()).getVariableIndex(); @@ -245,7 +267,9 @@ public class RasterizerAlpha extends AbstractInjector InstructionContext ic = resolveFieldThroughInvokes(array); if (ic == null) + { return false; + } return ((GetFieldInstruction) ic.getInstruction()).getMyField() == f; } diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java b/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java index 8827429..79c9abe 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; @@ -17,17 +17,9 @@ import net.runelite.asm.attributes.code.Instruction; import net.runelite.asm.attributes.code.Instructions; import net.runelite.asm.attributes.code.instructions.InvokeStatic; import net.runelite.asm.attributes.code.instructions.InvokeVirtual; -import net.runelite.asm.pool.Class; -import net.runelite.asm.signature.Signature; -import static com.openosrs.injector.injection.InjectData.HOOKS; public class RenderDraw extends AbstractInjector { - private static final net.runelite.asm.pool.Method RENDERDRAW = new net.runelite.asm.pool.Method( - new Class(HOOKS), - "renderDraw", - new Signature("(Lnet/runelite/api/Entity;IIIIIIIIJ)V") - ); private static final int EXPECTED = 21; public RenderDraw(InjectData inject) @@ -38,13 +30,15 @@ public class RenderDraw extends AbstractInjector @Override public void inject() { + final net.runelite.asm.pool.Method renderDraw = inject.toVanilla(inject.getDeobfuscated().findClass("Scene")).findMethod("renderDraw").getPoolMethod(); + int replaced = 0; /* * This class replaces entity draw invocation instructions * with the renderDraw method on drawcallbacks */ - final net.runelite.asm.pool.Method draw = InjectUtil.findMethod(inject, "draw", "Entity", null, true, false).getPoolMethod(); + final net.runelite.asm.pool.Method draw = InjectUtil.findMethod(inject, "draw", "Renderable", null, true, false).getPoolMethod(); final Method drawTile = InjectUtil.findMethod(inject, "drawTile", "Scene", null, true, false); @@ -56,7 +50,7 @@ public class RenderDraw extends AbstractInjector { if (((InvokeVirtual) i).getMethod().equals(draw)) { - iterator.set(new InvokeStatic(ins, RENDERDRAW)); + iterator.set(new InvokeStatic(ins, renderDraw)); log.debug("[DEBUG] Replaced method call at {}", i); ++replaced; } @@ -64,6 +58,8 @@ public class RenderDraw extends AbstractInjector } if (replaced != EXPECTED) + { throw new InjectException("Didn't replace the expected amount of method calls"); + } } } diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RuneLiteIterables.java b/src/main/java/com/openosrs/injector/injectors/raw/RuneLiteIterables.java new file mode 100644 index 0000000..0667971 --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/RuneLiteIterables.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021, Owain van Brakel + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.injectors.raw; + +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import java.util.List; +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.InstructionType; +import net.runelite.asm.attributes.code.Instructions; +import net.runelite.asm.attributes.code.instructions.ALoad; +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; + +public class RuneLiteIterables extends AbstractInjector +{ + private static final String RUNELITE_ITERABLE_HASHTABLE = "RuneLiteIterableNodeHashTable"; + private static final String RUNELITE_ITERABLE_NODE_DEQUE = "RuneLiteIterableNodeDeque"; + private static final String RUNELITE_ITERABLE_LINK_DEQUE = "RuneLiteIterableLinkDeque"; + + public RuneLiteIterables(InjectData inject) + { + super(inject); + } + + public void inject() + { + injectHashTable(); + injectNodeDeque(); + injectLinkDeque(); + } + + public void injectHashTable() + { + ClassFile runeliteIterableHashTableVanilla = inject.getVanilla().findClass(RUNELITE_ITERABLE_HASHTABLE); + + final ClassFile nodeHashTableVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass(RUNELITE_ITERABLE_HASHTABLE.replace("RuneLiteIterable", "")) + ); + + Method copy = new Method(nodeHashTableVanilla, "iterator", new Signature("()Ljava/util/Iterator;")); + copy.setPublic(); + + final Code code = new Code(copy); + code.setMaxStack(3); + copy.setCode(code); + nodeHashTableVanilla.addMethod(copy); + + final Instructions instructions = code.getInstructions(); + final List ins = instructions.getInstructions(); + + ins.add(new New(instructions, runeliteIterableHashTableVanilla.getPoolClass())); + ins.add(new Dup(instructions)); + ins.add(new ALoad(instructions, 0)); + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteIterableHashTableVanilla.getPoolClass(), "", new Signature("(L" + nodeHashTableVanilla.getName() + ";)V")))); + ins.add(new Return(instructions, InstructionType.ARETURN)); + } + + public void injectNodeDeque() + { + ClassFile runeliteIterableNodeDequeVanilla = inject.getVanilla().findClass(RUNELITE_ITERABLE_NODE_DEQUE); + + final ClassFile nodeDequeVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass(RUNELITE_ITERABLE_NODE_DEQUE.replace("RuneLiteIterable", "")) + ); + + Method copy = new Method(nodeDequeVanilla, "iterator", new Signature("()Ljava/util/Iterator;")); + copy.setPublic(); + + final Code code = new Code(copy); + code.setMaxStack(3); + copy.setCode(code); + nodeDequeVanilla.addMethod(copy); + + final Instructions instructions = code.getInstructions(); + final List ins = instructions.getInstructions(); + + ins.add(new New(instructions, runeliteIterableNodeDequeVanilla.getPoolClass())); + ins.add(new Dup(instructions)); + ins.add(new ALoad(instructions, 0)); + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteIterableNodeDequeVanilla.getPoolClass(), "", new Signature("(L" + nodeDequeVanilla.getName() + ";)V")))); + ins.add(new Return(instructions, InstructionType.ARETURN)); + } + + public void injectLinkDeque() + { + ClassFile runeliteIterableLinkDequeVanilla = inject.getVanilla().findClass(RUNELITE_ITERABLE_LINK_DEQUE); + + final ClassFile linkDequeVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass(RUNELITE_ITERABLE_LINK_DEQUE.replace("RuneLiteIterable", "")) + ); + + Method copy = new Method(linkDequeVanilla, "iterator", new Signature("()Ljava/util/Iterator;")); + copy.setPublic(); + + final Code code = new Code(copy); + code.setMaxStack(3); + copy.setCode(code); + linkDequeVanilla.addMethod(copy); + + final Instructions instructions = code.getInstructions(); + final List ins = instructions.getInstructions(); + + ins.add(new New(instructions, runeliteIterableLinkDequeVanilla.getPoolClass())); + ins.add(new Dup(instructions)); + ins.add(new ALoad(instructions, 0)); + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteIterableLinkDequeVanilla.getPoolClass(), "", new Signature("(L" + linkDequeVanilla.getName() + ";)V")))); + ins.add(new Return(instructions, InstructionType.ARETURN)); + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RuneliteMenuEntry.java b/src/main/java/com/openosrs/injector/injectors/raw/RuneliteMenuEntry.java new file mode 100644 index 0000000..150d87c --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/RuneliteMenuEntry.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021, Owain van Brakel + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.injectors.raw; + +//import com.openosrs.injector.InjectUtil; + +import com.openosrs.injector.InjectUtil; +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import java.util.List; +import java.util.ListIterator; +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.BAStore; +import net.runelite.asm.attributes.code.instructions.Dup; +import net.runelite.asm.attributes.code.instructions.GetStatic; +import net.runelite.asm.attributes.code.instructions.ILoad; +import net.runelite.asm.attributes.code.instructions.InvokeSpecial; +import net.runelite.asm.attributes.code.instructions.InvokeStatic; +import net.runelite.asm.attributes.code.instructions.New; +import net.runelite.asm.attributes.code.instructions.Return; +import net.runelite.asm.signature.Signature; + +public class RuneliteMenuEntry extends AbstractInjector +{ + private static final String RUNELITE_MENU_ENTRY = "RuneLiteMenuEntry"; + + public RuneliteMenuEntry(InjectData inject) + { + super(inject); + } + + public void inject() + { + addInvoke("newRuneliteMenuEntry", true); + addInvoke("newBareRuneliteMenuEntry", false); + addSwap(InjectUtil.findMethod(inject, "menu"), InjectUtil.findField(inject, "menuShiftClick", "Client")); + } + + private void addInvoke(String methodName, boolean iload) + { + ClassFile runeliteMenuEntryVanilla = inject.getVanilla().findClass(RUNELITE_MENU_ENTRY); + + final ClassFile clientVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("Client") + ); + + Method copy = clientVanilla.findMethod(methodName); + + final Code code = new Code(copy); + code.setMaxStack(3); + copy.setCode(code); + + final Instructions instructions = code.getInstructions(); + final List ins = instructions.getInstructions(); + + ins.add(new New(instructions, runeliteMenuEntryVanilla.getPoolClass())); + ins.add(new Dup(instructions)); + if (iload) + { + ins.add(new ILoad(instructions, 0)); + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteMenuEntryVanilla.getPoolClass(), "", new Signature("(I)V")))); + } + else + { + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteMenuEntryVanilla.getPoolClass(), "", new Signature("()V")))); + } + ins.add(new Return(instructions, InstructionType.ARETURN)); + } + + private void addSwap(Method method, Field field) + { + final ClassFile clientVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("Client") + ); + + Instructions ins = method.getCode().getInstructions(); + ListIterator iterator = ins.getInstructions().listIterator(); + + int found = 0; + + while (iterator.hasNext()) + { + Instruction i = iterator.next(); + + if (found < 1) + { + if (!(i instanceof GetStatic)) + { + continue; + } + + if (((GetStatic) i).getField().getName().equals(field.getName())) + { + found += 1; + } + } + else + { + if (!(i instanceof BAStore)) + { + continue; + } + + ILoad i1 = new ILoad(ins, 3); + InvokeStatic i2 = new InvokeStatic(ins, new net.runelite.asm.pool.Method(clientVanilla.getPoolClass(), "swapMenuEntries", new Signature("(I)V"))); + + iterator.add(i1); + iterator.add(i2); + + return; + } + } + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/RuneliteObject.java b/src/main/java/com/openosrs/injector/injectors/raw/RuneliteObject.java new file mode 100644 index 0000000..686c04b --- /dev/null +++ b/src/main/java/com/openosrs/injector/injectors/raw/RuneliteObject.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Owain van Brakel + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.injectors.raw; + +import com.openosrs.injector.injection.InjectData; +import com.openosrs.injector.injectors.AbstractInjector; +import java.util.List; +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.InstructionType; +import net.runelite.asm.attributes.code.Instructions; +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; + +public class RuneliteObject extends AbstractInjector +{ + private static final String RUNELITE_OBJECT = "RuneLiteObject"; + + public RuneliteObject(InjectData inject) + { + super(inject); + } + + public void inject() + { + ClassFile runeliteObjectVanilla = inject.getVanilla().findClass(RUNELITE_OBJECT); + + final ClassFile clientVanilla = inject.toVanilla( + inject.getDeobfuscated() + .findClass("Client") + ); + + Method copy = new Method(clientVanilla, "createRuneLiteObject", new Signature("()Lnet/runelite/api/RuneLiteObject;")); + copy.setPublic(); + + final Code code = new Code(copy); + code.setMaxStack(2); + copy.setCode(code); + clientVanilla.addMethod(copy); + + final Instructions instructions = code.getInstructions(); + final List ins = instructions.getInstructions(); + + ins.add(new New(instructions, runeliteObjectVanilla.getPoolClass())); + ins.add(new Dup(instructions)); + ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteObjectVanilla.getPoolClass(), "", new Signature("()V")))); + ins.add(new Return(instructions, InstructionType.ARETURN)); + } +} diff --git a/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java b/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java index 1d9c463..e103bc5 100644 --- a/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java +++ b/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2018 Abex * All rights reserved. diff --git a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java index 5ef6b25..29efa77 100644 --- a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java +++ b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. @@ -50,7 +50,9 @@ public class InjectGetter public static void inject(ClassFile targetClass, RSApiMethod apiMethod, Field field, Number getter) { if (targetClass.findMethod(apiMethod.getName(), apiMethod.getSignature()) != null) + { throw new InjectException("Duplicate getter method " + apiMethod.getMethod().toString()); + } final String name = apiMethod.getName(); final Signature sig = apiMethod.getSignature(); @@ -75,7 +77,9 @@ public class InjectGetter } if (getter != null) + { InjectUtil.injectObfuscatedGetter(getter, instructions, ins::add); + } ins.add(InjectUtil.createReturnForType(instructions, field.getType())); diff --git a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java index 61a273d..3b98e2d 100644 --- a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java +++ b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. @@ -43,6 +43,7 @@ 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.InvokeInterface; import net.runelite.asm.attributes.code.instructions.InvokeStatic; import net.runelite.asm.attributes.code.instructions.InvokeVirtual; import net.runelite.asm.attributes.code.instructions.LDC; @@ -131,6 +132,10 @@ public class InjectInvoke { ins.add(new InvokeStatic(instructions, vanillaMethod.getPoolMethod())); } + else if (vanillaMethod.getClassFile().isInterface()) + { + ins.add(new InvokeInterface(instructions, vanillaMethod.getPoolMethod())); + } else { ins.add(new InvokeVirtual(instructions, vanillaMethod.getPoolMethod())); diff --git a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java index 2ff9adb..59eda4e 100644 --- a/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java +++ b/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. @@ -53,7 +53,9 @@ public class InjectSetter public static void inject(ClassFile targetClass, RSApiMethod apiMethod, Field field, Number getter) { if (targetClass.findMethod(apiMethod.getName(), apiMethod.getSignature()) != null) + { throw new InjectException("Duplicate setter method " + apiMethod.getMethod().toString()); + } final String name = apiMethod.getName(); final Signature sig = apiMethod.getSignature(); @@ -69,7 +71,9 @@ public class InjectSetter // load this if (!field.isStatic()) + { ins.add(new ALoad(instructions, 0)); + } // load argument final Type argumentType = sig.getTypeOfArg(0); @@ -85,14 +89,28 @@ public class InjectSetter } if (getter != null) + { InjectUtil.injectObfuscatedSetter(getter, instructions, ins::add); + } if (field.isStatic()) + { ins.add(new PutStatic(instructions, field)); + } else + { ins.add(new PutField(instructions, field)); + } - ins.add(new VReturn(instructions)); + if (!apiMethod.getSignature().getReturnValue().equals(Type.VOID)) + { + ins.add(new ALoad(instructions, 0)); + ins.add(InjectUtil.createReturnForType(instructions, apiMethod.getSignature().getReturnValue())); + } + else + { + ins.add(new VReturn(instructions)); + } targetClass.addMethod(method); } diff --git a/src/main/java/com/openosrs/injector/rsapi/RSApi.java b/src/main/java/com/openosrs/injector/rsapi/RSApi.java index abee6c0..a1e97a6 100644 --- a/src/main/java/com/openosrs/injector/rsapi/RSApi.java +++ b/src/main/java/com/openosrs/injector/rsapi/RSApi.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; @@ -15,8 +15,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import lombok.Getter; import lombok.NoArgsConstructor; import net.runelite.asm.Type; @@ -45,7 +47,9 @@ public class RSApi implements Iterable for (File file : classes) { if (!file.getName().startsWith("RS")) + { continue; + } try (InputStream is = new FileInputStream(file)) { @@ -72,7 +76,9 @@ public class RSApi implements Iterable final ImmutableMap.Builder builder = ImmutableMap.builder(); for (RSApiClass clazz : this) + { builder.put(clazz.getName(), clazz); + } this.map = builder.build(); @@ -108,17 +114,19 @@ public class RSApi implements Iterable return findClass(name) != null; } - public RSApiClass withInterface(Class interf) + public Set withInterface(Class interf) { - RSApiClass clazz = findClass(interf.getName()); - if (clazz != null) - return clazz; + Set classes = new HashSet<>(); for (RSApiClass apiC : this) + { if (apiC.getInterfaces().contains(interf)) - return apiC; + { + classes.add(apiC); + } + } - return null; + return classes; } @NotNull diff --git a/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java b/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java index 88c4c2a..4786e37 100644 --- a/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java +++ b/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java @@ -3,10 +3,12 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; +import static com.openosrs.injector.rsapi.RSApi.CONSTRUCT; +import static com.openosrs.injector.rsapi.RSApi.IMPORT; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -22,8 +24,6 @@ import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -import static com.openosrs.injector.rsapi.RSApi.CONSTRUCT; -import static com.openosrs.injector.rsapi.RSApi.IMPORT; @Getter @Setter @@ -46,7 +46,9 @@ public class RSApiClass extends ClassVisitor implements Iterable for (RSApiMethod method : this) { if (method.isSynthetic()) + { continue; + } if (method.findAnnotation(CONSTRUCT) != null) { @@ -56,10 +58,12 @@ public class RSApiClass extends ClassVisitor implements Iterable final Annotation imported = method.findAnnotation(IMPORT); if (imported != null) + { imports.computeIfAbsent( imported.getValueString(), (str) -> new ArrayList<>() ).add(method); + } } } @@ -79,7 +83,9 @@ public class RSApiClass extends ClassVisitor implements Iterable { List imported = imports.get(str); if (imported == null) + { return; + } to.addAll(imported); } @@ -95,7 +101,9 @@ public class RSApiClass extends ClassVisitor implements Iterable clazz = new Class(name); for (String s : interfaces) + { this.interfaces.add(new Class(s)); + } } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) diff --git a/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java b/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java index 4b58c77..4d512f2 100644 --- a/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java +++ b/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java @@ -3,15 +3,13 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; import java.util.HashMap; import java.util.Map; -import lombok.Data; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Setter; import net.runelite.asm.Annotation; import net.runelite.asm.Named; diff --git a/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java b/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java index 270b8b7..798947c 100644 --- a/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java +++ b/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.transformers; diff --git a/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java b/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java new file mode 100644 index 0000000..fb4a579 --- /dev/null +++ b/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, ThatGamerBlue + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this submodule. + */ +package com.openosrs.injector.transformers; + +import com.openosrs.injector.injection.InjectData; +import java.util.List; +import java.util.stream.Collectors; +import net.runelite.asm.ClassFile; +import net.runelite.asm.Method; +import net.runelite.asm.attributes.Code; +import net.runelite.asm.attributes.code.Exception; +import org.objectweb.asm.Opcodes; + +public class Java8Ifier extends InjectTransformer +{ + public Java8Ifier(InjectData inject) + { + super(inject); + } + + @Override + void transformImpl() + { + inject.forEachPair(this::makeJava8); + } + + private void makeJava8(ClassFile rsc, ClassFile vanilla) + { + vanilla.setVersion(Opcodes.V1_8); + for (Method method : vanilla.getMethods()) + { + if (!method.getName().equals("")) + { + continue; + } + + fixTryCatch(method); + } + } + + private void fixTryCatch(Method method) + { + Code code = method.getCode(); + List remove = code.getExceptions().getExceptions().stream().filter(e -> e.getCatchType() != null && e.getCatchType().getName().equals("java/lang/RuntimeException")).collect(Collectors.toList()); + remove.forEach(code.getExceptions()::remove); + } +} \ No newline at end of file diff --git a/src/main/java/com/openosrs/injector/transformers/SourceChanger.java b/src/main/java/com/openosrs/injector/transformers/SourceChanger.java index 7e56e46..a45a8ae 100644 --- a/src/main/java/com/openosrs/injector/transformers/SourceChanger.java +++ b/src/main/java/com/openosrs/injector/transformers/SourceChanger.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.transformers; diff --git a/src/main/kotlin/com/openosrs/injector/Inject.kt b/src/main/kotlin/com/openosrs/injector/Inject.kt index e04f1e0..48517d8 100644 --- a/src/main/kotlin/com/openosrs/injector/Inject.kt +++ b/src/main/kotlin/com/openosrs/injector/Inject.kt @@ -16,13 +16,16 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @CacheableTask -abstract class Inject: DefaultTask() { +abstract class Inject : DefaultTask() { @get:Nested abstract val extension: InjectExtension @get:OutputFile abstract val output: RegularFileProperty + @get:OutputFile + abstract val hash: RegularFileProperty + @TaskAction fun inject() { val vanilla = extension.vanilla.get().asFile @@ -30,10 +33,11 @@ abstract class Inject: DefaultTask() { val mixins = extension.mixins.get().asFile val rsapi = project.zipTree(extension.rsapi) - val injector: InjectTaskHandler = Injection(vanilla, rsclient, mixins, rsapi) + val injector: InjectTaskHandler = Injection(vanilla, rsclient, mixins, rsapi, if (extension.development.isPresent) extension.development.get() else true) injector.inject() injector.save(output.get().asFile) + injector.hash(hash.get().asFile, vanilla) } } \ No newline at end of file diff --git a/src/main/kotlin/com/openosrs/injector/InjectExtension.kt b/src/main/kotlin/com/openosrs/injector/InjectExtension.kt index 91d462e..70e4ca2 100644 --- a/src/main/kotlin/com/openosrs/injector/InjectExtension.kt +++ b/src/main/kotlin/com/openosrs/injector/InjectExtension.kt @@ -8,17 +8,23 @@ package com.openosrs.injector import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* interface InjectExtension { + @get:Input + @get:Optional + val development: Property + @get:[InputFile PathSensitive(PathSensitivity.NONE)] val vanilla: RegularFileProperty + @get:[InputFile PathSensitive(PathSensitivity.NONE)] val rsclient: RegularFileProperty + @get:[InputFile PathSensitive(PathSensitivity.NONE)] val mixins: RegularFileProperty + @get:[InputFile PathSensitive(PathSensitivity.NONE)] val rsapi: RegularFileProperty } diff --git a/src/main/kotlin/com/openosrs/injector/InjectPlugin.kt b/src/main/kotlin/com/openosrs/injector/InjectPlugin.kt index 9aeaef5..894af07 100644 --- a/src/main/kotlin/com/openosrs/injector/InjectPlugin.kt +++ b/src/main/kotlin/com/openosrs/injector/InjectPlugin.kt @@ -10,19 +10,27 @@ package com.openosrs.injector import org.gradle.api.Plugin import org.gradle.api.Project -class InjectPlugin: Plugin { - override fun apply(project: Project) { with(project) { - val task = tasks.create("inject", Inject::class.java) - task.output.convention { file("$buildDir/libs/$name-$version.jar") } +class InjectPlugin : Plugin { + override fun apply(project: Project) { + with(project) { + val task = tasks.create("inject", Inject::class.java) + task.output.convention { + file("$buildDir/libs/injected-client.oprs") + } + task.hash.convention { + file("$buildDir/resources/main/client.hash") + } - artifacts { - it.add("runtimeElements", task.output) + artifacts { + it.add("runtimeElements", task.output) + it.add("runtimeElements", task.hash) + } + + tasks.getByName("assemble") { + it.finalizedBy("inject") + } + + extensions.add(InjectExtension::class.java, "injector", task.extension) } - - tasks.getByName("assemble") { - it.finalizedBy("inject") - } - - extensions.add(InjectExtension::class.java, "injector", task.extension) - }} + } } \ No newline at end of file diff --git a/src/test/java/com/openosrs/injector/injectors/MixinInjectorTest.java b/src/test/java/com/openosrs/injector/injectors/MixinInjectorTest.java index 1665f69..b9a334f 100644 --- a/src/test/java/com/openosrs/injector/injectors/MixinInjectorTest.java +++ b/src/test/java/com/openosrs/injector/injectors/MixinInjectorTest.java @@ -24,7 +24,6 @@ */ package com.openosrs.injector.injectors; -import com.google.common.io.ByteStreams; import com.openosrs.injector.TestInjection; import com.openosrs.injector.rsapi.RSApi; import java.io.IOException; @@ -50,12 +49,12 @@ import net.runelite.asm.attributes.code.instructions.InvokeVirtual; import net.runelite.asm.attributes.code.instructions.LDC; import net.runelite.asm.signature.Signature; import net.runelite.asm.visitors.ClassFileVisitor; -import net.runelite.deob.util.JarUtil; import net.runelite.mapping.ObfuscatedGetter; import net.runelite.mapping.ObfuscatedName; import net.runelite.mapping.ObfuscatedSignature; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.objectweb.asm.ClassReader; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; @@ -80,6 +79,25 @@ class DeobTarget } } +@ObfuscatedName("com/openosrs/injector/injectors/VanillaTarget2") +class DeobTarget2 +{ + @ObfuscatedName("ob_foo5") + @ObfuscatedGetter(intValue = 1157381415) + static int foo5; + + @ObfuscatedName("ob_foo6") + @ObfuscatedSignature( + descriptor = "(I)V", + garbageValue = "123" + ) + private void foo6() + { + // De-obfuscated foo6 + System.out.println("foo6"); + } +} + class VanillaTarget { static int ob_foo4; @@ -95,6 +113,21 @@ class VanillaTarget } } +class VanillaTarget2 +{ + static int ob_foo5; + + private void ob_foo6(int garbageValue) + { + // Obfuscated foo6 + if (garbageValue != 123) + { + return; + } + System.out.println("foo6"); + } +} + abstract class Source { @net.runelite.api.mixins.Inject @@ -130,28 +163,39 @@ abstract class Source2 } } +// Test injecting a field into multiple vanilla classes (@Mixins) +abstract class Source3 +{ + @net.runelite.api.mixins.Inject + private boolean injectMe; +} + public class MixinInjectorTest { @Test public void testInject() throws Exception { - InputStream deobIn = getClass().getResourceAsStream("DeobTarget.class"); - ClassFile deobTarget = JarUtil.loadClass(ByteStreams.toByteArray(deobIn)); + ClassFile deobTarget = loadClass(DeobTarget.class); + ClassFile deobTarget2 = loadClass(DeobTarget2.class); ClassGroup deob = new ClassGroup(); deob.addClass(deobTarget); + deob.addClass(deobTarget2); - InputStream vanillaIn = getClass().getResourceAsStream("VanillaTarget.class"); - ClassFile vanillaTarget = JarUtil.loadClass(ByteStreams.toByteArray(vanillaIn)); + ClassFile vanillaTarget = loadClass(VanillaTarget.class); + ClassFile vanillaTarget2 = loadClass(VanillaTarget2.class); ClassGroup vanilla = new ClassGroup(); vanilla.addClass(vanillaTarget); + vanilla.addClass(vanillaTarget2); Map, List> mixinClasses = new HashMap<>(); mixinClasses.put(() -> loadClass(Source.class), Collections.singletonList(vanillaTarget)); mixinClasses.put(() -> loadClass(Source2.class), Collections.singletonList(vanillaTarget)); + mixinClasses.put(() -> loadClass(Source3.class), List.of(vanillaTarget, vanillaTarget2)); TestInjection inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi()); + inject.initToVanilla(); new MixinInjector(inject).inject(mixinClasses); // Check if "foo" has been injected @@ -203,12 +247,7 @@ public class MixinInjectorTest return false; } - if (!field.getName().equals("ob_foo4")) - { - return false; - } - - return true; + return field.getName().equals("ob_foo4"); }) .count(), 1); @@ -233,12 +272,7 @@ public class MixinInjectorTest return false; } - if (!method.getName().equals("copy$foo3")) - { - return false; - } - - return true; + return method.getName().equals("copy$foo3"); }) .count(), 1); @@ -267,14 +301,11 @@ public class MixinInjectorTest return false; } - if (!field.getName().equals("foo")) - { - return false; - } - - return true; + return field.getName().equals("foo"); }) .count(), 1); + + assertTrue(fieldExists(List.of(vanillaTarget, vanillaTarget2), "injectMe")); } private boolean getStaticHasGetter(Method ob_foo3, String gottenField) @@ -283,12 +314,25 @@ public class MixinInjectorTest Instruction i; while (it.hasNext() && !((i = it.next()) instanceof GetStatic && - ((GetStatic) i).getField().getName().equals(gottenField))); + ((GetStatic) i).getField().getName().equals(gottenField))) ; return (i = it.next()) instanceof LDC && - ((LDC) i).getConstantAsInt() == 1157381415 && - it.next() instanceof IMul; + ((LDC) i).getConstantAsInt() == 1157381415 && + it.next() instanceof IMul; + } + + private boolean fieldExists(List classes, String name) + { + for (ClassFile c : classes) + { + if (c.findField(name) == null) + { + return false; + } + } + + return true; } private static ClassFile loadClass(Class clazz) diff --git a/src/test/java/com/openosrs/injector/injectors/raw/DrawAfterWidgetsTest.java b/src/test/java/com/openosrs/injector/injectors/raw/DrawAfterWidgetsTest.java index 6b905d5..aeb2f2f 100644 --- a/src/test/java/com/openosrs/injector/injectors/raw/DrawAfterWidgetsTest.java +++ b/src/test/java/com/openosrs/injector/injectors/raw/DrawAfterWidgetsTest.java @@ -30,6 +30,10 @@ import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.rsapi.RSApi; import net.runelite.asm.ClassFile; import net.runelite.asm.ClassGroup; +import net.runelite.asm.Field; +import net.runelite.asm.Type; +import static net.runelite.deob.DeobAnnotations.OBFUSCATED_NAME; +import static net.runelite.deob.DeobAnnotations.OBFUSCATED_SIGNATURE; import net.runelite.deob.util.JarUtil; import org.junit.Test; @@ -55,6 +59,8 @@ public class DrawAfterWidgetsTest vanilla.addClass(obRasterizer); InjectData inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi()); + addPhonyFields(deob, vanilla); + inject.initToVanilla(); new DrawAfterWidgets(inject).inject(); } @@ -78,6 +84,24 @@ public class DrawAfterWidgetsTest vanilla.addClass(obRasterizer); InjectData inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi()); + addPhonyFields(deob, vanilla); + inject.initToVanilla(); new DrawAfterWidgets(inject).inject(); } + + private static void addPhonyFields(ClassGroup deob, ClassGroup vanilla) + { + final ClassFile d = deob.findClass("Client"); + final ClassFile v = vanilla.findClass("client"); + final Field clientD = new Field(d, "client", new Type("LClient;")); + clientD.findAnnotation(OBFUSCATED_NAME, true).setElement("obclient"); + clientD.findAnnotation(OBFUSCATED_SIGNATURE, true).setElement("descriptor", "Lclient;"); + clientD.setStatic(); + d.addField(clientD); + final Field clientV = new Field(v, "obclient", new Type("Lclient;")); + clientV.setStatic(); + v.addField(clientV); + final Field callbacks = new Field(v, "callbacks", new Type("LCallbacks;")); + v.addField(callbacks); + } } diff --git a/src/test/java/com/openosrs/injector/injectors/raw/DrawMenuTest.java b/src/test/java/com/openosrs/injector/injectors/raw/DrawMenuTest.java index 4029395..eeb5255 100644 --- a/src/test/java/com/openosrs/injector/injectors/raw/DrawMenuTest.java +++ b/src/test/java/com/openosrs/injector/injectors/raw/DrawMenuTest.java @@ -13,6 +13,8 @@ import com.openosrs.injector.injection.InjectData; import com.openosrs.injector.rsapi.RSApi; import net.runelite.asm.ClassFile; import net.runelite.asm.ClassGroup; +import net.runelite.asm.Method; +import net.runelite.asm.signature.Signature; import net.runelite.deob.util.JarUtil; import org.junit.Test; @@ -32,6 +34,9 @@ public class DrawMenuTest InjectData inject = new TestInjection(van, deob, new ClassGroup(), new RSApi()); + addPhonyMethod(van); + + inject.initToVanilla(); new DrawMenu(inject).inject(); } @@ -49,6 +54,18 @@ public class DrawMenuTest InjectData inject = new TestInjection(van, deob, new ClassGroup(), new RSApi()); + addPhonyMethod(van); + + inject.initToVanilla(); new DrawMenu(inject).inject(); } + + private void addPhonyMethod(ClassGroup vanilla) + { + final ClassFile c = vanilla.findClass("client"); + + final Method clientM = new Method(c, "drawMenu", new Signature("()Z")); + clientM.setStatic(true); + c.addMethod(clientM); + } } diff --git a/src/test/java/com/openosrs/injector/transformers/SourceChangerTest.java b/src/test/java/com/openosrs/injector/transformers/SourceChangerTest.java index 18a1fcc..65085e9 100644 --- a/src/test/java/com/openosrs/injector/transformers/SourceChangerTest.java +++ b/src/test/java/com/openosrs/injector/transformers/SourceChangerTest.java @@ -36,9 +36,11 @@ public class SourceChangerTest new ClassReader(PACKAGE + "OldName").accept(vann, ClassReader.SKIP_FRAMES); new SourceChanger( - new InjectData(new ClassGroup(), new ClassGroup(), null, null) { + new InjectData(new ClassGroup(), new ClassGroup(), null, null) + { public void runChildInjector(Injector injector) throws InjectException - {} + { + } @Override public void forEachPair(BiConsumer consumer)