A rewrite + RDI
This commit is contained in:
parent
ee061a6204
commit
1ec1b3daf1
25 changed files with 729 additions and 1237 deletions
|
@ -34,10 +34,10 @@ Arguments are formatted the same way as in Mojang launch wrapper. <br>
|
|||
For example: `--username DemoUser --width 1280 --height 720 --title "Minecraft Demo" --demo`
|
||||
|
||||
Arguments may be as follows:
|
||||
- `awtFrame` - disable AWT patches (Does not work at that moment)
|
||||
- `awtFrame` - disable LWJGL frame patch and use AWT (Does not work at that moment)
|
||||
- `isom` - Launch IsomPreviewApplet
|
||||
- `forceVsync` - Launch the game with VSync enabled
|
||||
- `forceResizable` - Early Indev and Classic are don't properly update viewport, so the wrapper disables frame resizing. To enable it anyway use this argument
|
||||
- `forceResizable` - Early Indev and Classic don't properly update viewport, so the wrapper disables frame resizing. To enable it anyway use this argument
|
||||
- `skinProxy` - **classic** / **pre-b1.9-pre4** / **pre-1.8** / **default** - convert the skin to one of these specified formats
|
||||
- **classic** - flatten all skin layers, flip bottom textures and crop to 64x32
|
||||
- **pre-b1.9-pre4** - flip bottom textures and crop to 64x32
|
||||
|
@ -46,6 +46,7 @@ Arguments may be as follows:
|
|||
- `resourcesProxyPort` - Betacraft proxy port for sound resources
|
||||
- `serverSHA1` - Compare minecraft_server.jar in .minecraft/server against this hash
|
||||
- `serverURL` - URL to download the server from if the hash is mismatched or the jar is missing
|
||||
- `icon` - List of path of icon graphics separated by `;`
|
||||
- `icon` - List of paths of icon PNGs separated by `;`
|
||||
- `title` - The display title
|
||||
- \+ any Minecraft launch argument or applet parameter
|
||||
- `oneSixFlag` - Toggles notice about the release of 1.6 in 1.5.2
|
||||
- \+ any [Minecraft launch arguments](https://wiki.vg/Launching_the_game#Game_Arguments) or applet parameters
|
|
@ -17,6 +17,7 @@ version = '1.0'
|
|||
sourceCompatibility = 1.5
|
||||
|
||||
dependencies {
|
||||
implementation 'org.mcphackers.rdi:rdi:1.0'
|
||||
implementation 'org.lwjgl.lwjgl:lwjgl:2.9.4'
|
||||
implementation 'org.lwjgl.lwjgl:lwjgl_util:2.9.4'
|
||||
implementation 'org.ow2.asm:asm:9.3'
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package org.mcphackers.launchwrapper;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.mcphackers.launchwrapper.inject.Inject;
|
||||
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
||||
import org.mcphackers.launchwrapper.tweak.Tweak;
|
||||
|
||||
public class Launch {
|
||||
|
||||
private static final LaunchClassLoader CLASS_LOADER = LaunchClassLoader.instantiate();
|
||||
public static Launch INSTANCE;
|
||||
|
||||
/**
|
||||
* Class loader where overwritten classes are stored
|
||||
*/
|
||||
public static final LaunchClassLoader CLASS_LOADER = LaunchClassLoader.instantiate();
|
||||
static {
|
||||
CLASS_LOADER.addException(Launch.class);
|
||||
CLASS_LOADER.addException(Inject.class);
|
||||
}
|
||||
private static Launch INSTANCE;
|
||||
|
||||
public final LaunchConfig config;
|
||||
|
||||
private Launch(LaunchConfig config) {
|
||||
|
@ -23,8 +28,6 @@ public class Launch {
|
|||
}
|
||||
|
||||
public void launch() {
|
||||
CLASS_LOADER.addException(Launch.class);
|
||||
CLASS_LOADER.addException(Inject.class);
|
||||
Tweak mainTweak = Tweak.get(CLASS_LOADER, config);
|
||||
if(mainTweak == null) {
|
||||
System.err.println("Could not find launch target");
|
||||
|
@ -34,9 +37,13 @@ public class Launch {
|
|||
mainTweak.getLaunchTarget(CLASS_LOADER).launch();
|
||||
}
|
||||
}
|
||||
|
||||
public void setClassPath(URL[] urls) {
|
||||
CLASS_LOADER.setClassPath(urls);
|
||||
|
||||
public static Launch getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static LaunchConfig getConfig() {
|
||||
return INSTANCE.config;
|
||||
}
|
||||
|
||||
public static Launch create(LaunchConfig config) {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package org.mcphackers.launchwrapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mcphackers.launchwrapper.protocol.SkinType;
|
||||
|
||||
public class LaunchConfig {
|
||||
private Map<String, LaunchParameter<?>> parameters = new HashMap<String, LaunchParameter<?>>();
|
||||
|
||||
|
@ -46,12 +50,16 @@ public class LaunchConfig {
|
|||
public LaunchParameterSwitch isom = new LaunchParameterSwitch("isom", false, true);
|
||||
public LaunchParameterSwitch forceVsync = new LaunchParameterSwitch("forceVsync", false, true);
|
||||
public LaunchParameterSwitch forceResizable = new LaunchParameterSwitch("forceResizable", false, true);
|
||||
public LaunchParameterString skinProxy = new LaunchParameterString("skinProxy", "default", true);
|
||||
public LaunchParameterEnum<SkinType> skinProxy = new LaunchParameterEnum<SkinType>("skinProxy", SkinType.DEFAULT, true);
|
||||
public LaunchParameterNumber resourcesProxyPort = new LaunchParameterNumber("resourcesProxyPort", null, true);
|
||||
public LaunchParameterString serverURL = new LaunchParameterString("serverURL", null, true);
|
||||
public LaunchParameterString serverSHA1 = new LaunchParameterString("serverSHA1", null, true);
|
||||
public LaunchParameterFileList icon = new LaunchParameterFileList("icon", null, true);
|
||||
public LaunchParameterString title = new LaunchParameterString("title", null, true);
|
||||
public LaunchParameterSwitch oneSixFlag = new LaunchParameterSwitch("oneSixFlag", false, true);
|
||||
|
||||
public LaunchConfig() {
|
||||
}
|
||||
|
||||
public LaunchConfig(String[] args) {
|
||||
for(int i = 0; i < args.length; i++) {
|
||||
|
@ -63,7 +71,9 @@ public class LaunchConfig {
|
|||
}
|
||||
if(param.isSwitch()) {
|
||||
((LaunchParameterSwitch) param).setFlag();
|
||||
} else if(i + 1 < args.length) {
|
||||
continue;
|
||||
}
|
||||
if(i + 1 < args.length) {
|
||||
try {
|
||||
// TODO better handling for alternative parameter names
|
||||
if(param.equals(session)) {
|
||||
|
@ -146,7 +156,7 @@ public class LaunchConfig {
|
|||
|
||||
public abstract void setString(String argument);
|
||||
|
||||
public abstract Object get();
|
||||
public abstract T get();
|
||||
|
||||
public abstract void set(T value);
|
||||
}
|
||||
|
@ -372,4 +382,52 @@ public class LaunchConfig {
|
|||
}
|
||||
}
|
||||
|
||||
public class LaunchParameterEnum<T extends Enum<T>> extends LaunchParameter<T> {
|
||||
private T value;
|
||||
private Class<T> enumType;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LaunchParameterEnum(String name, Enum<T> defaultValue) {
|
||||
super(name);
|
||||
enumType = defaultValue.getDeclaringClass();
|
||||
value = (T)defaultValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LaunchParameterEnum(String name, Enum<T> defaultValue, boolean wrapper) {
|
||||
super(name, wrapper);
|
||||
enumType = defaultValue.getDeclaringClass();
|
||||
value = (T)defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setString(String argument) {
|
||||
try {
|
||||
Method getEnumFromString = enumType.getDeclaredMethod("getEnum", String.class);
|
||||
if((getEnumFromString.getModifiers() & Modifier.STATIC) != 0) {
|
||||
this.value = (T)getEnumFromString.invoke(null, argument);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
this.value = Enum.valueOf(enumType, argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,19 +18,17 @@ import org.mcphackers.launchwrapper.tweak.AppletWrapper;
|
|||
import org.mcphackers.launchwrapper.util.Util;
|
||||
|
||||
public class Inject {
|
||||
|
||||
private static final Launch LAUNCH = Launch.INSTANCE;
|
||||
private static final BufferedImage DEFAULT_ICON = getIcon();
|
||||
|
||||
public static AppletWrapper getApplet() {
|
||||
return new AppletWrapper(LAUNCH.config.getArgsAsMap());
|
||||
return new AppletWrapper(Launch.getConfig().getArgsAsMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indev load level injection
|
||||
*/
|
||||
public static File getLevelFile(int index) {
|
||||
return new File(LAUNCH.config.gameDir.get(), "levels/level" + index + ".dat");
|
||||
return new File(Launch.getConfig().gameDir.get(), "levels/level" + index + ".dat");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +36,7 @@ public class Inject {
|
|||
*/
|
||||
public static File saveLevel(int index, String levelName) {
|
||||
final int maxLevels = 5;
|
||||
File levels = new File(LAUNCH.config.gameDir.get(), "levels");
|
||||
File levels = new File(Launch.getConfig().gameDir.get(), "levels");
|
||||
File level = new File(levels, "level" + index + ".dat");
|
||||
File levelNames = new File(levels, "levels.txt");
|
||||
String[] lvlNames = new String[maxLevels];
|
||||
|
@ -103,7 +101,7 @@ public class Inject {
|
|||
|
||||
public static ByteBuffer[] loadIcons() {
|
||||
List<ByteBuffer> processedIcons = new ArrayList<ByteBuffer>();
|
||||
for(File icon : LAUNCH.config.icon.get()) {
|
||||
for(File icon : Launch.getConfig().icon.get()) {
|
||||
if(!icon.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package org.mcphackers.launchwrapper.inject;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
public class InjectUtils {
|
||||
|
||||
public static enum Access {
|
||||
PRIVATE, DEFAULT, PROTECTED, PUBLIC;
|
||||
|
||||
public static Access getFromBytecode(int acc) {
|
||||
if((acc & ACC_PRIVATE) == ACC_PRIVATE)
|
||||
return PRIVATE;
|
||||
if((acc & ACC_PROTECTED) == ACC_PROTECTED)
|
||||
return PROTECTED;
|
||||
if((acc & ACC_PUBLIC) == ACC_PUBLIC)
|
||||
return PUBLIC;
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
public int setAccess(int acc) {
|
||||
acc &= ~(ACC_PRIVATE | ACC_PROTECTED | ACC_PUBLIC);
|
||||
acc |= this == PRIVATE ? ACC_PRIVATE : 0;
|
||||
acc |= this == PROTECTED ? ACC_PROTECTED : 0;
|
||||
acc |= this == PUBLIC ? ACC_PUBLIC : 0;
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
public static FieldNode getField(ClassNode node, String name, String desc) {
|
||||
for(FieldNode field : node.fields) {
|
||||
if(field.name.equals(name) && field.desc.equals(desc)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodNode getMethod(ClassNode node, String name, String desc) {
|
||||
for(MethodNode method : node.methods) {
|
||||
if(method.name.equals(name) && method.desc.equals(desc)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<MethodNode> getConstructors(ClassNode node) {
|
||||
List<MethodNode> constructors = new ArrayList<MethodNode>();
|
||||
for(MethodNode method : node.methods) {
|
||||
if(method.name.equals("<init>")) {
|
||||
constructors.add(method);
|
||||
}
|
||||
}
|
||||
return constructors;
|
||||
}
|
||||
|
||||
public static boolean isAbstract(MethodNode method) {
|
||||
return (method.access & ACC_ABSTRACT) != 0;
|
||||
}
|
||||
|
||||
public static boolean isStatic(MethodNode method) {
|
||||
return (method.access & ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
public static boolean isStatic(FieldNode field) {
|
||||
return (field.access & ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,130 +3,11 @@ package org.mcphackers.launchwrapper.inject;
|
|||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.tree.AbstractInsnNode.*;
|
||||
|
||||
import org.mcphackers.rdi.util.OPHelper;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
public class InsnHelper {
|
||||
|
||||
public static AbstractInsnNode[] fill(AbstractInsnNode insn, int size) {
|
||||
AbstractInsnNode[] arr = new AbstractInsnNode[size];
|
||||
int i = 0;
|
||||
while(insn != null && i < size) {
|
||||
arr[i] = insn;
|
||||
insn = nextInsn(insn);
|
||||
i++;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode[] fillBackwards(AbstractInsnNode insn, int size) {
|
||||
AbstractInsnNode[] arr = new AbstractInsnNode[size];
|
||||
int i = size - 1;
|
||||
while(insn != null && i >= 0) {
|
||||
arr[i] = insn;
|
||||
insn = previousInsn(insn);
|
||||
i--;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static InsnList copyInsns(InsnList insnList) {
|
||||
MethodNode mv = new MethodNode();
|
||||
insnList.accept(mv);
|
||||
return mv.instructions;
|
||||
}
|
||||
|
||||
public static void removeRange(InsnList insns, AbstractInsnNode first, AbstractInsnNode last) {
|
||||
if(first == null || last == null) {
|
||||
return;
|
||||
}
|
||||
AbstractInsnNode next = first;
|
||||
while(next != null && next != last) {
|
||||
AbstractInsnNode forRemoval = next;
|
||||
next = next.getNext();
|
||||
insns.remove(forRemoval);
|
||||
}
|
||||
insns.remove(last);
|
||||
}
|
||||
|
||||
public static void remove(InsnList insns, AbstractInsnNode... toRemove) {
|
||||
for(AbstractInsnNode insn : toRemove) {
|
||||
insns.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode intInsn(int value) {
|
||||
switch(value) {
|
||||
case -1:
|
||||
return new InsnNode(ICONST_M1);
|
||||
case 0:
|
||||
return new InsnNode(ICONST_0);
|
||||
case 1:
|
||||
return new InsnNode(ICONST_1);
|
||||
case 2:
|
||||
return new InsnNode(ICONST_2);
|
||||
case 3:
|
||||
return new InsnNode(ICONST_3);
|
||||
case 4:
|
||||
return new InsnNode(ICONST_4);
|
||||
case 5:
|
||||
return new InsnNode(ICONST_5);
|
||||
default:
|
||||
if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
return new IntInsnNode(SIPUSH, value);
|
||||
}
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode booleanInsn(boolean value) {
|
||||
return new InsnNode(value ? ICONST_1 : ICONST_0);
|
||||
}
|
||||
|
||||
public static AbstractInsnNode floatInsn(float value) {
|
||||
if(value == 0F) {
|
||||
return new InsnNode(FCONST_0);
|
||||
} else if(value == 1F) {
|
||||
return new InsnNode(FCONST_1);
|
||||
} else if(value == 2F) {
|
||||
return new InsnNode(FCONST_2);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode doubleInsn(double value) {
|
||||
if(value == 0D) {
|
||||
return new InsnNode(DCONST_0);
|
||||
} else if(value == 1D) {
|
||||
return new InsnNode(DCONST_1);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static LabelNode labelBefore(AbstractInsnNode current) {
|
||||
if(current != null) {
|
||||
current = current.getPrevious();
|
||||
}
|
||||
while(current != null && current.getType() == LINE) {
|
||||
current = current.getPrevious();
|
||||
}
|
||||
if(current.getType() == LABEL) {
|
||||
return (LabelNode) current;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode nextInsn(AbstractInsnNode current) {
|
||||
while((current = current.getNext()) != null && current.getOpcode() == -1);
|
||||
return current;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode previousInsn(AbstractInsnNode current) {
|
||||
while((current = current.getPrevious()) != null && current.getOpcode() == -1);
|
||||
return current;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode getSuper(AbstractInsnNode first) {
|
||||
AbstractInsnNode insn = first;
|
||||
while(insn != null) {
|
||||
|
@ -142,7 +23,7 @@ public class InsnHelper {
|
|||
public static AbstractInsnNode getLastReturn(AbstractInsnNode last) {
|
||||
AbstractInsnNode insn = last;
|
||||
while(insn != null) {
|
||||
if(isReturn(insn.getOpcode())) {
|
||||
if(OPHelper.isReturn(insn.getOpcode())) {
|
||||
break;
|
||||
}
|
||||
insn = insn.getPrevious();
|
||||
|
@ -150,64 +31,6 @@ public class InsnHelper {
|
|||
return insn;
|
||||
}
|
||||
|
||||
public static boolean isReturn(int opcode) {
|
||||
return opcode == RETURN || opcode == IRETURN || opcode == LRETURN || opcode == FRETURN || opcode == DRETURN || opcode == ARETURN;
|
||||
}
|
||||
|
||||
public static boolean containsInvoke(InsnList insns, MethodInsnNode invoke) {
|
||||
for(AbstractInsnNode insn : insns) {
|
||||
if(insn.getType() == METHOD_INSN) {
|
||||
MethodInsnNode invoke2 = (MethodInsnNode) insn;
|
||||
if(invoke2.getOpcode() == invoke.getOpcode() && invoke2.owner.equals(invoke.owner) && invoke2.name.equals(invoke.name) && invoke2.desc.equals(invoke.desc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void tryCatchAdd(MethodNode method, AbstractInsnNode startInsn, AbstractInsnNode endInsn, String exception) {
|
||||
tryCatchAdd(method, startInsn, endInsn, null, exception);
|
||||
}
|
||||
|
||||
public static void tryCatchAdd(MethodNode method, AbstractInsnNode startInsn, AbstractInsnNode endInsn, InsnList handle, String exception) {
|
||||
InsnList instructions = method.instructions;
|
||||
if(!instructions.contains(startInsn) || !instructions.contains(endInsn)) {
|
||||
throw new IllegalArgumentException("Instruction does not belong to the list");
|
||||
}
|
||||
LabelNode start = new LabelNode();
|
||||
LabelNode end = new LabelNode();
|
||||
LabelNode handler = new LabelNode();
|
||||
LabelNode after = new LabelNode();
|
||||
instructions.insertBefore(startInsn, start);
|
||||
InsnList insert = new InsnList();
|
||||
insert.add(end);
|
||||
insert.add(new JumpInsnNode(GOTO, after));
|
||||
insert.add(handler);
|
||||
if(handle == null) {
|
||||
insert.add(new InsnNode(POP));
|
||||
} else {
|
||||
insert.add(handle);
|
||||
}
|
||||
insert.add(after);
|
||||
instructions.insert(endInsn, insert);
|
||||
int index = 0; // FIXME Shouldn't be 0 if there are try/catch blocks within the range
|
||||
method.tryCatchBlocks.add(index, new TryCatchBlockNode(start, end, handler, exception));
|
||||
}
|
||||
|
||||
public static int getFreeIndex(InsnList instructions) {
|
||||
int lastFree = 1;
|
||||
AbstractInsnNode insn = instructions.getFirst();
|
||||
while(insn != null) {
|
||||
if(insn.getType() == VAR_INSN) {
|
||||
VarInsnNode var = (VarInsnNode) insn;
|
||||
lastFree = Math.max(lastFree, var.var + 1);
|
||||
}
|
||||
insn = insn.getNext();
|
||||
}
|
||||
return lastFree;
|
||||
}
|
||||
|
||||
public static boolean compareInsn(AbstractInsnNode insn, int opcode, Object... compare) {
|
||||
if(insn == null) {
|
||||
return false;
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.mcphackers.launchwrapper.loader;
|
|||
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -17,8 +19,8 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
|
||||
import org.mcphackers.launchwrapper.inject.InjectUtils;
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
import org.mcphackers.rdi.util.NodeHelper;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
@ -37,6 +39,7 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
private Map<String, ClassNode> overridenClasses = new HashMap<String, ClassNode>();
|
||||
/** Keys should contain slashes */
|
||||
private Map<String, ClassNode> classNodeCache = new HashMap<String, ClassNode>();
|
||||
private File debugOutput;
|
||||
|
||||
public LaunchClassLoader(ClassLoader parent) {
|
||||
super(null);
|
||||
|
@ -46,6 +49,10 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
public void setClassPath(URL[] urls) {
|
||||
this.parent = new URLClassLoader(urls, parent);
|
||||
}
|
||||
|
||||
public void setDebugOutput(File directory) {
|
||||
debugOutput = directory;
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
return parent.getResource(name);
|
||||
|
@ -56,9 +63,6 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
}
|
||||
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
if(!classNodeCache.isEmpty()) {
|
||||
classNodeCache.clear();
|
||||
}
|
||||
if(name.startsWith("java.")) {
|
||||
return parent.loadClass(name);
|
||||
}
|
||||
|
@ -75,6 +79,7 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
}
|
||||
|
||||
public void invokeMain(String launchTarget, String... args) {
|
||||
classNodeCache.clear();
|
||||
try {
|
||||
Class<?> mainClass = findClass(launchTarget);
|
||||
mainClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) args);
|
||||
|
@ -91,7 +96,6 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
byte[] classData = getClassAsBytes(name);
|
||||
if(classData == null)
|
||||
return null;
|
||||
System.out.println(name);
|
||||
Class<?> definedClass = defineClass(name, classData, 0, classData.length, getProtectionDomain(name));
|
||||
classes.put(name, definedClass);
|
||||
return definedClass;
|
||||
|
@ -116,10 +120,29 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
public void overrideClass(ClassNode node) {
|
||||
if(node == null)
|
||||
return;
|
||||
saveDebugClass(node);
|
||||
overridenClasses.put(className(node.name), node);
|
||||
classNodeCache.put(node.name, node);
|
||||
}
|
||||
|
||||
private void saveDebugClass(ClassNode node) {
|
||||
if(debugOutput == null) {
|
||||
return;
|
||||
}
|
||||
ClassWriter writer = new SafeClassWriter(parent, COMPUTE_MAXS);
|
||||
node.accept(writer);
|
||||
byte[] classData = writer.toByteArray();
|
||||
File cls = new File(debugOutput, node.name + ".class");
|
||||
cls.getParentFile().mkdirs();
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(cls);
|
||||
fos.write(classData);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* can be specified either as net.minecraft.client.Minecraft or as
|
||||
|
@ -142,11 +165,11 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
}
|
||||
|
||||
public FieldNode getField(String owner, String name, String desc) {
|
||||
return InjectUtils.getField(getClass(owner), name, desc);
|
||||
return NodeHelper.getField(getClass(owner), name, desc);
|
||||
}
|
||||
|
||||
public MethodNode getMethod(String owner, String name, String desc) {
|
||||
return InjectUtils.getMethod(getClass(owner), name, desc);
|
||||
return NodeHelper.getMethod(getClass(owner), name, desc);
|
||||
}
|
||||
|
||||
private Class<?> redefineClass(ClassNode node) {
|
||||
|
|
|
@ -4,6 +4,8 @@ import java.io.IOException;
|
|||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.mcphackers.launchwrapper.Launch;
|
||||
|
||||
public class LegacyURLStreamHandler extends URLStreamHandlerProxy {
|
||||
|
||||
private SkinType skins;
|
||||
|
@ -21,54 +23,32 @@ public class LegacyURLStreamHandler extends URLStreamHandlerProxy {
|
|||
if(host.endsWith(".minecraft.net") || host.equals("s3.amazonaws.com")) {
|
||||
if(path.equals("/game/joinserver.jsp"))
|
||||
return new BasicResponseURLConnection(url, "ok");
|
||||
else if(path.equals("/login/session.jsp"))
|
||||
if(path.equals("/login/session.jsp"))
|
||||
return new BasicResponseURLConnection(url, "ok");
|
||||
else if(host.equals("login.minecraft.net") && path.equals("/session"))
|
||||
if(host.equals("login.minecraft.net") && path.equals("/session"))
|
||||
return new BasicResponseURLConnection(url, "ok");
|
||||
else if(path.equals("/game/"))
|
||||
if(path.equals("/game/"))
|
||||
return new BasicResponseURLConnection(url, "42069");
|
||||
else if(path.equals("/haspaid.jsp"))
|
||||
if(path.equals("/haspaid.jsp"))
|
||||
return new BasicResponseURLConnection(url, "true");
|
||||
else if(path.contains("/level/save.html"))
|
||||
if(path.contains("/level/save.html"))
|
||||
return new SaveLevelURLConnection(url);
|
||||
else if(path.contains("/level/load.html"))
|
||||
if(path.contains("/level/load.html"))
|
||||
return new LoadLevelURLConnection(url);
|
||||
else if(path.equals("/listmaps.jsp"))
|
||||
if(path.equals("/listmaps.jsp"))
|
||||
return new ListLevelsURLConnection(url);
|
||||
else if(path.startsWith("/MinecraftResources/") || path.startsWith("/resources/"))
|
||||
if(path.startsWith("/MinecraftResources/") || path.startsWith("/resources/"))
|
||||
return new ResourceIndexURLConnection(url, port);
|
||||
else if(path.startsWith("/MinecraftSkins/") || path.startsWith("/skin/") || path.startsWith("/MinecraftCloaks/") || path.startsWith("/cloak/"))
|
||||
if(path.startsWith("/MinecraftSkins/") || path.startsWith("/skin/") || path.startsWith("/MinecraftCloaks/") || path.startsWith("/cloak/"))
|
||||
return new SkinURLConnection(url, skins);
|
||||
else if(host.equals("assets.minecraft.net") && path.equals("/1_6_has_been_released.flag")) {
|
||||
return new BasicResponseURLConnection(url, "");
|
||||
// return new BasicResponseURLConnection(url, "https://web.archive.org/web/20130702232237if_/https://mojang.com/2013/07/minecraft-the-horse-update/");
|
||||
}
|
||||
if(host.equals("assets.minecraft.net") && path.equals("/1_6_has_been_released.flag"))
|
||||
if(Launch.getConfig().oneSixFlag.get())
|
||||
return new BasicResponseURLConnection(url, "https://web.archive.org/web/20130702232237if_/https://mojang.com/2013/07/minecraft-the-horse-update/");
|
||||
else
|
||||
return new BasicResponseURLConnection(url, "");
|
||||
if(host.equals("mcoapi.minecraft.net") && path.equals("/mco/available"))
|
||||
return new BasicResponseURLConnection(url, "true");
|
||||
}
|
||||
return super.openConnection(url);
|
||||
}
|
||||
|
||||
public enum SkinType {
|
||||
CLASSIC("classic"),
|
||||
PRE_B1_9("pre-b1.9-pre4"),
|
||||
PRE_1_8("pre-1.8"),
|
||||
DEFAULT("default");
|
||||
|
||||
private String name;
|
||||
|
||||
private SkinType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static SkinType get(String name) {
|
||||
for(SkinType skinType : values()) {
|
||||
if(skinType.name.equals(name)) {
|
||||
return skinType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ResourceIndex {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class ListLevelsURLConnection extends URLConnection {
|
|||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
File levels = new File(Launch.INSTANCE.config.gameDir.get(), "levels");
|
||||
File levels = new File(Launch.getConfig().gameDir.get(), "levels");
|
||||
if(!levels.exists())
|
||||
levels.mkdirs();
|
||||
File levelNames = new File(levels, "levels.txt");
|
||||
|
@ -47,6 +47,9 @@ public class ListLevelsURLConnection extends URLConnection {
|
|||
levelsChanged = true;
|
||||
lvlNames[i] = EMPTY_LEVEL;
|
||||
}
|
||||
} else if(lvlNames[i].equals(EMPTY_LEVEL)) {
|
||||
levelsChanged = true;
|
||||
lvlNames[i] = "Unnamed level";
|
||||
}
|
||||
}
|
||||
String lvls = "";
|
||||
|
|
|
@ -48,7 +48,7 @@ public class LoadLevelURLConnection extends HttpURLConnection {
|
|||
throw new MalformedURLException("Query is missing \"id\" parameter");
|
||||
}
|
||||
int levelId = Integer.parseInt(query.get("id"));
|
||||
File levels = new File(Launch.INSTANCE.config.gameDir.get(), "levels");
|
||||
File levels = new File(Launch.getConfig().gameDir.get(), "levels");
|
||||
File level = new File(levels, "level" + levelId + ".dat");
|
||||
if(!level.exists()) {
|
||||
throw new FileNotFoundException("Level doesn't exist");
|
||||
|
|
|
@ -5,13 +5,9 @@ import java.io.InputStream;
|
|||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler.ResourceIndex;
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
|
||||
public class ResourceIndexURLConnection extends URLConnection {
|
||||
private static final String[] RESOURCES = new String[] { "/resources/", "/MinecraftResources/" };
|
||||
|
||||
ResourceIndex index = null;
|
||||
int port = -1;
|
||||
|
||||
public ResourceIndexURLConnection(URL url, int port) {
|
||||
|
@ -25,13 +21,7 @@ public class ResourceIndexURLConnection extends URLConnection {
|
|||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
String path = url.getPath();
|
||||
for(String template : RESOURCES) {
|
||||
if(path.startsWith(template)) {
|
||||
return Util.replaceHost(url, "betacraft.uk", getPort()).openStream();
|
||||
}
|
||||
}
|
||||
return url.openStream();
|
||||
return Util.replaceHost(url, "betacraft.uk", getPort()).openStream();
|
||||
}
|
||||
|
||||
protected int getPort() {
|
||||
|
|
|
@ -20,7 +20,6 @@ import static org.mcphackers.launchwrapper.protocol.ListLevelsURLConnection.EMPT
|
|||
public class SaveLevelURLConnection extends HttpURLConnection {
|
||||
|
||||
ByteArrayOutputStream levelOutput = new ByteArrayOutputStream();
|
||||
Exception exception;
|
||||
|
||||
public SaveLevelURLConnection(URL url) {
|
||||
super(url);
|
||||
|
@ -41,8 +40,9 @@ public class SaveLevelURLConnection extends HttpURLConnection {
|
|||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
Exception exception = null;
|
||||
try {
|
||||
File levels = new File(Launch.INSTANCE.config.gameDir.get(), "levels");
|
||||
File levels = new File(Launch.getConfig().gameDir.get(), "levels");
|
||||
byte[] data = levelOutput.toByteArray();
|
||||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
|
||||
String username = in.readUTF();
|
||||
|
|
|
@ -6,7 +6,6 @@ import java.net.URL;
|
|||
import java.util.HashMap;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler.SkinType;
|
||||
import org.mcphackers.launchwrapper.util.Base64;
|
||||
import org.mcphackers.launchwrapper.util.ImageUtils;
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.mcphackers.launchwrapper.protocol;
|
||||
|
||||
public enum SkinType {
|
||||
CLASSIC("classic"),
|
||||
PRE_B1_9("pre-b1.9-pre4"),
|
||||
PRE_1_8("pre-1.8"),
|
||||
DEFAULT("default");
|
||||
|
||||
private String name;
|
||||
|
||||
private SkinType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static SkinType getEnum(String name) {
|
||||
for(SkinType skinType : values()) {
|
||||
if(skinType.name.equals(name)) {
|
||||
return skinType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ import java.net.HttpURLConnection;
|
|||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler.SkinType;
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
|
||||
public class SkinURLConnection extends HttpURLConnection {
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.util.HashMap;
|
|||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
import org.mcphackers.launchwrapper.util.UnsafeUtils;
|
||||
|
||||
public abstract class URLStreamHandlerProxy extends URLStreamHandler {
|
||||
private static final Map<String, URLStreamHandler> DEFAULT_HANDLERS = new HashMap<String, URLStreamHandler>();
|
||||
|
@ -34,7 +34,7 @@ public abstract class URLStreamHandlerProxy extends URLStreamHandler {
|
|||
handler.parent = getURLStreamHandler(protocol);
|
||||
try {
|
||||
Field handlersField = URL.class.getDeclaredField("handlers");
|
||||
Hashtable handlers = (Hashtable) Util.getStaticObjectUnsafe(handlersField);
|
||||
Hashtable handlers = (Hashtable) UnsafeUtils.getStaticObject(handlersField);
|
||||
handlers.put(protocol, handler);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -49,7 +49,7 @@ public abstract class URLStreamHandlerProxy extends URLStreamHandler {
|
|||
try {
|
||||
URL url = new URL(protocol + ":");
|
||||
Field handlerField = URL.class.getDeclaredField("handler");
|
||||
handler = (URLStreamHandler) Util.getObjectUnsafe(url, handlerField);
|
||||
handler = (URLStreamHandler) UnsafeUtils.getObject(url, handlerField);
|
||||
DEFAULT_HANDLERS.put(protocol, handler);
|
||||
return handler;
|
||||
} catch (Exception e) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,7 @@ import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
|||
|
||||
public abstract class Tweak {
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
protected ClassNodeSource source;
|
||||
|
||||
|
@ -39,7 +39,7 @@ public abstract class Tweak {
|
|||
return null; // Tweak not found
|
||||
}
|
||||
|
||||
protected void debugInfo(String msg) {
|
||||
protected void tweakInfo(String msg) {
|
||||
if(DEBUG)
|
||||
System.out.println("TWEAK: " + msg);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mcphackers.launchwrapper.tweak;
|
||||
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.*;
|
||||
import static org.mcphackers.rdi.util.InsnHelper.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -12,13 +13,19 @@ import org.mcphackers.launchwrapper.LaunchConfig.LaunchParameter;
|
|||
import org.mcphackers.launchwrapper.LaunchTarget;
|
||||
import org.mcphackers.launchwrapper.MainLaunchTarget;
|
||||
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
|
||||
import org.mcphackers.launchwrapper.inject.InjectUtils;
|
||||
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler;
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler.SkinType;
|
||||
import org.mcphackers.launchwrapper.protocol.URLStreamHandlerProxy;
|
||||
import org.mcphackers.rdi.util.IdentifyCall;
|
||||
import org.objectweb.asm.tree.*;
|
||||
import org.mcphackers.rdi.util.NodeHelper;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
|
||||
public class VanillaTweak extends Tweak {
|
||||
public static final String MAIN_CLASS = "net/minecraft/client/main/Main";
|
||||
|
@ -36,7 +43,7 @@ public class VanillaTweak extends Tweak {
|
|||
|
||||
public boolean transform() {
|
||||
minecraftMain = source.getClass(MAIN_CLASS);
|
||||
MethodNode main = InjectUtils.getMethod(minecraftMain, "main", "([Ljava/lang/String;)V");
|
||||
MethodNode main = NodeHelper.getMethod(minecraftMain, "main", "([Ljava/lang/String;)V");
|
||||
if(main == null)
|
||||
return false;
|
||||
AbstractInsnNode insn = main.instructions.getLast();
|
||||
|
@ -45,8 +52,8 @@ public class VanillaTweak extends Tweak {
|
|||
if(insn.getOpcode() == INVOKEVIRTUAL) {
|
||||
MethodInsnNode invoke = (MethodInsnNode) insn;
|
||||
minecraft = source.getClass(invoke.owner);
|
||||
run = InjectUtils.getMethod(minecraft, invoke.name, invoke.desc);
|
||||
debugInfo(minecraft.name + "." + invoke.name + invoke.desc + " is Minecraft.run()");
|
||||
run = NodeHelper.getMethod(minecraft, invoke.name, invoke.desc);
|
||||
tweakInfo(minecraft.name + "." + invoke.name + invoke.desc + " is Minecraft.run()");
|
||||
break;
|
||||
}
|
||||
insn = previousInsn(insn);
|
||||
|
@ -77,12 +84,8 @@ public class VanillaTweak extends Tweak {
|
|||
boolean fixedTitle = replaceTitle(init);
|
||||
boolean fixedIcon = replaceIcon(init);
|
||||
for(MethodNode m : minecraft.methods) {
|
||||
if(!fixedTitle) {
|
||||
fixedTitle = replaceTitle(m);
|
||||
}
|
||||
if(!fixedIcon) {
|
||||
fixedIcon = replaceIcon(m);
|
||||
}
|
||||
fixedTitle = fixedTitle || replaceTitle(m);
|
||||
fixedIcon = fixedIcon || replaceIcon(m);
|
||||
if(fixedTitle && fixedIcon) {
|
||||
break;
|
||||
}
|
||||
|
@ -95,11 +98,12 @@ public class VanillaTweak extends Tweak {
|
|||
AbstractInsnNode insn = m.instructions.getFirst();
|
||||
while(insn != null) {
|
||||
AbstractInsnNode[] insns = fill(insn, 2);
|
||||
if(compareInsn(insns[0], LDC) && compareInsn(insns[1], INVOKESTATIC, "org/lwjgl/opengl/Display", "setTitle", "(Ljava/lang/String;)V")) {
|
||||
if(compareInsn(insns[0], LDC)
|
||||
&& compareInsn(insns[1], INVOKESTATIC, "org/lwjgl/opengl/Display", "setTitle", "(Ljava/lang/String;)V")) {
|
||||
LdcInsnNode ldc = (LdcInsnNode) insn;
|
||||
if(ldc.cst instanceof String) {
|
||||
if(launch.title.get() != null) {
|
||||
debugInfo("Replaced title");
|
||||
tweakInfo("Replaced title");
|
||||
ldc.cst = launch.title.get();
|
||||
return true;
|
||||
}
|
||||
|
@ -113,14 +117,15 @@ public class VanillaTweak extends Tweak {
|
|||
private boolean replaceIcon(MethodNode m) {
|
||||
AbstractInsnNode insn = m.instructions.getFirst();
|
||||
while(insn != null) {
|
||||
if(launch.icon.get() != null && hasIcon(launch.icon.get()) && compareInsn(insn, INVOKESTATIC, "org/lwjgl/opengl/Display", "setIcon", "([Ljava/nio/ByteBuffer;)I")) {
|
||||
if(launch.icon.get() != null && hasIcon(launch.icon.get())
|
||||
&& compareInsn(insn, INVOKESTATIC, "org/lwjgl/opengl/Display", "setIcon", "([Ljava/nio/ByteBuffer;)I")) {
|
||||
IdentifyCall call = new IdentifyCall((MethodInsnNode) insn);
|
||||
for(AbstractInsnNode[] arg : call.getArguments()) {
|
||||
remove(m.instructions, arg);
|
||||
}
|
||||
MethodInsnNode insert = new MethodInsnNode(INVOKESTATIC, "org/mcphackers/launchwrapper/inject/Inject", "loadIcons", "()[Ljava/nio/ByteBuffer;");
|
||||
m.instructions.insertBefore(insn, insert);
|
||||
debugInfo("Replaced icon");
|
||||
tweakInfo("Replaced icon");
|
||||
return true;
|
||||
}
|
||||
insn = nextInsn(insn);
|
||||
|
@ -142,7 +147,7 @@ public class VanillaTweak extends Tweak {
|
|||
if(insn.getType() == AbstractInsnNode.METHOD_INSN) {
|
||||
MethodInsnNode invoke = (MethodInsnNode) insn;
|
||||
if(invoke.owner.equals(minecraft.name)) {
|
||||
return InjectUtils.getMethod(minecraft, invoke.name, invoke.desc);
|
||||
return NodeHelper.getMethod(minecraft, invoke.name, invoke.desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +159,12 @@ public class VanillaTweak extends Tweak {
|
|||
AbstractInsnNode insn = m.instructions.getFirst();
|
||||
while(insn != null) {
|
||||
AbstractInsnNode[] insns = fill(insn, 6);
|
||||
if(compareInsn(insns[0], NEW, "java/io/File") && compareInsn(insns[1], DUP) && compareInsn(insns[2], ALOAD, 0) && compareInsn(insns[3], GETFIELD, minecraft.name, null, "Ljava/io/File;") && compareInsn(insns[4], LDC) && compareInsn(insns[5], INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/io/File;Ljava/lang/String;)V")) {
|
||||
if(compareInsn(insns[0], NEW, "java/io/File")
|
||||
&& compareInsn(insns[1], DUP)
|
||||
&& compareInsn(insns[2], ALOAD, 0)
|
||||
&& compareInsn(insns[3], GETFIELD, minecraft.name, null, "Ljava/io/File;")
|
||||
&& compareInsn(insns[4], LDC)
|
||||
&& compareInsn(insns[5], INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/io/File;Ljava/lang/String;)V")) {
|
||||
LdcInsnNode ldc = (LdcInsnNode) insns[4];
|
||||
String s = (String) ldc.cst;
|
||||
if(s.startsWith("assets/")) {
|
||||
|
@ -173,7 +183,7 @@ public class VanillaTweak extends Tweak {
|
|||
m.instructions.remove(insns[3]);
|
||||
m.instructions.set(insns[5], new MethodInsnNode(INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/lang/String;)V"));
|
||||
}
|
||||
debugInfo("Replaced assets path");
|
||||
tweakInfo("Replaced assets path");
|
||||
}
|
||||
insn = nextInsn(insn);
|
||||
}
|
||||
|
@ -181,7 +191,8 @@ public class VanillaTweak extends Tweak {
|
|||
}
|
||||
|
||||
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
|
||||
URLStreamHandlerProxy.setURLStreamHandler("http", new LegacyURLStreamHandler(SkinType.get(launch.skinProxy.get()), 11707));
|
||||
URLStreamHandlerProxy.setURLStreamHandler("http", new LegacyURLStreamHandler(launch.skinProxy.get(), 11707));
|
||||
URLStreamHandlerProxy.setURLStreamHandler("https", new LegacyURLStreamHandler(launch.skinProxy.get(), 11707));
|
||||
MainLaunchTarget target = new MainLaunchTarget(loader, MAIN_CLASS);
|
||||
target.args = launch.getArgs();
|
||||
return target;
|
||||
|
|
161
src/main/java/org/mcphackers/launchwrapper/util/UnsafeUtils.java
Normal file
161
src/main/java/org/mcphackers/launchwrapper/util/UnsafeUtils.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
package org.mcphackers.launchwrapper.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
public final class UnsafeUtils {
|
||||
private static final Unsafe theUnsafe = getUnsafe();
|
||||
|
||||
private static Unsafe getUnsafe() {
|
||||
try {
|
||||
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||
return unsafe;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Static set
|
||||
|
||||
public static void setStaticBoolean(final Field field, boolean value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putBoolean(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticInt(final Field field, int value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putInt(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticLong(final Field field, long value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putLong(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticFloat(final Field field, float value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putFloat(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticDouble(final Field field, double value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putDouble(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticObject(final Field field, Object value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putObject(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
// Non-static set
|
||||
|
||||
public static void setBoolean(final Object base, final Field field, boolean value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putBoolean(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setInt(final Object base, final Field field, int value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putInt(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setLong(final Object base, final Field field, long value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putLong(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setFloat(final Object base, final Field field, float value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putFloat(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setDouble(final Object base, final Field field, double value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putDouble(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setObject(final Object base, final Field field, Object value) {
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putObject(base, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
// Static get
|
||||
|
||||
public static boolean getStaticBoolean(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getBoolean(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static int getStaticInt(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getInt(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static long getStaticLong(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getLong(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static float getStaticFloat(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getFloat(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static double getStaticDouble(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getDouble(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static Object getStaticObject(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getObject(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
// Non-static get
|
||||
|
||||
public static boolean getBoolean(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getBoolean(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static int getInt(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getInt(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static long getLong(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getLong(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static float getFloat(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getFloat(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static double getDouble(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getDouble(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static Object getObject(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getObject(base, fieldOffset);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
package org.mcphackers.launchwrapper.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
|
@ -10,47 +14,8 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
public final class Util {
|
||||
|
||||
private static final Unsafe theUnsafe = getUnsafe();
|
||||
|
||||
private static Unsafe getUnsafe() {
|
||||
try {
|
||||
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||
return unsafe;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setStaticBooleanUnsafe(final Field field, boolean value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putBoolean(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static void setStaticObjectUnsafe(final Field field, Object value) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
theUnsafe.putObject(staticFieldBase, staticFieldOffset, value);
|
||||
}
|
||||
|
||||
public static Object getStaticObjectUnsafe(final Field field) {
|
||||
final Object staticFieldBase = theUnsafe.staticFieldBase(field);
|
||||
final long staticFieldOffset = theUnsafe.staticFieldOffset(field);
|
||||
return theUnsafe.getObject(staticFieldBase, staticFieldOffset);
|
||||
}
|
||||
|
||||
public static Object getObjectUnsafe(final Object base, final Field field) {
|
||||
final long fieldOffset = theUnsafe.objectFieldOffset(field);
|
||||
return theUnsafe.getObject(base, fieldOffset);
|
||||
}
|
||||
|
||||
public static void closeSilently(Closeable closeable) {
|
||||
if(closeable != null) {
|
||||
try {
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
package org.mcphackers.rdi.util;
|
||||
|
||||
import static org.mcphackers.rdi.util.InsnHelper.range;
|
||||
import static org.mcphackers.rdi.util.InsnHelper.remove;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
public class IdentifyCall {
|
||||
protected MethodInsnNode invokeInsn;
|
||||
protected List<AbstractInsnNode[]> arguments;
|
||||
|
||||
public IdentifyCall(MethodInsnNode invoke) {
|
||||
invokeInsn = invoke;
|
||||
arguments = initArguments();
|
||||
}
|
||||
|
||||
private List<AbstractInsnNode[]> initArguments() {
|
||||
boolean isStatic = invokeInsn.getOpcode() == Opcodes.INVOKESTATIC;
|
||||
Type[] argTypes = Type.getArgumentTypes(invokeInsn.desc);
|
||||
List<AbstractInsnNode[]> args = new ArrayList<AbstractInsnNode[]>(argTypes.length);
|
||||
if(argTypes.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
AbstractInsnNode insn = invokeInsn.getPrevious();
|
||||
AbstractInsnNode start = null;
|
||||
AbstractInsnNode end = insn;
|
||||
for(int currentArg = argTypes.length - 1; currentArg >= 0; currentArg--) {
|
||||
int stack = argTypes[currentArg].getSize();
|
||||
end = insn;
|
||||
while(stack > 0) {
|
||||
stack -= OPHelper.getStackSizeDelta(insn);
|
||||
if(stack == 0) {
|
||||
start = insn;
|
||||
}
|
||||
insn = insn.getPrevious();
|
||||
}
|
||||
if(start != null) {
|
||||
while(args.size() <= currentArg) {
|
||||
args.add(null);
|
||||
}
|
||||
args.set(currentArg, range(start, end));
|
||||
}
|
||||
}
|
||||
if(!isStatic) {
|
||||
int stack = 1;
|
||||
end = insn;
|
||||
while(stack > 0) {
|
||||
stack -= OPHelper.getStackSizeDelta(insn);
|
||||
if(stack == 0) {
|
||||
start = insn;
|
||||
}
|
||||
insn = insn.getPrevious();
|
||||
}
|
||||
args.add(0, range(start, end));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of insn node arrays with each argument Non-static
|
||||
* methods include the owner of a call at index 0
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<AbstractInsnNode[]> getArguments() {
|
||||
return Collections.unmodifiableList(arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of insn nodes belonging to the argument at that index
|
||||
* Non-static methods include the owner of a call at index 0
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public AbstractInsnNode[] getArgument(int index) {
|
||||
return arguments.get(index);
|
||||
}
|
||||
|
||||
public static void removeCall(InsnList insns, MethodInsnNode invoke) {
|
||||
for(AbstractInsnNode[] arg : new IdentifyCall(invoke).getArguments()) {
|
||||
remove(insns, arg);
|
||||
}
|
||||
insns.remove(invoke);
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
package org.mcphackers.rdi.util;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static org.objectweb.asm.tree.AbstractInsnNode.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
public class InsnHelper {
|
||||
|
||||
public static AbstractInsnNode[] fill(AbstractInsnNode insn, int size) {
|
||||
AbstractInsnNode[] arr = new AbstractInsnNode[size];
|
||||
int i = 0;
|
||||
while(insn != null && i < size) {
|
||||
arr[i] = insn;
|
||||
insn = insn.getNext();
|
||||
i++;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static void removeRange(InsnList insns, AbstractInsnNode first, AbstractInsnNode last) {
|
||||
if(first == null || last == null) {
|
||||
return;
|
||||
}
|
||||
AbstractInsnNode next = first;
|
||||
while(next != null && next != last) {
|
||||
AbstractInsnNode forRemoval = next;
|
||||
next = next.getNext();
|
||||
insns.remove(forRemoval);
|
||||
}
|
||||
insns.remove(last);
|
||||
}
|
||||
|
||||
public static void remove(InsnList insns, AbstractInsnNode... toRemove) {
|
||||
for(AbstractInsnNode insn : toRemove) {
|
||||
insns.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode[] range(AbstractInsnNode start, AbstractInsnNode end) {
|
||||
List<AbstractInsnNode> list = new LinkedList<AbstractInsnNode>();
|
||||
AbstractInsnNode insn = start;
|
||||
while(insn != end) {
|
||||
list.add(insn);
|
||||
insn = insn.getNext();
|
||||
}
|
||||
list.add(end);
|
||||
AbstractInsnNode[] arr = new AbstractInsnNode[list.size()];
|
||||
return list.toArray(arr);
|
||||
}
|
||||
|
||||
public static InsnList clone(Iterable<AbstractInsnNode> insnList) {
|
||||
Map<LabelNode, LabelNode> labels = new HashMap<LabelNode, LabelNode>();
|
||||
for(AbstractInsnNode insn : insnList) {
|
||||
if(insn.getType() == LABEL) {
|
||||
LabelNode label = (LabelNode) insn;
|
||||
labels.put(label, new LabelNode());
|
||||
}
|
||||
}
|
||||
|
||||
InsnList destList = new InsnList();
|
||||
for(AbstractInsnNode insn : insnList) {
|
||||
AbstractInsnNode insnCopy = insn.clone(labels);
|
||||
destList.add(insnCopy);
|
||||
}
|
||||
return destList;
|
||||
}
|
||||
|
||||
public static InsnList clone(AbstractInsnNode[] insnList) {
|
||||
return clone(Arrays.asList(insnList));
|
||||
}
|
||||
|
||||
public static LabelNode labelBefore(AbstractInsnNode current) {
|
||||
current = current.getPrevious();
|
||||
while(current.getType() == LINE) { // Skip line number nodes
|
||||
current = current.getPrevious();
|
||||
}
|
||||
if(current.getType() == LABEL) {
|
||||
return (LabelNode) current;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode nextInsn(AbstractInsnNode current) {
|
||||
while((current = current.getNext()) != null && current.getOpcode() == -1);
|
||||
return current;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode previousInsn(AbstractInsnNode current) {
|
||||
while((current = current.getPrevious()) != null && current.getOpcode() == -1);
|
||||
return current;
|
||||
}
|
||||
|
||||
public static boolean containsInvoke(InsnList insns, MethodInsnNode invoke) {
|
||||
for(AbstractInsnNode insn : insns) {
|
||||
if(insn.getType() == METHOD_INSN) {
|
||||
MethodInsnNode invoke2 = (MethodInsnNode) insn;
|
||||
if(invoke2.getOpcode() == invoke.getOpcode() && invoke2.owner.equals(invoke.owner) && invoke2.name.equals(invoke.name) && invoke2.desc.equals(invoke.desc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void addTryCatch(MethodNode method, AbstractInsnNode startInsn, AbstractInsnNode endInsn, String exception) {
|
||||
addTryCatch(method, startInsn, endInsn, null, exception);
|
||||
}
|
||||
|
||||
public static void addTryCatch(MethodNode method, AbstractInsnNode startInsn, AbstractInsnNode endInsn, InsnList handle, String exception) {
|
||||
InsnList instructions = method.instructions;
|
||||
if(!instructions.contains(startInsn) || !instructions.contains(endInsn)) {
|
||||
throw new IllegalArgumentException("Instruction does not belong to the list");
|
||||
}
|
||||
LabelNode start = new LabelNode();
|
||||
LabelNode end = new LabelNode();
|
||||
LabelNode handler = new LabelNode();
|
||||
LabelNode after = new LabelNode();
|
||||
instructions.insertBefore(startInsn, start);
|
||||
InsnList insert = new InsnList();
|
||||
insert.add(end);
|
||||
insert.add(new JumpInsnNode(GOTO, after));
|
||||
insert.add(handler);
|
||||
if(handle == null) {
|
||||
insert.add(new InsnNode(POP));
|
||||
} else {
|
||||
insert.add(handle);
|
||||
}
|
||||
insert.add(after);
|
||||
instructions.insert(endInsn, insert);
|
||||
int index = 0; // FIXME Shouldn't be 0 if there are try/catch blocks within the range
|
||||
method.tryCatchBlocks.add(index, new TryCatchBlockNode(start, end, handler, exception));
|
||||
}
|
||||
|
||||
public static int getFreeIndex(InsnList instructions) {
|
||||
int lastFree = 1;
|
||||
AbstractInsnNode insn = instructions.getFirst();
|
||||
while(insn != null) {
|
||||
if(insn.getType() == VAR_INSN) {
|
||||
VarInsnNode var = (VarInsnNode) insn;
|
||||
lastFree = Math.max(lastFree, var.var + 1); // FIXME 2 if it's a double or long?
|
||||
}
|
||||
insn = insn.getNext();
|
||||
}
|
||||
return lastFree;
|
||||
}
|
||||
|
||||
public static AbstractInsnNode intInsn(int value) {
|
||||
switch(value) {
|
||||
case -1:
|
||||
return new InsnNode(ICONST_M1);
|
||||
case 0:
|
||||
return new InsnNode(ICONST_0);
|
||||
case 1:
|
||||
return new InsnNode(ICONST_1);
|
||||
case 2:
|
||||
return new InsnNode(ICONST_2);
|
||||
case 3:
|
||||
return new InsnNode(ICONST_3);
|
||||
case 4:
|
||||
return new InsnNode(ICONST_4);
|
||||
case 5:
|
||||
return new InsnNode(ICONST_5);
|
||||
default:
|
||||
if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
return new IntInsnNode(BIPUSH, value);
|
||||
} else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
return new IntInsnNode(SIPUSH, value);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode longInsn(long value) {
|
||||
if(value == 0L) {
|
||||
return new InsnNode(LCONST_0);
|
||||
} else if(value == 1L) {
|
||||
return new InsnNode(LCONST_1);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode booleanInsn(boolean value) {
|
||||
return new InsnNode(value ? ICONST_1 : ICONST_0);
|
||||
}
|
||||
|
||||
public static AbstractInsnNode floatInsn(float value) {
|
||||
if(value == 0F) {
|
||||
return new InsnNode(FCONST_0);
|
||||
} else if(value == 1F) {
|
||||
return new InsnNode(FCONST_1);
|
||||
} else if(value == 2F) {
|
||||
return new InsnNode(FCONST_2);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractInsnNode doubleInsn(double value) {
|
||||
if(value == 0D) {
|
||||
return new InsnNode(DCONST_0);
|
||||
} else if(value == 1D) {
|
||||
return new InsnNode(DCONST_1);
|
||||
} else {
|
||||
return new LdcInsnNode(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,375 +0,0 @@
|
|||
package org.mcphackers.rdi.util;
|
||||
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
public final class OPHelper {
|
||||
|
||||
private static final int[] STACK_SIZE_DELTA = new int[0xFF];
|
||||
|
||||
static {
|
||||
STACK_SIZE_DELTA[NOP] = 0;
|
||||
STACK_SIZE_DELTA[ACONST_NULL] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_M1] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_0] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_1] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_2] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_3] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_4] = 1;
|
||||
STACK_SIZE_DELTA[ICONST_5] = 1;
|
||||
STACK_SIZE_DELTA[LCONST_0] = 2;
|
||||
STACK_SIZE_DELTA[LCONST_1] = 1;
|
||||
STACK_SIZE_DELTA[FCONST_0] = 1;
|
||||
STACK_SIZE_DELTA[FCONST_1] = 1;
|
||||
STACK_SIZE_DELTA[FCONST_2] = 1;
|
||||
STACK_SIZE_DELTA[DCONST_0] = 2;
|
||||
STACK_SIZE_DELTA[DCONST_1] = 2;
|
||||
STACK_SIZE_DELTA[BIPUSH] = 1;
|
||||
STACK_SIZE_DELTA[SIPUSH] = 1;
|
||||
STACK_SIZE_DELTA[LDC] = 1; // special case
|
||||
STACK_SIZE_DELTA[ILOAD] = 1;
|
||||
STACK_SIZE_DELTA[LLOAD] = 2;
|
||||
STACK_SIZE_DELTA[FLOAD] = 1;
|
||||
STACK_SIZE_DELTA[DLOAD] = 2;
|
||||
STACK_SIZE_DELTA[ALOAD] = 1;
|
||||
STACK_SIZE_DELTA[IALOAD] = -1;
|
||||
STACK_SIZE_DELTA[LALOAD] = 0;
|
||||
STACK_SIZE_DELTA[FALOAD] = -1;
|
||||
STACK_SIZE_DELTA[DALOAD] = 0;
|
||||
STACK_SIZE_DELTA[AALOAD] = -1;
|
||||
STACK_SIZE_DELTA[BALOAD] = -1;
|
||||
STACK_SIZE_DELTA[CALOAD] = -1;
|
||||
STACK_SIZE_DELTA[SALOAD] = -1;
|
||||
STACK_SIZE_DELTA[ISTORE] = -1;
|
||||
STACK_SIZE_DELTA[LSTORE] = -2;
|
||||
STACK_SIZE_DELTA[FSTORE] = -1;
|
||||
STACK_SIZE_DELTA[DSTORE] = -2;
|
||||
STACK_SIZE_DELTA[ASTORE] = -1;
|
||||
STACK_SIZE_DELTA[IASTORE] = -3;
|
||||
STACK_SIZE_DELTA[LASTORE] = -4;
|
||||
STACK_SIZE_DELTA[FASTORE] = -3;
|
||||
STACK_SIZE_DELTA[DASTORE] = -4;
|
||||
STACK_SIZE_DELTA[AASTORE] = -3;
|
||||
STACK_SIZE_DELTA[BASTORE] = -3;
|
||||
STACK_SIZE_DELTA[CASTORE] = -3;
|
||||
STACK_SIZE_DELTA[SASTORE] = -3;
|
||||
STACK_SIZE_DELTA[POP] = -1;
|
||||
STACK_SIZE_DELTA[POP2] = -2;
|
||||
STACK_SIZE_DELTA[DUP] = 1;
|
||||
STACK_SIZE_DELTA[DUP_X1] = 1;
|
||||
STACK_SIZE_DELTA[DUP_X2] = 1;
|
||||
STACK_SIZE_DELTA[DUP2] = 2;
|
||||
STACK_SIZE_DELTA[DUP2_X1] = 2;
|
||||
STACK_SIZE_DELTA[DUP2_X2] = 2;
|
||||
STACK_SIZE_DELTA[SWAP] = 0;
|
||||
STACK_SIZE_DELTA[IADD] = -1;
|
||||
STACK_SIZE_DELTA[LADD] = -2;
|
||||
STACK_SIZE_DELTA[FADD] = -1;
|
||||
STACK_SIZE_DELTA[DADD] = -2;
|
||||
STACK_SIZE_DELTA[ISUB] = -1;
|
||||
STACK_SIZE_DELTA[LSUB] = -2;
|
||||
STACK_SIZE_DELTA[FSUB] = -1;
|
||||
STACK_SIZE_DELTA[DSUB] = -2;
|
||||
STACK_SIZE_DELTA[IMUL] = -1;
|
||||
STACK_SIZE_DELTA[LMUL] = -2;
|
||||
STACK_SIZE_DELTA[FMUL] = -1;
|
||||
STACK_SIZE_DELTA[DMUL] = -2;
|
||||
STACK_SIZE_DELTA[IDIV] = -1;
|
||||
STACK_SIZE_DELTA[LDIV] = -2;
|
||||
STACK_SIZE_DELTA[FDIV] = -1;
|
||||
STACK_SIZE_DELTA[DDIV] = -2;
|
||||
STACK_SIZE_DELTA[IREM] = -1;
|
||||
STACK_SIZE_DELTA[LREM] = -2;
|
||||
STACK_SIZE_DELTA[FREM] = -1;
|
||||
STACK_SIZE_DELTA[DREM] = -2;
|
||||
STACK_SIZE_DELTA[INEG] = 0;
|
||||
STACK_SIZE_DELTA[LNEG] = 0;
|
||||
STACK_SIZE_DELTA[FNEG] = 0;
|
||||
STACK_SIZE_DELTA[DNEG] = 0;
|
||||
STACK_SIZE_DELTA[ISHL] = -1;
|
||||
STACK_SIZE_DELTA[LSHL] = -1;
|
||||
STACK_SIZE_DELTA[ISHR] = -1;
|
||||
STACK_SIZE_DELTA[LSHR] = -1;
|
||||
STACK_SIZE_DELTA[IUSHR] = -1;
|
||||
STACK_SIZE_DELTA[LUSHR] = -1;
|
||||
STACK_SIZE_DELTA[IAND] = -1;
|
||||
STACK_SIZE_DELTA[LAND] = -2;
|
||||
STACK_SIZE_DELTA[IOR] = -1;
|
||||
STACK_SIZE_DELTA[LOR] = -2;
|
||||
STACK_SIZE_DELTA[IXOR] = -1;
|
||||
STACK_SIZE_DELTA[LXOR] = -2;
|
||||
STACK_SIZE_DELTA[IINC] = 0;
|
||||
STACK_SIZE_DELTA[I2L] = 1;
|
||||
STACK_SIZE_DELTA[I2F] = 0;
|
||||
STACK_SIZE_DELTA[I2D] = 1;
|
||||
STACK_SIZE_DELTA[L2I] = -1;
|
||||
STACK_SIZE_DELTA[L2F] = -1;
|
||||
STACK_SIZE_DELTA[L2D] = 0;
|
||||
STACK_SIZE_DELTA[F2I] = 0;
|
||||
STACK_SIZE_DELTA[F2L] = 1;
|
||||
STACK_SIZE_DELTA[F2D] = 1;
|
||||
STACK_SIZE_DELTA[D2I] = -1;
|
||||
STACK_SIZE_DELTA[D2L] = 0;
|
||||
STACK_SIZE_DELTA[D2F] = -1;
|
||||
STACK_SIZE_DELTA[I2B] = 0;
|
||||
STACK_SIZE_DELTA[I2C] = 0;
|
||||
STACK_SIZE_DELTA[I2S] = 0;
|
||||
STACK_SIZE_DELTA[LCMP] = -3;
|
||||
STACK_SIZE_DELTA[FCMPL] = -1;
|
||||
STACK_SIZE_DELTA[FCMPG] = -1;
|
||||
STACK_SIZE_DELTA[DCMPL] = -3;
|
||||
STACK_SIZE_DELTA[DCMPG] = -3;
|
||||
STACK_SIZE_DELTA[IFEQ] = -1;
|
||||
STACK_SIZE_DELTA[IFNE] = -1;
|
||||
STACK_SIZE_DELTA[IFLT] = -1;
|
||||
STACK_SIZE_DELTA[IFGE] = -1;
|
||||
STACK_SIZE_DELTA[IFGT] = -1;
|
||||
STACK_SIZE_DELTA[IFLE] = -1;
|
||||
STACK_SIZE_DELTA[IF_ICMPEQ] = -2;
|
||||
STACK_SIZE_DELTA[IF_ICMPNE] = -2;
|
||||
STACK_SIZE_DELTA[IF_ICMPLT] = -2;
|
||||
STACK_SIZE_DELTA[IF_ICMPGE] = -2;
|
||||
STACK_SIZE_DELTA[IF_ICMPGT] = -2;
|
||||
STACK_SIZE_DELTA[IF_ICMPLE] = -2;
|
||||
STACK_SIZE_DELTA[IF_ACMPEQ] = -2;
|
||||
STACK_SIZE_DELTA[IF_ACMPNE] = -2;
|
||||
STACK_SIZE_DELTA[GOTO] = 0;
|
||||
STACK_SIZE_DELTA[JSR] = 1;
|
||||
STACK_SIZE_DELTA[RET] = 0;
|
||||
STACK_SIZE_DELTA[TABLESWITCH] = -1;
|
||||
STACK_SIZE_DELTA[LOOKUPSWITCH] = -1;
|
||||
STACK_SIZE_DELTA[IRETURN] = -1;
|
||||
STACK_SIZE_DELTA[LRETURN] = -2;
|
||||
STACK_SIZE_DELTA[FRETURN] = -1;
|
||||
STACK_SIZE_DELTA[DRETURN] = -2;
|
||||
STACK_SIZE_DELTA[ARETURN] = -1;
|
||||
STACK_SIZE_DELTA[RETURN] = 0;
|
||||
STACK_SIZE_DELTA[GETSTATIC] = 0; // special case
|
||||
STACK_SIZE_DELTA[PUTSTATIC] = 0; // special case
|
||||
STACK_SIZE_DELTA[GETFIELD] = 0; // special case
|
||||
STACK_SIZE_DELTA[PUTFIELD] = 0; // special case
|
||||
STACK_SIZE_DELTA[INVOKEVIRTUAL] = 0; // special case
|
||||
STACK_SIZE_DELTA[INVOKESPECIAL] = 0; // special case
|
||||
STACK_SIZE_DELTA[INVOKESTATIC] = 0; // special case
|
||||
STACK_SIZE_DELTA[INVOKEINTERFACE] = 0; // special case
|
||||
STACK_SIZE_DELTA[INVOKEDYNAMIC] = 0; // special case
|
||||
STACK_SIZE_DELTA[NEW] = 1;
|
||||
STACK_SIZE_DELTA[NEWARRAY] = 0;
|
||||
STACK_SIZE_DELTA[ANEWARRAY] = 0;
|
||||
STACK_SIZE_DELTA[ARRAYLENGTH] = 0;
|
||||
STACK_SIZE_DELTA[ATHROW] = 0; // special case
|
||||
STACK_SIZE_DELTA[CHECKCAST] = 0;
|
||||
STACK_SIZE_DELTA[INSTANCEOF] = 0;
|
||||
STACK_SIZE_DELTA[MONITORENTER] = -1;
|
||||
STACK_SIZE_DELTA[MONITOREXIT] = -1;
|
||||
STACK_SIZE_DELTA[MULTIANEWARRAY] = 0; // special case
|
||||
STACK_SIZE_DELTA[IFNULL] = -1;
|
||||
STACK_SIZE_DELTA[IFNONNULL] = -1;
|
||||
}
|
||||
|
||||
public static final int getStackSizeDelta(AbstractInsnNode insn) {
|
||||
int opcode = insn.getOpcode();
|
||||
if(opcode == -1) {
|
||||
return 0;
|
||||
}
|
||||
if(insn.getType() == AbstractInsnNode.LDC_INSN) {
|
||||
LdcInsnNode ldc = (LdcInsnNode) insn;
|
||||
if(ldc.cst instanceof Double || ldc.cst instanceof Long) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if(insn.getType() == AbstractInsnNode.METHOD_INSN) {
|
||||
MethodInsnNode invoke = (MethodInsnNode) insn;
|
||||
int sizes = Type.getArgumentsAndReturnSizes(invoke.desc);
|
||||
int argumentSizes = sizes >> 2;
|
||||
if(opcode == INVOKESTATIC) {
|
||||
argumentSizes--;
|
||||
}
|
||||
int returnSize = sizes & 3;
|
||||
return returnSize - argumentSizes;
|
||||
}
|
||||
if(insn.getType() == AbstractInsnNode.FIELD_INSN) {
|
||||
FieldInsnNode field = (FieldInsnNode) insn;
|
||||
int returnSize = Type.getType(field.desc).getSize();
|
||||
if(opcode == GETSTATIC) {
|
||||
return returnSize;
|
||||
}
|
||||
return returnSize - 1;
|
||||
} else if(opcode != ATHROW && opcode != MULTIANEWARRAY && opcode != INVOKEDYNAMIC) {
|
||||
return STACK_SIZE_DELTA[opcode];
|
||||
}
|
||||
throw new IllegalArgumentException("Could not get stack size delta for " + opcode);
|
||||
}
|
||||
|
||||
public static boolean isReturn(int opcode) {
|
||||
return opcode == RETURN || opcode == IRETURN || opcode == LRETURN || opcode == FRETURN || opcode == DRETURN || opcode == ARETURN;
|
||||
}
|
||||
|
||||
public static final boolean isArrayLoad(int opcode) {
|
||||
switch(opcode) {
|
||||
case AALOAD:
|
||||
case BALOAD: // for booleans and bytes
|
||||
case CALOAD:
|
||||
case DALOAD:
|
||||
case FALOAD:
|
||||
case IALOAD:
|
||||
case LALOAD:
|
||||
case SALOAD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean isVarLoad(int opcode) {
|
||||
switch(opcode) {
|
||||
case ALOAD:
|
||||
// case BLOAD: // bytes & booleans are also regular integers
|
||||
// case CLOAD: // characters are regular integers (?) under the hood
|
||||
case DLOAD:
|
||||
case FLOAD:
|
||||
case ILOAD:
|
||||
case LLOAD:
|
||||
// case SLOAD: // and so are shorts
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int reverseVarOpcode(int opcode) {
|
||||
switch(opcode) {
|
||||
case AALOAD:
|
||||
return AASTORE;
|
||||
case BALOAD:
|
||||
return BASTORE;
|
||||
case CALOAD:
|
||||
return CASTORE;
|
||||
case IALOAD:
|
||||
return IASTORE;
|
||||
case SALOAD:
|
||||
return IASTORE;
|
||||
case DALOAD:
|
||||
return IASTORE;
|
||||
case FALOAD:
|
||||
return IASTORE;
|
||||
case LALOAD:
|
||||
return IASTORE;
|
||||
case ALOAD:
|
||||
return IASTORE;
|
||||
case DLOAD:
|
||||
return IASTORE;
|
||||
case FLOAD:
|
||||
return IASTORE;
|
||||
case ILOAD:
|
||||
return IASTORE;
|
||||
case LLOAD:
|
||||
return IASTORE;
|
||||
case AASTORE:
|
||||
return AALOAD;
|
||||
case BASTORE:
|
||||
return BALOAD;
|
||||
case CASTORE:
|
||||
return CALOAD;
|
||||
case IASTORE:
|
||||
return IALOAD;
|
||||
case SASTORE:
|
||||
return SALOAD;
|
||||
case DASTORE:
|
||||
return DALOAD;
|
||||
case FASTORE:
|
||||
return FALOAD;
|
||||
case LASTORE:
|
||||
return LALOAD;
|
||||
case ASTORE:
|
||||
return ALOAD;
|
||||
case DSTORE:
|
||||
return DLOAD;
|
||||
case FSTORE:
|
||||
return FLOAD;
|
||||
case ISTORE:
|
||||
return ILOAD;
|
||||
case LSTORE:
|
||||
return LLOAD;
|
||||
default:
|
||||
throw new IllegalArgumentException("Opcode not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean isVarSimilarType(int opcode1, int opcode2) {
|
||||
switch(opcode1) {
|
||||
case AALOAD:
|
||||
return opcode2 == ALOAD;
|
||||
case BALOAD:
|
||||
case CALOAD:
|
||||
case IALOAD:
|
||||
case SALOAD:
|
||||
return opcode2 == ILOAD;
|
||||
case DALOAD:
|
||||
return opcode2 == DLOAD;
|
||||
case FALOAD:
|
||||
return opcode2 == FLOAD;
|
||||
case LALOAD:
|
||||
return opcode2 == LLOAD;
|
||||
case ALOAD:
|
||||
return opcode2 == AALOAD;
|
||||
case DLOAD:
|
||||
return opcode2 == DALOAD;
|
||||
case FLOAD:
|
||||
return opcode2 == FALOAD;
|
||||
case ILOAD:
|
||||
return opcode2 == IALOAD || opcode2 == SALOAD || opcode2 == BALOAD || opcode2 == CALOAD;
|
||||
case LLOAD:
|
||||
return opcode2 == LALOAD;
|
||||
// -- The same story for the store operation family --
|
||||
case AASTORE:
|
||||
return opcode2 == ASTORE;
|
||||
case BASTORE:
|
||||
case CASTORE:
|
||||
case IASTORE:
|
||||
case SASTORE:
|
||||
return opcode2 == ISTORE;
|
||||
case DASTORE:
|
||||
return opcode2 == DSTORE;
|
||||
case FASTORE:
|
||||
return opcode2 == FSTORE;
|
||||
case LASTORE:
|
||||
return opcode2 == LSTORE;
|
||||
case ASTORE:
|
||||
return opcode2 == ASTORE;
|
||||
case DSTORE:
|
||||
return opcode2 == DSTORE;
|
||||
case FSTORE:
|
||||
return opcode2 == FSTORE;
|
||||
case ISTORE:
|
||||
return opcode2 == IASTORE || opcode2 == SASTORE || opcode2 == BASTORE || opcode2 == CASTORE;
|
||||
case LSTORE:
|
||||
return opcode2 == LSTORE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Opcode1 not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean isVarStore(int opcode) {
|
||||
switch(opcode) {
|
||||
case ASTORE:
|
||||
// case BSTORE: // bytes & booleans are also regular integers
|
||||
// case CSTORE: // characters are regular integers (?) under the hood
|
||||
case DSTORE:
|
||||
case FSTORE:
|
||||
case ISTORE:
|
||||
case LSTORE:
|
||||
// case SSTORE: // and so are shorts
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue