From 54d17da93c6708e54c296d63783a60f1c024797b Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 2 Mar 2015 01:01:51 -0500 Subject: finished most of the matching gui --- src/cuchaz/enigma/ConvertMain.java | 1 + src/cuchaz/enigma/Deobfuscator.java | 4 + src/cuchaz/enigma/convert/MappingsConverter.java | 61 ++++ src/cuchaz/enigma/convert/Matches.java | 26 ++ src/cuchaz/enigma/gui/ClassSelector.java | 19 +- src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 3 + src/cuchaz/enigma/gui/DecoratedClassEntry.java | 20 ++ src/cuchaz/enigma/gui/MatchingGui.java | 413 +++++++++++++++++----- 8 files changed, 446 insertions(+), 101 deletions(-) create mode 100644 src/cuchaz/enigma/gui/DecoratedClassEntry.java (limited to 'src') diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 7ba4761..4fc58e8 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -58,6 +58,7 @@ public class ConvertMain { sourceDeobfuscator.setMappings(mappings); System.out.println("Indexing dest jar..."); Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + destDeobfuscator.setMappings(MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator)); System.out.println("Starting GUI..."); new MatchingGui(matches, sourceDeobfuscator, destDeobfuscator); } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0b7808d..e5d0e3d 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -99,6 +99,10 @@ public class Deobfuscator { setMappings(new Mappings()); } + public JarFile getJar() { + return m_jar; + } + public String getJarName() { return m_jar.getName(); } diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index d0f9382..aa067d4 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -11,17 +11,25 @@ package cuchaz.enigma.convert; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.jar.JarFile; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.BiMap; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; import cuchaz.enigma.mapping.Mappings; public class MappingsConverter { @@ -100,6 +108,59 @@ public class MappingsConverter { return lastMatching; } + public static Mappings newMappings(Matches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { + + // sort the unique matches by size of inner class chain + Multimap> matchesByDestChainSize = HashMultimap.create(); + for (Entry match : matches.getUniqueMatches().entrySet()) { + int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); + matchesByDestChainSize.put(chainSize, match); + } + + // build the mappings (in order of small-to-large inner chains) + Mappings newMappings = new Mappings(); + List chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); + Collections.sort(chainSizes); + for (int chainSize : chainSizes) { + for (Entry match : matchesByDestChainSize.get(chainSize)) { + + // get class info + ClassEntry sourceClassEntry = match.getKey(); + ClassEntry deobfClassEntry = sourceDeobfuscator.deobfuscateEntry(sourceClassEntry); + ClassEntry destClassEntry = match.getValue(); + List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(destClassEntry); + + // find out where to make the dest class mapping + if (destClassChain.size() == 1) { + // not an inner class, add directly to mappings + newMappings.addClassMapping(new ClassMapping(destClassEntry.getName(), deobfClassEntry.getName())); + } else { + // inner class, find the outer class mapping + ClassMapping destMapping = null; + for (int i=0; i changes) { // sort the changes so classes are renamed in the correct order diff --git a/src/cuchaz/enigma/convert/Matches.java b/src/cuchaz/enigma/convert/Matches.java index 75ecc2a..5faa923 100644 --- a/src/cuchaz/enigma/convert/Matches.java +++ b/src/cuchaz/enigma/convert/Matches.java @@ -17,6 +17,8 @@ import cuchaz.enigma.mapping.ClassEntry; public class Matches implements Iterable { Collection m_matches; + Map m_matchesBySource; + Map m_matchesByDest; BiMap m_uniqueMatches; Map m_ambiguousMatchesBySource; Map m_ambiguousMatchesByDest; @@ -29,6 +31,8 @@ public class Matches implements Iterable { public Matches(Collection matches) { m_matches = matches; + m_matchesBySource = Maps.newHashMap(); + m_matchesByDest = Maps.newHashMap(); m_uniqueMatches = HashBiMap.create(); m_ambiguousMatchesBySource = Maps.newHashMap(); m_ambiguousMatchesByDest = Maps.newHashMap(); @@ -73,6 +77,12 @@ public class Matches implements Iterable { m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); } } + for (ClassEntry entry : match.sourceClasses) { + m_matchesBySource.put(entry, match); + } + for (ClassEntry entry : match.destClasses) { + m_matchesByDest.put(entry, match); + } } public BiMap getUniqueMatches() { @@ -86,4 +96,20 @@ public class Matches implements Iterable { public Set getUnmatchedDestClasses() { return m_unmatchedDestClasses; } + + public Set getAmbiguouslyMatchedSourceClasses() { + return m_ambiguousMatchesBySource.keySet(); + } + + public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { + return m_ambiguousMatchesBySource.get(sourceClass); + } + + public ClassMatch getMatchBySource(ClassEntry sourceClass) { + return m_matchesBySource.get(sourceClass); + } + + public ClassMatch getMatchByDest(ClassEntry destClass) { + return m_matchesByDest.get(destClass); + } } diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 654bfbe..e5f550b 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java @@ -41,21 +41,32 @@ public class ClassSelector extends JTree { public static Comparator ObfuscatedClassEntryComparator; public static Comparator DeobfuscatedClassEntryComparator; + private static String getClassEntryDisplayName(ClassEntry entry) { + if (entry instanceof DecoratedClassEntry) { + return ((DecoratedClassEntry)entry).getDecoration() + entry.getName(); + } + return entry.getName(); + } + static { ObfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - if (a.getName().length() != b.getName().length()) { - return a.getName().length() - b.getName().length(); + String aname = getClassEntryDisplayName(a); + String bname = getClassEntryDisplayName(b); + if (aname.length() != bname.length()) { + return aname.length() - bname.length(); } - return a.getName().compareTo(b.getName()); + return aname.compareTo(bname); } }; DeobfuscatedClassEntryComparator = new Comparator() { @Override public int compare(ClassEntry a, ClassEntry b) { - return a.getName().compareTo(b.getName()); + String aname = getClassEntryDisplayName(a); + String bname = getClassEntryDisplayName(b); + return aname.compareTo(bname); } }; } diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index 66e931b..b3d7b89 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java @@ -30,6 +30,9 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { @Override public String toString() { + if (m_classEntry instanceof DecoratedClassEntry) { + return ((DecoratedClassEntry)m_classEntry).getDecoration() + m_classEntry.getSimpleName(); + } return m_classEntry.getSimpleName(); } } diff --git a/src/cuchaz/enigma/gui/DecoratedClassEntry.java b/src/cuchaz/enigma/gui/DecoratedClassEntry.java new file mode 100644 index 0000000..dd8b4fa --- /dev/null +++ b/src/cuchaz/enigma/gui/DecoratedClassEntry.java @@ -0,0 +1,20 @@ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.ClassEntry; + + +public class DecoratedClassEntry extends ClassEntry { + + private static final long serialVersionUID = -8798725308554217105L; + + private String m_decoration; + + public DecoratedClassEntry(ClassEntry other, String decoration) { + super(other); + m_decoration = decoration; + } + + public String getDecoration() { + return m_decoration; + } +} diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 53c767a..e9dff16 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -1,133 +1,352 @@ package cuchaz.enigma.gui; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.WindowConstants; + +import com.beust.jcommander.internal.Lists; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import cuchaz.enigma.Constants; import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.convert.ClassIdentifier; +import cuchaz.enigma.convert.ClassIdentity; +import cuchaz.enigma.convert.ClassMatch; +import cuchaz.enigma.convert.ClassNamer; import cuchaz.enigma.convert.Matches; +import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; +import cuchaz.enigma.mapping.ClassEntry; +import de.sciss.syntaxpane.DefaultSyntaxKit; public class MatchingGui { - - public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - // TODO Auto-generated constructor stub + + private static enum SourceType { + Matched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUniqueMatches().keySet(); + } + }, + Unmatched { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getUnmatchedSourceClasses(); + } + }, + Ambiguous { + + @Override + public Collection getSourceClasses(Matches matches) { + return matches.getAmbiguouslyMatchedSourceClasses(); + } + }; + + public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { + JRadioButton button = new JRadioButton(name(), this == getDefault()); + button.setActionCommand(name()); + button.addActionListener(listener); + group.add(button); + return button; + } + + public abstract Collection getSourceClasses(Matches matches); + + public static SourceType getDefault() { + return values()[0]; + } } + // controls + private JFrame m_frame; + private ClassSelector m_sourceClasses; + private ClassSelector m_destClasses; + private JEditorPane m_sourceReader; + private JEditorPane m_destReader; + private JLabel m_sourceClassLabel; + private JLabel m_destClassLabel; + private JButton m_matchButton; + + private Matches m_matches; + private Deobfuscator m_sourceDeobfuscator; + private Deobfuscator m_destDeobfuscator; + private ClassEntry m_sourceClass; + private ClassEntry m_destClass; - /* TODO: see if we can use any of this here - public static doTheThings() { + public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { - // get all the obf class names used in the mappings - Set usedClasses = Sets.newHashSet(); - for (String className : mappings.getAllObfClassNames()) { - usedClasses.add(new ClassEntry(className)); - } - System.out.println(String.format("Mappings reference %d/%d classes", - usedClasses.size(), sourceIndex.getObfClassEntries().size() - )); - - // get the used matches - Collection matches = matching.matches(); - Matches usedMatches = new Matches(); - for (ClassMatch match : matching.matches()) { - if (!match.intersectSourceClasses(usedClasses).isEmpty()) { - usedMatches.add(match); + m_matches = matches; + m_sourceDeobfuscator = sourceDeobfuscator; + m_destDeobfuscator = destDeobfuscator; + + // init frame + m_frame = new JFrame(Constants.Name); + final Container pane = m_frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + // init source side + JPanel sourcePanel = new JPanel(); + sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); + sourcePanel.setPreferredSize(new Dimension(200, 0)); + pane.add(sourcePanel, BorderLayout.WEST); + sourcePanel.add(new JLabel("Source Classes")); + + // init source type radios + JPanel sourceTypePanel = new JPanel(); + sourcePanel.add(sourceTypePanel); + sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); + ActionListener sourceTypeListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + setSourceType(SourceType.valueOf(event.getActionCommand())); } + }; + ButtonGroup sourceTypeButtons = new ButtonGroup(); + for (SourceType sourceType : SourceType.values()) { + sourceTypePanel.add(sourceType.newRadio(sourceTypeListener, sourceTypeButtons)); } - System.out.println(String.format("Mappings reference %d/%d match groups", - usedMatches.size(), matches.size() - )); - - // see what the used classes map to - BiMap uniqueUsedMatches = HashBiMap.create(); - Map ambiguousUsedMatches = Maps.newHashMap(); - Set unmatchedUsedClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - Set matchUsedClasses = match.intersectSourceClasses(usedClasses); - if (matchUsedClasses.isEmpty()) { - continue; + + m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_sourceClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setSourceClass(classEntry); } - - usedMatches.add(match); + }); + JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); + sourcePanel.add(sourceScroller); + + // init dest side + JPanel destPanel = new JPanel(); + destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); + destPanel.setPreferredSize(new Dimension(200, 0)); + pane.add(destPanel, BorderLayout.WEST); + destPanel.add(new JLabel("Destination Classes")); + + m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); + m_destClasses.setListener(new ClassSelectionListener() { + @Override + public void onSelectClass(ClassEntry classEntry) { + setDestClass(classEntry); + } + }); + JScrollPane destScroller = new JScrollPane(m_destClasses); + destPanel.add(destScroller); + + // init source panels + DefaultSyntaxKit.initKit(); + m_sourceReader = new JEditorPane(); + m_sourceReader.setEditable(false); + m_sourceReader.setContentType("text/java"); + m_destReader = new JEditorPane(); + m_destReader.setEditable(false); + m_destReader.setContentType("text/java"); + + // init all the splits + JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); + splitLeft.setResizeWeight(0); // let the right side take all the slack + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel); + 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(); + + // init bottom panel + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new FlowLayout()); + + m_sourceClassLabel = new JLabel(); + m_sourceClassLabel.setPreferredSize(new Dimension(300, 0)); + m_destClassLabel = new JLabel(); + m_destClassLabel.setPreferredSize(new Dimension(300, 0)); + + m_matchButton = new JButton(); + m_matchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + onMatchClick(); + } + }); + m_matchButton.setPreferredSize(new Dimension(140, 24)); + + bottomPanel.add(m_sourceClassLabel); + bottomPanel.add(m_matchButton); + bottomPanel.add(m_destClassLabel); + pane.add(bottomPanel, BorderLayout.SOUTH); + + // show the frame + pane.doLayout(); + m_frame.setSize(1024, 576); + m_frame.setMinimumSize(new Dimension(640, 480)); + m_frame.setVisible(true); + m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + // init state + setSourceType(SourceType.getDefault()); + updateMatchButton(); + } - // classify the match - if (!match.isMatched()) { - // unmatched - unmatchedUsedClasses.addAll(matchUsedClasses); + protected void setSourceType(SourceType val) { + // show the source classes + m_sourceClasses.setClasses(deobfuscateClasses(val.getSourceClasses(m_matches), m_sourceDeobfuscator)); + } + + private Collection deobfuscateClasses(Collection in, Deobfuscator deobfuscator) { + List out = Lists.newArrayList(); + for (ClassEntry entry : in) { + out.add(deobfuscator.deobfuscateEntry(entry)); + } + return out; + } + + protected void setSourceClass(ClassEntry classEntry) { + + // update the current source class + m_sourceClass = classEntry; + m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); + + if (m_sourceClass != null) { + + // show the dest class(es) + ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + assert(match != null); + if (match.destClasses.isEmpty()) { + m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); } else { - if (match.isAmbiguous()) { - // ambiguously matched - for (ClassEntry matchUsedClass : matchUsedClasses) { - ambiguousUsedMatches.put(matchUsedClass, match); - } - } else { - // uniquely matched - uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); - } + m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); } + m_destClasses.expandRow(0); } - // get unmatched dest classes - Set unmatchedDestClasses = Sets.newHashSet(); - for (ClassMatch match : matching.matches()) { - if (!match.isMatched()) { - unmatchedDestClasses.addAll(match.destClasses); - } + setDestClass(null); + readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); + + updateMatchButton(); + } + + private Collection getLikelyMatches(ClassEntry sourceClass) { + + ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); + + // set up identifiers + ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); + ClassIdentifier sourceIdentifier = new ClassIdentifier( + m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), + namer.getSourceNamer(), true + ); + ClassIdentifier destIdentifier = new ClassIdentifier( + m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), + namer.getDestNamer(), true + ); + + // rank all the unmatched dest classes against the source class + ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); + Multimap scoredDestClasses = ArrayListMultimap.create(); + for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { + ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); + float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) + /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); + scoredDestClasses.put(score, unmatchedDestClass); } - // warn about the ambiguous used matches - if (ambiguousUsedMatches.size() > 0) { - System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); - List ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); - Collections.sort(ambiguousMatchesList, new Comparator() { - @Override - public int compare(ClassMatch a, ClassMatch b) { - String aName = a.sourceClasses.iterator().next().getName(); - String bName = b.sourceClasses.iterator().next().getName(); - return aName.compareTo(bName); + // sort by scores + List scores = new ArrayList(scoredDestClasses.keySet()); + Collections.sort(scores, Collections.reverseOrder()); + + // collect the scored classes in order + List scoredClasses = Lists.newArrayList(); + for (float score : scores) { + for (ClassEntry classEntry : scoredDestClasses.get(score)) { + scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%.0f%% ", score))); + if (scoredClasses.size() > 10) { + return scoredClasses; } - }); - for (ClassMatch match : ambiguousMatchesList) { - System.out.println("Ambiguous matching:"); - System.out.println("\tSource: " + getClassNames(match.sourceClasses)); - System.out.println("\tDest: " + getClassNames(match.destClasses)); } } + return scoredClasses; + } + + protected void setDestClass(ClassEntry classEntry) { - // warn about unmatched used classes - for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { - System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); - - // rank all the unmatched dest classes against the used class - ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); - Multimap scoredDestClasses = ArrayListMultimap.create(); - for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { - ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); - scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); - } - - List scores = new ArrayList(scoredDestClasses.keySet()); - Collections.sort(scores, Collections.reverseOrder()); - printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); - } + // update the current source class + m_destClass = classEntry; + m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); + + readSource(m_destClass, m_destDeobfuscator, m_destReader); + + updateMatchButton(); + } + + protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { - // bail if there were unmatched classes - if (!unmatchedUsedClasses.isEmpty()) { - throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); + if (classEntry == null) { + reader.setText(null); + return; } + + reader.setText("(decompiling...)"); + + // run decompiler in a separate thread to keep ui responsive + new Thread() { + @Override + public void run() { + + // get the outermost class + ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); + List classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); + ClassEntry obfOutermostClassEntry = classChain.get(0); + + // decompile it + reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); + } + }.start(); } - private static void printScoredMatches(int maxScore, List scores, Multimap scoredMatches) { - int numScoredMatchesShown = 0; - for (int score : scores) { - for (ClassEntry classEntry : scoredMatches.get(score)) { - System.out.println(String.format("\tScore: %3d %3.0f%% %s", - score, 100.0 * score / maxScore, classEntry.getName() - )); - if (numScoredMatchesShown++ > 10) { - return; - } + private void updateMatchButton() { + + boolean twoSelected = m_sourceClass != null && m_destClass != null; + boolean isMatched = twoSelected && m_matches.getUniqueMatches().containsKey(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); + + m_matchButton.setEnabled(twoSelected); + if (twoSelected) { + if (isMatched) { + m_matchButton.setText("Unmatch"); + } else { + m_matchButton.setText("Match"); } + } else { + m_matchButton.setText(""); } } + protected void onMatchClick() { + // TODO + } + + /* private static List getClassNames(Collection classes) { List out = Lists.newArrayList(); for (ClassEntry c : classes) { -- cgit v1.2.3