More tweaks and URLStreamHandler overriding

This commit is contained in:
Lassebq 2022-12-22 15:33:38 +02:00
parent da1b9c9bdc
commit 89f25af378
No known key found for this signature in database
GPG key ID: DE0866BB0C980B6E
33 changed files with 5601 additions and 1322 deletions

View file

@ -6,6 +6,9 @@ repositories {
url "https://libraries.minecraft.net/"
}
mavenCentral()
flatDir {
dirs "lib"
}
}
group = 'org.mcphackers'
@ -14,9 +17,11 @@ version = '1.0'
sourceCompatibility = 1.6
dependencies {
implementation name: 'lwjgl-2.9.4'
implementation name: 'lwjgl_util-2.9.4'
implementation 'org.ow2.asm:asm:9.3'
implementation 'org.ow2.asm:asm-tree:9.3'
implementation 'org.lwjgl.lwjgl:lwjgl:2.9.3'
implementation 'org.json:json:20220924'
}
task sourcesJar(type: Jar) {

BIN
lib/lwjgl-2.9.4.jar Normal file

Binary file not shown.

BIN
lib/lwjgl.jar Normal file

Binary file not shown.

BIN
lib/lwjgl_util-2.9.4.jar Normal file

Binary file not shown.

BIN
lib/lwjgl_util.jar Normal file

Binary file not shown.

View file

@ -0,0 +1,48 @@
package org.mcphackers.launchwrapper;
import java.applet.Applet;
import java.awt.Image;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.tweak.AppletWrapper;
public class AppletLaunchTarget extends LaunchTarget {
private final String targetClass;
private String title;
private int width = 200, height = 200;
private Image icon;
public AppletLaunchTarget(LaunchClassLoader classLoader, String classTarget) {
super(classLoader);
targetClass = classTarget;
title = classTarget;
}
public void setTitle(String title) {
this.title = title;
}
public void setResolution(int w, int h) {
width = w;
height = h;
}
public void setIcon(Image img) {
icon = img;
}
public void launch() {
Class<? extends Applet> appletClass;
try {
appletClass = classLoader.findClass(targetClass).asSubclass(Applet.class);
} catch (Exception e) {
e.printStackTrace();
return;
}
AppletWrapper.startApplet(appletClass, width, height, title, icon);
}
}

View file

@ -1,179 +1,45 @@
package org.mcphackers.launchwrapper;
import java.applet.Applet;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.net.URL;
import javax.imageio.ImageIO;
import org.mcphackers.launchwrapper.LaunchTarget.Type;
import org.mcphackers.launchwrapper.inject.AppletWrapper;
import org.mcphackers.launchwrapper.inject.Inject;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.tweak.DefaultTweak;
import org.mcphackers.launchwrapper.tweak.Tweak;
public class Launch {
private static final LaunchClassLoader CLASS_LOADER = LaunchClassLoader.instantiate();
public static final BufferedImage ICON = getIcon();
public static Launch INSTANCE;
public final LaunchConfig config;
private Launch(LaunchConfig config) {
this.config = config;
}
public static void main(String[] args) {
File gameDir = null;
String username = null;
String sessionId = null;
for(int i = 0; i < args.length; i++) {
switch(i) {
case 0:
username = args[i];
break;
case 1:
sessionId = args[i];
break;
case 2:
gameDir = new File(args[i]);
break;
}
}
Launch.create()
.setUser(username, sessionId)
.setGameDirectory(gameDir)
.setResolution(1280, 720)
.setLWJGLFrame(true)
.launch();
LaunchConfig config = new LaunchConfig(args);
Launch.create(config).launch();
}
public void launch() {
CLASS_LOADER.addException(Launch.class);
Tweak mainTweak = DefaultTweak.get(CLASS_LOADER, this);
CLASS_LOADER.addException(Inject.class);
Tweak mainTweak = Tweak.get(CLASS_LOADER, config);
if(mainTweak == null) {
System.err.println("Could not find launch target");
return;
}
if(mainTweak.transform()) {
launch(mainTweak.getLaunchTarget());
mainTweak.getLaunchTarget(CLASS_LOADER).launch();
}
}
public void launch(LaunchTarget target) {
if(target.type == Type.MAIN) {
CLASS_LOADER.invokeMain(target.targetClass, username, sessionId);
} else if(target.type == Type.APPLET) {
Class<? extends Applet> appletClass;
try {
appletClass = CLASS_LOADER.findClass(target.targetClass).asSubclass(Applet.class);
} catch (Exception e) {
e.printStackTrace();
return;
}
AppletWrapper.startApplet(appletClass, width, height, isom ? "Isometric Preview" : "Minecraft");
}
public void setClassPath(URL[] urls) {
CLASS_LOADER.setClassPath(urls);
}
private static BufferedImage getIcon() {
try {
return ImageIO.read(Launch.class.getResourceAsStream("/favicon.png"));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static ByteBuffer loadIcon() {
final BufferedImage icon = ICON;
final int[] rgb = icon.getRGB(0, 0, icon.getWidth(), icon.getHeight(), null, 0, icon.getWidth());
final ByteBuffer buffer = ByteBuffer.allocate(4 * rgb.length);
for (int color : rgb) {
buffer.putInt(color << 8 | ((color >> 24) & 0xFF));
}
buffer.flip();
return buffer;
}
public static LaunchBuilder create() {
return new LaunchBuilder();
}
public static class LaunchBuilder {
private String username = "Player" + System.currentTimeMillis() % 1000L;
private String sessionId = "-";
private int width = 854;
private int height = 480;
private boolean fullscreen;
private File gameDir;
private String serverIp;
private int serverPort;
private boolean lwjglFrame;
private boolean isom;
private LaunchBuilder() {
}
public LaunchBuilder setIsom(boolean b) {
this.isom = b;
return this;
}
public LaunchBuilder setUser(String username, String sessionId) {
this.username = username != null ? username : "Player" + System.currentTimeMillis() % 1000L;
this.sessionId = sessionId != null ? sessionId : "-";
return this;
}
public LaunchBuilder setServer(String ip, int port) {
this.serverIp = ip;
this.serverPort = port;
return this;
}
public LaunchBuilder setResolution(int w, int h) {
this.width = w;
this.height = h;
return this;
}
public LaunchBuilder setFullscreen(boolean fullscreen) {
this.fullscreen = fullscreen;
return this;
}
public LaunchBuilder setGameDirectory(File file) {
this.gameDir = file;
return this;
}
public LaunchBuilder setLWJGLFrame(boolean f) {
lwjglFrame = f;
return this;
}
public Launch build() {
return new Launch(username, sessionId, width, height, fullscreen, gameDir, serverIp, serverPort, lwjglFrame, isom);
}
public void launch() {
build().launch();
}
}
public final String username, sessionId;
public final int width, height;
public final boolean fullscreen;
public final File gameDir;
public final String serverIp;
public final int serverPort;
public final boolean lwjglFrame;
public final boolean isom;
private Launch(String username, String sessionId, int width, int height, boolean fullscreen, File gameDir, String serverIp, int serverPort, boolean lwjglFrame, boolean isom) {
this.username = username;
this.sessionId = sessionId;
this.width = width;
this.height = height;
this.fullscreen = fullscreen;
this.gameDir = gameDir;
this.serverIp = serverIp;
this.serverPort = serverPort;
this.lwjglFrame = lwjglFrame;
this.isom = isom;
public static Launch create(LaunchConfig config) {
return INSTANCE = new Launch(config);
}
}

View file

@ -0,0 +1,142 @@
package org.mcphackers.launchwrapper;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class LaunchConfig {
private Map<String, LaunchParameter<Object>> parameters = new HashMap<String, LaunchParameter<Object>>();
public LaunchParameter<Boolean> demo = new LaunchParameter<Boolean>("demo", Boolean.class, false);
public LaunchParameter<Boolean> fullscreen = new LaunchParameter<Boolean>("fullscreen", Boolean.class, false);
public LaunchParameter<Boolean> checkGlErrors = new LaunchParameter<Boolean>("checkGlErrors", Boolean.class, false);
public LaunchParameter<String> server = new LaunchParameter<String>("server", String.class);
public LaunchParameter<Integer> port = new LaunchParameter<Integer>("port", Integer.class, 25565);
public LaunchParameter<File> gameDir = new LaunchParameter<File>("gameDir", File.class);
public LaunchParameter<File> assetsDir = new LaunchParameter<File>("assetsDir", File.class);
public LaunchParameter<File> resourcePackDir = new LaunchParameter<File>("resourcePackDir", File.class);
public LaunchParameter<String> proxyHost = new LaunchParameter<String>("proxyHost", String.class);
public LaunchParameter<Integer> proxyPort = new LaunchParameter<Integer>("proxyPort", Integer.class, 8080);
public LaunchParameter<String> proxyUser = new LaunchParameter<String>("proxyUser", String.class);
public LaunchParameter<String> proxyPass = new LaunchParameter<String>("proxyPass", String.class);
public LaunchParameter<String> username = new LaunchParameter<String>("username", String.class, "Player" + System.currentTimeMillis() % 1000L);
public LaunchParameter<String> sessionid = new LaunchParameter<String>("sessionid", String.class, "-");
public LaunchParameter<String> password = new LaunchParameter<String>("password", String.class);
public LaunchParameter<String> uuid = new LaunchParameter<String>("uuid", String.class);
public LaunchParameter<String> accessToken = new LaunchParameter<String>("accessToken", String.class);
public LaunchParameter<String> version = new LaunchParameter<String>("version", String.class);
public LaunchParameter<Integer> width = new LaunchParameter<Integer>("width", Integer.class, 854);
public LaunchParameter<Integer> height = new LaunchParameter<Integer>("height", Integer.class, 480);
public LaunchParameter<String> userProperties = new LaunchParameter<String>("userProperties", String.class, "{}");
public LaunchParameter<String> profileProperties = new LaunchParameter<String>("profileProperties", String.class);
public LaunchParameter<String> assetIndex = new LaunchParameter<String>("assetIndex", String.class);
public LaunchParameter<String> userType = new LaunchParameter<String>("userType", String.class);
public LaunchParameter<String> versionType = new LaunchParameter<String>("versionType", String.class);
public LaunchParameter<Boolean> applet = new LaunchParameter<Boolean>("applet", Boolean.class, false);
public LaunchParameter<Boolean> haspaid = new LaunchParameter<Boolean>("haspaid", Boolean.class, true);
public LaunchParameter<Boolean> lwjglFrame = new LaunchParameter<Boolean>("lwjglFrame", Boolean.class, true, true);
public LaunchParameter<Boolean> isom = new LaunchParameter<Boolean>("isom", Boolean.class, false, true);
public LaunchParameter<Boolean> forceVsync = new LaunchParameter<Boolean>("forceVsync", Boolean.class, false, true);
public LaunchConfig(String[] args) {
int i = 0;
while(i < args.length) {
if(args[i].startsWith("--")) {
String paramName = args[i].substring(2);
LaunchParameter<Object> param = parameters.get(paramName);
if(param != null && param.type == Boolean.class) {
param.set(true);
}
else if(paramName.equalsIgnoreCase("awtFrame")) {
lwjglFrame.set(false);
}
else if(i + 1 < args.length) {
try {
LaunchParameter<Object> param2 = parameters.get(paramName);
if(param2 != null) {
if(param.type == String.class) {
param2.set(args[i + 1]);
}
else if(param.type == File.class) {
param2.set(new File(args[i + 1]));
}
else if(param.type == Integer.class) {
param2.set(Integer.valueOf(args[i + 1]));
}
i++;
}
} catch (IllegalArgumentException e) {}
}
}
i++;
}
}
public Map<String, String> getArgsAsMap() {
Map<String, String> map = new HashMap<String, String>();
for(Entry<String, LaunchParameter<Object>> param : parameters.entrySet()) {
if(!param.getValue().wrapperOnly && param.getValue().value != null) {
map.put(param.getKey(), param.getValue().getString());
}
}
return map;
}
public String[] getArgs() {
List<String> list = new ArrayList<String>();
for(Entry<String, LaunchParameter<Object>> param : parameters.entrySet()) {
if(!param.getValue().wrapperOnly && param.getValue().value != null) {
if(param.getValue().type == Boolean.class) {
if(param.getValue().get().equals(true))
list.add("--" + param.getKey());
} else {
list.add("--" + param.getKey());
list.add(param.getValue().getString());
}
}
}
String[] arr = new String[list.size()];
return list.toArray(arr);
}
public class LaunchParameter<T> {
private Class<?> type;
private T value;
public boolean wrapperOnly;
public LaunchParameter(String name, Class<?> type) {
this(name, type, null);
}
public LaunchParameter(String name, Class<?> type, T defaultValue, boolean wrapper) {
this(name, type, defaultValue);
wrapperOnly = wrapper;
}
@SuppressWarnings("unchecked")
public LaunchParameter(String name, Class<?> type, T defaultValue) {
this.type = type;
this.value = defaultValue;
parameters.put(name, (LaunchParameter<Object>) this);
}
public String getString() {
if(value == null) return null;
if(value instanceof File) {
return ((File)value).getAbsolutePath();
}
return value.toString();
}
public T get() {
return value;
}
public void set(T value) {
this.value = value;
}
}
}

View file

@ -1,16 +1,14 @@
package org.mcphackers.launchwrapper;
public class LaunchTarget {
public enum Type {
MAIN,
APPLET;
}
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
public abstract class LaunchTarget {
public Type type;
public String targetClass;
protected final LaunchClassLoader classLoader;
public LaunchTarget(String classTarget, Type launchType) {
targetClass = classTarget;
type = launchType;
public LaunchTarget(LaunchClassLoader classLoader) {
this.classLoader = classLoader;
}
public abstract void launch();
}

View file

@ -0,0 +1,19 @@
package org.mcphackers.launchwrapper;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
public class MainLaunchTarget extends LaunchTarget {
public String targetClass;
public String[] args;
public MainLaunchTarget(LaunchClassLoader classLoader, String classTarget) {
super(classLoader);
targetClass = classTarget;
}
public void launch() {
classLoader.invokeMain(targetClass, args);
}
}

View file

@ -1,42 +0,0 @@
package org.mcphackers.launchwrapper;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
public final class Util {
public static void setField(Object instance, Field field, Object value) {
if(field != null) {
try {
field.set(instance, value);
}
catch (Exception e) { }
}
}
public static void closeSilently(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ignored) {
}
}
}
public static byte[] readStream(InputStream stream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
}
}

View file

@ -1,74 +1,119 @@
package org.mcphackers.launchwrapper.inject;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static org.mcphackers.launchwrapper.protocol.ListLevelsURLConnection.EMPTY_LEVEL;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.tweak.AppletWrapper;
import org.mcphackers.launchwrapper.util.Util;
// This class will be overriden in the new class loader context
public class Inject {
public static Runnable getMinecraftInstance(Frame frame, Canvas canvas, Applet applet, int width, int height, boolean fullscreen) {
return null;
}
private static final Launch LAUNCH = Launch.INSTANCE;
private static final BufferedImage DEFAULT_ICON = getIcon();
public static Applet getAppletInstance() {
return null;
}
public static void shutdown(Runnable runnable) {
}
public static void launchFrame(int width, int height, boolean fullscreen) {
Canvas canvas = new Canvas();
Applet applet = getAppletInstance();
applet.setLayout(new BorderLayout());
applet.add(canvas, "Center");
Frame frame = new Frame("Minecraft");
frame.setBackground(Color.BLACK);
try {
frame.setIconImage(ImageIO.read(Launch.class.getResource("/favicon.png")));
} catch (Exception exception10) { }
frame.setLayout(new BorderLayout());
frame.add(applet, "Center");
canvas.setPreferredSize(new Dimension(width, height));
frame.pack();
frame.setLocationRelativeTo(null);
if(applet != null) {
applet.setStub(new AppletWrapper());
public static AppletWrapper getApplet() {
return new AppletWrapper(LAUNCH.config.getArgsAsMap());
}
public static File getLevelFile(int index) {
return new File(LAUNCH.config.gameDir.get(), "levels/level" + index + ".dat");
}
public static File saveLevel(int index, String levelName) {
final int maxLevels = 5;
File levels = new File(LAUNCH.config.gameDir.get(), "levels");
File level = new File(levels, "level" + index + ".dat");
File levelNames = new File(levels, "levels.txt");
String[] lvlNames = new String[maxLevels];
for(int i = 0; i < maxLevels; i++) {
lvlNames[i] = EMPTY_LEVEL;
}
final Runnable mc = getMinecraftInstance(frame, canvas, applet, width, height, fullscreen);
if(mc == null) {
System.err.println("Could not create Minecraft instance!");
if(frame != null) frame.dispose();
return;
}
final Thread mcThread = new Thread(mc, "Minecraft main thread");
mcThread.setPriority(10);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
shutdown(mc);
try {
mcThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
if(levelNames.exists()) {
try {
FileInputStream levelNamesStream = new FileInputStream(levelNames);
lvlNames = new String(Util.readStream(levelNamesStream)).split(";");
levelNamesStream.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
});
mcThread.start();
}
lvlNames[index] = levelName;
if(levelName.equals("---")) {
level.delete();
level = null;
lvlNames[index] = EMPTY_LEVEL;
}
if(!levels.exists()) {
levels.mkdirs();
}
try {
FileOutputStream outputNames = new FileOutputStream(levelNames);
String lvls = "";
for(int i = 0; i < maxLevels; i++) {
lvls += lvlNames[i] + ";";
}
outputNames.write(lvls.getBytes());
outputNames.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return level;
}
public static ByteBuffer loadIcon(BufferedImage icon) {
final int[] rgb = icon.getRGB(0, 0, icon.getWidth(), icon.getHeight(), null, 0, icon.getWidth());
final ByteBuffer buffer = ByteBuffer.allocate(4 * rgb.length);
for (int color : rgb) {
buffer.putInt(color << 8 | ((color >> 24) & 0xFF));
}
buffer.flip();
return buffer;
}
public static ByteBuffer loadIcon(File iconFile) throws IOException {
return loadIcon(ImageIO.read(iconFile));
}
public static ByteBuffer[] loadIcons(boolean useDefault) {
if(!useDefault) {
try {
File assetsDir = LAUNCH.config.assetsDir.get();
if(assetsDir != null) {
final File smallIcon = new File(assetsDir, "icons/icon_16x16.png");
final File bigIcon = new File(assetsDir, "icons/icon_32x32.png");
return new ByteBuffer[] {
loadIcon(smallIcon),
loadIcon(bigIcon)
};
}
} catch (IOException e) {
//e.printStackTrace();
}
}
return new ByteBuffer[] { loadIcon(DEFAULT_ICON) };
}
public static BufferedImage getIcon() {
if(DEFAULT_ICON != null) {
return DEFAULT_ICON;
}
try {
return ImageIO.read(Inject.class.getResourceAsStream("/favicon.png"));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View file

@ -1,6 +1,5 @@
package org.mcphackers.launchwrapper.inject;
import static org.mcphackers.launchwrapper.inject.InsnHelper.Opcodes.ANY;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.tree.AbstractInsnNode.*;
@ -18,34 +17,43 @@ import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
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();
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 = insn.getPrevious();
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;
@ -58,13 +66,13 @@ public class InsnHelper {
}
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:
@ -88,11 +96,74 @@ public class InsnHelper {
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) {
AbstractInsnNode prev = insn.getPrevious();
if(prev != null && prev.getOpcode() == ALOAD && ((VarInsnNode)prev).var == 0
&& insn.getOpcode() == INVOKESPECIAL && ((MethodInsnNode)insn).name.equals("<init>")) {
break;
}
insn = insn.getNext();
}
return insn;
}
public static AbstractInsnNode getLastReturn(AbstractInsnNode last) {
AbstractInsnNode insn = last;
while(insn != null) {
@ -103,7 +174,7 @@ public class InsnHelper {
}
return insn;
}
public static boolean isReturn(int opcode) {
return
opcode == RETURN ||
@ -113,12 +184,67 @@ public class InsnHelper {
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;
}
@SuppressWarnings("unused")
public static boolean compareInsn(AbstractInsnNode insn, int opcode, Object... compare) {
if(insn == null) {
return false;
}
boolean opcodeEqual = opcode == insn.getOpcode() || opcode == ANY;
boolean opcodeEqual = opcode == insn.getOpcode() || opcode == -1;
if(!opcodeEqual) return false;
if(compare.length == 0 && opcodeEqual) {
return true;
@ -242,12 +368,7 @@ public class InsnHelper {
return matches;
}
return false;
}
public interface Opcodes extends org.objectweb.asm.Opcodes {
int ANY = -1;
}
}

View file

@ -8,13 +8,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.mcphackers.launchwrapper.Util;
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
import org.mcphackers.launchwrapper.inject.InjectUtils;
import org.mcphackers.launchwrapper.util.Util;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
@ -39,6 +40,10 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
this.parent = parent;
}
public void setClassPath(URL[] urls) {
this.parent = new URLClassLoader(urls, parent);
}
public URL getResource(String name) {
return parent.getResource(name);
}
@ -48,6 +53,9 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
}
public Class<?> findClass(String name) throws ClassNotFoundException {
if(!classNodeCache.isEmpty()) {
classNodeCache.clear();
}
name = className(name);
Class<?> cls = exceptions.get(name);
if(cls != null) {
@ -83,21 +91,21 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
public void overrideClass(ClassNode node) {
if(node == null) return;
if(true) {
File saveFolder = new File("C:/Users/User/Desktop/debug");
ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
node.accept(writer);
byte[] classData = writer.toByteArray();
File cls = new File(saveFolder, node.name + ".class");
cls.getParentFile().mkdirs();
try {
FileOutputStream fos = new FileOutputStream(cls);
fos.write(classData);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// if(true) {
// File saveFolder = new File("C:/Users/User/Desktop/debug");
// ClassWriter writer = new SafeClassWriter(parent, COMPUTE_MAXS);
// node.accept(writer);
// byte[] classData = writer.toByteArray();
// File cls = new File(saveFolder, node.name + ".class");
// cls.getParentFile().mkdirs();
// try {
// FileOutputStream fos = new FileOutputStream(cls);
// fos.write(classData);
// fos.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
overridenClasses.put(className(node.name), node);
classNodeCache.put(node.name, node);
}
@ -132,7 +140,7 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
if(node == null) {
return null;
}
ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
ClassWriter writer = new SafeClassWriter(parent, COMPUTE_MAXS | COMPUTE_FRAMES);
node.accept(writer);
byte[] classData = writer.toByteArray();
Class<?> definedClass = defineClass(className(node.name), classData, 0, classData.length);
@ -148,8 +156,12 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
return nameWithDots.replace('.', '/');
}
private static String classResourceName(String name) {
return name.replace('.', '/') + ".class";
}
private byte[] getClassAsBytes(String name) {
InputStream is = parent.getResourceAsStream(name.replace('.', '/') + ".class");
InputStream is = parent.getResourceAsStream(classResourceName(name));
if(is == null) return null;
byte[] classData;
try {
@ -175,7 +187,6 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
}
public void addException(Class<?> cls) {
if(cls == null) return;
exceptions.put(cls.getName(), cls);
}
@ -183,9 +194,7 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
if(INSTANCE != null) {
throw new IllegalStateException("Can only have one instance of LaunchClassLoader!");
}
INSTANCE = new LaunchClassLoader(getSystemClassLoader());
Thread.currentThread().setContextClassLoader(INSTANCE);
return INSTANCE;
return INSTANCE = new LaunchClassLoader(getSystemClassLoader());
}
}

View file

@ -0,0 +1,24 @@
package org.mcphackers.launchwrapper.loader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
public class SafeClassWriter extends ClassWriter {
private final ClassLoader classLoader;
public SafeClassWriter(final int flags) {
this(null, flags);
}
public SafeClassWriter(ClassLoader parent, int flags) {
this(parent, null, flags);
}
public SafeClassWriter(ClassLoader parent, ClassReader classReader, int flags) {
super(classReader, flags);
classLoader = parent == null ? ClassLoader.getSystemClassLoader() : parent;
}
protected ClassLoader getClassLoader() {
return classLoader;
}
}

View file

@ -0,0 +1,42 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class BasicResponseURLConnection extends HttpURLConnection {
final String responseMessage;
final int responseCode;
public BasicResponseURLConnection(URL url, String response) {
this(url, 200, response);
}
public BasicResponseURLConnection(URL url, int responseCode, String response) {
super(url);
this.responseMessage = response;
this.responseCode = responseCode;
}
@Override
public void connect() throws IOException {}
@Override
public void disconnect() {}
@Override
public boolean usingProxy() {
return false;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(responseMessage.getBytes());
}
public int getResponseCode() {
return responseCode;
}
}

View file

@ -0,0 +1,51 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
public class LegacyURLStreamHandler extends URLStreamHandlerProxy {
// private LegacyProxyData data;
//
// public LegacyURLStreamHandler(LegacyProxyData proxyData) {
// data = proxyData;
// }
@Override
protected URLConnection openConnection(URL url) throws IOException {
String host = url.getHost();
String path = url.getPath();
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"))
return new BasicResponseURLConnection(url, "ok");
else if (host.equals("login.minecraft.net") && path.equals("/session"))
return new BasicResponseURLConnection(url, "ok");
else if (path.equals("/game/"))
return new BasicResponseURLConnection(url, "42069");
else if (path.equals("/haspaid.jsp"))
return new BasicResponseURLConnection(url, "true");
else if (path.contains("/level/save.html"))
return new SaveLevelURLConnection(url);
else if (path.contains("/level/load.html"))
return new LoadLevelURLConnection(url);
else if (path.equals("/listmaps.jsp"))
return new ListLevelsURLConnection(url);
else if (path.startsWith("/MinecraftResources/"))
return new ResourceIndexURLConnection(url, true);
else if (path.startsWith("/resources/"))
return new ResourceIndexURLConnection(url, false);
else if (path.startsWith("/MinecraftSkins/") || path.startsWith("/skin/"))
return new SkinURLConnection(url);
else if (path.startsWith("/MinecraftCloaks/") || path.startsWith("/cloak/"))
return new SkinURLConnection(url);
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/");
}
}
return super.openConnection(url);
}
}

View file

@ -0,0 +1,60 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.util.Util;
public class ListLevelsURLConnection extends URLConnection {
public static final String EMPTY_LEVEL = "-";
public ListLevelsURLConnection(URL url) {
super(url);
}
@Override
public void connect() throws IOException {}
public InputStream getInputStream() throws IOException {
File levels = new File(Launch.INSTANCE.config.gameDir.get(), "levels");
File levelNames = new File(levels, "levels.txt");
int maxLevels = 5;
String[] lvlNames = new String[maxLevels];
for(int i = 0; i < maxLevels; i++) {
lvlNames[i] = EMPTY_LEVEL;
}
if(levelNames.exists()) {
FileInputStream levelNamesStream = new FileInputStream(levelNames);
lvlNames = new String(Util.readStream(levelNamesStream)).split(";");
levelNamesStream.close();
}
boolean levelsChanged = false;
for(int i = 0; i < maxLevels; i++) {
File level = new File(levels, "level" + i + ".dat");
if(!level.exists()) {
if(!lvlNames[i].equals(EMPTY_LEVEL)) {
levelsChanged = true;
lvlNames[i] = EMPTY_LEVEL;
}
}
}
String lvls = "";
for(int i = 0; i < maxLevels; i++) {
lvls += lvlNames[i] + ";";
}
if(levelsChanged) {
FileOutputStream outputNames = new FileOutputStream(levelNames);
outputNames.write(lvls.getBytes());
outputNames.close();
}
return new FileInputStream(levelNames);
}
}

View file

@ -0,0 +1,77 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.util.Util;
public class LoadLevelURLConnection extends HttpURLConnection {
Exception exception;
public LoadLevelURLConnection(URL url) {
super(url);
}
@Override
public void connect() throws IOException {}
@Override
public void disconnect() {
}
@Override
public boolean usingProxy() {
return false;
}
@Override
public InputStream getInputStream() throws IOException {
ByteArrayOutputStream data = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(data);
try {
Map<String, String> query = Util.queryMap(url);
if(!query.containsKey("id")) {
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 level = new File(levels, "level" + levelId + ".dat");
if(!level.exists()) {
throw new FileNotFoundException("Level doesn't exist");
}
DataInputStream in = new DataInputStream(new FileInputStream(level));
byte[] levelData = Util.readStream(in);
output.writeUTF("ok");
output.write(levelData);
output.close();
} catch (Exception e) {
exception = e;
}
if(exception != null) {
try {
Thread.sleep(100L); // Needs some sleep because it won't display error message if it's too fast
} catch (InterruptedException e) {
}
ByteArrayOutputStream errorData = new ByteArrayOutputStream();
DataOutputStream errorOutput = new DataOutputStream(errorData);
errorOutput.writeUTF("error");
errorOutput.writeUTF(exception.getMessage());
errorOutput.close();
return new ByteArrayInputStream(errorData.toByteArray());
}
return new ByteArrayInputStream(data.toByteArray());
}
}

View file

@ -0,0 +1,59 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.mcphackers.launchwrapper.Launch;
public class ResourceIndexURLConnection extends URLConnection {
private static final String[] RESOURCES = new String[] {"/resources/", "/MinecraftResources/"};
private boolean xmlMode;
public ResourceIndexURLConnection(URL u, boolean xmlMode) {
super(u);
this.xmlMode = xmlMode;
}
@Override
public void connect() throws IOException {}
@Override
public InputStream getInputStream() throws IOException {
File assetsDir = new File(Launch.INSTANCE.config.gameDir.get(), "resources");
String path = url.getPath();
for(String template : RESOURCES) {
if(path.startsWith(template)) {
String assetName = path.substring(template.length());
if(assetName.isEmpty()) {
File indexFile;
if (xmlMode) {
indexFile = new File(assetsDir, "index.xml");
} else {
indexFile = new File(assetsDir, "index.txt");
}
if(indexFile.exists()) {
return new FileInputStream(indexFile);
} else {
//TODO download from another host if missing locally
throw new FileNotFoundException("Missing resource index");
}
}
//TODO download from another host at all times
// it only requests it if (file.length() != resourceIndexFileLength || !file.exists())
File asset = new File(assetsDir, assetName);
if(asset.exists()) {
return new FileInputStream(asset);
} else {
throw new FileNotFoundException(assetName);
}
}
}
throw new MalformedURLException(url.toExternalForm() + " is not a resource index");
}
}

View file

@ -0,0 +1,108 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.util.Util;
import static org.mcphackers.launchwrapper.protocol.ListLevelsURLConnection.EMPTY_LEVEL;
public class SaveLevelURLConnection extends HttpURLConnection {
ByteArrayOutputStream levelOutput = new ByteArrayOutputStream();
Exception exception;
public SaveLevelURLConnection(URL url) {
super(url);
}
@Override
public void connect() {
}
@Override
public void disconnect() {
}
@Override
public boolean usingProxy() {
return false;
}
@SuppressWarnings("unused")
@Override
public InputStream getInputStream() throws IOException {
try {
File levels = new File(Launch.INSTANCE.config.gameDir.get(), "levels");
byte[] data = levelOutput.toByteArray();
DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
String username = in.readUTF();
String sessionid = in.readUTF();
String levelName = in.readUTF();
byte levelId = in.readByte();
int length = in.readInt();
byte[] levelData = new byte[length];
in.read(levelData);
in.close();
File level = new File(levels, "level" + levelId + ".dat");
File levelNames = new File(levels, "levels.txt");
int maxLevels = 5;
String[] lvlNames = new String[maxLevels];
for(int i = 0; i < maxLevels; i++) {
lvlNames[i] = EMPTY_LEVEL;
}
if(levelNames.exists()) {
FileInputStream levelNamesStream = new FileInputStream(levelNames);
lvlNames = new String(Util.readStream(levelNamesStream)).split(";");
levelNamesStream.close();
}
lvlNames[levelId] = levelName;
if(levelName.equals("---")) {
level.delete();
lvlNames[levelId] = EMPTY_LEVEL;
}
else {
if(!levels.exists()) {
levels.mkdirs();
}
OutputStream fileOutput = new FileOutputStream(level);
fileOutput.write(levelData);
fileOutput.close();
}
FileOutputStream outputNames = new FileOutputStream(levelNames);
String lvls = "";
for(int i = 0; i < maxLevels; i++) {
lvls += lvlNames[i] + ";";
}
outputNames.write(lvls.getBytes());
outputNames.close();
} catch (Exception e) {
exception = e;
}
if(exception != null) {
try {
Thread.sleep(100L); // Needs some sleep because it won't display error message if it's too fast
} catch (InterruptedException e) {
}
String err = "error\n" + exception.getMessage();
exception.printStackTrace();
return new ByteArrayInputStream(err.getBytes());
}
return new ByteArrayInputStream("ok".getBytes());
}
@Override
public OutputStream getOutputStream() throws IOException {
return levelOutput;
}
}

View file

@ -0,0 +1,239 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import org.json.JSONObject;
import org.mcphackers.launchwrapper.util.Base64;
import org.mcphackers.launchwrapper.util.ImageUtils;
import org.mcphackers.launchwrapper.util.Util;
public class SkinRequests {
// name->uuid requests have a rate limit, so here's cache
public static HashMap<String, String> nameToUUID = new HashMap<String, String>();
public static String getUUIDfromName(String username) {
String cachedUUID = nameToUUID.get(username);
if (cachedUUID != null) {
return cachedUUID;
} else {
JSONObject obj = requestUUIDfromName(username);
if (obj != null) {
String uuid = obj.optString("id");
nameToUUID.put(username, uuid);
return uuid;
}
}
return null;
}
public static JSONObject requestUUIDfromName(String name) {
try {
int attemptCount = 0;
while(attemptCount < 2) {
attemptCount++;
URL nametouuidURL = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
HttpURLConnection connection = (HttpURLConnection)nametouuidURL.openConnection();
if(connection.getResponseCode() == 429) {
// wait for the block to pass
Thread.sleep(300000);
continue;
}
InputStream connStream = connection.getInputStream();
JSONObject uuidjson = new JSONObject(new String(Util.readStream(connStream), "UTF-8"));
return uuidjson;
}
} catch (Throwable t) {
}
return null;
}
public static SkinData fetchSkin(String uuid) {
try {
URL uuidtoprofileURL = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid);
JSONObject profilejson = new JSONObject(new String(Util.readStream(uuidtoprofileURL.openStream()), "UTF-8"));
String base64tex = profilejson.getJSONArray("properties").getJSONObject(0).optString("value");
String texjsonstr = new String(Base64.decode(base64tex), "UTF-8");
JSONObject texjson = new JSONObject(texjsonstr);
JSONObject txts = texjson.optJSONObject("textures");
JSONObject skinjson = txts.optJSONObject("SKIN");
String skinURLstr = skinjson.optString("url");
byte[] officialCape = null;
byte[] officialSkin = null;
JSONObject capejson = txts.optJSONObject("CAPE");
if (capejson != null) {
String capeURLstr = capejson.optString("url");
URL capeURL = new URL(capeURLstr);
officialCape = Util.readStream(capeURL.openStream());
}
boolean slim = false;
JSONObject metadata = skinjson.optJSONObject("metadata");
if(metadata != null) {
slim = "slim".equals(metadata.optString("model"));
}
URL skinURL = new URL(skinURLstr);
officialSkin = Util.readStream(skinURL.openStream());
return new SkinData(officialSkin, officialCape, slim);
} catch (Throwable t) {
t.printStackTrace();
}
return null;
}
public static class SkinData {
public byte[] skin;
public byte[] cape;
public boolean slim;
public SkinData(byte[] skin, byte[] cape, boolean slim) {
this.skin = skin;
this.cape = cape;
this.slim = slim;
}
}
public static byte[] getCape(String name, SkinType skinType) {
String uuid = getUUIDfromName(name);
if(uuid == null) {
return null;
}
SkinData skindata = fetchSkin(uuid);
byte[] cape = null;
try {
if (skindata.cape != null) {
switch(skinType) {
case CLASSIC:
case PRE_B1_9:
case PRE_1_7:
case PRE_1_8:
ByteArrayInputStream bis = new ByteArrayInputStream(skindata.cape);
ImageUtils imgu = new ImageUtils(bis);
imgu = imgu.crop(0, 0, 64, 32);
cape = imgu.getInByteForm();
break;
default:
return skindata.cape;
}
}
} catch (Throwable t) {
t.printStackTrace();
}
return cape;
}
public static byte[] getSkin(String name, SkinType skinType) {
String uuid = getUUIDfromName(name);
if(uuid == null) {
return null;
}
SkinData skindata = fetchSkin(uuid);
byte[] skin = null;
try {
if (skindata.skin != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(skindata.skin);
ImageUtils imgu = new ImageUtils(bis);
switch(skinType) {
case CLASSIC:
imgu = overlayHeadLayer(imgu);
case PRE_B1_9:
rotateBottomTX(imgu);
case PRE_1_7:
useLeftArm(imgu);
useLeftLeg(imgu);
case PRE_1_8:
imgu = overlay64to32(imgu);
if(skindata.slim)
alexToSteve(imgu);
imgu = imgu.crop(0, 0, 64, 32);
default:
break;
}
skin = imgu.getInByteForm();
}
} catch (Throwable t) {
t.printStackTrace();
}
return skin;
}
public static void rotateBottomTX(ImageUtils imgu) {
// bottom head
imgu.setArea(16, 0, imgu.crop(16, 0, 8, 8).flip(false, true).getImage());
// bottom head layer
imgu.setArea(48, 0, imgu.crop(48, 0, 8, 8).flip(false, true).getImage());
// bottom feet
imgu.setArea(8, 16, imgu.crop(8, 16, 4, 4).flip(false, true).getImage());
// bottom hand
imgu.setArea(48, 16, imgu.crop(48, 16, 4, 4).flip(false, true).getImage());
// bottom torso
imgu.setArea(28, 16, imgu.crop(28, 16, 8, 4).flip(false, true).getImage());
}
public static ImageUtils overlay64to32(ImageUtils imgu) {
// 32-64 body
return imgu.setArea(0, 16, imgu.crop(0, 32, 56, 16).getImage(), false);
}
public static ImageUtils overlayHeadLayer(ImageUtils imgu) {
return imgu.setArea(0, 0, imgu.crop(32, 0, 32, 16).getImage(), false);
}
public static void useLeftLeg(ImageUtils imgu) {
// leg layer
imgu.setArea(16, 48, imgu.crop(0, 48, 16, 16).getImage(), false);
// overlay on the 0-32 part
imgu.setArea(0, 16, imgu.crop(16, 48, 16, 16).getImage());
}
public static void useLeftArm(ImageUtils imgu) {
// leg layer
imgu.setArea(32, 48, imgu.crop(48, 48, 16, 16).getImage(), false);
// overlay on the 0-32 part
imgu.setArea(40, 16, imgu.crop(32, 48, 16, 16).getImage());
}
public static void alexToSteve(ImageUtils imgu) {
// make space for the left arm 1px texture
imgu.setArea(48, 20, imgu.crop(47, 20, 7, 12).getImage());
// fill the 1px space in between
imgu.setArea(47, 20, imgu.crop(46, 20, 1, 12).getImage());
// fill the 1px space on the right
imgu.setArea(55, 20, imgu.crop(54, 20, 1, 12).getImage());
// bottom hand
imgu.setArea(48, 16, imgu.crop(47, 16, 3, 4).getImage());
// fill the 1px of the shoulder
imgu.setArea(47, 16, imgu.crop(46, 16, 1, 4).getImage());
// fill the 1px space on the right
imgu.setArea(51, 16, imgu.crop(50, 16, 1, 4).getImage());
}
public enum SkinType {
CLASSIC,
PRE_B1_9,
PRE_1_7,
PRE_1_8
}
}

View file

@ -0,0 +1,79 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.protocol.SkinRequests.SkinType;
import org.mcphackers.launchwrapper.util.Util;
public class SkinURLConnection extends HttpURLConnection {
private static final String[] CLOAKS = new String[] {"/cloak/", "/MinecraftCloaks/"};
private static final String[] SKINS = new String[] {"/skin/", "/MinecraftSkins/"};
protected SkinType skinType = SkinType.PRE_1_8;
protected InputStream inputStream;
public SkinURLConnection(URL url) {
super(url);
responseCode = 200;
}
@Override
public void disconnect() {}
@Override
public boolean usingProxy() {
return false;
}
@Override
public void connect() throws IOException {
File skinDir = new File(Launch.INSTANCE.config.gameDir.get(), "skins");
Map<String, String> queryMap = Util.queryMap(url);
String username = queryMap.get("user");
String path = url.getPath();
for (String template : CLOAKS) {
if (path.startsWith(template)) {
if(username == null)
username = path.substring(template.length()).replace(".png", "");
byte[] skinData = SkinRequests.getCape(username, skinType);
if(skinData != null) {
inputStream = new ByteArrayInputStream(skinData);
return;
}
}
}
for (String template : SKINS) {
if (path.startsWith(template)) {
if(username == null)
username = path.substring(template.length()).replace(".png", "");
File cached = new File(skinDir, username + ".png");
if(cached.exists()) {
inputStream = new FileInputStream(cached);
return;
}
byte[] skinData = SkinRequests.getSkin(username, skinType);
if(skinData != null) {
inputStream = new ByteArrayInputStream(skinData);
return;
}
}
}
responseCode = 404;
}
@Override
public InputStream getInputStream() throws IOException {
return inputStream;
}
}

View file

@ -0,0 +1,50 @@
package org.mcphackers.launchwrapper.protocol;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Hashtable;
import org.mcphackers.launchwrapper.util.Util;
public abstract class URLStreamHandlerProxy extends URLStreamHandler {
protected URLStreamHandler parent;
@Override
protected final URLConnection openConnection(URL url, Proxy p) throws IOException {
return openConnection(url);
}
protected URLConnection openConnection(URL url) throws IOException {
if(parent == null) {
return null;
}
return new URL(null, url.toString(), parent).openConnection();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void setURLStreamHandler(String protocol, URLStreamHandlerProxy handler) {
handler.parent = getURLStreamHandler(protocol);
try {
Field handlersField = URL.class.getDeclaredField("handlers");
Hashtable handlers = (Hashtable)Util.getStaticObjectUnsafe(handlersField);
handlers.put(protocol, handler);
} catch (Exception e) {
e.printStackTrace();
}
}
public static URLStreamHandler getURLStreamHandler(String protocol) {
try {
URL url = new URL(protocol + ":");
Field handlerField = URL.class.getDeclaredField("handler");
return (URLStreamHandler)Util.getObjectUnsafe(url, handlerField);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View file

@ -1,4 +1,4 @@
package org.mcphackers.launchwrapper.inject;
package org.mcphackers.launchwrapper.tweak;
import java.applet.Applet;
import java.applet.AppletStub;
@ -11,11 +11,19 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
public class AppletWrapper extends Applet implements AppletStub {
private static final long serialVersionUID = 1L;
private Map<String, String> args;
public void appletResize(int width, int height) {
public AppletWrapper(Map<String, String> argsAsMap) {
args = argsAsMap;
}
public void appletResize(int width, int height) {
}
@Override
@ -45,12 +53,7 @@ public class AppletWrapper extends Applet implements AppletStub {
@Override
public String getParameter(String paramName) {
//TODO custom parameters
if(paramName.equals("stand-alone")) {
return "true";
}
System.err.println("Client asked for parameter: " + paramName);
return null;
return args.get(paramName);
}
public static void startApplet(Class<? extends Applet> appletClass, int width, int height, String title) {
@ -61,7 +64,7 @@ public class AppletWrapper extends Applet implements AppletStub {
try {
final Applet applet = appletClass.newInstance();
if(applet != null) {
applet.setStub(new AppletWrapper());
applet.setStub(new AppletWrapper(Collections.<String, String>emptyMap()));
}
final Frame frame = new Frame(title);
frame.setBackground(Color.BLACK);

View file

@ -1,990 +0,0 @@
package org.mcphackers.launchwrapper.tweak;
import static org.mcphackers.launchwrapper.inject.InsnHelper.*;
import static org.objectweb.asm.Opcodes.*;
import java.util.Arrays;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.LaunchTarget;
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
import org.mcphackers.launchwrapper.inject.InjectUtils;
import org.mcphackers.launchwrapper.inject.InjectUtils.Access;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
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.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class DefaultTweak extends Tweak {
protected Launch launch;
public static final String[] MAIN_CLASSES = {
"net/minecraft/client/Minecraft",
"com/mojang/minecraft/Minecraft",
"com/mojang/minecraft/RubyDung",
"com/mojang/rubydung/RubyDung"
};
public static final String[] MAIN_APPLETS = {
"net/minecraft/client/MinecraftApplet",
"com/mojang/minecraft/MinecraftApplet"
};
protected ClassNode minecraft;
protected ClassNode minecraftApplet;
/** Class containing username and sessionId */
protected ClassNode user;
/** Field of user class */
protected FieldNode userField;
/** Field that determines if Minecraft should exit */
protected FieldNode running;
/** Frame width */
protected FieldNode width;
/** Frame height */
protected FieldNode height;
/** Working game directory */
protected FieldNode mcDir;
/** public static main(String[]) */
protected MethodNode main;
/** Class used to instantiate a Minecraft instance */
protected ClassNode minecraftImpl;
public DefaultTweak(ClassNodeSource source, Launch launch) {
super(source);
this.launch = launch;
init();
}
public boolean transform() {
if(minecraft == null) {
return false;
}
if(mcDir != null) {
if(InjectUtils.isStatic(mcDir)) {
for(MethodNode m : minecraft.methods) {
if(m.name.equals("<clinit>") && m.desc.equals("()V")) {
InsnList insns = new InsnList();
insns.add(new TypeInsnNode(NEW, "java/io/File"));
insns.add(new InsnNode(DUP));
insns.add(new LdcInsnNode(launch.gameDir.getAbsolutePath()));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/lang/String;)V"));
insns.add(new FieldInsnNode(PUTSTATIC, minecraft.name, mcDir.name, mcDir.desc));
m.instructions.insertBefore(getLastReturn(m.instructions.getLast()), insns);
break;
}
}
} else {
for(MethodNode m : minecraft.methods) {
if(m.name.equals("<init>")) {
InsnList insns = new InsnList();
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new TypeInsnNode(NEW, "java/io/File"));
insns.add(new InsnNode(DUP));
insns.add(new LdcInsnNode(launch.gameDir.getAbsolutePath()));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/lang/String;)V"));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, mcDir.name, mcDir.desc));
m.instructions.insertBefore(getLastReturn(m.instructions.getLast()), insns);
}
}
}
}
MethodNode constructor = null;
if(width != null && height != null) {
for(MethodNode m : minecraft.methods) {
if(m.name.equals("<init>")) {
constructor = m;
AbstractInsnNode insn = m.instructions.getFirst();
boolean widthReplaced = false;
boolean heightReplaced = false;
while(insn != null) {
AbstractInsnNode[] insns = fill(insn, 3);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[2], PUTFIELD, minecraft.name, width.name, width.desc)) {
m.instructions.set(insns[1], intInsn(launch.width));
widthReplaced = true;
}
else if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[2], PUTFIELD, minecraft.name, height.name, height.desc)) {
m.instructions.set(insns[1], intInsn(launch.height));
heightReplaced = true;
}
insn = insn.getNext();
}
if(!widthReplaced || !heightReplaced) {
InsnList insns = new InsnList();
if(!widthReplaced) {
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(intInsn(launch.width));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, width.name, width.desc));
}
if(!heightReplaced) {
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(intInsn(launch.height));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, height.name, height.desc));
}
m.instructions.insertBefore(getLastReturn(m.instructions.getLast()), insns);
}
}
}
}
boolean hasInit = true;
MethodNode init = null;
MethodNode update = null;
MethodNode run = InjectUtils.getMethod(minecraft, "run", "()V");
if(run != null) {
AbstractInsnNode insn = run.instructions.getFirst();
while(insn != null) {
if(hasInit && insn.getType() == AbstractInsnNode.METHOD_INSN) {
hasInit = insn.getOpcode() == INVOKEVIRTUAL;
}
if(hasInit && init == null && insn.getOpcode() == INVOKEVIRTUAL) {
MethodInsnNode invoke = (MethodInsnNode)insn;
init = InjectUtils.getMethod(minecraft, invoke.name, invoke.desc);
}
AbstractInsnNode[] insns = fill(insn, 3);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], GETFIELD, minecraft.name, running.name, running.desc)
&& compareInsn(insns[2], IFEQ)) {
while(insn != null) {
if(compareInsn(insn.getPrevious(), ALOAD, 0)
&& compareInsn(insn, INVOKESPECIAL, minecraft.name, null, "()V")) {
if(update == null) {
MethodInsnNode invoke = (MethodInsnNode)insn;
update = InjectUtils.getMethod(minecraft, invoke.name, invoke.desc);
} else {
break;
}
}
insn = insn.getNext();
}
break;
}
insn = insn.getNext();
}
} else {
return false;
}
for(InsnList insnList1 : update == null ? new InsnList[] {run.instructions} : new InsnList[] {run.instructions, update.instructions}) {
AbstractInsnNode insn1 = insnList1.getFirst();
while(insn1 != null) {
AbstractInsnNode[] insns = fill(insn1, 3);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], GETFIELD, minecraft.name, null, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], INVOKEVIRTUAL, "java/awt/Canvas", "getWidth", "()I")) {
MethodInsnNode invoke = new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getWidth", "()I");
insnList1.insert(insns[2], invoke);
insnList1.remove(insns[0]);
insnList1.remove(insns[1]);
insnList1.remove(insns[2]);
insn1 = invoke;
}
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], GETFIELD, minecraft.name, null, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], INVOKEVIRTUAL, "java/awt/Canvas", "getHeight", "()I")) {
MethodInsnNode invoke = new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getHeight", "()I");
insnList1.insert(insns[2], invoke);
insnList1.remove(insns[0]);
insnList1.remove(insns[1]);
insnList1.remove(insns[2]);
insn1 = invoke;
}
insn1 = insn1.getNext();
}
insn1 = insnList1.getFirst();
while(insn1 != null) {
AbstractInsnNode[] insns = fill(insn1, 6);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], GETFIELD, minecraft.name, null, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], IFNULL)
&& compareInsn(insns[3], ALOAD)
&& compareInsn(insns[4], GETFIELD, minecraft.name, null, "Z")
&& compareInsn(insns[5], IFNE)) {
if(((JumpInsnNode)insns[2]).label != ((JumpInsnNode)insns[5]).label) {
continue;
}
insnList1.remove(insns[0]);
insnList1.remove(insns[1]);
insnList1.remove(insns[2]);
break;
}
insn1 = insn1.getNext();
}
}
AbstractInsnNode insn1 = run.instructions.getLast();
while(insn1 != null && insn1.getOpcode() != ATHROW) {
insn1 = insn1.getPrevious();
}
{
AbstractInsnNode[] insns = fillBackwards(insn1, 4);
if(compareInsn(insns[3], ATHROW)
&& compareInsn(insns[1], INVOKEVIRTUAL, minecraft.name, null, "()V")
&& compareInsn(insns[0], ALOAD)
&& compareInsn(insns[2], ALOAD)) {
MethodInsnNode invoke = (MethodInsnNode)insns[1];
MethodNode stop = InjectUtils.getMethod(minecraft, invoke.name, invoke.desc);
AbstractInsnNode insn2 = stop.instructions.getFirst();
while(insn2 != null) {
if(compareInsn(insn2, INVOKESTATIC, "org/lwjgl/opengl/Display", "destroy", "()V")) {
AbstractInsnNode insn3 = insn2.getNext();
while(insn3 != null && (insn3.getType() == AbstractInsnNode.LINE || insn3.getType() == AbstractInsnNode.LABEL)) {
insn3 = insn3.getNext();
}
if(insn3 != null) {
AbstractInsnNode[] insns2 = fill(insn3, 2);
if(compareInsn(insns2[0], ICONST_0)
&& compareInsn(insns2[1], INVOKESTATIC, "java/lang/System", "exit", "(I)V")) {
} else {
InsnList insert = new InsnList();
insert.add(new InsnNode(ICONST_0));
insert.add(new MethodInsnNode(INVOKESTATIC, "java/lang/System", "exit", "(I)V"));
stop.instructions.insert(insn2, insert);
}
}
}
insn2 = insn2.getNext();
}
}
}
FieldInsnNode fullscreenField = null;
String canvasName = null;
{
int thisIndex = 0;
LabelNode aLabel = new LabelNode();
LabelNode iLabel = null;
LabelNode oLabel = null;
AbstractInsnNode afterLabel = null;
JumpInsnNode ifNoCanvas = null;
JumpInsnNode ifFullscreen = null;
InsnList insnList = init == null ? run.instructions : init.instructions;
AbstractInsnNode insn = insnList.getFirst();
while(insn != null) {
AbstractInsnNode[] insns = fill(insn, 6);
if(iLabel == null
&& compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], GETFIELD, minecraft.name, null, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], IFNULL)) {
thisIndex = ((VarInsnNode)insns[0]).var;
canvasName = ((FieldInsnNode)insns[1]).name;
ifNoCanvas = (JumpInsnNode)insns[2];
iLabel = ifNoCanvas.label;
afterLabel = insns[0];
}
if(iLabel == null
&& compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], DUP)
&& compareInsn(insns[2], ASTORE)
&& compareInsn(insns[3], GETFIELD, minecraft.name, null, "Ljava/awt/Canvas;")
&& compareInsn(insns[4], IFNULL)) {
thisIndex = ((VarInsnNode)insns[2]).var;
canvasName = ((FieldInsnNode)insns[3]).name;
ifNoCanvas = (JumpInsnNode)insns[4];
iLabel = ifNoCanvas.label;
VarInsnNode aload = new VarInsnNode(ALOAD, thisIndex);
insnList.insert(insns[2], aload);
insnList.remove(insns[1]);
afterLabel = aload;
}
// Any other pre-classic version
if(compareInsn(insns[0], NEW, "org/lwjgl/opengl/DisplayMode")
&& compareInsn(insns[1], DUP)
&& compareInsn(insns[2], SIPUSH)
&& compareInsn(insns[3], SIPUSH)
&& compareInsn(insns[4], INVOKESPECIAL, "org/lwjgl/opengl/DisplayMode", "<init>", "(II)V")
&& compareInsn(insns[5], INVOKESTATIC, "org/lwjgl/opengl/Display", "setDisplayMode", "(Lorg/lwjgl/opengl/DisplayMode;)V")) {
InsnList insert = getIcon();
if(!launch.fullscreen) {
insnList.insert(insns[2], intInsn(launch.width));
insnList.insert(insns[3], intInsn(launch.height));
insnList.insert(insns[5], insert);
insnList.remove(insns[2]);
insnList.remove(insns[3]);
} else {
insert.add(new InsnNode(ICONST_1));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setFullscreen", "(Z)V"));
insnList.insert(insns[5], insert);
removeRange(insnList, insns[0], insns[5]);
}
}
// rd-152252
else if(compareInsn(insns[0], ICONST_1)
&& compareInsn(insns[1], INVOKESTATIC, "org/lwjgl/opengl/Display", "setFullscreen", "(Z)V")
&& insns[2] != null && insns[2].getType() == AbstractInsnNode.LABEL
&& compareInsn(insns[4], INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "()V")) {
InsnList insert = getIcon();
if(!launch.fullscreen) {
insert.add(new TypeInsnNode(NEW, "org/lwjgl/opengl/DisplayMode"));
insert.add(new InsnNode(DUP));
insert.add(intInsn(launch.width));
insert.add(intInsn(launch.height));
insert.add(new MethodInsnNode(INVOKESPECIAL, "org/lwjgl/opengl/DisplayMode", "<init>", "(II)V"));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setDisplayMode", "(Lorg/lwjgl/opengl/DisplayMode;)V"));
insnList.insert(insns[1], insert);
insnList.remove(insns[0]);
insnList.remove(insns[1]);
} else {
insnList.insert(insns[1], insert);
}
}
if(oLabel == null
&& compareInsn(insns[0], ICONST_1)
&& compareInsn(insns[1], INVOKESTATIC, "org/lwjgl/opengl/Display", "setFullscreen", "(Z)V")) {
AbstractInsnNode insn2 = insns[0];
while(insn2 != null) {
if(insn2.getOpcode() == IFEQ) {
if(compareInsn(insn2.getPrevious(), GETFIELD)) {
fullscreenField = (FieldInsnNode)insn2.getPrevious();
}
ifFullscreen = (JumpInsnNode)insn2;
oLabel = ifFullscreen.label;
}
insn2 = insn2.getPrevious();
}
}
if(insn.getOpcode() == LDC) {
LdcInsnNode ldc = (LdcInsnNode)insn;
if(ldc.cst instanceof String) {
String value = (String)ldc.cst;
if(value.startsWith("Minecraft Minecraft")) {
ldc.cst = value.substring(10);
}
}
}
if(canvasName != null && launch.lwjglFrame) {
boolean found = compareInsn(insns[0], ALOAD, thisIndex)
&& compareInsn(insns[1], GETFIELD, minecraft.name, canvasName, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], INVOKESPECIAL, null, "<init>", "(Ljava/awt/Component;)V");
boolean found2 = found ? false :
compareInsn(insns[0], ALOAD, thisIndex)
&& compareInsn(insns[1], GETFIELD, minecraft.name, canvasName, "Ljava/awt/Canvas;")
&& compareInsn(insns[2], ALOAD, thisIndex)
&& compareInsn(insns[3], GETFIELD, minecraft.name)
&& compareInsn(insns[4], INVOKESPECIAL, null, "<init>");
if(found || found2) {
String owner = found ? ((MethodInsnNode)insns[2]).owner : ((MethodInsnNode)insns[4]).owner;
ClassNode mouseHelper = source.getClass(owner);
for(MethodNode m : mouseHelper.methods) {
AbstractInsnNode insn2 = m.instructions.getFirst();
while(insn2 != null) {
AbstractInsnNode[] insns2 = fill(insn2, 4);
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], GETFIELD, mouseHelper.name, null, "Ljava/awt/Component;")
&& compareInsn(insns2[2], INVOKEVIRTUAL, "java/awt/Component", "getParent", "()Ljava/awt/Container;")
&& compareInsn(insns2[3], IFNULL)) {
LabelNode gotoLabel = ((JumpInsnNode)insns2[3]).label;
m.instructions.insertBefore(insns2[0], new JumpInsnNode(GOTO, gotoLabel));
}
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], GETFIELD, mouseHelper.name, null, "Ljava/awt/Component;")
&& compareInsn(insns2[2], INVOKEVIRTUAL, "java/awt/Component", "getWidth", "()I")) {
MethodInsnNode invoke = new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getWidth", "()I");
m.instructions.insert(insns2[2], invoke);
m.instructions.remove(insns2[0]);
m.instructions.remove(insns2[1]);
m.instructions.remove(insns2[2]);
insn2 = invoke;
}
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], GETFIELD, mouseHelper.name, null, "Ljava/awt/Component;")
&& compareInsn(insns2[2], INVOKEVIRTUAL, "java/awt/Component", "getHeight", "()I")) {
MethodInsnNode invoke = new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getHeight", "()I");
m.instructions.insert(insns2[2], invoke);
m.instructions.remove(insns2[0]);
m.instructions.remove(insns2[1]);
m.instructions.remove(insns2[2]);
insn2 = invoke;
}
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], GETFIELD, mouseHelper.name, null, "Ljava/awt/Component;")
&& compareInsn(insns2[2], INVOKEVIRTUAL, "java/awt/Component", "getLocationOnScreen", "()Ljava/awt/Point;")) {
InsnList insert = new InsnList();
insert.add(new TypeInsnNode(NEW, "java/awt/Point"));
insert.add(new InsnNode(DUP));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getX", "()I"));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "getY", "()I"));
insert.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/Point", "<init>", "(II)V"));
insn2 = insert.getLast();
m.instructions.insert(insns2[2], insert);
m.instructions.remove(insns2[0]);
m.instructions.remove(insns2[1]);
m.instructions.remove(insns2[2]);
}
insn2 = insn2.getNext();
}
}
source.overrideClass(mouseHelper);
}
}
insn = insn.getNext();
}
if(afterLabel != null
&& iLabel != null
&& oLabel != null
&& ifNoCanvas != null
&& ifFullscreen != null) {
insnList.insertBefore(afterLabel, aLabel);
InsnList insert = new InsnList();
insert.add(new InsnNode(ICONST_1));
insert.add(new TypeInsnNode(ANEWARRAY, "java/nio/ByteBuffer"));
insert.add(new InsnNode(DUP));
insert.add(new InsnNode(ICONST_0));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/mcphackers/launchwrapper/Launch", "loadIcon", "()Ljava/nio/ByteBuffer;"));
insert.add(new InsnNode(AASTORE));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setIcon", "([Ljava/nio/ByteBuffer;)I"));
insert.add(new InsnNode(POP));
insert.add(new InsnNode(ICONST_1));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setResizable", "(Z)V"));
insert.add(new VarInsnNode(ALOAD, thisIndex));
insert.add(new FieldInsnNode(GETFIELD, minecraft.name, canvasName, "Ljava/awt/Canvas;"));
insert.add(new JumpInsnNode(IFNULL, iLabel));
insert.add(new VarInsnNode(ALOAD, thisIndex));
insert.add(new FieldInsnNode(GETFIELD, minecraft.name, canvasName, "Ljava/awt/Canvas;"));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setParent", "(Ljava/awt/Canvas;)V"));
insert.add(new JumpInsnNode(GOTO, iLabel));
insnList.insertBefore(aLabel, insert);
ifNoCanvas.label = oLabel;
ifFullscreen.label = aLabel;
}
}
if(fullscreenField != null) {
FieldNode defaultWidth = null;
FieldNode defaultHeight = null;
methodLoop:
for(MethodNode m : minecraft.methods) {
AbstractInsnNode insn2 = m.instructions.getFirst();
while(insn2 != null) {
if(insn2.getOpcode() == GETFIELD) {
if(compareInsn(insn2, GETFIELD, minecraft.name, fullscreenField.name, fullscreenField.desc)) {
break;
} else {
continue methodLoop;
}
}
insn2 = insn2.getNext();
}
while(insn2 != null) {
AbstractInsnNode[] insns2 = fill(insn2, 4);
if(compareInsn(insns2[0], INVOKESTATIC, "org/lwjgl/opengl/Display", "setFullscreen", "(Z)V")) {
InsnList insert = new InsnList();
// OpenGL is fucked up
// setResizable returns early if the "previous" value of this flag is equal to the newly set one
// Toggling fullscreen resets the window resizability but does not update the flag
// Therefore it needs to be re-toggled to function properly
insert.add(new InsnNode(ICONST_0));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setResizable", "(Z)V"));
insert.add(new InsnNode(ICONST_1));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setResizable", "(Z)V"));
m.instructions.insert(insns2[0], insert);
}
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], ALOAD)
&& compareInsn(insns2[2], GETFIELD, minecraft.name, null, width.desc)
&& compareInsn(insns2[3], PUTFIELD, minecraft.name, width.name, width.desc)) {
defaultWidth = InjectUtils.getField(minecraft, ((FieldInsnNode)insns2[2]).name, width.desc);
}
if(compareInsn(insns2[0], ALOAD)
&& compareInsn(insns2[1], ALOAD)
&& compareInsn(insns2[2], GETFIELD, minecraft.name, null, height.desc)
&& compareInsn(insns2[3], PUTFIELD, minecraft.name, height.name, height.desc)) {
defaultHeight = InjectUtils.getField(minecraft, ((FieldInsnNode)insns2[2]).name, height.desc);
}
if(defaultWidth != null && defaultHeight != null
&& compareInsn(insns2[0], IFGT)
&& compareInsn(insns2[1], ALOAD)
&& compareInsn(insns2[2], ICONST_1)
&& compareInsn(insns2[3], PUTFIELD, minecraft.name, height.name, height.desc)) {
JumpInsnNode jump = (JumpInsnNode)insns2[0];
LabelNode newLabel = new LabelNode();
jump.label = newLabel;
InsnList insert = new InsnList();
insert.add(newLabel);
insert.add(new TypeInsnNode(NEW, "org/lwjgl/opengl/DisplayMode"));
insert.add(new InsnNode(DUP));
insert.add(new VarInsnNode(ALOAD, 0));
insert.add(new FieldInsnNode(GETFIELD, minecraft.name, defaultWidth.name, defaultWidth.desc));
insert.add(new VarInsnNode(ALOAD, 0));
insert.add(new FieldInsnNode(GETFIELD, minecraft.name, defaultHeight.name, defaultHeight.desc));
insert.add(new MethodInsnNode(INVOKESPECIAL, "org/lwjgl/opengl/DisplayMode", "<init>", "(II)V"));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setDisplayMode", "(Lorg/lwjgl/opengl/DisplayMode;)V"));
m.instructions.insert(insns2[0], insert);
}
insn2 = insn2.getNext();
}
}
AbstractInsnNode insn = constructor.instructions.getFirst();
boolean replaced = false;
while(insn != null) {
AbstractInsnNode[] insns = fill(insn, 3);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[2], PUTFIELD, minecraft.name, fullscreenField.name, fullscreenField.desc)) {
constructor.instructions.set(insns[1], booleanInsn(launch.fullscreen));
replaced = true;
}
insn = insn.getNext();
}
//TODO try to place it after super() call, not before return
if(!replaced) {
InsnList insns = new InsnList();
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(booleanInsn(launch.fullscreen));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, fullscreenField.name, fullscreenField.desc));
constructor.instructions.insertBefore(getLastReturn(constructor.instructions.getLast()), insns);
}
if(defaultWidth != null && defaultHeight != null) {
InsnList insns = new InsnList();
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(intInsn(launch.width));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, defaultWidth.name, defaultWidth.desc));
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(intInsn(launch.height));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, defaultHeight.name, defaultHeight.desc));
constructor.instructions.insertBefore(getLastReturn(constructor.instructions.getLast()), insns);
}
}
final boolean replaceMain = true;
if(main == null) {
minecraft.methods.add(main = getMain());
} else if(replaceMain) {
minecraft.methods.remove(main);
minecraft.methods.add(main = getMain());
} else {
//TODO inject into main
}
source.overrideClass(minecraft);
return true;
}
private InsnList getIcon() {
InsnList insert = new InsnList();
insert.add(new InsnNode(ICONST_1));
insert.add(new TypeInsnNode(ANEWARRAY, "java/nio/ByteBuffer"));
insert.add(new InsnNode(DUP));
insert.add(new InsnNode(ICONST_0));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/mcphackers/launchwrapper/Launch", "loadIcon", "()Ljava/nio/ByteBuffer;"));
insert.add(new InsnNode(AASTORE));
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "setIcon", "([Ljava/nio/ByteBuffer;)I"));
insert.add(new InsnNode(POP));
return insert;
}
private MethodNode getMain() {
MethodNode node = new MethodNode(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
InsnList insns = node.instructions;
final int appletIndex = 1;
final int frameIndex = 2;
final int canvasIndex = 3;
final int mcIndex = 4;
final int threadIndex = 5;
final String listenerClass = "org/mcphackers/launchwrapper/inject/WindowListener";
if(minecraftApplet != null) {
insns.add(new TypeInsnNode(NEW, minecraftApplet.name));
insns.add(new InsnNode(DUP));
insns.add(new MethodInsnNode(INVOKESPECIAL, minecraftApplet.name, "<init>", "()V"));
insns.add(new VarInsnNode(ASTORE, appletIndex));
insns.add(new VarInsnNode(ALOAD, appletIndex));
insns.add(new TypeInsnNode(NEW, "org/mcphackers/launchwrapper/inject/AppletWrapper"));
insns.add(new InsnNode(DUP));
insns.add(new MethodInsnNode(INVOKESPECIAL, "org/mcphackers/launchwrapper/inject/AppletWrapper", "<init>", "()V"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/applet/Applet", "setStub", "(Ljava/applet/AppletStub;)V"));
}
if(!launch.lwjglFrame) {
insns.add(new TypeInsnNode(NEW, "java/awt/Frame"));
insns.add(new InsnNode(DUP));
insns.add(new LdcInsnNode("Minecraft"));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/Frame", "<init>", "(Ljava/lang/String;)V"));
insns.add(new VarInsnNode(ASTORE, frameIndex));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new FieldInsnNode(GETSTATIC, "org/mcphackers/launchwrapper/Launch", "ICON", "Ljava/awt/image/BufferedImage;"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "setIconImage", "(Ljava/awt/Image;)V"));
insns.add(new TypeInsnNode(NEW, "java/awt/Canvas"));
insns.add(new InsnNode(DUP));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/Canvas", "<init>", "()V"));
insns.add(new VarInsnNode(ASTORE, canvasIndex));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new TypeInsnNode(NEW, "java/awt/BorderLayout"));
insns.add(new InsnNode(DUP));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/BorderLayout", "<init>", "()V"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "setLayout", "(Ljava/awt/LayoutManager;)V"));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new VarInsnNode(ALOAD, canvasIndex));
insns.add(new LdcInsnNode("Center"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "add", "(Ljava/awt/Component;Ljava/lang/Object;)V"));
insns.add(new VarInsnNode(ALOAD, canvasIndex));
insns.add(new TypeInsnNode(NEW, "java/awt/Dimension"));
insns.add(new InsnNode(DUP));
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/Dimension", "<init>", "(II)V"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Canvas", "setPreferredSize", "(Ljava/awt/Dimension;)V"));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "pack", "()V"));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new InsnNode(ACONST_NULL));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "setLocationRelativeTo", "(Ljava/awt/Component;)V"));
}
insns.add(getMinecraftImpl());
insns.add(new VarInsnNode(ASTORE, mcIndex));
insns.add(new TypeInsnNode(NEW, "java/lang/Thread"));
insns.add(new InsnNode(DUP));
insns.add(new VarInsnNode(ALOAD, mcIndex));
insns.add(new LdcInsnNode("Minecraft main thread"));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/lang/Thread", "<init>", "(Ljava/lang/Runnable;Ljava/lang/String;)V"));
insns.add(new VarInsnNode(ASTORE, threadIndex));
insns.add(new VarInsnNode(ALOAD, threadIndex));
insns.add(new IntInsnNode(BIPUSH, 10));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Thread", "setPriority", "(I)V"));
if(user != null) {
insns.add(new VarInsnNode(ALOAD, mcIndex));
insns.add(new TypeInsnNode(NEW, user.name));
insns.add(new InsnNode(DUP));
insns.add(new LdcInsnNode(launch.username));
insns.add(new LdcInsnNode(launch.sessionId));
insns.add(new MethodInsnNode(INVOKESPECIAL, user.name, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, userField.name, userField.desc));
}
if(!launch.lwjglFrame) {
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new InsnNode(ICONST_1));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "setVisible", "(Z)V"));
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new TypeInsnNode(NEW, listenerClass));
insns.add(new InsnNode(DUP));
insns.add(new VarInsnNode(ALOAD, mcIndex));
insns.add(new VarInsnNode(ALOAD, threadIndex));
insns.add(new MethodInsnNode(INVOKESPECIAL, listenerClass, "<init>", "(L" + minecraft.name + ";Ljava/lang/Thread;)V"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "addWindowListener", "(Ljava/awt/event/WindowListener;)V"));
createWindowListener(listenerClass);
}
insns.add(new VarInsnNode(ALOAD, threadIndex));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Thread", "start", "()V"));
insns.add(new InsnNode(RETURN));
return node;
}
private void createWindowListener(String listenerClass) {
running.access = Access.PUBLIC.setAccess(running.access);
ClassNode node = new ClassNode();
node.visit(49, ACC_PUBLIC, listenerClass, null, "java/awt/event/WindowAdapter", null);
node.fields.add(new FieldNode(ACC_PRIVATE, "mc", "L" + minecraft.name + ";", null, null));
node.fields.add(new FieldNode(ACC_PRIVATE, "thread", "Ljava/lang/Thread;", null, null));
MethodNode init = new MethodNode(ACC_PUBLIC, "<init>", "(L" + minecraft.name + ";Ljava/lang/Thread;)V", null, null);
InsnList insns = init.instructions;
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new VarInsnNode(ALOAD, 1));
insns.add(new FieldInsnNode(PUTFIELD, listenerClass, "mc", "L" + minecraft.name + ";"));
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new VarInsnNode(ALOAD, 2));
insns.add(new FieldInsnNode(PUTFIELD, listenerClass, "thread", "Ljava/lang/Thread;"));
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/awt/event/WindowAdapter", "<init>", "()V"));
insns.add(new InsnNode(RETURN));
node.methods.add(init);
MethodNode windowClosing = new MethodNode(ACC_PUBLIC, "windowClosing", "(Ljava/awt/event/WindowEvent;)V", null, null);
insns = windowClosing.instructions;
LabelNode l0 = new LabelNode();
LabelNode l1 = new LabelNode();
LabelNode l2 = new LabelNode();
LabelNode l4 = new LabelNode();
windowClosing.tryCatchBlocks.add(new TryCatchBlockNode(l0, l1, l2, "java/lang/InterruptedException"));
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new FieldInsnNode(GETFIELD, listenerClass, "mc", "L" + minecraft.name + ";"));
insns.add(new InsnNode(ICONST_0));
insns.add(new FieldInsnNode(PUTFIELD, minecraft.name, running.name, running.desc));
insns.add(l0);
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new FieldInsnNode(GETFIELD, listenerClass, "thread", "Ljava/lang/Thread;"));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Thread", "join", "()V"));
insns.add(l1);
insns.add(new JumpInsnNode(GOTO, l4));
insns.add(l2);
insns.add(new VarInsnNode(ASTORE, 2));
insns.add(new VarInsnNode(ALOAD, 2));
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/InterruptedException", "printStackTrace", "()V"));
insns.add(l4);
insns.add(new InsnNode(ICONST_0));
insns.add(new MethodInsnNode(INVOKESTATIC, "java/lang/System", "exit", "(I)V"));
insns.add(new InsnNode(RETURN));
node.methods.add(windowClosing);
source.overrideClass(node);
}
private InsnList getMinecraftImpl() {
MethodNode init = null;
for(MethodNode m : minecraftImpl.methods) {
if(m.name.equals("<init>")) {
init = m;
}
}
if(init == null) {
throw new NullPointerException();
}
final int appletIndex = 1;
final int frameIndex = 2;
final int canvasIndex = 3;
InsnList insns = new InsnList();
insns.add(new TypeInsnNode(NEW, minecraftImpl.name));
insns.add(new InsnNode(DUP));
Type[] types = Type.getArgumentTypes(init.desc);
if(types.length == 0) {
// Prevent any further checks
}
else if(Arrays.equals(types, new Type[] { Type.getType("Ljava/awt/Canvas;"), Type.getType("I"), Type.getType("I"), Type.getType("Z") })) {
if(launch.lwjglFrame) {
insns.add(new InsnNode(ACONST_NULL));
} else {
insns.add(new VarInsnNode(ALOAD, canvasIndex));
}
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(booleanInsn(launch.fullscreen));
}
else if(minecraftApplet == null) {
throw new NullPointerException("minecraftApplet is null");
}
else if(Arrays.equals(types, new Type[] { Type.getType("Ljava/awt/Canvas;"), Type.getType("L" + minecraftApplet.name + ";"), Type.getType("I"), Type.getType("I"), Type.getType("Z") })) {
if(launch.lwjglFrame) {
insns.add(new InsnNode(ACONST_NULL));
} else {
insns.add(new VarInsnNode(ALOAD, canvasIndex));
}
insns.add(new VarInsnNode(ALOAD, appletIndex));
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(booleanInsn(launch.fullscreen));
}
else if(Arrays.equals(types, new Type[] { Type.getType("Ljava/awt/Component;"), Type.getType("Ljava/awt/Canvas;"), Type.getType("L" + minecraftApplet.name + ";"), Type.getType("I"), Type.getType("I"), Type.getType("Z") })) {
if(launch.lwjglFrame) {
insns.add(new InsnNode(ACONST_NULL));
insns.add(new InsnNode(ACONST_NULL));
} else {
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new VarInsnNode(ALOAD, canvasIndex));
}
insns.add(new VarInsnNode(ALOAD, appletIndex));
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(booleanInsn(launch.fullscreen));
}
else if(Arrays.equals(types, new Type[] { Type.getType("L" + minecraftApplet.name + ";"), Type.getType("Ljava/awt/Canvas;"), Type.getType("L" + minecraftApplet.name + ";"), Type.getType("I"), Type.getType("I"), Type.getType("Z") })) {
insns.add(new VarInsnNode(ALOAD, appletIndex));
if(launch.lwjglFrame) {
insns.add(new InsnNode(ACONST_NULL));
} else {
insns.add(new VarInsnNode(ALOAD, canvasIndex));
}
insns.add(new VarInsnNode(ALOAD, appletIndex));
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(booleanInsn(launch.fullscreen));
}
else if(Arrays.equals(types, new Type[] { Type.getType("L" + minecraftApplet.name + ";"), Type.getType("Ljava/awt/Component;"), Type.getType("Ljava/awt/Canvas;"), Type.getType("L" + minecraftApplet.name + ";"), Type.getType("I"), Type.getType("I"), Type.getType("Z") })) {
insns.add(new VarInsnNode(ALOAD, appletIndex));
if(launch.lwjglFrame) {
insns.add(new InsnNode(ACONST_NULL));
insns.add(new InsnNode(ACONST_NULL));
} else {
insns.add(new VarInsnNode(ALOAD, frameIndex));
insns.add(new VarInsnNode(ALOAD, canvasIndex));
}
insns.add(new VarInsnNode(ALOAD, appletIndex));
insns.add(intInsn(launch.width));
insns.add(intInsn(launch.height));
insns.add(booleanInsn(launch.fullscreen));
}
insns.add(new MethodInsnNode(INVOKESPECIAL, minecraftImpl.name, "<init>", init.desc));
return insns;
}
protected void init() {
minecraftApplet = getApplet();
minecraft = getMinecraft(minecraftApplet);
if(minecraft == null) {
return;
}
for(MethodNode method : minecraft.methods) {
if("main".equals(method.name) && "([Ljava/lang/String;)V".equals(method.desc) && (method.access & Opcodes.ACC_STATIC) != 0) {
main = method;
}
if("run".equals(method.name) && "()V".equals(method.desc)) {
AbstractInsnNode insn = method.instructions.getFirst();
while(insn != null) {
if(insn.getOpcode() == Opcodes.PUTFIELD) {
FieldInsnNode putField = (FieldInsnNode)insn;
if("Z".equals(putField.desc)) {
running = InjectUtils.getField(minecraft, putField.name, putField.desc);
}
break;
}
insn = insn.getNext();
}
}
}
int i = 0;
for(FieldNode field : minecraft.fields) {
if("I".equals(field.desc) && i <= 1) {
// Width and height are always the first two ints in Minecraft class
if(i == 0) width = field;
if(i == 1) height = field;
i++;
}
if("Ljava/io/File;".equals(field.desc)) {
mcDir = field; // Possible candidate (Needed for infdev)
if((field.access & Opcodes.ACC_STATIC) != 0) {
// Definitely the mc directory
break;
}
}
}
if(minecraftApplet != null) {
String mcDesc = "L" + minecraft.name + ";";
String mcField = null;
for(FieldNode field : minecraftApplet.fields) {
if(mcDesc.equals(field.desc)) {
mcField = field.name;
}
}
for(MethodNode method : minecraftApplet.methods) {
if("init".equals(method.name) && "()V".equals(method.desc)) {
AbstractInsnNode insn = method.instructions.getFirst();
while(insn != null) {
AbstractInsnNode[] insns = fillBackwards(insn, 2);
if(compareInsn(insns[1], PUTFIELD, minecraftApplet.name, mcField, mcDesc)
&& compareInsn(insns[0], INVOKESPECIAL, null, "<init>")) {
MethodInsnNode invoke = (MethodInsnNode)insns[0];
minecraftImpl = source.getClass(invoke.owner);
}
insns = fill(insn, 8);
if(compareInsn(insns[0], ALOAD)
&& compareInsn(insns[1], LDC, "username")
&& compareInsn(insns[2], INVOKEVIRTUAL, minecraftApplet.name, "getParameter", "(Ljava/lang/String;)Ljava/lang/String;")
&& compareInsn(insns[3], ALOAD)
&& compareInsn(insns[4], LDC, "sessionid")
&& compareInsn(insns[5], INVOKEVIRTUAL, minecraftApplet.name, "getParameter", "(Ljava/lang/String;)Ljava/lang/String;")
&& compareInsn(insns[6], INVOKESPECIAL, null, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V")
) {
MethodInsnNode invokespecial = (MethodInsnNode)insns[6];
user = source.getClass(invokespecial.owner);
if(compareInsn(insns[7], PUTFIELD, minecraft.name)) {
FieldInsnNode field = (FieldInsnNode)insns[7];
userField = InjectUtils.getField(minecraft, field.name, field.desc);
}
}
if(compareInsn(insns[0], GETFIELD, minecraftApplet.name, mcField, mcDesc)
&& compareInsn(insns[1], NEW)
&& compareInsn(insns[2], DUP)
&& compareInsn(insns[3], INVOKESPECIAL, null, "<init>")) {
if(compareInsn(insns[4], PUTFIELD, minecraft.name)) {
FieldInsnNode field = (FieldInsnNode)insns[4];
if(field.desc.startsWith("L") && field.desc.endsWith(";")) {
user = source.getClass(field.desc.substring(1, field.desc.length() - 1));
userField = InjectUtils.getField(minecraft, field.name, field.desc);
}
}
}
insn = insn.getNext();
}
}
}
}
if(minecraftImpl == null) {
minecraftImpl = minecraft;
}
}
public LaunchTarget getLaunchTarget() {
if(main != null) {
return new LaunchTarget(minecraft.name, LaunchTarget.Type.MAIN);
}
return null;
}
public ClassNode getApplet() {
ClassNode applet = null;
for(String main : MAIN_APPLETS) {
applet = source.getClass(main);
if(applet != null) break;
}
return applet;
}
public ClassNode getMinecraft(ClassNode applet) {
ClassNode launchTarget = null;
for(String main : MAIN_CLASSES) {
ClassNode cls = source.getClass(main);
if(cls != null && cls.interfaces.contains("java/lang/Runnable")) {
launchTarget = cls;
break;
}
}
if(launchTarget == null && applet != null) {
for(FieldNode field : applet.fields) {
String desc = field.desc;
if(!desc.equals("Ljava/awt/Canvas;")
&& !desc.equals("Ljava/lang/Thread;")
&& desc.startsWith("L")
&& desc.endsWith(";")) {
launchTarget = source.getClass(desc.substring(1, desc.length() - 1));
}
}
}
return launchTarget;
}
public boolean supportsCanvas() {
for(MethodNode m : InjectUtils.getConstructors(minecraftImpl)) {
if(Type.getArgumentTypes(m.desc).length > 0) {
return true;
}
}
return false;
}
public static Tweak get(LaunchClassLoader classLoader, Launch launch) {
if(launch.isom) {
return new IsomTweak(classLoader, launch);
}
return new DefaultTweak(classLoader, launch);
}
}

