A rewrite + RDI

This commit is contained in:
Lassebq 2023-03-18 20:23:17 +02:00
parent ee061a6204
commit 1ec1b3daf1
No known key found for this signature in database
GPG key ID: DE0866BB0C980B6E
25 changed files with 729 additions and 1237 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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