|
| 1 | +package com.fox2code.foxloader.patching.dev; |
| 2 | + |
| 3 | +import com.fox2code.foxloader.patching.TransformerUtils; |
| 4 | +import org.objectweb.asm.ClassReader; |
| 5 | +import org.objectweb.asm.Opcodes; |
| 6 | +import org.objectweb.asm.tree.*; |
| 7 | + |
| 8 | +import java.io.File; |
| 9 | +import java.io.IOException; |
| 10 | +import java.io.InputStream; |
| 11 | +import java.util.ArrayList; |
| 12 | +import java.util.IdentityHashMap; |
| 13 | +import java.util.Objects; |
| 14 | +import java.util.jar.JarEntry; |
| 15 | +import java.util.jar.JarFile; |
| 16 | + |
| 17 | +public class DevelopmentSourceConstantData { |
| 18 | + private static final int STATUS_DISABLED = -1; |
| 19 | + private static final int STATUS_DEFAULT = 0; |
| 20 | + private static final int STATUS_DISPLAY_FIRST = 1; |
| 21 | + public final String internalVersion, version, displayVersion; |
| 22 | + private final ConstantCheck[] regular, displayFirst; |
| 23 | + |
| 24 | + private DevelopmentSourceConstantData(ClassNode classNode) { |
| 25 | + this.internalVersion = TransformerUtils.getFieldStringData(classNode, "INTERNAL_VERSION"); |
| 26 | + this.version = TransformerUtils.getFieldStringData(classNode, "VERSION"); |
| 27 | + this.displayVersion = TransformerUtils.getFieldStringData(classNode, "DISPLAY_VERSION"); |
| 28 | + CoreConstantCheck internalVersionCheck = new CoreConstantCheck(this.internalVersion, "INTERNAL_VERSION"); |
| 29 | + CoreConstantCheck versionCheck = new CoreConstantCheck(this.version, "VERSION"); |
| 30 | + CoreConstantCheck displayVersionCheck = new CoreConstantCheck(this.displayVersion, "DISPLAY_VERSION"); |
| 31 | + if (Objects.equals(this.version, this.displayVersion)) { |
| 32 | + this.regular = new ConstantCheck[]{versionCheck, internalVersionCheck}; |
| 33 | + this.displayFirst = new ConstantCheck[]{displayVersionCheck, internalVersionCheck}; |
| 34 | + } else { |
| 35 | + this.displayFirst = this.regular = new ConstantCheck[]{ |
| 36 | + displayVersionCheck, versionCheck, internalVersionCheck}; |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + public static DevelopmentSourceConstantData fromPatchedJar(File patchedJar) throws IOException { |
| 41 | + try (JarFile jarFile = new JarFile(patchedJar)) { |
| 42 | + JarEntry jarEntry = jarFile.getJarEntry("net/minecraft/common/CoreConstants.class"); |
| 43 | + ClassNode coreConstantsClassNode = new ClassNode(); |
| 44 | + try (InputStream inputStream = jarFile.getInputStream(jarEntry)) { |
| 45 | + new ClassReader(inputStream).accept(coreConstantsClassNode, ClassReader.SKIP_FRAMES); |
| 46 | + } |
| 47 | + return new DevelopmentSourceConstantData(coreConstantsClassNode); |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + public int methodStatus(ClassNode classNode, MethodNode methodNode) { |
| 52 | + switch (classNode.name) { |
| 53 | + case "net/minecraft/common/CoreConstants": |
| 54 | + return STATUS_DISABLED; |
| 55 | + case "net/minecraft/client/Minecraft": |
| 56 | + return "startGame".equals(methodNode.name) ? STATUS_DISPLAY_FIRST : STATUS_DEFAULT; |
| 57 | + case "net/minecraft/client/gui/GuiDebug": |
| 58 | + return "drawScreen".equals(methodNode.name) ? STATUS_DISPLAY_FIRST : STATUS_DEFAULT; |
| 59 | + } |
| 60 | + return STATUS_DEFAULT; |
| 61 | + } |
| 62 | + |
| 63 | + public void patchStringConstant(IdentityHashMap<AbstractInsnNode, InsnList> constantPatching, |
| 64 | + int state, LdcInsnNode ldcInsnNode, ArrayList<AbstractInsnNode> list) { |
| 65 | + if (state == STATUS_DISABLED) return; |
| 66 | + if (!list.isEmpty()) { |
| 67 | + list.clear(); |
| 68 | + } |
| 69 | + list.add(ldcInsnNode); |
| 70 | + ConstantCheck[] check = // Some method should prioritize display instead of version. |
| 71 | + state == STATUS_DISPLAY_FIRST ? this.displayFirst : this.regular; |
| 72 | + for (ConstantCheck constantCheck : check) { |
| 73 | + constantCheck.applyCheck(list); |
| 74 | + } |
| 75 | + if (list.size() != 1 || list.get(0).getOpcode() != Opcodes.LDC) { |
| 76 | + constantPatching.put(ldcInsnNode, |
| 77 | + TransformerUtils.compileStringAppendChain(list)); |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + private static abstract class ConstantCheck { |
| 82 | + private final String value; |
| 83 | + |
| 84 | + private ConstantCheck(String value) { |
| 85 | + this.value = value; |
| 86 | + } |
| 87 | + |
| 88 | + public void applyCheck(ArrayList<AbstractInsnNode> list) { |
| 89 | + for (int listIndex = 0; listIndex < list.size(); listIndex++) { |
| 90 | + AbstractInsnNode abstractInsnNode = list.get(listIndex); |
| 91 | + if (abstractInsnNode.getOpcode() == Opcodes.LDC) { |
| 92 | + String data = (String) ((LdcInsnNode) abstractInsnNode).cst; |
| 93 | + int valIndex = data.indexOf(this.value); |
| 94 | + if (valIndex == -1) continue; |
| 95 | + if (valIndex == 0) { |
| 96 | + list.set(listIndex, this.makeFieldInsnNode()); |
| 97 | + if (data.length() != this.value.length()) { |
| 98 | + list.add(listIndex + 1, new LdcInsnNode(data.substring(this.value.length()))); |
| 99 | + } |
| 100 | + } else { |
| 101 | + if (list.size() == 1) { |
| 102 | + list.set(listIndex, new LdcInsnNode(data.substring(0, valIndex))); |
| 103 | + } else { |
| 104 | + ((LdcInsnNode) abstractInsnNode).cst = data.substring(0, valIndex); |
| 105 | + } |
| 106 | + list.add(listIndex + 1, this.makeFieldInsnNode()); |
| 107 | + if (data.length() != (valIndex + this.value.length())) { |
| 108 | + list.add(listIndex + 2, new LdcInsnNode(data.substring(valIndex + this.value.length()))); |
| 109 | + } |
| 110 | + // skip next instruction as we know it is our field instruction |
| 111 | + listIndex++; |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + public abstract FieldInsnNode makeFieldInsnNode(); |
| 118 | + } |
| 119 | + |
| 120 | + private static class CoreConstantCheck extends ConstantCheck{ |
| 121 | + private final String fieldName; |
| 122 | + |
| 123 | + private CoreConstantCheck(String value, String fieldName) { |
| 124 | + super(value); |
| 125 | + this.fieldName = fieldName; |
| 126 | + } |
| 127 | + |
| 128 | + public FieldInsnNode makeFieldInsnNode() { |
| 129 | + return new FieldInsnNode(Opcodes.GETSTATIC, |
| 130 | + "net/minecraft/common/CoreConstants", this.fieldName, "Ljava/lang/String;"); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
0 commit comments