mirror of
https://github.com/GTMEGA/EndlessIDs.git
synced 2026-11-12 16:37:49 +00:00
more ASM tweaks
This commit is contained in:
2 files changed
+74
-65
No files matched your search
@@ -3,19 +3,16 @@ package com.falsepattern.endlessids.asm;
|
||||
import com.falsepattern.endlessids.Tags;
|
||||
import com.falsepattern.endlessids.asm.transformer.ChunkProviderSuperPatcher;
|
||||
import com.falsepattern.endlessids.asm.transformer.DevFixer;
|
||||
import lombok.val;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.util.CheckClassAdapter;
|
||||
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IETransformer implements IClassTransformer {
|
||||
public static final Logger logger;
|
||||
public static boolean isObfuscated;
|
||||
@@ -32,44 +29,53 @@ public class IETransformer implements IClassTransformer {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
ClassEdit edit = ClassEdit.get(transformedName);
|
||||
final ClassNode cn = new ClassNode(Opcodes.ASM5);
|
||||
final ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(cn, edit == null ? ClassReader.EXPAND_FRAMES : 0);
|
||||
if (ChunkProviderSuperPatcher.isChunkProvider(cn)) {
|
||||
if (edit == null) {
|
||||
edit = ClassEdit.ChunkProviderSuperPatcher;
|
||||
ClassEdit[] edits = new ClassEdit[2];
|
||||
edits[0] = ClassEdit.get(transformedName);
|
||||
ClassNode cn = new ClassNode(Opcodes.ASM5);
|
||||
{
|
||||
val reader = new ClassReader(bytes);
|
||||
reader.accept(cn, 0);
|
||||
}
|
||||
boolean expanded = false;
|
||||
if (ChunkProviderSuperPatcher.shouldPatch(cn)) {
|
||||
expanded = true;
|
||||
{
|
||||
cn = new ClassNode(Opcodes.ASM5);
|
||||
val reader = new ClassReader(bytes);
|
||||
reader.accept(cn, ClassReader.EXPAND_FRAMES);
|
||||
}
|
||||
if (edits[0] == null) {
|
||||
edits[0] = ClassEdit.ChunkProviderSuperPatcher;
|
||||
} else {
|
||||
ClassEdit.ChunkProviderSuperPatcher.getTransformer().transform(cn, isObfuscated);
|
||||
edits[1] = ClassEdit.ChunkProviderSuperPatcher;
|
||||
}
|
||||
}
|
||||
if (edit == null) {
|
||||
if (edits[0] == null) {
|
||||
return isObfuscated ? bytes : DevFixer.fixDev(bytes);
|
||||
}
|
||||
if (!isObfuscated) {
|
||||
DevFixer.transform(cn);
|
||||
}
|
||||
IETransformer.logger.debug("Patching {} with {}...", transformedName, edit.getName());
|
||||
try {
|
||||
edit.getTransformer().transform(cn, IETransformer.isObfuscated);
|
||||
} catch (AsmTransformException t) {
|
||||
IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(),
|
||||
t.getMessage());
|
||||
throw t;
|
||||
} catch (Throwable t2) {
|
||||
IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(),
|
||||
t2.getMessage());
|
||||
throw new RuntimeException(t2);
|
||||
}
|
||||
final ClassWriter writer = new ClassWriter(edit == ClassEdit.ChunkProviderSuperPatcher ? ClassWriter.COMPUTE_FRAMES : 0);
|
||||
try {
|
||||
final ClassVisitor check = new CheckClassAdapter(writer);
|
||||
cn.accept(check);
|
||||
} catch (Throwable t3) {
|
||||
IETransformer.logger.error("Error verifying {} transformed with {}: {}", transformedName, edit.getName(),
|
||||
t3.getMessage());
|
||||
throw new RuntimeException(t3);
|
||||
for (int i = 0; i < edits.length; i++) {
|
||||
val edit = edits[i];
|
||||
if (edit == null) {
|
||||
continue;
|
||||
}
|
||||
IETransformer.logger.debug("Patching {} with {}...", transformedName, edit.getName());
|
||||
try {
|
||||
edit.getTransformer().transform(cn, IETransformer.isObfuscated);
|
||||
} catch (AsmTransformException t) {
|
||||
IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(),
|
||||
t.getMessage());
|
||||
throw t;
|
||||
} catch (Throwable t2) {
|
||||
IETransformer.logger.error("Error transforming {} with {}: {}", transformedName, edit.getName(),
|
||||
t2.getMessage());
|
||||
throw new RuntimeException(t2);
|
||||
}
|
||||
}
|
||||
final ClassWriter writer = new ClassWriter(expanded ? ClassWriter.COMPUTE_FRAMES : 0);
|
||||
cn.accept(writer);
|
||||
IETransformer.logger.debug("Patched {} successfully.", transformedName);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
+35
-32
@@ -141,15 +141,32 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isChunkProvider(final ClassNode cn) {
|
||||
return //100% certain that this is a chunk provider
|
||||
anyMatch(cn.interfaces, CLASS_IChunkProvider) ||
|
||||
//100% certain that this is a chunk provider
|
||||
anyMatch(cn.superName, CLASS_ChunkProviderGenerate) ||
|
||||
//It *might* be a chunk provider given the name, so try patching it.
|
||||
cn.name.contains("ChunkProvider") ||
|
||||
//Look for the specific buggy invocation as a last effort
|
||||
containsBrokenCall(cn);
|
||||
public static boolean shouldPatch(final ClassNode cn) {
|
||||
for (MethodNode method : cn.methods) {
|
||||
if (scanForBrokenCall(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean scanForBrokenCall(MethodNode method) {
|
||||
val instructions = method.instructions;
|
||||
val insnCount = instructions.size();
|
||||
for (int i = 0; i < insnCount; i++) {
|
||||
val insn = instructions.get(i);
|
||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) {
|
||||
continue;
|
||||
}
|
||||
val invoke = (MethodInsnNode) insn;
|
||||
|
||||
if (anyMatch(invoke.owner, CLASS_Chunk) &&
|
||||
anyMatch(invoke.name, METHOD_getBiomeArray) &&
|
||||
invoke.desc.equals("()[B")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean anyMatch(List<String> str, String[] candidates) {
|
||||
@@ -181,27 +198,6 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean containsBrokenCall(final ClassNode cn) {
|
||||
for (val method: cn.methods) {
|
||||
val instructions = method.instructions;
|
||||
val insnCount = instructions.size();
|
||||
for (int i = 0; i < insnCount; i++) {
|
||||
val insn = instructions.get(i);
|
||||
if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) {
|
||||
continue;
|
||||
}
|
||||
val invoke = (MethodInsnNode) insn;
|
||||
|
||||
if (anyMatch(invoke.owner, CLASS_Chunk) &&
|
||||
anyMatch(invoke.name, METHOD_getBiomeArray) &&
|
||||
invoke.desc.equals("()[B")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface State<T extends AbstractInsnNode> extends BiFunction<T, Memory, State<AbstractInsnNode>> {}
|
||||
|
||||
private static State<AbstractInsnNode> STATE0 = null;
|
||||
@@ -276,7 +272,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
STATE6 = typeCheck(LabelNode.class, () -> STATE7);
|
||||
|
||||
STATE7 = casting(FrameNode.class, (insn, memory) ->
|
||||
memory.locals.get(memory.chunkIndex).equals(CLASS_Chunk) &&
|
||||
anyMatch((String)memory.locals.get(memory.chunkIndex), CLASS_Chunk) &&
|
||||
memory.locals.get(memory.biomeArrayIndex).equals("[B") &&
|
||||
memory.locals.get(memory.iteratorIndex).equals(1)
|
||||
? STATE8 : STATE0);
|
||||
@@ -320,7 +316,7 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
|
||||
STATE14_ABD1 = casting(VarInsnNode.class, (insn, memory) -> {
|
||||
if (insn.getOpcode() == Opcodes.ALOAD) {
|
||||
if (memory.locals.get(insn.var).equals("[L" + CLASS_BiomeGenBase + ";")) {
|
||||
if (anyMatch((String)memory.locals.get(insn.var), CLASS_BiomeGenBase, "[L", ";")) {
|
||||
return STATE14_ABD1_BA2;
|
||||
} else {
|
||||
return STATE14_ABD1_BD1;
|
||||
@@ -403,9 +399,12 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
|
||||
@Override
|
||||
public void transform(final ClassNode cn, final boolean obfuscated) {
|
||||
boolean patched = false;
|
||||
for (val method: cn.methods) {
|
||||
val memory = findTheTarget(method);
|
||||
if (memory != null) {
|
||||
patched = true;
|
||||
IETransformer.logger.debug("[ChunkProvider patcher]: Match found in method " + method.name);
|
||||
val insnList = method.instructions;
|
||||
for (val node: method.localVariables) {
|
||||
if (node.desc.equals("[B") && node.index == memory.biomeArrayIndex) {
|
||||
@@ -418,6 +417,10 @@ public class ChunkProviderSuperPatcher implements IClassNodeTransformer {
|
||||
insnList.set(insnList.get(memory.arrayStoreInsn), new InsnNode(Opcodes.SASTORE));
|
||||
}
|
||||
}
|
||||
if (!patched) {
|
||||
IETransformer.logger.debug("[ChunkProvider patcher]: No matches found in class! This is not explicitly " +
|
||||
"an error, because the patch might be applied by a mixin instead!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user