More tweaks and URLStreamHandler overriding
This commit is contained in:
parent
da1b9c9bdc
commit
89f25af378
33 changed files with 5601 additions and 1322 deletions
|
@ -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
BIN
lib/lwjgl-2.9.4.jar
Normal file
Binary file not shown.
BIN
lib/lwjgl.jar
Normal file
BIN
lib/lwjgl.jar
Normal file
Binary file not shown.
BIN
lib/lwjgl_util-2.9.4.jar
Normal file
BIN
lib/lwjgl_util-2.9.4.jar
Normal file
Binary file not shown.
BIN
lib/lwjgl_util.jar
Normal file
BIN
lib/lwjgl_util.jar
Normal file
Binary file not shown.
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
142
src/main/java/org/mcphackers/launchwrapper/LaunchConfig.java
Normal file
142
src/main/java/org/mcphackers/launchwrapper/LaunchConfig.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
1908
src/main/java/org/mcphackers/launchwrapper/tweak/LegacyTweak.java
Normal file
1908
src/main/java/org/mcphackers/launchwrapper/tweak/LegacyTweak.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
2064
src/main/java/org/mcphackers/launchwrapper/util/Base64.java
Normal file
2064
src/main/java/org/mcphackers/launchwrapper/util/Base64.java
Normal file
File diff suppressed because it is too large
Load diff
111
src/main/java/org/mcphackers/launchwrapper/util/ImageUtils.java
Normal file
111
src/main/java/org/mcphackers/launchwrapper/util/ImageUtils.java
Normal 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;
|
||||
}
|
||||
}
|
93
src/main/java/org/mcphackers/launchwrapper/util/Util.java
Normal file
93
src/main/java/org/mcphackers/launchwrapper/util/Util.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue