Skip to content

Instantly share code, notes, and snippets.

@ZekerZhayard
Last active April 12, 2021 12:59
Show Gist options
  • Save ZekerZhayard/2a4310dd7525f15a00cc1db37ebca0ae to your computer and use it in GitHub Desktop.
Save ZekerZhayard/2a4310dd7525f15a00cc1db37ebca0ae to your computer and use it in GitHub Desktop.
custom_earlyloading_screen
package io.github.zekerzhayard.custom_earlyloading_screen;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.net.URLClassLoader;
import sun.misc.Unsafe;
public class Constants {
public final static Unsafe UNSAFE;
public final static MethodHandles.Lookup IMPL_LOOKUP;
public final static ClassLoader APP_CLASS_LOADER = ClassLoader.getSystemClassLoader();
public final static boolean IS_JAVA_8 = APP_CLASS_LOADER instanceof URLClassLoader;
public final static String PACKAGE_NAME = IS_JAVA_8 ? "sun.misc." : "jdk.internal.loader.";
public final static Class<?> URL_CLASS_PATH_CLASS;
public final static Object UCP;
static {
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
UNSAFE = (Unsafe) unsafeField.get(null);
Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
IMPL_LOOKUP = (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(implLookupField), UNSAFE.staticFieldOffset(implLookupField));
URL_CLASS_PATH_CLASS = Class.forName(PACKAGE_NAME + "URLClassPath");
UCP = IMPL_LOOKUP.findGetter(APP_CLASS_LOADER.getClass().getSuperclass(), "ucp", URL_CLASS_PATH_CLASS).invokeWithArguments(APP_CLASS_LOADER);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
package io.github.zekerzhayard.custom_earlyloading_screen;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import com.google.common.collect.Lists;
import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ITransformationService;
import cpw.mods.modlauncher.api.ITransformer;
public class Entrance implements ITransformationService {
@Nonnull
@Override
public String name() {
return "custom_earlyloading_screen";
}
@Override
public void initialize(@Nonnull IEnvironment environment) {
}
@Override
public void beginScanning(@Nonnull IEnvironment environment) {
}
@Override
public void onLoad(@Nonnull IEnvironment env, @Nonnull Set<String> otherServices) {
try {
Transformer.start();
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
@Nonnull
@Override
@SuppressWarnings("rawtypes")
public List<ITransformer> transformers() {
return Lists.newArrayList();
}
}
package io.github.zekerzhayard.custom_earlyloading_screen;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Objects;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import net.minecraftforge.fml.loading.FMLServiceProvider;
import org.apache.commons.io.IOUtils;
public class FakeJarFile extends JarFile {
public static void replaceLoaders() throws Throwable {
ArrayList<?> loaders = (ArrayList<?>) Constants.IMPL_LOOKUP.findGetter(Constants.URL_CLASS_PATH_CLASS, "loaders", ArrayList.class).invokeWithArguments(Constants.UCP);
Class<?> jarLoaderClass = Class.forName(Constants.PACKAGE_NAME + "URLClassPath$JarLoader");
for (Object loader : loaders) {
if (jarLoaderClass.isInstance(loader)) {
JarFile jar = (JarFile) Constants.IMPL_LOOKUP.findGetter(jarLoaderClass, "jar", JarFile.class).invokeWithArguments(loader);
if (jar != null && Files.isSameFile(Paths.get(jar.getName()), Paths.get(FMLServiceProvider.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) {
FakeJarFile fakeJarFile = (FakeJarFile) Constants.UNSAFE.allocateInstance(FakeJarFile.class);
fakeJarFile.clone(jar, JarFile.class);
Constants.IMPL_LOOKUP.findSetter(jarLoaderClass, "jar", JarFile.class).invokeWithArguments(loader, fakeJarFile);
}
}
}
}
// This constructor will be skipped.
public FakeJarFile(String name) throws IOException {
super(name);
}
public void clone(JarFile jarFile, Class<?> clazz) throws Throwable {
if (clazz == null || !clazz.isInstance(jarFile)) {
return;
}
Field[] fields = (Field[]) Constants.IMPL_LOOKUP.findVirtual(Class.class, "getDeclaredFields0", MethodType.methodType(Field[].class, boolean.class)).invokeWithArguments(clazz, false);
for (Field field : fields) {
if ((field.getModifiers() | Modifier.STATIC) != field.getModifiers()) {
Constants.IMPL_LOOKUP.findSetter(field.getDeclaringClass(), field.getName(), field.getType())
.invokeWithArguments(this, Constants.IMPL_LOOKUP.findGetter(field.getDeclaringClass(), field.getName(), field.getType()).invokeWithArguments(jarFile));
}
}
this.clone(jarFile, clazz.getSuperclass());
}
@Override
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
InputStream is = super.getInputStream(ze);
if (ze == null || !Objects.equals(ze.getName(), "net/minecraftforge/fml/loading/progress/ClientVisualization.class")) {
return is;
}
byte[] classBytes = Transformer.transform(IOUtils.toByteArray(is));
ze.setSize(classBytes.length);
return new ByteArrayInputStream(classBytes);
}
}
package io.github.zekerzhayard.custom_earlyloading_screen;
import java.util.Objects;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
public class Transformer {
public static void start() throws Throwable {
FakeJarFile.replaceLoaders();
}
public static byte[] transform(byte[] input) {
ClassNode cn = new ClassNode();
new ClassReader(input).accept(cn, ClassReader.EXPAND_FRAMES);
for (MethodNode mn : cn.methods) {
if (Objects.equals(mn.name, "renderBackground") && Objects.equals(mn.desc, "()V")) {
for (AbstractInsnNode ain : mn.instructions.toArray()) {
if (ain.getOpcode() == Opcodes.INVOKESTATIC) {
MethodInsnNode min = (MethodInsnNode) ain;
if (Objects.equals(min.owner, "org/lwjgl/opengl/GL11") && Objects.equals(min.name, "glColor4f") && Objects.equals(min.desc, "(FFFF)V")) {
InsnList il = new InsnList();
il.add(new InsnNode(Opcodes.POP2));
il.add(new InsnNode(Opcodes.POP2));
il.add(new LdcInsnNode(0x2E / 255F));
il.add(new LdcInsnNode(34 / 255F));
il.add(new LdcInsnNode(40 / 255F));
il.add(new InsnNode(Opcodes.FCONST_1));
mn.instructions.insertBefore(min, il);
}
}
}
}
}
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
return cw.toByteArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment