Unfinished URLClassLoader
This commit is contained in:
parent
1ec1b3daf1
commit
b7ba767175
15 changed files with 310 additions and 85 deletions
15
README.md
15
README.md
|
@ -6,14 +6,16 @@ LaunchWrapper is bundled in [BetterJSONs](https://github.com/MCPHackers/BetterJS
|
|||
|
||||
# Features
|
||||
- Strips game window from Java **AWT** (**Abstract Window Toolkit**) and lets the game use internal **LWJGL** frame
|
||||
- BitDepthFix for versions before Beta 1.8
|
||||
- Allows changing assets directory in 1.6 snapshots (the game didn't have a parameter to do that yet)
|
||||
- Allows changing game directory in versions before 1.6
|
||||
- Replaces mouse input code with **LWJGL** calls
|
||||
- Fixes TimSort crash when using java 8+
|
||||
- Fixes TimSort crash when using Java 8+
|
||||
- Adds ability to customize built-in **LWJGL** frame
|
||||
- Changing display title
|
||||
- Setting icon
|
||||
- Proxies all requests from old skin servers to the current skin API and converts skins to required format
|
||||
- Skins don't work in Java versions before 8u181, due to the requrement of TLS 1.2 support
|
||||
- Uses Betacraft proxy to download sounds for versions before 1.6
|
||||
- Adds ability to launch classic and pre-classic at custom resolution and fullscreen
|
||||
- Makes save slots in classic and indev functional and saves to `.minecraft/levels` directory
|
||||
|
@ -21,11 +23,13 @@ LaunchWrapper is bundled in [BetterJSONs](https://github.com/MCPHackers/BetterJS
|
|||
- A VSync toggle (Used to prevent extreme framerate in early versions which causes **coil whine**)
|
||||
- Does not depend on any hard-coded obfuscated names and is mostly compatible with every Minecraft version
|
||||
- This also includes modded versions
|
||||
- A parameter to download `minecraft_server.jar` for snapshots which require it: `12w18a`, `12w19a`, `12w21a`
|
||||
- The wrapper is fully compatible with java 5+ if the game is vanilla
|
||||
- A parameter to automatically download `minecraft_server.jar` for snapshots: `12w18a`, `12w19a`, `12w21a`
|
||||
- The wrapper is fully compatible with Java 5+ if the game is vanilla
|
||||
- The wrapper also fixes Beta 1.3, Pre-classic and Classic compatibility with Java 5
|
||||
- Built-in [Modloader Fix](https://github.com/coffeenotfound/ModloaderFix-b1.7.3)
|
||||
|
||||
# How to use
|
||||
*This is a more in-depth guide for people experienced in Java*
|
||||
*This is a more in-depth guide for those who will be using the wrapper*
|
||||
|
||||
Make sure all minecraft and wrapper libraries are on classpath<br>
|
||||
Use `java -cp <classpath> org.mcphackers.launchwrapper.Launch <arguments>` to launch the game
|
||||
|
@ -38,7 +42,8 @@ Arguments may be as follows:
|
|||
- `isom` - Launch IsomPreviewApplet
|
||||
- `forceVsync` - Launch the game with VSync enabled
|
||||
- `forceResizable` - Early Indev and Classic don't properly update viewport, so the wrapper disables frame resizing. To enable it anyway use this argument
|
||||
- `skinProxy` - **classic** / **pre-b1.9-pre4** / **pre-1.8** / **default** - convert the skin to one of these specified formats
|
||||
- `skinProxy` - **pre-c0.0.19a** / **classic** / **pre-b1.9-pre4** / **pre-1.8** / **default** - convert the skin to one of these specified formats
|
||||
- **pre-c0.0.19a** - flatten all skin layers, flip bottom textures, mirror entire skin and crop to 64x32
|
||||
- **classic** - flatten all skin layers, flip bottom textures and crop to 64x32
|
||||
- **pre-b1.9-pre4** - flip bottom textures and crop to 64x32
|
||||
- **pre-1.8** - crop to 64x32
|
||||
|
|
|
@ -13,8 +13,7 @@ public class AppletLaunchTarget extends LaunchTarget {
|
|||
private int width = 200, height = 200;
|
||||
private Image icon;
|
||||
|
||||
public AppletLaunchTarget(LaunchClassLoader classLoader, String classTarget) {
|
||||
super(classLoader);
|
||||
public AppletLaunchTarget(String classTarget) {
|
||||
targetClass = classTarget;
|
||||
title = classTarget;
|
||||
}
|
||||
|
@ -32,7 +31,7 @@ public class AppletLaunchTarget extends LaunchTarget {
|
|||
icon = img;
|
||||
}
|
||||
|
||||
public void launch() {
|
||||
public void launch(LaunchClassLoader classLoader) {
|
||||
Class<? extends Applet> appletClass;
|
||||
try {
|
||||
appletClass = classLoader.findClass(targetClass).asSubclass(Applet.class);
|
||||
|
|
|
@ -34,7 +34,9 @@ public class Launch {
|
|||
return;
|
||||
}
|
||||
if(mainTweak.transform()) {
|
||||
mainTweak.getLaunchTarget(CLASS_LOADER).launch();
|
||||
mainTweak.getLaunchTarget().launch(CLASS_LOADER);
|
||||
} else {
|
||||
System.err.println("Tweak could not be applied");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,5 @@ import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
|||
|
||||
public abstract class LaunchTarget {
|
||||
|
||||
protected final LaunchClassLoader classLoader;
|
||||
|
||||
public LaunchTarget(LaunchClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public abstract void launch();
|
||||
public abstract void launch(LaunchClassLoader classLoader);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,11 @@ public class MainLaunchTarget extends LaunchTarget {
|
|||
public String targetClass;
|
||||
public String[] args;
|
||||
|
||||
public MainLaunchTarget(LaunchClassLoader classLoader, String classTarget) {
|
||||
super(classLoader);
|
||||
public MainLaunchTarget(String classTarget) {
|
||||
targetClass = classTarget;
|
||||
}
|
||||
|
||||
public void launch() {
|
||||
public void launch(LaunchClassLoader classLoader) {
|
||||
classLoader.invokeMain(targetClass, args);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,10 @@ public class InsnHelper {
|
|||
return matches;
|
||||
case LABEL:
|
||||
LabelNode insn9 = (LabelNode) insn;
|
||||
// TODO
|
||||
if(compare.length > 0) {
|
||||
LabelNode label = (LabelNode) compare[0];
|
||||
matches &= label == insn9;
|
||||
}
|
||||
return matches;
|
||||
case LDC_INSN:
|
||||
LdcInsnNode insn10 = (LdcInsnNode) insn;
|
||||
|
@ -149,8 +152,6 @@ public class InsnHelper {
|
|||
}
|
||||
return matches;
|
||||
case FRAME:
|
||||
FrameNode insn15 = (FrameNode) insn;
|
||||
// TODO
|
||||
return matches;
|
||||
case LINE:
|
||||
LineNumberNode insn16 = (LineNumberNode) insn;
|
||||
|
|
|
@ -8,9 +8,9 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.security.cert.Certificate;
|
||||
|
@ -19,6 +19,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
|
||||
import org.mcphackers.launchwrapper.tweak.ClassLoaderTweak;
|
||||
import org.mcphackers.launchwrapper.util.Util;
|
||||
import org.mcphackers.rdi.util.NodeHelper;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
|
@ -27,11 +28,14 @@ import org.objectweb.asm.tree.ClassNode;
|
|||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
||||
// URLClassLoader is required to support ModLoader loading mods from mod folder
|
||||
public class LaunchClassLoader extends URLClassLoader implements ClassNodeSource {
|
||||
|
||||
public static final int CLASS_VERSION = getSupportedClassVersion();
|
||||
private static LaunchClassLoader INSTANCE;
|
||||
|
||||
private ClassLoader parent;
|
||||
private ClassLoaderTweak tweak;
|
||||
private Map<String, Class<?>> exceptions = new HashMap<String, Class<?>>();
|
||||
/** Keys should contain dots */
|
||||
private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
|
||||
|
@ -42,23 +46,34 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
private File debugOutput;
|
||||
|
||||
public LaunchClassLoader(ClassLoader parent) {
|
||||
super(null);
|
||||
super(new URL[0], null);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setClassPath(URL[] urls) {
|
||||
this.parent = new URLClassLoader(urls, parent);
|
||||
for(URL url : urls) {
|
||||
super.addURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebugOutput(File directory) {
|
||||
debugOutput = directory;
|
||||
}
|
||||
|
||||
protected void addURL(URL url) {
|
||||
super.addURL(url);
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
URL url = super.getResource(name);
|
||||
if(url != null) {
|
||||
return url;
|
||||
}
|
||||
return parent.getResource(name);
|
||||
}
|
||||
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
//TODO ?
|
||||
return parent.getResources(name);
|
||||
}
|
||||
|
||||
|
@ -67,7 +82,12 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
return parent.loadClass(name);
|
||||
}
|
||||
name = className(name);
|
||||
Class<?> cls = exceptions.get(name);
|
||||
Class<?> cls;
|
||||
cls = exceptions.get(name);
|
||||
if(cls != null) {
|
||||
return cls;
|
||||
}
|
||||
cls = classes.get(name);
|
||||
if(cls != null) {
|
||||
return cls;
|
||||
}
|
||||
|
@ -92,28 +112,30 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
}
|
||||
}
|
||||
|
||||
protected Class<?> redefineClass(String name) {
|
||||
byte[] classData = getClassAsBytes(name);
|
||||
if(classData == null)
|
||||
return null;
|
||||
Class<?> definedClass = defineClass(name, classData, 0, classData.length, getProtectionDomain(name));
|
||||
classes.put(name, definedClass);
|
||||
return definedClass;
|
||||
}
|
||||
|
||||
private ProtectionDomain getProtectionDomain(String name) {
|
||||
final URL resource = parent.getResource(classResourceName(name));
|
||||
final URL resource = getResource(classResourceName(name));
|
||||
if(resource == null) {
|
||||
return null;
|
||||
}
|
||||
URLConnection urlConnection;
|
||||
try {
|
||||
urlConnection = resource.openConnection();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
CodeSource codeSource;
|
||||
if(resource.getProtocol().equals("jar")) {
|
||||
String path = resource.getPath();
|
||||
if(path.startsWith("file:")) {
|
||||
path = path.substring("file:".length());
|
||||
int i = path.lastIndexOf('!');
|
||||
if(i != -1) {
|
||||
path.substring(0, i);
|
||||
}
|
||||
}
|
||||
try {
|
||||
URL newResource = new URL("file", "", path);
|
||||
codeSource = new CodeSource(newResource, new Certificate[0]);
|
||||
} catch (MalformedURLException e) {
|
||||
codeSource = new CodeSource(resource, new Certificate[0]);
|
||||
}
|
||||
} else {
|
||||
codeSource = new CodeSource(resource, new Certificate[0]);
|
||||
}
|
||||
CodeSource codeSource = urlConnection == null ? null : new CodeSource(urlConnection.getURL(), new Certificate[0]);
|
||||
return new ProtectionDomain(codeSource, null);
|
||||
}
|
||||
|
||||
|
@ -172,15 +194,30 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
return NodeHelper.getMethod(getClass(owner), name, desc);
|
||||
}
|
||||
|
||||
protected Class<?> redefineClass(String name) {
|
||||
ClassNode classNode = getClass(name);
|
||||
if(classNode != null && tweak != null && tweak.tweakClass(classNode)) {
|
||||
return redefineClass(classNode);
|
||||
}
|
||||
byte[] classData = getClassAsBytes(name);
|
||||
if(classData == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> definedClass = defineClass(name, classData, 0, classData.length, getProtectionDomain(name));
|
||||
classes.put(name, definedClass);
|
||||
return definedClass;
|
||||
}
|
||||
|
||||
private Class<?> redefineClass(ClassNode node) {
|
||||
if(node == null) {
|
||||
return null;
|
||||
}
|
||||
ClassWriter writer = new SafeClassWriter(parent, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
ClassWriter writer = new SafeClassWriter(this, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
node.accept(writer);
|
||||
byte[] classData = writer.toByteArray();
|
||||
Class<?> definedClass = defineClass(className(node.name), classData, 0, classData.length);
|
||||
classes.put(className(node.name), definedClass);
|
||||
String name = className(node.name);
|
||||
Class<?> definedClass = defineClass(name, classData, 0, classData.length, getProtectionDomain(name));
|
||||
classes.put(name, definedClass);
|
||||
return definedClass;
|
||||
}
|
||||
|
||||
|
@ -196,8 +233,17 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
return name.replace('.', '/') + ".class";
|
||||
}
|
||||
|
||||
private InputStream getClassAsStream(String name) {
|
||||
String className = classResourceName(name);
|
||||
InputStream is = super.getResourceAsStream(className);
|
||||
if(is != null) {
|
||||
return is;
|
||||
}
|
||||
return parent.getResourceAsStream(className);
|
||||
}
|
||||
|
||||
private byte[] getClassAsBytes(String name) {
|
||||
InputStream is = parent.getResourceAsStream(classResourceName(name));
|
||||
InputStream is = getClassAsStream(name);
|
||||
if(is == null)
|
||||
return null;
|
||||
byte[] classData;
|
||||
|
@ -212,12 +258,11 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
}
|
||||
|
||||
private Class<?> transformedClass(String name) {
|
||||
Class<?> cls = classes.get(name);
|
||||
if(cls != null) {
|
||||
return cls;
|
||||
}
|
||||
ClassNode transformed = overridenClasses.get(name);
|
||||
if(transformed != null) {
|
||||
if(tweak != null) {
|
||||
tweak.tweakClass(transformed);
|
||||
}
|
||||
return redefineClass(transformed);
|
||||
}
|
||||
return redefineClass(name);
|
||||
|
@ -227,6 +272,30 @@ public class LaunchClassLoader extends ClassLoader implements ClassNodeSource {
|
|||
exceptions.put(cls.getName(), cls);
|
||||
}
|
||||
|
||||
public void setLoaderTweak(ClassLoaderTweak classLoaderTweak) {
|
||||
tweak = classLoaderTweak;
|
||||
}
|
||||
|
||||
private static ClassNode getSystemClass(Class<?> cls) {
|
||||
InputStream is = ClassLoader.getSystemResourceAsStream(classResourceName(cls.getName()));
|
||||
if(is == null)
|
||||
return null;
|
||||
try {
|
||||
ClassNode classNode = new ClassNode();
|
||||
ClassReader classReader = new ClassReader(is);
|
||||
classReader.accept(classNode, 0);
|
||||
return classNode;
|
||||
} catch (IOException e) {
|
||||
Util.closeSilently(is);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getSupportedClassVersion() {
|
||||
ClassNode objectClass = getSystemClass(Object.class);
|
||||
return objectClass.version;
|
||||
}
|
||||
|
||||
public static LaunchClassLoader instantiate() {
|
||||
if(INSTANCE != null) {
|
||||
throw new IllegalStateException("Can only have one instance of LaunchClassLoader!");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mcphackers.launchwrapper.protocol;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
@ -108,6 +109,7 @@ public class SkinRequests {
|
|||
try {
|
||||
if(skindata.cape != null) {
|
||||
switch(skinType) {
|
||||
case PRE_19A:
|
||||
case CLASSIC:
|
||||
case PRE_B1_9:
|
||||
case PRE_1_8:
|
||||
|
@ -142,15 +144,19 @@ public class SkinRequests {
|
|||
ImageUtils imgu = new ImageUtils(bis);
|
||||
|
||||
switch(skinType) {
|
||||
case PRE_19A:
|
||||
case CLASSIC:
|
||||
overlayHeadLayer(imgu);
|
||||
case PRE_B1_9:
|
||||
rotateBottomTX(imgu);
|
||||
case PRE_1_8:
|
||||
if(imgu.getImage().getHeight() == 64)
|
||||
overlay64to32(imgu);
|
||||
if(skindata.slim)
|
||||
alexToSteve(imgu);
|
||||
if(skinType == SkinType.PRE_B1_9 || skinType == SkinType.CLASSIC || skinType == SkinType.PRE_19A)
|
||||
rotateBottomTX(imgu);
|
||||
if(skinType == SkinType.PRE_19A)
|
||||
flipSkin(imgu);
|
||||
imgu = imgu.crop(0, 0, 64, 32);
|
||||
case DEFAULT:
|
||||
break;
|
||||
|
@ -163,6 +169,43 @@ public class SkinRequests {
|
|||
return skin;
|
||||
}
|
||||
|
||||
private static void flipSkin(ImageUtils imgu) {
|
||||
BufferedImage sideLeft;
|
||||
BufferedImage sideRight;
|
||||
// head
|
||||
imgu.setArea(8, 0, imgu.crop(8, 0, 8, 16).flip(true, false).getImage());
|
||||
imgu.setArea(16, 0, imgu.crop(16, 0, 8, 8).flip(true, false).getImage());
|
||||
imgu.setArea(24, 8, imgu.crop(24, 8, 8, 8).flip(true, false).getImage());
|
||||
sideLeft = imgu.crop(0, 8, 8, 8).flip(true, false).getImage();
|
||||
sideRight = imgu.crop(16, 8, 8, 8).flip(true, false).getImage();
|
||||
imgu.setArea(16, 8, sideLeft);
|
||||
imgu.setArea(0, 8, sideRight);
|
||||
// body
|
||||
imgu.setArea(20, 16, imgu.crop(20, 16, 8, 16).flip(true, false).getImage());
|
||||
imgu.setArea(28, 16, imgu.crop(28, 16, 8, 4).flip(true, false).getImage());
|
||||
imgu.setArea(32, 20, imgu.crop(32, 20, 8, 12).flip(true, false).getImage());
|
||||
sideLeft = imgu.crop(16, 20, 4, 12).flip(true, false).getImage();
|
||||
sideRight = imgu.crop(28, 20, 4, 12).flip(true, false).getImage();
|
||||
imgu.setArea(28, 20, sideLeft);
|
||||
imgu.setArea(16, 20, sideRight);
|
||||
// leg
|
||||
imgu.setArea(4, 16, imgu.crop(4, 16, 4, 16).flip(true, false).getImage());
|
||||
imgu.setArea(8, 16, imgu.crop(8, 16, 4, 4).flip(true, false).getImage());
|
||||
imgu.setArea(12, 20, imgu.crop(12, 20, 4, 12).flip(true, false).getImage());
|
||||
sideLeft = imgu.crop(0, 20, 4, 12).flip(true, false).getImage();
|
||||
sideRight = imgu.crop(8, 20, 4, 12).flip(true, false).getImage();
|
||||
imgu.setArea(8, 20, sideLeft);
|
||||
imgu.setArea(0, 20, sideRight);
|
||||
// arm
|
||||
imgu.setArea(44, 16, imgu.crop(44, 16, 4, 16).flip(true, false).getImage());
|
||||
imgu.setArea(48, 16, imgu.crop(48, 16, 4, 4).flip(true, false).getImage());
|
||||
imgu.setArea(52, 20, imgu.crop(52, 20, 4, 12).flip(true, false).getImage());
|
||||
sideLeft = imgu.crop(40, 20, 4, 12).flip(true, false).getImage();
|
||||
sideRight = imgu.crop(48, 20, 4, 12).flip(true, false).getImage();
|
||||
imgu.setArea(48, 20, sideLeft);
|
||||
imgu.setArea(40, 20, sideRight);
|
||||
}
|
||||
|
||||
public static void rotateBottomTX(ImageUtils imgu) {
|
||||
// bottom head
|
||||
imgu.setArea(16, 0, imgu.crop(16, 0, 8, 8).flip(false, true).getImage());
|
||||
|
@ -201,18 +244,9 @@ public class SkinRequests {
|
|||
}
|
||||
|
||||
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());
|
||||
imgu.setArea(48, 16, imgu.crop(47, 16, 7, 16).getImage());
|
||||
imgu.setArea(47, 16, imgu.crop(45, 16, 1, 16).getImage());
|
||||
imgu.setArea(55, 20, imgu.crop(53, 20, 1, 12).getImage());
|
||||
imgu.setArea(51, 16, imgu.crop(49, 16, 1, 4).getImage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mcphackers.launchwrapper.protocol;
|
||||
|
||||
public enum SkinType {
|
||||
PRE_19A("pre-c0.0.19a"),
|
||||
CLASSIC("classic"),
|
||||
PRE_B1_9("pre-b1.9-pre4"),
|
||||
PRE_1_8("pre-1.8"),
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package org.mcphackers.launchwrapper.tweak;
|
||||
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
public abstract class ClassLoaderTweak {
|
||||
|
||||
public abstract boolean tweakClass(ClassNode node);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.mcphackers.launchwrapper.tweak;
|
||||
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.*;
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.getLastReturn;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.mcphackers.launchwrapper.AppletLaunchTarget;
|
||||
|
@ -8,7 +8,6 @@ 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;
|
||||
|
@ -71,9 +70,9 @@ public class IsomTweak extends LegacyTweak {
|
|||
return true;
|
||||
}
|
||||
|
||||
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
|
||||
public LaunchTarget getLaunchTarget() {
|
||||
if(isomApplet != null) {
|
||||
AppletLaunchTarget launchTarget = new AppletLaunchTarget(loader, isomApplet.name);
|
||||
AppletLaunchTarget launchTarget = new AppletLaunchTarget(isomApplet.name);
|
||||
if(launch.title.get() != null) {
|
||||
launchTarget.setTitle(launch.title.get());
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package org.mcphackers.launchwrapper.tweak;
|
||||
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.compareInsn;
|
||||
import static org.mcphackers.rdi.util.InsnHelper.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
public class LegacyClassLoaderTweak extends ClassLoaderTweak {
|
||||
|
||||
public boolean tweakClass(ClassNode node) {
|
||||
boolean changed = false;
|
||||
if(node.version > LaunchClassLoader.CLASS_VERSION) {
|
||||
node.version = LaunchClassLoader.CLASS_VERSION;
|
||||
changed = true;
|
||||
}
|
||||
for(MethodNode m : node.methods) {
|
||||
AbstractInsnNode insn = m.instructions.getFirst();
|
||||
while(insn != null) {
|
||||
AbstractInsnNode[] insns = fill(insn, 3);
|
||||
if(compareInsn(insns[0], INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z")) {
|
||||
MethodInsnNode invoke = new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "length", "()I");
|
||||
m.instructions.set(insns[0], invoke);
|
||||
InsnList insert = new InsnList();
|
||||
LabelNode not = new LabelNode();
|
||||
LabelNode end = new LabelNode();
|
||||
insert.add(new JumpInsnNode(IFEQ, not));
|
||||
insert.add(booleanInsn(false));
|
||||
insert.add(new JumpInsnNode(GOTO, end));
|
||||
insert.add(not);
|
||||
insert.add(booleanInsn(true));
|
||||
insert.add(end);
|
||||
m.instructions.insert(invoke, insert);
|
||||
insn = nextInsn(invoke);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if(compareInsn(insns[1], INVOKESTATIC, "java/nio/charset/Charset", "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;")
|
||||
&& compareInsn(insns[2], INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/nio/charset/Charset;)[B")
|
||||
&& compareInsn(insns[0], LDC)) {
|
||||
m.instructions.set(insns[2], new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/lang/String;)[B"));
|
||||
m.instructions.remove(insns[1]);
|
||||
changed = true;
|
||||
}
|
||||
if(compareInsn(insns[1], INVOKESTATIC, "java/nio/charset/Charset", "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;")
|
||||
&& compareInsn(insns[2], INVOKESPECIAL, "java/lang/String", "<init>", "([BLjava/nio/charset/Charset;)V")
|
||||
&& compareInsn(insns[0], LDC)) {
|
||||
m.instructions.set(insns[2], new MethodInsnNode(INVOKESPECIAL, "java/lang/String", "<init>", "([BLjava/lang/String;)V"));
|
||||
m.instructions.remove(insns[1]);
|
||||
changed = true;
|
||||
}
|
||||
insn = nextInsn(insn);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ 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.loader.LaunchClassLoader;
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler;
|
||||
import org.mcphackers.launchwrapper.protocol.SkinType;
|
||||
import org.mcphackers.launchwrapper.protocol.URLStreamHandlerProxy;
|
||||
|
@ -105,6 +104,7 @@ public class LegacyTweak extends Tweak {
|
|||
replaceGameDirectory(init, mcDir);
|
||||
optionsLoadFix(init);
|
||||
displayPatch(init, supportsResizing);
|
||||
bitDepthFix(init);
|
||||
fixMouseHelper(mouseHelperName);
|
||||
patchMinecraftInit();
|
||||
|
||||
|
@ -119,6 +119,10 @@ public class LegacyTweak extends Tweak {
|
|||
return true;
|
||||
}
|
||||
|
||||
public ClassLoaderTweak getLoaderTweak() {
|
||||
return new LegacyClassLoaderTweak();
|
||||
}
|
||||
|
||||
private void downloadServer() {
|
||||
if(launch.serverURL.get() == null || launch.gameDir.get() == null) {
|
||||
return;
|
||||
|
@ -143,7 +147,7 @@ public class LegacyTweak extends Tweak {
|
|||
}
|
||||
}
|
||||
|
||||
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
|
||||
public LaunchTarget getLaunchTarget() {
|
||||
if(main == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -151,7 +155,7 @@ public class LegacyTweak extends Tweak {
|
|||
enableLegacyMergeSort();
|
||||
URLStreamHandlerProxy.setURLStreamHandler("http", new LegacyURLStreamHandler(skinType, port));
|
||||
URLStreamHandlerProxy.setURLStreamHandler("https", new LegacyURLStreamHandler(skinType, port));
|
||||
MainLaunchTarget target = new MainLaunchTarget(loader, minecraft.name);
|
||||
MainLaunchTarget target = new MainLaunchTarget(minecraft.name);
|
||||
target.args = new String[] { launch.username.get(), launch.sessionid.get() };
|
||||
return target;
|
||||
}
|
||||
|
@ -374,6 +378,32 @@ public class LegacyTweak extends Tweak {
|
|||
insn1 = nextInsn(insn1);
|
||||
}
|
||||
}
|
||||
|
||||
private void bitDepthFix(MethodNode init) {
|
||||
for(TryCatchBlockNode tryCatch : init.tryCatchBlocks) {
|
||||
if(!"org/lwjgl/LWJGLException".equals(tryCatch.type)) {
|
||||
continue;
|
||||
}
|
||||
AbstractInsnNode insn = tryCatch.end;
|
||||
while(insn != null && insn != tryCatch.start) {
|
||||
if(compareInsn(insn, INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "(Lorg/lwjgl/opengl/PixelFormat;)V")) {
|
||||
return;
|
||||
}
|
||||
if(compareInsn(insn, INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "()V")) {
|
||||
InsnList insert = new InsnList();
|
||||
insert.add(new TypeInsnNode(NEW, "org/lwjgl/opengl/PixelFormat"));
|
||||
insert.add(new InsnNode(DUP));
|
||||
insert.add(new MethodInsnNode(INVOKESPECIAL, "org/lwjgl/opengl/PixelFormat", "<init>", "()V"));
|
||||
insert.add(intInsn(24));
|
||||
insert.add(new MethodInsnNode(INVOKEVIRTUAL, "org/lwjgl/opengl/PixelFormat", "withDepthBits", "(I)Lorg/lwjgl/opengl/PixelFormat;"));
|
||||
insert.add(new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "(Lorg/lwjgl/opengl/PixelFormat;)V"));
|
||||
init.instructions.insert(insn, insert);
|
||||
init.instructions.remove(insn);
|
||||
}
|
||||
insn = previousInsn(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayPatch(MethodNode init, boolean supportsResizing) {
|
||||
boolean foundTitle = false; // TODO
|
||||
|
@ -453,8 +483,7 @@ public class LegacyTweak extends Tweak {
|
|||
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 //FIXME fill no longer stores labels
|
||||
&& compareInsn(insns[4], INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "()V")) {
|
||||
&& compareInsn(insns[2], INVOKESTATIC, "org/lwjgl/opengl/Display", "create", "()V")) {
|
||||
tweakInfo("Pre-classic resolution patch");
|
||||
InsnList insert = getIcon(classic);
|
||||
if(launch.forceVsync.get()) {
|
||||
|
@ -603,6 +632,7 @@ public class LegacyTweak extends Tweak {
|
|||
&& compareInsn(insns3[7], INVOKESTATIC, "org/lwjgl/opengl/Display", "setDisplayMode", "(Lorg/lwjgl/opengl/DisplayMode;)V")) {
|
||||
m.instructions.set(insns3[3], new FieldInsnNode(GETFIELD, minecraft.name, defaultWidth.name, defaultWidth.desc));
|
||||
m.instructions.set(insns3[5], new FieldInsnNode(GETFIELD, minecraft.name, defaultHeight.name, defaultHeight.desc));
|
||||
break methodLoop;
|
||||
} else {
|
||||
JumpInsnNode jump = (JumpInsnNode) insns2[0];
|
||||
LabelNode newLabel = new LabelNode();
|
||||
|
@ -618,6 +648,7 @@ public class LegacyTweak extends Tweak {
|
|||
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[3], insert);
|
||||
break methodLoop;
|
||||
}
|
||||
}
|
||||
insn2 = nextInsn(insn2);
|
||||
|
@ -1139,7 +1170,8 @@ public class LegacyTweak extends Tweak {
|
|||
|
||||
String mcField = null;
|
||||
String mcDesc = "L" + minecraft.name + ";";
|
||||
if(minecraftApplet != null) {
|
||||
boolean invokeAppletInit = patchAppletInit();
|
||||
if(invokeAppletInit) {
|
||||
for(FieldNode field : minecraftApplet.fields) {
|
||||
if(mcDesc.equals(field.desc)) {
|
||||
mcField = field.name;
|
||||
|
@ -1154,7 +1186,6 @@ public class LegacyTweak extends Tweak {
|
|||
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/applet/Applet", "setStub", "(Ljava/applet/AppletStub;)V"));
|
||||
insns.add(new VarInsnNode(ALOAD, appletIndex));
|
||||
insns.add(new MethodInsnNode(INVOKEVIRTUAL, minecraftApplet.name, "init", "()V"));
|
||||
patchAppletInit();
|
||||
}
|
||||
if(!launch.lwjglFrame.get()) {
|
||||
insns.add(new TypeInsnNode(NEW, "java/awt/Frame"));
|
||||
|
@ -1191,7 +1222,7 @@ public class LegacyTweak extends Tweak {
|
|||
insns.add(new InsnNode(ACONST_NULL));
|
||||
insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/awt/Frame", "setLocationRelativeTo", "(Ljava/awt/Component;)V"));
|
||||
}
|
||||
if(minecraftApplet != null) {
|
||||
if(invokeAppletInit) {
|
||||
insns.add(new VarInsnNode(ALOAD, appletIndex));
|
||||
insns.add(new FieldInsnNode(GETFIELD, minecraftApplet.name, mcField, mcDesc));
|
||||
} else {
|
||||
|
@ -1250,13 +1281,16 @@ public class LegacyTweak extends Tweak {
|
|||
return node;
|
||||
}
|
||||
|
||||
private void patchAppletInit() {
|
||||
private boolean patchAppletInit() {
|
||||
if(minecraftApplet == null) {
|
||||
return false;
|
||||
}
|
||||
String mcField = null;
|
||||
String canvasField = null;
|
||||
String mcDesc = "L" + minecraft.name + ";";
|
||||
MethodNode init = NodeHelper.getMethod(minecraftApplet, "init", "()V");
|
||||
if(init == null) {
|
||||
throw new IllegalStateException("Missing applet init");
|
||||
return false;
|
||||
}
|
||||
for(FieldNode field : minecraftApplet.fields) {
|
||||
if(mcDesc.equals(field.desc)) {
|
||||
|
@ -1339,6 +1373,7 @@ public class LegacyTweak extends Tweak {
|
|||
insn = nextInsn(insn);
|
||||
}
|
||||
source.overrideClass(minecraftApplet);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void createWindowListener(String listenerClass) {
|
||||
|
|
|
@ -17,9 +17,19 @@ public abstract class Tweak {
|
|||
|
||||
public abstract boolean transform();
|
||||
|
||||
public abstract LaunchTarget getLaunchTarget(LaunchClassLoader loader);
|
||||
public abstract ClassLoaderTweak getLoaderTweak();
|
||||
|
||||
public abstract LaunchTarget getLaunchTarget();
|
||||
|
||||
public static Tweak get(LaunchClassLoader classLoader, LaunchConfig launch) {
|
||||
Tweak tweak = getTweak(classLoader, launch);
|
||||
if(tweak != null) {
|
||||
classLoader.setLoaderTweak(tweak.getLoaderTweak());
|
||||
}
|
||||
return tweak;
|
||||
}
|
||||
|
||||
private static Tweak getTweak(LaunchClassLoader classLoader, LaunchConfig launch) {
|
||||
if(launch.isom.get()) {
|
||||
return new IsomTweak(classLoader, launch);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.mcphackers.launchwrapper.tweak;
|
||||
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.*;
|
||||
import static org.mcphackers.launchwrapper.inject.InsnHelper.compareInsn;
|
||||
import static org.mcphackers.rdi.util.InsnHelper.*;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
|
@ -13,7 +13,6 @@ import org.mcphackers.launchwrapper.LaunchConfig.LaunchParameter;
|
|||
import org.mcphackers.launchwrapper.LaunchTarget;
|
||||
import org.mcphackers.launchwrapper.MainLaunchTarget;
|
||||
import org.mcphackers.launchwrapper.inject.ClassNodeSource;
|
||||
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
|
||||
import org.mcphackers.launchwrapper.protocol.LegacyURLStreamHandler;
|
||||
import org.mcphackers.launchwrapper.protocol.URLStreamHandlerProxy;
|
||||
import org.mcphackers.rdi.util.IdentifyCall;
|
||||
|
@ -190,12 +189,17 @@ public class VanillaTweak extends Tweak {
|
|||
}
|
||||
}
|
||||
|
||||
public LaunchTarget getLaunchTarget(LaunchClassLoader loader) {
|
||||
public LaunchTarget getLaunchTarget() {
|
||||
URLStreamHandlerProxy.setURLStreamHandler("http", new LegacyURLStreamHandler(launch.skinProxy.get(), 11707));
|
||||
URLStreamHandlerProxy.setURLStreamHandler("https", new LegacyURLStreamHandler(launch.skinProxy.get(), 11707));
|
||||
MainLaunchTarget target = new MainLaunchTarget(loader, MAIN_CLASS);
|
||||
MainLaunchTarget target = new MainLaunchTarget(MAIN_CLASS);
|
||||
target.args = launch.getArgs();
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoaderTweak getLoaderTweak() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue