Compare commits

...

10 Commits

Author SHA1 Message Date
Owain van Brakel
ccb7c86741 project(gradle): Bump version 2022-06-30 21:51:42 +02:00
Owain van Brakel
dcfe05d2af project: Make it possible to skip injectors 2022-06-30 21:51:26 +02:00
Owain van Brakel
318b03ec58 project: Bump version 2022-06-23 18:50:23 +02:00
Owain van Brakel
e7dc8fd2c9 project: Update injector 2022-06-23 18:26:28 +02:00
ThatGamerBlue
50f415e8ba Merge pull request #12 from open-osrs/revert-11-no-export
revert: #11 injectors add CreateAnnotations
2020-10-27 12:08:28 +01:00
ThatGamerBlue
313bee84e6 Update build.gradle.kts 2020-10-27 12:07:23 +01:00
ThatGamerBlue
f714312bcc Revert "injectors: add CreateAnnotations" 2020-10-27 12:06:54 +01:00
Tyler Bochard
91972a7ed3 Merge pull request #11 from open-osrs/no-export
injectors: add CreateAnnotations
2020-10-04 21:42:39 -04:00
therealnull
a1c9346233 injectors: also handle Implements 2020-10-04 21:07:33 -04:00
therealnull
c984198dd2 injectors: add CreateAnnotations 2020-10-04 20:35:58 -04:00
47 changed files with 1780 additions and 309 deletions

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/Lucwousin>
* 2022, Owain van Brakel <https://github.com/Owain94>
* 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.4.0"
val oprsver = "4.31.1"
group = "com.openosrs"
version = "1.1.5"
version = "2.0.2"
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<JavaPluginConvention> {
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

View File

@@ -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

View File

@@ -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<Signature> 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<Signature> 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
*
* <p>
* 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<RSApiClass> 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<Type> 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.
*
* <p>
* If the annotation doesn't exist return the current name instead.
*/
static <T extends Annotated & Named> 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<Type> findArgs(final String str, final List<Type> 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);
}
}

View File

@@ -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,38 @@ 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 static String skips = "";
public Injection(File vanilla, File rsclient, File mixins, FileTree rsapi) throws IOException
public Injection(File vanilla, File rsclient, File mixins, FileTree rsapi, boolean development, String skip)
{
super(
JarUtil.loadJar(vanilla),
JarUtil.loadJar(rsclient),
JarUtil.loadJar(mixins),
load(vanilla),
load(rsclient),
load(mixins),
new RSApi(rsapi)
);
Injection.development = development;
Injection.skips = skip;
}
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 +92,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 +105,63 @@ 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);
if (injector.shouldRun())
{
injector.start();
injector.start();
injector.inject();
injector.inject();
String completionMsg = injector.getCompletionMsg();
log.lifecycle("{} {}", name, injector.getCompletionMsg());
if (completionMsg != null)
{
log.lifecycle("{} {}", name, completionMsg);
}
}
if (injector instanceof Validator)
{
validate((Validator) injector);
}
}
private void validate(Validator validator)
@@ -121,8 +178,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());

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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<ClassFile, ClassFile> toVanilla;
@Getter
private final Map<ClassFile, ClassFile> toVanilla = new HashMap<>();
/**
* Strings -> Deobfuscated ClassFiles
@@ -52,26 +62,15 @@ public abstract class InjectData
* - Obfuscated name
* - RSApi implementing name
*/
private final Map<String, ClassFile> 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<String, ClassFile> toDeob = new HashMap<>();
public abstract void runChildInjector(Injector injector);
private Map<ClassFile, ClassFile> initToVanilla()
public void initToVanilla()
{
ImmutableMap.Builder<ClassFile, ClassFile> 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.
*
* <p>
* Key = deobfuscated, Value = vanilla
*/
public void forEachPair(BiConsumer<ClassFile, ClassFile> action)

View File

@@ -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);
}

View File

