From c19dee36823f27f9b6f2d7c994551ee50b1584fa Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Sat, 23 Jul 2022 14:14:58 +0200 Subject: [PATCH] state machine improvements, autopatching galacticraft and derivatives --- .../ChunkProviderSuperPatcher.java | 341 ++++++++++-------- 1 file changed, 200 insertions(+), 141 deletions(-) diff --git a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java b/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java index d2f5655..a5db010 100644 --- a/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java +++ b/src/main/java/com/falsepattern/endlessids/asm/transformer/ChunkProviderSuperPatcher.java @@ -4,6 +4,7 @@ import com.falsepattern.endlessids.asm.IClassNodeTransformer; import com.falsepattern.endlessids.asm.IETransformer; import lombok.Data; import lombok.val; +import lombok.var; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; @@ -18,11 +19,10 @@ import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; /** *

This class is the bread and butter of the patching logic from now on. It covers about 90% of cases where manual patching @@ -39,38 +39,76 @@ import java.util.function.Function; * * //Possibility B: * abyte[k] = (byte)this.biomesToGenerate[k].biomeID; //from arbitrary field //replace byte cast with short cast + * + * //Possibility C: + * abyte[k] = (byte)SomeClass.biome.biomeID; //usually galacticraft and derivatives do this for space stations. + * + * //Possibility D: + * abyte[k] = (byte)object.biome.biomeID; //could also happen. * } * ... * In ASM form: * * L1 + * //state 0 * ALOAD chunk + * //state 1 * INVOKEVIRTUAL net/minecraft/world/chunk/Chunk.getBiomeArray ()[B //Replace with INVOKEVIRTUAL net/minecraft/world/chunk/Chunk.getBiomeShortArray ()[S + * //state 2 * ASTORE abyte + * //state 3 * L2 + * //state 4 * ICONST_0 + * //state 5 * ISTORE k + * //state 6 * L3 + * //state 7 * FRAME + * //state 8 * ILOAD k + * //state 9 * ALOAD abyte + * //state 10 * ARRAYLENGTH + * //state 11 * IF_ICMPGE L6 + * //state 12 * L4 + * //state 13 * ALOAD abyte + * //state 14 * ILOAD k * - * //Possibility A: + * //Branch A | B | C | D + * //A: * ALOAD abiomegenbase - * //Possibility B: - * ALOAD + * + * //B: + * ALOAD * GETFIELD classname.fieldname : [Lnet/minecraft/world/biome/BiomeGenBase; * + * //C: + * GETSTATIC classname.fieldname : Lnet/minecraft/world/biome/BiomeGenBase; + * + * //D: + * ALOAD + * GETFIELD classname.fieldname : Lnet/minecraft/world/biome/BiomeGenBase; + * + * //Join A | B * ILOAD k * AALOAD + * + * //Join A,B | C | D + * + * //state 15 * GETFIELD net/minecraft/world/biome/BiomeGenBase.biomeID : I + * //state 16 * I2B //Replace with I2S + * //state 17 * BASTORE //Replace with SASTORE + * //state final, matched. * L5 * IINC k 1 * GOTO L3 @@ -82,173 +120,202 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { public static final String CLASS_Chunk = IETransformer.isObfuscated ? "apx" : "net/minecraft/world/chunk/Chunk"; public static final String FIELD_biomeID = IETransformer.isObfuscated ? "ay" : "biomeID"; public static final String METHOD_getBiomeArray = IETransformer.isObfuscated ? "m" : "getBiomeArray"; - public static final Map> STATES = new HashMap<>(); + + public interface State extends BiFunction> {} + + private static State STATE0 = null; + private static State STATE1 = null; + private static State STATE2 = null; + private static State STATE3 = null; + private static State STATE4 = null; + private static State STATE5 = null; + private static State STATE6 = null; + private static State STATE7 = null; + private static State STATE8 = null; + private static State STATE9 = null; + private static State STATE10 = null; + private static State STATE11 = null; + private static State STATE12 = null; + private static State STATE13 = null; + private static State STATE14 = null; + private static State STATE14_ABD1 = null; + private static State STATE14_ABD1_BD1 = null; + private static State STATE14_ABD1_BA2 = null; + private static State STATE14_ABD1_BA3 = null; + private static State STATE14_C1 = null; + private static State STATE15 = null; + private static State STATE16 = null; + private static State STATE17 = null; + private static State STATE_FINAL = null; static { - STATES.put(0, casting(VarInsnNode.class, (insn, memory) -> { + STATE_FINAL = (insn, memory) -> STATE_FINAL; + + STATE0 = casting(VarInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.ALOAD) { memory.chunkIndex = insn.var; memory.startingInsn = memory.currentInsn; - return 1; + return STATE1; } - return 0; - })); - STATES.put(1, casting(MethodInsnNode.class, (insn, memory) -> { + return STATE0; + }); + + STATE1 = casting(MethodInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL && insn.owner.equals(CLASS_Chunk) && insn.name.equals(METHOD_getBiomeArray) && insn.desc.equals("()[B")) { memory.getBiomeArrayInsn = memory.currentInsn; - return 2; + return STATE2; } - return 0; - })); - STATES.put(2, casting(VarInsnNode.class, (insn, memory) -> { + return STATE0; + }); + + STATE2 = casting(VarInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.ASTORE) { memory.biomeArrayIndex = insn.var; - return 3; + return STATE3; } - return 0; - })); - STATES.put(3, casting(LabelNode.class, 4)); - STATES.put(4, casting(InsnNode.class, (insn) -> + return STATE0; + }); + + STATE3 = typeCheck(LabelNode.class, () -> STATE4); + + STATE4 = casting(InsnNode.class, (insn) -> insn.getOpcode() == Opcodes.ICONST_0 - ? 5 : 0)); - STATES.put(5, casting(VarInsnNode.class, (insn, memory) -> { + ? STATE5 : STATE0); + + STATE5 = casting(VarInsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.ISTORE) { memory.iteratorIndex = insn.var; - return 6; + return STATE6; } - return 0; - })); - STATES.put(6, casting(LabelNode.class, 7)); - STATES.put(7, casting(FrameNode.class, (insn, memory) -> + return STATE0; + }); + + STATE6 = typeCheck(LabelNode.class, () -> STATE7); + + STATE7 = casting(FrameNode.class, (insn, memory) -> memory.locals.get(memory.chunkIndex).equals(CLASS_Chunk) && memory.locals.get(memory.biomeArrayIndex).equals("[B") && memory.locals.get(memory.iteratorIndex).equals(1) - ? 8 : 0)); - STATES.put(8, casting(VarInsnNode.class, (insn, memory) -> { - if (insn.getOpcode() == Opcodes.ILOAD && - insn.var == memory.iteratorIndex) { - memory.tmp = 1; - return 9; - } else if (insn.getOpcode() == Opcodes.ALOAD && - insn.var == memory.biomeArrayIndex) { - memory.tmp = 2; - return 10; - } - memory.tmp = 0; - return 0; - })); - STATES.put(9, casting(VarInsnNode.class, (insn, memory) -> { - if (memory.tmp == 1) { - if (insn.getOpcode() == Opcodes.ALOAD && - insn.var == memory.biomeArrayIndex) { - return 10; - } - } else if (memory.tmp == 2) { - if (insn.getOpcode() == Opcodes.ILOAD && - insn.var == memory.iteratorIndex) { - return 11; - } - } - memory.tmp = 0; - return 0; - })); - STATES.put(10, casting(InsnNode.class, (insn, memory) -> { - if (insn.getOpcode() == Opcodes.ARRAYLENGTH) { - if (memory.tmp == 1) { - return 11; - } else if (memory.tmp == 2) { - return 9; - } - } - memory.tmp = 0; - return 0; - })); - STATES.put(11, casting(JumpInsnNode.class, (insn, memory) -> { - if ((memory.tmp == 1 && insn.getOpcode() == Opcodes.IF_ICMPGE) || - (memory.tmp == 2 && insn.getOpcode() == Opcodes.IF_ICMPLE)) { - memory.tmp = 0; - return 12; - } - memory.tmp = 0; - return 0; - })); - STATES.put(12, casting(LabelNode.class, 13)); - STATES.put(13, casting(VarInsnNode.class, (insn, memory) -> + ? STATE8 : STATE0); + + STATE8 = casting(VarInsnNode.class, (insn, memory) -> + insn.getOpcode() == Opcodes.ILOAD && + insn.var == memory.iteratorIndex + ? STATE9 : STATE0); + + STATE9 = casting(VarInsnNode.class, (insn, memory) -> + insn.getOpcode() == Opcodes.ALOAD + && insn.var == memory.biomeArrayIndex + ? STATE10 : STATE0); + + STATE10 = casting(InsnNode.class, (insn) -> + insn.getOpcode() == Opcodes.ARRAYLENGTH + ? STATE11 : STATE0); + + STATE11 = casting(JumpInsnNode.class, (insn) -> + insn.getOpcode() == Opcodes.IF_ICMPGE + ? STATE12 : STATE0); + + STATE12 = typeCheck(LabelNode.class, () -> STATE13); + + STATE13 = casting(VarInsnNode.class, (insn, memory) -> insn.getOpcode() == Opcodes.ALOAD && insn.var == memory.biomeArrayIndex - ? 14 : 0)); - STATES.put(14, casting(VarInsnNode.class, (insn, memory) -> - insn.getOpcode() == Opcodes.ILOAD && - insn.var == memory.iteratorIndex - ? 15 : 0)); - STATES.put(15, casting(VarInsnNode.class, (insn, memory) -> { - if (insn.getOpcode() == Opcodes.ALOAD) { - if (insn.var == 0) { - memory.altFlags = 1; - return 16; - } else if (memory.locals.get(insn.var).equals("[L" + CLASS_BiomeGenBase + ";")) { - memory.altFlags = 0; - memory.biomesForGenerationIndex = insn.var; - return 17; + ? STATE14 : STATE0); + + STATE14 = casting(VarInsnNode.class, (insn, memory) -> { + if (insn.getOpcode() == Opcodes.ILOAD && + insn.var == memory.iteratorIndex) { + if (insn.getNext() instanceof FieldInsnNode) { + return STATE14_C1; + } else { + return STATE14_ABD1; } } - return 0; - })); - STATES.put(16, casting(FieldInsnNode.class, (insn, memory) -> { - if ((insn.getOpcode() == Opcodes.GETFIELD || insn.getOpcode() == Opcodes.GETSTATIC) && - insn.desc.equals("[L" + CLASS_BiomeGenBase + ";")) { - memory.biomesForGenerationFieldOwner = insn.owner; - memory.biomesForGenerationFieldName = insn.name; - return 17; + return STATE0; + }); + + STATE14_ABD1 = casting(VarInsnNode.class, (insn, memory) -> { + if (insn.getOpcode() == Opcodes.ALOAD) { + if (memory.locals.get(insn.var).equals("[L" + CLASS_BiomeGenBase + ";")) { + return STATE14_ABD1_BA2; + } else { + return STATE14_ABD1_BD1; + } } - return 0; - })); - STATES.put(17, casting(VarInsnNode.class, (insn, memory) -> + return STATE0; + }); + + STATE14_ABD1_BD1 = casting(FieldInsnNode.class, (insn, memory) -> { + if (insn.getOpcode() == Opcodes.GETFIELD) { + if (insn.desc.equals("[L" + CLASS_BiomeGenBase + ";")) { + return STATE14_ABD1_BA2; + } else if (insn.desc.equals("L" + CLASS_BiomeGenBase + ";")) { + return STATE15; + } + } + return STATE0; + }); + + STATE14_ABD1_BA2 = casting(VarInsnNode.class, (insn, memory) -> insn.getOpcode() == Opcodes.ILOAD && insn.var == memory.iteratorIndex - ? 18 : 0)); - STATES.put(18, casting(InsnNode.class, (insn) -> + ? STATE14_ABD1_BA3 : STATE0); + + STATE14_ABD1_BA3 = casting(InsnNode.class, (insn) -> insn.getOpcode() == Opcodes.AALOAD - ? 19 : 0)); - STATES.put(19, casting(FieldInsnNode.class, (insn) -> + ? STATE15 : STATE0); + + STATE14_C1 = casting(FieldInsnNode.class, (insn, memory) -> { + if (insn.getOpcode() == Opcodes.GETSTATIC && + insn.desc.equals("L" + CLASS_BiomeGenBase + ";")) { + return STATE15; + } + return STATE0; + }); + + STATE15 = casting(FieldInsnNode.class, (insn) -> insn.getOpcode() == Opcodes.GETFIELD && insn.owner.equals(CLASS_BiomeGenBase) && insn.name.equals(FIELD_biomeID) & insn.desc.equals("I") - ? 20 : 0)); - STATES.put(20, casting(InsnNode.class, (insn, memory) -> { + ? STATE16 : STATE0); + + STATE16 = casting(InsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.I2B) { memory.castInsn = memory.currentInsn; - return 21; + return STATE17; } - return 0; - })); - STATES.put(21, casting(InsnNode.class, (insn, memory) -> { + return STATE0; + }); + + STATE17 = casting(InsnNode.class, (insn, memory) -> { if (insn.getOpcode() == Opcodes.BASTORE) { memory.arrayStoreInsn = memory.currentInsn; - return -2; + return STATE_FINAL; } - return 0; - })); - } - private static BiFunction casting(Class clazz, int okState) { - return casting(clazz, (insn, memory) -> okState); + return STATE0; + }); } - private static BiFunction casting(Class clazz, Function okCode) { - return casting(clazz, (insn, memory) -> okCode.apply(insn)); + private static State typeCheck(Class clazz, Supplier> okState) { + return casting(clazz, (insn, memory) -> okState.get()); } - private static BiFunction casting(Class clazz, BiFunction okCode) { + private static State casting(Class clazz, Function> okState) { + return casting(clazz, (insn, memory) -> okState.apply(insn)); + } + + private static State casting(Class clazz, State okState) { return (insn, memory) -> { if (insn instanceof LineNumberNode) { - return -1; + return null; } else if (clazz.isInstance(insn)) { - return okCode.apply(clazz.cast(insn), memory); + return okState.apply(clazz.cast(insn), memory); } else { - return 0; + return STATE0; } }; } @@ -264,7 +331,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { node.desc = "[S"; } } - insnList.set(insnList.get(memory.getBiomeArrayInsn), new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/world/chunk/Chunk", "getBiomeShortArray", "()[S", false)); + insnList.set(insnList.get(memory.getBiomeArrayInsn), new MethodInsnNode(Opcodes.INVOKEVIRTUAL, CLASS_Chunk, "getBiomeShortArray", "()[S", false)); insnList.set(insnList.get(memory.castInsn), new InsnNode(Opcodes.I2S)); insnList.set(insnList.get(memory.arrayStoreInsn), new InsnNode(Opcodes.SASTORE)); } @@ -274,7 +341,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { private static Memory findTheTarget(MethodNode method) { val insnList = method.instructions; - int state = 0; + var currentState = STATE0; val memory = new Memory(); for (int insnIndex = 0; insnIndex < insnList.size(); insnIndex++) { memory.currentInsn = insnIndex; @@ -286,14 +353,12 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { memory.locals.addAll(frame.local); } } - val newState = STATES.get(state).apply(insn, memory); - switch (newState) { - case -1: - continue; - case -2: + val newState = currentState.apply(insn, memory); + if (newState != null) { + if (newState == STATE_FINAL) { return memory; - default: - state = newState; + } + currentState = newState; } } return null; @@ -301,6 +366,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { @Data private static class Memory { + int currentInsn; int startingInsn; @@ -312,13 +378,6 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer { int biomeArrayIndex; int iteratorIndex; - long altFlags; - String biomesForGenerationFieldOwner; - String biomesForGenerationFieldName; - int biomesForGenerationIndex; - - int tmp; - final List locals = new ArrayList<>(); } } \ No newline at end of file