View file

@ -1,11 +1,14 @@
package org.mcphackers.launchwrapper.tweak;
import static org.mcphackers.launchwrapper.inject.InsnHelper.getLastReturn;
import static org.mcphackers.launchwrapper.inject.InsnHelper.*;
import static org.objectweb.asm.Opcodes.*;
import org.mcphackers.launchwrapper.Launch;
import org.mcphackers.launchwrapper.AppletLaunchTarget;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.LaunchTarget;
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
import org.mcphackers.launchwrapper.inject.Inject;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
@ -17,26 +20,27 @@ import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class IsomTweak extends DefaultTweak {
public class IsomTweak extends LegacyTweak {
public static final String MAIN_ISOM = "net/minecraft/isom/IsomPreviewApplet";
protected ClassNode isomApplet;
protected ClassNode isomCanvas;
protected FieldNode mcDirIsom;
public IsomTweak(ClassNodeSource source, Launch launch) {
public IsomTweak(ClassNodeSource source, LaunchConfig launch) {
super(source, launch);
}
protected void init() {
super.init();
ClassNode isomEntryPoint = source.getClass(MAIN_ISOM);
if(isomEntryPoint != null) {
String desc = isomEntryPoint.fields.get(0).desc;
isomApplet = source.getClass(MAIN_ISOM);
if(isomApplet != null) {
String desc = isomApplet.fields.get(0).desc;
if(desc.startsWith("L") && desc.endsWith(";")) {
isomApplet = source.getClass(desc.substring(1, desc.length() - 1));
isomCanvas = source.getClass(desc.substring(1, desc.length() - 1));
}
}
if(isomApplet != null) {
if(isomCanvas != null) {
for(FieldNode field : isomApplet.fields) {
if(field.desc.equals("Ljava/io/File;")) {
mcDirIsom = field;
@ -50,26 +54,30 @@ public class IsomTweak extends DefaultTweak {
return false;
}
if(mcDirIsom != null) {
for(MethodNode m : isomApplet.methods) {
for(MethodNode m : isomCanvas.methods) {
if(m.name.equals("<init>")) {
InsnList insns = new InsnList();
insns.add(new VarInsnNode(ALOAD, 0));
insns.add(new TypeInsnNode(NEW, "java/io/File"));
insns.add(new InsnNode(DUP));
insns.add(new LdcInsnNode(launch.gameDir.getAbsolutePath()));
insns.add(new LdcInsnNode(launch.gameDir.getString()));
insns.add(new MethodInsnNode(INVOKESPECIAL, "java/io/File", "<init>", "(Ljava/lang/String;)V"));
insns.add(new FieldInsnNode(PUTFIELD, isomApplet.name, mcDirIsom.name, mcDirIsom.desc));
insns.add(new FieldInsnNode(PUTFIELD, isomCanvas.name, mcDirIsom.name, mcDirIsom.desc));
m.instructions.insertBefore(getLastReturn(m.instructions.getLast()), insns);
}
}
}
source.overrideClass(isomApplet);
source.overrideClass(isomCanvas);
return true;
}
public LaunchTarget getLaunchTarget() {
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
if(isomApplet != null) {
return new LaunchTarget(isomApplet.name, LaunchTarget.Type.APPLET);
AppletLaunchTarget launchTarget = new AppletLaunchTarget(loader, isomApplet.name);
launchTarget.setTitle("Isometric Preview");
launchTarget.setIcon(Inject.getIcon());
launchTarget.setResolution(launch.width.get(), launch.height.get());
return launchTarget;
}
return null;
}

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,13 @@
package org.mcphackers.launchwrapper.tweak;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.LaunchTarget;
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
public abstract class Tweak {
private static final boolean DEBUG = true;
protected ClassNodeSource source;
@ -13,5 +17,31 @@ public abstract class Tweak {
public abstract boolean transform();
public abstract LaunchTarget getLaunchTarget();
public abstract LaunchTarget getLaunchTarget(LaunchClassLoader loader);
public static Tweak get(LaunchClassLoader classLoader, LaunchConfig launch) {
if(launch.isom.get()) {
return new IsomTweak(classLoader, launch);
}
if(classLoader.getClass(VanillaTweak.MAIN_CLASS) == null) {
for(String cls : LegacyTweak.MAIN_CLASSES) {
if(classLoader.getClass(cls) != null) {
return new LegacyTweak(classLoader, launch);
}
}
for(String cls : LegacyTweak.MAIN_APPLETS) {
if(classLoader.getClass(cls) != null) {
return new LegacyTweak(classLoader, launch);
}
}
return null; // Tweak not found
} else {
return new VanillaTweak(classLoader, launch);
}
}
protected void debugInfo(String msg) {
if(DEBUG)
System.out.println("TWEAK: " + msg);
}
}

View file

@ -0,0 +1,52 @@
package org.mcphackers.launchwrapper.tweak;
import static org.mcphackers.launchwrapper.inject.InsnHelper.compareInsn;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import org.mcphackers.launchwrapper.LaunchConfig;
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.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
public class VanillaTweak extends Tweak {
public static final String MAIN_CLASS = "net/minecraft/client/main/Main";
protected LaunchConfig launch;
protected ClassNode minecraftMain;
protected ClassNode minecraft;
public VanillaTweak(ClassNodeSource source, LaunchConfig launch) {
super(source);
this.launch = launch;
}
public boolean transform() {
minecraftMain = source.getClass(MAIN_CLASS);
MethodNode main = InjectUtils.getMethod(minecraftMain, "main", "([Ljava/lang/String;)V");
if(main == null) return false;
AbstractInsnNode insn = main.instructions.getLast();
while(insn != null) {
// Last call inside of main is minecraft.run();
if(compareInsn(insn, INVOKEVIRTUAL)) {
minecraft = source.getClass(((MethodInsnNode)insn).owner);
break;
}
insn = insn.getPrevious();
}
return true;
}
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
MainLaunchTarget target = new MainLaunchTarget(loader, MAIN_CLASS);
launch.sessionid.set(null);
target.args = launch.getArgs();
return target;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,111 @@
package org.mcphackers.launchwrapper.util;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
/**
* use this for whatever you want
* @author Moresteck
*/
public class ImageUtils {
public final BufferedImage bufImg;
public ImageUtils(BufferedImage img) {
this.bufImg = img;
}
public ImageUtils(InputStream stream) throws IOException {
this.bufImg = ImageIO.read(stream);
}
public ImageUtils crop(int x, int y, int width, int height) {
int[][] pixels = this.getPixelColors();
int bufWidth = this.bufImg.getWidth();
int bufHeight = this.bufImg.getHeight();
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < bufWidth; i++) {
for (int j = 0; j < bufHeight; j++) {
if (j >= y && j < y+height && i >= x && i < x+width) {
img.setRGB(i-x, j-y, pixels[i][j]);
}
}
}
return new ImageUtils(img);
}
public ImageUtils setArea(int x, int y, BufferedImage img) {
return this.setArea(x, y, img, true);
}
public ImageUtils setArea(int x, int y, BufferedImage img, boolean forceTransparent) {
int width = this.bufImg.getWidth();
int height = this.bufImg.getHeight();
int[][] pixels = new int[width][height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int pixel = this.bufImg.getRGB(i, j);
if (j >= y && j < y+img.getHeight() && i >= x && i < x+img.getWidth()) {
int toset = img.getRGB(i-x, j-y);
if (!forceTransparent) {
if ((toset>>24) != 0x00) {
pixel = toset;
}
} else {
pixel = toset;
}
}
pixels[i][j] = pixel;
}
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
this.bufImg.setRGB(i, j, pixels[i][j]);
}
}
return this;
}
public ImageUtils flip(boolean flipX, boolean flipY) {
int[][] pixels = this.getPixelColors();
int width = this.bufImg.getWidth();
int height = this.bufImg.getHeight();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
this.bufImg.setRGB(flipX ? width-1-i : i, flipY ? height-1-j : j, pixels[i][j]);
}
}
return this;
}
public int[][] getPixelColors() {
int width = this.bufImg.getWidth();
int height = this.bufImg.getHeight();
int[][] pixels = new int[width][height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
pixels[i][j] = this.bufImg.getRGB(i, j);
}
}
return pixels;
}
public byte[] getInByteForm() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(this.bufImg, "PNG", baos);
return baos.toByteArray();
}
public BufferedImage getImage() {
return this.bufImg;
}
}

View file

@ -0,0 +1,93 @@
package org.mcphackers.launchwrapper.util;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import sun.misc.Unsafe;
@SuppressWarnings("restriction")
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 {
closeable.close();
} catch (IOException ignored) {
}
}
}
public static byte[] readStream(InputStream stream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
}
public static Map<String, String> queryMap(URL url) throws UnsupportedEncodingException {
Map<String, String> queryMap = new HashMap<String, String>();
String query = url.getQuery();
if(query != null) {
String[] pairs = query.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
queryMap.put(key, value);
}
}
return queryMap;
}
}