@@ -3,11 +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.injectors;
import com.google.common.base.Stopwatch;
import com.openosrs.injector.Injection;
import com.openosrs.injector.injection.InjectData;
import lombok.RequiredArgsConstructor;
import org.gradle.api.logging.Logger;
@@ -25,8 +26,13 @@ public abstract class AbstractInjector implements Injector
stopwatch = Stopwatch.createStarted();
}
public final String getCompletionMsg()
public String getCompletionMsg()
{
return "finished in " + stopwatch.toString();
}
public boolean shouldRun()
{
return !Injection.skips.contains(this.getName());
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/Lucwousin>
* 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 <Adam@sigterm.info>
* 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());
}
}
}

View File

@@ -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 <Adam@sigterm.info>
* 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("<init>", constr);
if (constructor == null)
{
throw new InjectException("Unable to find constructor for " + classToConstruct.getName() + ".<init>" + constr);
}
net.runelite.asm.Method setterMethod = new net.runelite.asm.Method(targetClass, apiMethod.getName(), apiMethod.getType());

View File

@@ -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 <Adam@sigterm.info>
* All rights reserved.
*
@@ -86,7 +86,9 @@ public class InjectHook extends AbstractInjector
public void inject()
{
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> 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<Instruction> 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);

View File

@@ -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 <Adam@sigterm.info>
* All rights reserved.
@@ -65,7 +65,9 @@ public class InjectHookMethod extends AbstractInjector
public void inject()
{
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> 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("<init>"))
{
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())
{

View File

@@ -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;
@@ -33,4 +33,9 @@ public interface Injector extends Named
* Gets a message logged at quiet level when the injector ends
*/
String getCompletionMsg();
/**
* Checks if this injector is in the skip list
*/
boolean shouldRun();
}

View File

@@ -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
);

View File

@@ -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 <Adam@sigterm.info>
* 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<String, Field> injectedFields = new HashMap<>();
private final Map<net.runelite.asm.pool.Field, ShadowField> shadowFields = new HashMap<>();
private int copied = 0, replaced = 0, injected = 0;
@@ -106,6 +107,13 @@ public class MixinInjector extends AbstractInjector
@VisibleForTesting
void inject(Map<Provider<ClassFile>, List<ClassFile>> mixinTargets)
{
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> entry : mixinTargets.entrySet())
{
injectInterfaces(entry.getKey(), entry.getValue());
}
log.info("[INFO] Injected {} interfaces", injectedInterfaces);
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> entry : mixinTargets.entrySet())
{
injectFields(entry.getKey(), entry.getValue());
@@ -136,49 +144,103 @@ public class MixinInjector extends AbstractInjector
{
ImmutableMap.Builder<Provider<ClassFile>, List<ClassFile>> builder = ImmutableMap.builder();
for (ClassFile mixinClass : inject.getMixins())
try
{
final List<ClassFile> ret = getMixins(mixinClass);
builder.put(
(ret.size() > 1 ? mixinProvider(mixinClass) : () -> mixinClass),
ret
);
for (ClassFile mixinClass : inject.getMixins())
{
final List<ClassFile> ret = getMixins(mixinClass);
builder.put(
(ret.size() > 1 ? mixinProvider(mixinClass) : () -> mixinClass),
ret
);
}
}
catch (Exception e)
{
e.printStackTrace();
}
return builder.build();
}
private void injectFields(Provider<ClassFile> mixinProvider, List<ClassFile> targetClasses)
private void injectInterfaces(Provider<ClassFile> mixinProvider, List<ClassFile> 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<Type, Annotation> 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<ClassFile> mixinProvider, List<ClassFile> 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<Type, Annotation> 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<ClassFile> 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 <clinit> already just inject ours, otherwise rename it
// This is always true for <init>
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<ClassFile> 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;

View File

@@ -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 <Adam@sigterm.info>
* 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<RSApiMethod> 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<RSApiMethod> 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<RSApiMethod> 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());

View File

@@ -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<Instruction> 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<Instruction> 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)
{

View File

@@ -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());
}
}

View File

@@ -0,0 +1,307 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* 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<String> RUNELITE_OBJECTS = List.of(
"RuneLiteClanMember",
"RuneLiteIterableLinkDeque",
"RuneLiteIterableNodeDeque",
"RuneLiteIterableNodeHashTable",
"RuneLiteMenuEntry",
"RuneLiteObject"
);
private final Set<Type> 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<Instruction> 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;
}
}
}

View File

@@ -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 <Adam@sigterm.info>
* 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;
}
}

View File

@@ -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<Instruction> 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);

View File

@@ -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("<clinit>");
Instructions ins = clinit.getCode().getInstructions();
ListIterator<Instruction> 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));
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* 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, "<init>", 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(), "<init>", new Signature("()V"))));
ins.addInstruction(new VReturn(ins));
}
}

View File

@@ -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");
}
}
}

View File

@@ -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;
}

View File

@@ -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");
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* 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<Instruction> 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(), "<init>", 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<Instruction> 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(), "<init>", 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<Instruction> 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(), "<init>", new Signature("(L" + linkDequeVanilla.getName() + ";)V"))));
ins.add(new Return(instructions, InstructionType.ARETURN));
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* 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<Instruction> 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(), "<init>", new Signature("(I)V"))));
}
else
{
ins.add(new InvokeSpecial(instructions, new net.runelite.asm.pool.Method(runeliteMenuEntryVanilla.getPoolClass(), "<init>", 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<Instruction> 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;
}
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2021, Owain van Brakel <https://github.com/Owain94>
* 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<Instruction> 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(), "<init>", new Signature("()V"))));
ins.add(new Return(instructions, InstructionType.ARETURN));
}
}

View File

@@ -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.

View File

@@ -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 <Adam@sigterm.info>
* 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()));

View File

@@ -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 <Adam@sigterm.info>
* 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()));

View File

@@ -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 <Adam@sigterm.info>
* 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);
}

View File

@@ -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<RSApiClass>
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<RSApiClass>
final ImmutableMap.Builder<String, RSApiClass> 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<RSApiClass>
return findClass(name) != null;
}
public RSApiClass withInterface(Class interf)
public Set<RSApiClass> withInterface(Class interf)
{
RSApiClass clazz = findClass(interf.getName());
if (clazz != null)
return clazz;
Set<RSApiClass> classes = new HashSet<>();
for (RSApiClass apiC : this)
{
if (apiC.getInterfaces().contains(interf))
return apiC;
{
classes.add(apiC);
}
}
return null;
return classes;
}
@NotNull

View File

@@ -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<RSApiMethod>
for (RSApiMethod method : this)
{
if (method.isSynthetic())
{
continue;
}
if (method.findAnnotation(CONSTRUCT) != null)
{
@@ -56,10 +58,12 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
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<RSApiMethod>
{
List<RSApiMethod> imported = imports.get(str);
if (imported == null)
{
return;
}
to.addAll(imported);
}
@@ -95,7 +101,9 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, ThatGamerBlue <thatgamerblue@gmail.com>
* 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("<init>"))
{
continue;
}
fixTryCatch(method);
}
}
private void fixTryCatch(Method method)
{
Code code = method.getCode();
List<Exception> 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);
}
}

View File

@@ -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;

View File

@@ -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,18 @@ 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,
if (extension.skip.isPresent) extension.skip.get() else ""
)
injector.inject()
injector.save(output.get().asFile)
injector.hash(hash.get().asFile, vanilla)
}
}

View File

@@ -8,17 +8,27 @@
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<Boolean>
@get:Input
@get:Optional
val skip: Property<String>
@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
}

View File

@@ -10,19 +10,27 @@ package com.openosrs.injector
import org.gradle.api.Plugin
import org.gradle.api.Project
class InjectPlugin: Plugin<Project> {
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<Project> {
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)
}}
}
}

View File

@@ -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<Provider<ClassFile>, List<ClassFile>> 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<ClassFile> classes, String name)
{
for (ClassFile c : classes)
{
if (c.findField(name) == null)
{
return false;
}
}
return true;
}
private static ClassFile loadClass(Class<?> clazz)

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<ClassFile, ClassFile> consumer)