diff --git a/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjector.java b/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjector.java index f98c16a..c041092 100644 --- a/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjector.java +++ b/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjector.java @@ -31,6 +31,7 @@ public class MCInjector private Path excIn, excOut; private Path accIn, accOut; private Path ctrIn, ctrOut; + private Path overrides; private LVTNaming lvt; public MCInjector(Path fileIn, Path fileOut) @@ -110,6 +111,12 @@ public MCInjector constructorsOut(Path out) return this; } + public MCInjector overrides(Path overrides) + { + this.overrides = overrides; + return this; + } + public MCInjector lvt(LVTNaming lvt) { this.lvt = lvt; @@ -123,7 +130,7 @@ public void process() throws IOException accIn, accOut, ctrIn, ctrOut, excIn, excOut, - lvt); + overrides, lvt); } private static ValueConverter PATH_ARG = new ValueConverter() @@ -175,6 +182,7 @@ public static void main(String[] args) throws Exception OptionSpec accOut = parser.accepts("accOut").withRequiredArg().withValuesConvertedBy(PATH_ARG); OptionSpec ctr = parser.accepts("ctr") .withRequiredArg().withValuesConvertedBy(PATH_ARG); OptionSpec ctrOut = parser.accepts("ctrOut").withRequiredArg().withValuesConvertedBy(PATH_ARG); + OptionSpec over = parser.accepts("overrides").withRequiredArg().withValuesConvertedBy(PATH_ARG); OptionSpec logLvl = parser.accepts("level") .withRequiredArg().withValuesConvertedBy(LEVEL_ARG).defaultsTo(Level.INFO); OptionSpec lvt = parser.accepts("lvt").withRequiredArg().ofType(LVTNaming.class).defaultsTo(LVTNaming.STRIP); @@ -206,6 +214,7 @@ else if (o.has(ver)) LOG.info(" " + o.valueOf(accOut)); LOG.info("Constructors: " + o.valueOf(ctr)); LOG.info(" " + o.valueOf(ctrOut)); + LOG.info("Overrides: " + o.valueOf(over)); LOG.info("LVT: " + o.valueOf(lvt)); try @@ -220,6 +229,7 @@ else if (o.has(ver)) .accessOut(o.valueOf(accOut)) .constructors(o.valueOf(ctr)) .constructorsOut(o.valueOf(ctrOut)) + .overrides(o.valueOf(over)) .process(); } catch (Exception e) diff --git a/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjectorImpl.java b/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjectorImpl.java index c415ca5..4089c0c 100644 --- a/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjectorImpl.java +++ b/src/main/java/de/oceanlabs/mcp/mcinjector/MCInjectorImpl.java @@ -31,9 +31,11 @@ import de.oceanlabs.mcp.mcinjector.adaptors.ApplyMap; import de.oceanlabs.mcp.mcinjector.adaptors.InnerClassInitAdder; import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer; +import de.oceanlabs.mcp.mcinjector.adaptors.OverrideAnnotationInjector; import de.oceanlabs.mcp.mcinjector.data.Access; import de.oceanlabs.mcp.mcinjector.data.Constructors; import de.oceanlabs.mcp.mcinjector.data.Exceptions; +import de.oceanlabs.mcp.mcinjector.data.Overrides; import de.oceanlabs.mcp.mcinjector.lvt.LVTFernflower; import de.oceanlabs.mcp.mcinjector.lvt.LVTLvt; import de.oceanlabs.mcp.mcinjector.lvt.LVTNaming; @@ -49,7 +51,7 @@ static void process( Path accIn, Path accOut, Path ctrIn, Path ctrOut, Path excIn, Path excOut, - LVTNaming naming) + Path overrides, LVTNaming naming) throws IOException { if (accIn != null) @@ -58,6 +60,8 @@ static void process( Constructors.INSTANCE.load(ctrIn); if (excIn != null) Exceptions.INSTANCE.load(excIn); + if (overrides != null) + Overrides.INSTANCE.load(overrides); MCInjector.LOG.info("Processing: " + in); MCInjector.LOG.info(" Output: " + out); @@ -176,6 +180,8 @@ public byte[] processClass(byte[] cls, boolean readOnly) ca = new AccessFixer(ca); ca = new ParameterAnnotationFixer(ca, this); + + ca = new OverrideAnnotationInjector(ca); } ca = new InnerClassInitAdder(ca); diff --git a/src/main/java/de/oceanlabs/mcp/mcinjector/adaptors/OverrideAnnotationInjector.java b/src/main/java/de/oceanlabs/mcp/mcinjector/adaptors/OverrideAnnotationInjector.java new file mode 100644 index 0000000..cc4726b --- /dev/null +++ b/src/main/java/de/oceanlabs/mcp/mcinjector/adaptors/OverrideAnnotationInjector.java @@ -0,0 +1,50 @@ +package de.oceanlabs.mcp.mcinjector.adaptors; + +import de.oceanlabs.mcp.mcinjector.MCInjector; +import de.oceanlabs.mcp.mcinjector.MCInjectorImpl; +import de.oceanlabs.mcp.mcinjector.data.Overrides; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; +import java.util.ArrayList; +import java.util.Set; + +public class OverrideAnnotationInjector extends ClassVisitor +{ + private Set overrides; + public OverrideAnnotationInjector(ClassVisitor cv) + { + super(Opcodes.ASM6, cv); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) + { + this.overrides = Overrides.INSTANCE.getOverrides(name); + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public void visitEnd() + { + super.visitEnd(); + if (!this.overrides.isEmpty()) + { + ClassNode cls = MCInjectorImpl.getClassNode(cv); + for (MethodNode mn : cls.methods) + { + if (this.overrides.contains(mn.name + " " + mn.desc)) + { + MCInjector.LOG.fine(" Override annotation injected for " + cls.name + " " + mn.name + " " + mn.desc); + if (mn.invisibleAnnotations == null) + { + mn.invisibleAnnotations = new ArrayList<>(1); + } + mn.invisibleAnnotations.add(new AnnotationNode("Ljava/lang/Override;")); + } + } + } + } +} diff --git a/src/main/java/de/oceanlabs/mcp/mcinjector/data/Overrides.java b/src/main/java/de/oceanlabs/mcp/mcinjector/data/Overrides.java new file mode 100644 index 0000000..fa5ed28 --- /dev/null +++ b/src/main/java/de/oceanlabs/mcp/mcinjector/data/Overrides.java @@ -0,0 +1,60 @@ +package de.oceanlabs.mcp.mcinjector.data; + +import de.oceanlabs.mcp.mcinjector.MCInjector; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public enum Overrides +{ + INSTANCE; + + private Map> classMemberOverrides = new HashMap<>(); + + public boolean load(Path file) + { + this.classMemberOverrides.clear(); + try + { + MCInjector.LOG.fine("Loading Override list from: " + file); + Set mthSet = null; + for(String line : Files.readAllLines(file)) + { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) + continue; + + String[] parts = line.split(" " ); + if (parts[0].charAt(0) == '\t') + { + if (mthSet == null) + { + throw new IOException("Invalid TSRG line, missing class: " + line); + } + mthSet.add(parts[1] + " " + parts[2]); + } + else + { + mthSet = this.classMemberOverrides.computeIfAbsent(parts[0], k -> new HashSet<>()); + } + } + } + catch (IOException e) + { + e.printStackTrace(); + MCInjector.LOG.warning("Could not load Override list: " + e.toString()); + return false; + } + return true; + } + + public Set getOverrides(String className) + { + return this.classMemberOverrides.getOrDefault(className, Collections.emptySet()); + } +}