From 1ad33bfe0a96b1b4a1f3c02cf2c054e8a101dfd8 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 9 Mar 2015 20:08:15 -0400 Subject: field matcher is starting to be useful --- src/cuchaz/enigma/ConvertMain.java | 135 +++++++++++++---- src/cuchaz/enigma/analysis/JarIndex.java | 28 +++- src/cuchaz/enigma/convert/FieldMatches.java | 69 +++++++-- src/cuchaz/enigma/convert/MatchesReader.java | 40 +++++ src/cuchaz/enigma/convert/MatchesWriter.java | 38 +++++ src/cuchaz/enigma/gui/ClassMatchingGui.java | 3 - src/cuchaz/enigma/gui/CodeReader.java | 56 ++++++- src/cuchaz/enigma/gui/FieldMatchingGui.java | 212 +++++++++++++++++++++++---- src/cuchaz/enigma/mapping/EntryFactory.java | 16 ++ 9 files changed, 525 insertions(+), 72 deletions(-) diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 624eb40..a5a00e8 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -4,8 +4,12 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.Set; import java.util.jar.JarFile; +import com.google.common.collect.BiMap; +import com.google.common.collect.Sets; + import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.convert.MappingsConverter; @@ -13,11 +17,18 @@ import cuchaz.enigma.convert.MatchesReader; import cuchaz.enigma.convert.MatchesWriter; import cuchaz.enigma.gui.ClassMatchingGui; import cuchaz.enigma.gui.FieldMatchingGui; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsChecker; import cuchaz.enigma.mapping.MappingsReader; import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.Type; public class ConvertMain { @@ -32,13 +43,14 @@ public class ConvertMain { File inMappingsFile = new File("../Enigma Mappings/1.8.mappings"); File outMappingsFile = new File("../Enigma Mappings/1.8.3.mappings"); Mappings mappings = new MappingsReader().read(new FileReader(inMappingsFile)); - File classMatchingFile = new File(inMappingsFile.getName() + ".class.matching"); - File fieldMatchingFile = new File(inMappingsFile.getName() + ".field.matching"); + File classMatchesFile = new File(inMappingsFile.getName() + ".class.matches"); + File fieldMatchesFile = new File(inMappingsFile.getName() + ".field.matches"); - //computeMatches(classMatchingFile, sourceJar, destJar, mappings); + //computeClassMatches(classMatchingFile, sourceJar, destJar, mappings); //editClasssMatches(classMatchingFile, sourceJar, destJar, mappings); //convertMappings(outMappingsFile, sourceJar, destJar, mappings, classMatchingFile); - editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchingFile, fieldMatchingFile); + //computeFieldMatches(fieldMatchesFile, destJar, outMappingsFile, classMatchesFile); + editFieldMatches(sourceJar, destJar, outMappingsFile, mappings, classMatchesFile, fieldMatchesFile); /* TODO // write out the converted mappings @@ -49,17 +61,17 @@ public class ConvertMain { */ } - private static void computeMatches(File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); - MatchesWriter.writeClasses(classMatches, classMatchingFile); - System.out.println("Wrote:\n\t" + classMatchingFile.getAbsolutePath()); + MatchesWriter.writeClasses(classMatches, classMatchesFile); + System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); } - private static void editClasssMatches(final File classMatchingFile, JarFile sourceJar, JarFile destJar, Mappings mappings) + private static void editClasssMatches(final File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) throws IOException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(mappings); System.out.println("Starting GUI..."); @@ -67,7 +79,7 @@ public class ConvertMain { @Override public void save(ClassMatches matches) { try { - MatchesWriter.writeClasses(matches, classMatchingFile); + MatchesWriter.writeClasses(matches, classMatchesFile); } catch (IOException ex) { throw new Error(ex); } @@ -75,10 +87,10 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File classMatchesFile) throws IOException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(mappings); @@ -90,19 +102,90 @@ public class ConvertMain { System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } - private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchingFile, final File fieldMatchingFile) + private static void computeFieldMatches(File fieldMatchesFile, JarFile destJar, File destMappingsFile, File classMatchesFile) throws IOException, MappingParseException { - System.out.println("Reading matches..."); - ClassMatches classMatches = MatchesReader.readClasses(classMatchingFile); - FieldMatches fieldMatches; - if (fieldMatchingFile.exists() /* TEMP */ && false) { - // TODO - //fieldMatches = MatchesReader.readFields(fieldMatchingFile); - } else { - fieldMatches = new FieldMatches(); + System.out.println("Reading class matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + System.out.println("Reading mappings..."); + Mappings destMappings = new MappingsReader().read(new FileReader(destMappingsFile)); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + + System.out.println("Writing field matches..."); + + // get the matched and unmatched field mappings + FieldMatches fieldMatches = new FieldMatches(); + + // unmatched source fields are easy + MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); + checker.dropBrokenMappings(destMappings); + for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addUnmatchedSourceField(srcObfField); + } + + // get matched fields (anything that's left after the checks/drops is matched( + for (ClassMapping classMapping : destMappings.classes()) { + collectMatchedFields(fieldMatches, classMapping, classMatches); + } + + // get unmatched dest fields + Set unmatchedDestFields = Sets.newHashSet(); + for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { + if (!fieldMatches.isDestMatched(destFieldEntry)) { + unmatchedDestFields.add(destFieldEntry); + } + } + fieldMatches.addUnmatchedDestFields(unmatchedDestFields); + + MatchesWriter.writeFields(fieldMatches, fieldMatchesFile); + System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath()); + } + + private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { + + // get the fields for this class + for (FieldMapping destFieldMapping : destClassMapping.fields()) { + FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); + FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); + fieldMatches.addMatch(srcObfField, destObfField); } + // recurse + for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { + collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); + } + } + + private static FieldEntry translate(FieldEntry in, BiMap map) { + return new FieldEntry( + map.get(in.getClassEntry()), + in.getName(), + translate(in.getType(), map) + ); + } + + private static Type translate(Type type, final BiMap map) { + return new Type(type, new ClassNameReplacer() { + @Override + public String replace(String inClassName) { + ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); + if (outClassEntry == null) { + return null; + } + return outClassEntry.getName(); + } + }); + } + + private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) + throws IOException, MappingParseException { + + System.out.println("Reading matches..."); + ClassMatches classMatches = MatchesReader.readClasses(classMatchesFile); + FieldMatches fieldMatches = MatchesReader.readFields(fieldMatchesFile); + // prep deobfuscators Deobfuscators deobfuscators = new Deobfuscators(sourceJar, destJar); deobfuscators.source.setMappings(sourceMappings); @@ -111,16 +194,14 @@ public class ConvertMain { checker.dropBrokenMappings(destMappings); deobfuscators.dest.setMappings(destMappings); - new FieldMatchingGui(classMatches, fieldMatches, checker.getDroppedFieldMappings(), deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { + new FieldMatchingGui(classMatches, fieldMatches, deobfuscators.source, deobfuscators.dest).setSaveListener(new FieldMatchingGui.SaveListener() { @Override public void save(FieldMatches matches) { - /* TODO try { - MatchesWriter.writeFields(matches, fieldMatchingFile); + MatchesWriter.writeFields(matches, fieldMatchesFile); } catch (IOException ex) { throw new Error(ex); } - */ } }); } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index e0a8bf5..7ebbd97 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -60,6 +60,8 @@ public class JarIndex { private TranslationIndex m_translationIndex; private Multimap m_interfaces; private Map m_access; + private Multimap m_fields; + private Multimap m_behaviors; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; @@ -73,6 +75,8 @@ public class JarIndex { m_translationIndex = new TranslationIndex(); m_interfaces = HashMultimap.create(); m_access = Maps.newHashMap(); + m_fields = HashMultimap.create(); + m_behaviors = HashMultimap.create(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); @@ -97,10 +101,14 @@ public class JarIndex { for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); for (CtField field : c.getDeclaredFields()) { - m_access.put(EntryFactory.getFieldEntry(field), Access.get(field)); + FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); + m_access.put(fieldEntry, Access.get(field)); + m_fields.put(fieldEntry.getClassEntry(), fieldEntry); } for (CtBehavior behavior : c.getDeclaredBehaviors()) { - m_access.put(EntryFactory.getBehaviorEntry(behavior), Access.get(behavior)); + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + m_access.put(behaviorEntry, Access.get(behavior)); + m_behaviors.put(behaviorEntry.getClassEntry(), behaviorEntry); } } @@ -520,6 +528,22 @@ public class JarIndex { return m_obfClassEntries; } + public Collection getObfFieldEntries() { + return m_fields.values(); + } + + public Collection getObfFieldEntries(ClassEntry classEntry) { + return m_fields.get(classEntry); + } + + public Collection getObfBehaviorEntries() { + return m_behaviors.values(); + } + + public Collection getObfBehaviorEntries(ClassEntry classEntry) { + return m_behaviors.get(classEntry); + } + public TranslationIndex getTranslationIndex() { return m_translationIndex; } diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java index f78a8f5..6335974 100644 --- a/src/cuchaz/enigma/convert/FieldMatches.java +++ b/src/cuchaz/enigma/convert/FieldMatches.java @@ -5,7 +5,8 @@ import java.util.Set; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.google.common.collect.Sets; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; @@ -14,22 +15,70 @@ import cuchaz.enigma.mapping.FieldEntry; public class FieldMatches { private BiMap m_matches; - private Set m_unmatchedSourceFields; + private Multimap m_unmatchedSourceFields; + private Multimap m_unmatchedDestFields; public FieldMatches() { m_matches = HashBiMap.create(); - m_unmatchedSourceFields = Sets.newHashSet(); + m_unmatchedSourceFields = HashMultimap.create(); + m_unmatchedDestFields = HashMultimap.create(); } - public void addUnmatchedSourceFields(Set fieldEntries) { - m_unmatchedSourceFields.addAll(fieldEntries); + public void addMatch(FieldEntry srcField, FieldEntry destField) { + m_matches.put(srcField, destField); } - public Collection getSourceClassesWithUnmatchedFields() { - Set classEntries = Sets.newHashSet(); - for (FieldEntry fieldEntry : m_unmatchedSourceFields) { - classEntries.add(fieldEntry.getClassEntry()); + public void addUnmatchedSourceField(FieldEntry fieldEntry) { + m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); + } + + public void addUnmatchedSourceFields(Iterable fieldEntries) { + for (FieldEntry fieldEntry : fieldEntries) { + addUnmatchedSourceField(fieldEntry); + } + } + + public void addUnmatchedDestField(FieldEntry fieldEntry) { + m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); + } + + public void addUnmatchedDestFields(Iterable fieldEntries) { + for (FieldEntry fieldEntry : fieldEntries) { + addUnmatchedDestField(fieldEntry); } - return classEntries; + } + + public Set getSourceClassesWithUnmatchedFields() { + return m_unmatchedSourceFields.keySet(); + } + + public Collection getUnmatchedSourceFields() { + return m_unmatchedSourceFields.values(); + } + + public Collection getUnmatchedSourceFields(ClassEntry sourceClass) { + return m_unmatchedSourceFields.get(sourceClass); + } + + public Collection getUnmatchedDestFields() { + return m_unmatchedDestFields.values(); + } + + public Collection getUnmatchedDestFields(ClassEntry sourceClass) { + return m_unmatchedDestFields.get(sourceClass); + } + + public BiMap matches() { + return m_matches; + } + + public boolean isDestMatched(FieldEntry destFieldEntry) { + return m_matches.containsValue(destFieldEntry); + } + + public void makeMatch(FieldEntry sourceField, FieldEntry destField) { + m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); + m_unmatchedDestFields.remove(destField.getClassEntry(), destField); + m_matches.put(sourceField, destField); } } diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java index b43535c..1dd042d 100644 --- a/src/cuchaz/enigma/convert/MatchesReader.java +++ b/src/cuchaz/enigma/convert/MatchesReader.java @@ -10,6 +10,8 @@ import java.util.List; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.Type; public class MatchesReader { @@ -42,4 +44,42 @@ public class MatchesReader { } return entries; } + + public static FieldMatches readFields(File file) + throws IOException { + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + FieldMatches matches = new FieldMatches(); + String line = null; + while ((line = in.readLine()) != null) { + readFieldMatch(matches, line); + } + return matches; + } + } + + private static void readFieldMatch(FieldMatches matches, String line) { + String[] parts = line.split(":", 2); + FieldEntry source = readField(parts[0]); + FieldEntry dest = readField(parts[1]); + if (source != null && dest != null) { + matches.addMatch(source, dest); + } else if (source != null) { + matches.addUnmatchedSourceField(source); + } else if (dest != null) { + matches.addUnmatchedDestField(dest); + } + } + + private static FieldEntry readField(String in) { + if (in.length() <= 0) { + return null; + } + String[] parts = in.split(" "); + assert(parts.length == 3); + return new FieldEntry( + new ClassEntry(parts[0]), + parts[1], + new Type(parts[2]) + ); + } } diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java index 6658e2a..6e371bc 100644 --- a/src/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/cuchaz/enigma/convert/MatchesWriter.java @@ -3,8 +3,10 @@ package cuchaz.enigma.convert; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Map; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.FieldEntry; public class MatchesWriter { @@ -38,4 +40,40 @@ public class MatchesWriter { out.write(entry.toString()); } } + + public static void writeFields(FieldMatches fieldMatches, File file) + throws IOException { + try (FileWriter out = new FileWriter(file)) { + for (Map.Entry match : fieldMatches.matches().entrySet()) { + writeFieldMatch(out, match.getKey(), match.getValue()); + } + for (FieldEntry fieldEntry : fieldMatches.getUnmatchedSourceFields()) { + writeFieldMatch(out, fieldEntry, null); + } + for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { + writeFieldMatch(out, null, fieldEntry); + } + } + } + + private static void writeFieldMatch(FileWriter out, FieldEntry source, FieldEntry dest) + throws IOException { + if (source != null) { + writeField(out, source); + } + out.write(":"); + if (dest != null) { + writeField(out, dest); + } + out.write("\n"); + } + + private static void writeField(FileWriter out, FieldEntry fieldEntry) + throws IOException { + out.write(fieldEntry.getClassName()); + out.write(" "); + out.write(fieldEntry.getName()); + out.write(" "); + out.write(fieldEntry.getType().toString()); + } } diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java index b674451..6943c3e 100644 --- a/src/cuchaz/enigma/gui/ClassMatchingGui.java +++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java @@ -201,13 +201,10 @@ public class ClassMatchingGui { m_sourceClassLabel = new JLabel(); m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT); - m_sourceClassLabel.setPreferredSize(new Dimension(300, 24)); m_destClassLabel = new JLabel(); m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT); - m_destClassLabel.setPreferredSize(new Dimension(300, 24)); m_matchButton = new JButton(); - m_matchButton.setPreferredSize(new Dimension(140, 24)); m_advanceCheck = new JCheckBox("Advance to next likely match"); m_advanceCheck.addActionListener(new ActionListener() { diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java index 05feb59..aa7e2db 100644 --- a/src/cuchaz/enigma/gui/CodeReader.java +++ b/src/cuchaz/enigma/gui/CodeReader.java @@ -7,12 +7,15 @@ import java.awt.event.ActionListener; import javax.swing.JEditorPane; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter.HighlightPainter; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; import cuchaz.enigma.mapping.ClassEntry; @@ -26,8 +29,13 @@ public class CodeReader extends JEditorPane { private static final Object m_lock = new Object(); - private SelectionHighlightPainter m_highlightPainter; + public static interface SelectionListener { + void onSelect(EntryReference reference); + } + + private SelectionHighlightPainter m_selectionHighlightPainter; private SourceIndex m_sourceIndex; + private SelectionListener m_selectionListener; public CodeReader() { @@ -38,8 +46,28 @@ public class CodeReader extends JEditorPane { DefaultSyntaxKit kit = (DefaultSyntaxKit)getEditorKit(); kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); - m_highlightPainter = new SelectionHighlightPainter(); + // hook events + addCaretListener(new CaretListener() { + @Override + public void caretUpdate(CaretEvent event) { + if (m_selectionListener != null && m_sourceIndex != null) { + Token token = m_sourceIndex.getReferenceToken(event.getDot()); + if (token != null) { + m_selectionListener.onSelect(m_sourceIndex.getDeobfReference(token)); + } else { + m_selectionListener.onSelect(null); + } + } + } + }); + + m_selectionHighlightPainter = new SelectionHighlightPainter(); m_sourceIndex = null; + m_selectionListener = null; + } + + public void setSelectionListener(SelectionListener val) { + m_selectionListener = val; } public void setCode(String code) { @@ -49,6 +77,10 @@ public class CodeReader extends JEditorPane { } } + public SourceIndex getSourceIndex() { + return m_sourceIndex; + } + public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { decompileClass(classEntry, deobfuscator, null); } @@ -110,7 +142,7 @@ public class CodeReader extends JEditorPane { } public void navigateToToken(final Token token) { - navigateToToken(this, token, m_highlightPainter); + navigateToToken(this, token, m_selectionHighlightPainter); } // HACKHACK: someday we can update the main GUI to use this code reader @@ -161,4 +193,22 @@ public class CodeReader extends JEditorPane { }); timer.start(); } + + public void setHighlightedTokens(Iterable tokens, HighlightPainter painter) { + for (Token token : tokens) { + setHighlightedToken(token, painter); + } + } + + public void setHighlightedToken(Token token, HighlightPainter painter) { + try { + getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); + } + } + + public void clearHighlights() { + getHighlighter().removeAllHighlights(); + } } diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java index de9ba14..ef374c8 100644 --- a/src/cuchaz/enigma/gui/FieldMatchingGui.java +++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java @@ -4,24 +4,34 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.util.Map; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; +import java.util.Set; import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.WindowConstants; +import javax.swing.text.Highlighter.HighlightPainter; + +import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; import cuchaz.enigma.convert.ClassMatches; import cuchaz.enigma.convert.FieldMatches; import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; -import cuchaz.enigma.mapping.FieldMapping; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -36,19 +46,28 @@ public class FieldMatchingGui { private ClassSelector m_sourceClasses; private CodeReader m_sourceReader; private CodeReader m_destReader; + private JButton m_matchButton; + private JLabel m_sourceLabel; + private JLabel m_destLabel; + private HighlightPainter m_unmatchedHighlightPainter; + private HighlightPainter m_matchedHighlightPainter; private ClassMatches m_classMatches; private FieldMatches m_fieldMatches; - private Map m_droppedFieldMappings; private Deobfuscator m_sourceDeobfuscator; private Deobfuscator m_destDeobfuscator; private SaveListener m_saveListener; + private ClassEntry m_obfSourceClass; + private ClassEntry m_obfDestClass; + private FieldEntry m_obfSourceField; + private FieldEntry m_obfDestField; + private Set m_obfUnmatchedSourceFields; + private Set m_obfUnmatchedDestFields; - public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Map droppedFieldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { m_classMatches = classMatches; m_fieldMatches = fieldMatches; - m_droppedFieldMappings = droppedFieldMappings; m_sourceDeobfuscator = sourceDeobfuscator; m_destDeobfuscator = destDeobfuscator; @@ -74,31 +93,57 @@ public class FieldMatchingGui { JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); classesPanel.add(sourceScroller); - // init fields side - JPanel fieldsPanel = new JPanel(); - fieldsPanel.setLayout(new BoxLayout(fieldsPanel, BoxLayout.PAGE_AXIS)); - fieldsPanel.setPreferredSize(new Dimension(200, 0)); - pane.add(fieldsPanel, BorderLayout.WEST); - fieldsPanel.add(new JLabel("Destination Fields")); - // init readers DefaultSyntaxKit.initKit(); m_sourceReader = new CodeReader(); + m_sourceReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectSource(reference.entry); + } else { + onSelectSource(null); + } + } + }); m_destReader = new CodeReader(); + m_destReader.setSelectionListener(new CodeReader.SelectionListener() { + @Override + public void onSelect(EntryReference reference) { + if (reference != null) { + onSelectDest(reference.entry); + } else { + onSelectDest(null); + } + } + }); // init all the splits - JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, new JScrollPane(m_sourceReader)); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); + splitRight.setResizeWeight(0.5); // resize 50:50 + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight); splitLeft.setResizeWeight(0); // let the right side take all the slack - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), fieldsPanel); - splitRight.setResizeWeight(1); // let the left side take all the slack - JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); - splitCenter.setResizeWeight(0.5); // resize 50:50 - pane.add(splitCenter, BorderLayout.CENTER); - splitCenter.resetToPreferredSizes(); + pane.add(splitLeft, BorderLayout.CENTER); + splitLeft.resetToPreferredSizes(); // init bottom panel JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new FlowLayout()); + pane.add(bottomPanel, BorderLayout.SOUTH); + + m_matchButton = new JButton("Match"); + m_matchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + match(); + } + }); + + m_sourceLabel = new JLabel(); + bottomPanel.add(m_sourceLabel); + bottomPanel.add(m_matchButton); + m_destLabel = new JLabel(); + bottomPanel.add(m_destLabel); // show the frame pane.doLayout(); @@ -106,26 +151,139 @@ public class FieldMatchingGui { m_frame.setMinimumSize(new Dimension(640, 480)); m_frame.setVisible(true); m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter(); + m_matchedHighlightPainter = new DeobfuscatedHighlightPainter(); // init state m_saveListener = null; - m_fieldMatches.addUnmatchedSourceFields(droppedFieldMappings.keySet()); - m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + m_obfSourceClass = null; + m_obfDestClass = null; + m_obfSourceField = null; + m_obfDestField = null; + m_obfUnmatchedSourceFields = null; + m_obfUnmatchedDestFields = null; + updateSourceClasses(); + updateMatchButton(); } public void setSaveListener(SaveListener val) { m_saveListener = val; } + private void updateSourceClasses() { + m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); + m_sourceClasses.expandAll(); + } + protected void setSourceClass(ClassEntry obfSourceClass) { - // get the matched dest class - final ClassEntry obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); - if (obfDestClass == null) { - throw new Error("No matching dest class for source class: " + obfSourceClass); + m_obfSourceClass = obfSourceClass; + m_obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); + if (m_obfDestClass == null) { + throw new Error("No matching dest class for source class: " + m_obfSourceClass); } + + updateUnmatchedFields(); + m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, new Runnable() { + @Override + public void run() { + updateSourceHighlights(); + } + }); + m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, new Runnable() { + @Override + public void run() { + updateDestHighlights(); + } + }); + } + + private void updateUnmatchedFields() { + m_obfUnmatchedSourceFields = Sets.newHashSet(m_fieldMatches.getUnmatchedSourceFields(m_obfSourceClass)); + m_obfUnmatchedDestFields = Sets.newHashSet(); + for (FieldEntry destFieldEntry : m_destDeobfuscator.getJarIndex().getObfFieldEntries(m_obfDestClass)) { + if (!m_fieldMatches.isDestMatched(destFieldEntry)) { + m_obfUnmatchedDestFields.add(destFieldEntry); + } + } + } + + protected void updateSourceHighlights() { + highlightFields(m_sourceReader, m_sourceDeobfuscator, m_obfUnmatchedSourceFields, m_fieldMatches.matches().keySet()); + } + + protected void updateDestHighlights() { + highlightFields(m_destReader, m_destDeobfuscator, m_obfUnmatchedDestFields, m_fieldMatches.matches().values()); + } + + private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection obfFieldEntries, Collection obfMatchedFieldEntries) { + reader.clearHighlights(); + SourceIndex index = reader.getSourceIndex(); + for (FieldEntry obfFieldEntry : obfFieldEntries) { + FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); + Token token = index.getDeclarationToken(deobfFieldEntry); + if (token == null) { + System.err.println("WARNING: Can't find declaration token for " + deobfFieldEntry); + } else { + reader.setHighlightedToken( + token, + obfMatchedFieldEntries.contains(obfFieldEntry) ? m_matchedHighlightPainter : m_unmatchedHighlightPainter + ); + } + } + } + + protected void onSelectSource(Entry entry) { + m_sourceLabel.setText(""); + m_obfSourceField = null; + if (entry != null && entry instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)entry; + FieldEntry obfFieldEntry = m_sourceDeobfuscator.obfuscateEntry(fieldEntry); + if (m_obfUnmatchedSourceFields.contains(obfFieldEntry)) { + m_obfSourceField = obfFieldEntry; + m_sourceLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + } + } + updateMatchButton(); + } - m_sourceReader.decompileClass(obfSourceClass, m_sourceDeobfuscator); - m_destReader.decompileClass(obfDestClass, m_destDeobfuscator); + protected void onSelectDest(Entry entry) { + m_destLabel.setText(""); + m_obfDestField = null; + if (entry != null && entry instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry)entry; + FieldEntry obfFieldEntry = m_destDeobfuscator.obfuscateEntry(fieldEntry); + if (m_obfUnmatchedDestFields.contains(obfFieldEntry)) { + m_obfDestField = obfFieldEntry; + m_destLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); + } + } + updateMatchButton(); + } + + private void updateMatchButton() { + m_matchButton.setEnabled(m_obfSourceField != null && m_obfDestField != null); + } + + protected void match() { + + // update the field matches + m_fieldMatches.makeMatch(m_obfSourceField, m_obfDestField); + save(); + + // update the ui + onSelectSource(null); + onSelectDest(null); + updateUnmatchedFields(); + updateSourceHighlights(); + updateDestHighlights(); + updateSourceClasses(); + } + + private void save() { + if (m_saveListener != null) { + m_saveListener.save(m_fieldMatches); + } } } diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index f4d62c8..333bb09 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -26,6 +26,10 @@ public class EntryFactory { return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); } + private static ClassEntry getObfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getObfFullName()); + } + public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { return new ClassEntry(classMapping.getDeobfName()); } @@ -50,6 +54,14 @@ public class EntryFactory { ); } + public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { + return new FieldEntry( + getObfClassEntry(classMapping), + fieldMapping.getObfName(), + fieldMapping.getObfType() + ); + } + public static MethodEntry getMethodEntry(CtMethod method) { return new MethodEntry( getClassEntry(method.getDeclaringClass()), @@ -148,4 +160,8 @@ public class EntryFactory { public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); } + + public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { + return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); + } } -- cgit v1.2.3