diff options
| author | 2015-03-02 01:01:51 -0500 | |
|---|---|---|
| committer | 2015-03-02 01:01:51 -0500 | |
| commit | 54d17da93c6708e54c296d63783a60f1c024797b (patch) | |
| tree | cf42c12c0956786256ff06bbb43c947addc2a573 /src | |
| parent | refactor converter a bit for upcoming convert gui (diff) | |
| download | enigma-54d17da93c6708e54c296d63783a60f1c024797b.tar.gz enigma-54d17da93c6708e54c296d63783a60f1c024797b.tar.xz enigma-54d17da93c6708e54c296d63783a60f1c024797b.zip | |
finished most of the matching gui
Diffstat (limited to 'src')
| -rw-r--r-- | src/cuchaz/enigma/ConvertMain.java | 1 | ||||
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 4 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/MappingsConverter.java | 61 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/Matches.java | 26 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/ClassSelector.java | 19 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 3 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/DecoratedClassEntry.java | 20 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/MatchingGui.java | 413 |
8 files changed, 446 insertions, 101 deletions
diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index 7ba47617..4fc58e87 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java | |||
| @@ -58,6 +58,7 @@ public class ConvertMain { | |||
| 58 | sourceDeobfuscator.setMappings(mappings); | 58 | sourceDeobfuscator.setMappings(mappings); |
| 59 | System.out.println("Indexing dest jar..."); | 59 | System.out.println("Indexing dest jar..."); |
| 60 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); | 60 | Deobfuscator destDeobfuscator = new Deobfuscator(destJar); |
| 61 | destDeobfuscator.setMappings(MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator)); | ||
| 61 | System.out.println("Starting GUI..."); | 62 | System.out.println("Starting GUI..."); |
| 62 | new MatchingGui(matches, sourceDeobfuscator, destDeobfuscator); | 63 | new MatchingGui(matches, sourceDeobfuscator, destDeobfuscator); |
| 63 | } | 64 | } |
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 0b7808da..e5d0e3d9 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -99,6 +99,10 @@ public class Deobfuscator { | |||
| 99 | setMappings(new Mappings()); | 99 | setMappings(new Mappings()); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | public JarFile getJar() { | ||
| 103 | return m_jar; | ||
| 104 | } | ||
| 105 | |||
| 102 | public String getJarName() { | 106 | public String getJarName() { |
| 103 | return m_jar.getName(); | 107 | return m_jar.getName(); |
| 104 | } | 108 | } |
diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index d0f9382a..aa067d4d 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java | |||
| @@ -11,17 +11,25 @@ | |||
| 11 | package cuchaz.enigma.convert; | 11 | package cuchaz.enigma.convert; |
| 12 | 12 | ||
| 13 | import java.util.Arrays; | 13 | import java.util.Arrays; |
| 14 | import java.util.Collections; | ||
| 14 | import java.util.Iterator; | 15 | import java.util.Iterator; |
| 15 | import java.util.LinkedHashMap; | 16 | import java.util.LinkedHashMap; |
| 17 | import java.util.List; | ||
| 16 | import java.util.Map; | 18 | import java.util.Map; |
| 19 | import java.util.Map.Entry; | ||
| 17 | import java.util.jar.JarFile; | 20 | import java.util.jar.JarFile; |
| 18 | 21 | ||
| 22 | import com.beust.jcommander.internal.Lists; | ||
| 19 | import com.google.common.collect.BiMap; | 23 | import com.google.common.collect.BiMap; |
| 24 | import com.google.common.collect.HashMultimap; | ||
| 20 | import com.google.common.collect.Maps; | 25 | import com.google.common.collect.Maps; |
| 26 | import com.google.common.collect.Multimap; | ||
| 21 | 27 | ||
| 28 | import cuchaz.enigma.Deobfuscator; | ||
| 22 | import cuchaz.enigma.analysis.JarIndex; | 29 | import cuchaz.enigma.analysis.JarIndex; |
| 23 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 30 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| 24 | import cuchaz.enigma.mapping.ClassEntry; | 31 | import cuchaz.enigma.mapping.ClassEntry; |
| 32 | import cuchaz.enigma.mapping.ClassMapping; | ||
| 25 | import cuchaz.enigma.mapping.Mappings; | 33 | import cuchaz.enigma.mapping.Mappings; |
| 26 | 34 | ||
| 27 | public class MappingsConverter { | 35 | public class MappingsConverter { |
| @@ -100,6 +108,59 @@ public class MappingsConverter { | |||
| 100 | return lastMatching; | 108 | return lastMatching; |
| 101 | } | 109 | } |
| 102 | 110 | ||
| 111 | public static Mappings newMappings(Matches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | ||
| 112 | |||
| 113 | // sort the unique matches by size of inner class chain | ||
| 114 | Multimap<Integer,Entry<ClassEntry,ClassEntry>> matchesByDestChainSize = HashMultimap.create(); | ||
| 115 | for (Entry<ClassEntry,ClassEntry> match : matches.getUniqueMatches().entrySet()) { | ||
| 116 | int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); | ||
| 117 | matchesByDestChainSize.put(chainSize, match); | ||
| 118 | } | ||
| 119 | |||
| 120 | // build the mappings (in order of small-to-large inner chains) | ||
| 121 | Mappings newMappings = new Mappings(); | ||
| 122 | List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); | ||
| 123 | Collections.sort(chainSizes); | ||
| 124 | for (int chainSize : chainSizes) { | ||
| 125 | for (Entry<ClassEntry,ClassEntry> match : matchesByDestChainSize.get(chainSize)) { | ||
| 126 | |||
| 127 | // get class info | ||
| 128 | ClassEntry sourceClassEntry = match.getKey(); | ||
| 129 | ClassEntry deobfClassEntry = sourceDeobfuscator.deobfuscateEntry(sourceClassEntry); | ||
| 130 | ClassEntry destClassEntry = match.getValue(); | ||
| 131 | List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(destClassEntry); | ||
| 132 | |||
| 133 | // find out where to make the dest class mapping | ||
| 134 | if (destClassChain.size() == 1) { | ||
| 135 | // not an inner class, add directly to mappings | ||
| 136 | newMappings.addClassMapping(new ClassMapping(destClassEntry.getName(), deobfClassEntry.getName())); | ||
| 137 | } else { | ||
| 138 | // inner class, find the outer class mapping | ||
| 139 | ClassMapping destMapping = null; | ||
| 140 | for (int i=0; i<destClassChain.size()-1; i++) { | ||
| 141 | ClassEntry destChainClassEntry = destClassChain.get(i); | ||
| 142 | if (destMapping == null) { | ||
| 143 | destMapping = newMappings.getClassByObf(destChainClassEntry); | ||
| 144 | if (destMapping == null) { | ||
| 145 | destMapping = new ClassMapping(destChainClassEntry.getName()); | ||
| 146 | newMappings.addClassMapping(destMapping); | ||
| 147 | } | ||
| 148 | } else { | ||
| 149 | destMapping = destMapping.getInnerClassByObf(destChainClassEntry.getInnerClassName()); | ||
| 150 | if (destMapping == null) { | ||
| 151 | destMapping = new ClassMapping(destChainClassEntry.getName()); | ||
| 152 | destMapping.addInnerClassMapping(destMapping); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | String deobfName = deobfClassEntry.isInnerClass() ? deobfClassEntry.getInnerClassName() : deobfClassEntry.getSimpleName(); | ||
| 157 | destMapping.addInnerClassMapping(new ClassMapping(destClassEntry.getName(), deobfName)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return newMappings; | ||
| 162 | } | ||
| 163 | |||
| 103 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry,ClassEntry> changes) { | 164 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry,ClassEntry> changes) { |
| 104 | 165 | ||
| 105 | // sort the changes so classes are renamed in the correct order | 166 | // 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 75ecc2a8..5faa923c 100644 --- a/src/cuchaz/enigma/convert/Matches.java +++ b/src/cuchaz/enigma/convert/Matches.java | |||
| @@ -17,6 +17,8 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 17 | public class Matches implements Iterable<ClassMatch> { | 17 | public class Matches implements Iterable<ClassMatch> { |
| 18 | 18 | ||
| 19 | Collection<ClassMatch> m_matches; | 19 | Collection<ClassMatch> m_matches; |
| 20 | Map<ClassEntry,ClassMatch> m_matchesBySource; | ||
| 21 | Map<ClassEntry,ClassMatch> m_matchesByDest; | ||
| 20 | BiMap<ClassEntry,ClassEntry> m_uniqueMatches; | 22 | BiMap<ClassEntry,ClassEntry> m_uniqueMatches; |
| 21 | Map<ClassEntry,ClassMatch> m_ambiguousMatchesBySource; | 23 | Map<ClassEntry,ClassMatch> m_ambiguousMatchesBySource; |
| 22 | Map<ClassEntry,ClassMatch> m_ambiguousMatchesByDest; | 24 | Map<ClassEntry,ClassMatch> m_ambiguousMatchesByDest; |
| @@ -29,6 +31,8 @@ public class Matches implements Iterable<ClassMatch> { | |||
| 29 | 31 | ||
| 30 | public Matches(Collection<ClassMatch> matches) { | 32 | public Matches(Collection<ClassMatch> matches) { |
| 31 | m_matches = matches; | 33 | m_matches = matches; |
| 34 | m_matchesBySource = Maps.newHashMap(); | ||
| 35 | m_matchesByDest = Maps.newHashMap(); | ||
| 32 | m_uniqueMatches = HashBiMap.create(); | 36 | m_uniqueMatches = HashBiMap.create(); |
| 33 | m_ambiguousMatchesBySource = Maps.newHashMap(); | 37 | m_ambiguousMatchesBySource = Maps.newHashMap(); |
| 34 | m_ambiguousMatchesByDest = Maps.newHashMap(); | 38 | m_ambiguousMatchesByDest = Maps.newHashMap(); |
| @@ -73,6 +77,12 @@ public class Matches implements Iterable<ClassMatch> { | |||
| 73 | m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); | 77 | m_uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); |
| 74 | } | 78 | } |
| 75 | } | 79 | } |
| 80 | for (ClassEntry entry : match.sourceClasses) { | ||
| 81 | m_matchesBySource.put(entry, match); | ||
| 82 | } | ||
| 83 | for (ClassEntry entry : match.destClasses) { | ||
| 84 | m_matchesByDest.put(entry, match); | ||
| 85 | } | ||
| 76 | } | 86 | } |
| 77 | 87 | ||
| 78 | public BiMap<ClassEntry,ClassEntry> getUniqueMatches() { | 88 | public BiMap<ClassEntry,ClassEntry> getUniqueMatches() { |
| @@ -86,4 +96,20 @@ public class Matches implements Iterable<ClassMatch> { | |||
| 86 | public Set<ClassEntry> getUnmatchedDestClasses() { | 96 | public Set<ClassEntry> getUnmatchedDestClasses() { |
| 87 | return m_unmatchedDestClasses; | 97 | return m_unmatchedDestClasses; |
| 88 | } | 98 | } |
| 99 | |||
| 100 | public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { | ||
| 101 | return m_ambiguousMatchesBySource.keySet(); | ||
| 102 | } | ||
| 103 | |||
| 104 | public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { | ||
| 105 | return m_ambiguousMatchesBySource.get(sourceClass); | ||
| 106 | } | ||
| 107 | |||
| 108 | public ClassMatch getMatchBySource(ClassEntry sourceClass) { | ||
| 109 | return m_matchesBySource.get(sourceClass); | ||
| 110 | } | ||
| 111 | |||
| 112 | public ClassMatch getMatchByDest(ClassEntry destClass) { | ||
| 113 | return m_matchesByDest.get(destClass); | ||
| 114 | } | ||
| 89 | } | 115 | } |
diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index 654bfbed..e5f550bb 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -41,21 +41,32 @@ public class ClassSelector extends JTree { | |||
| 41 | public static Comparator<ClassEntry> ObfuscatedClassEntryComparator; | 41 | public static Comparator<ClassEntry> ObfuscatedClassEntryComparator; |
| 42 | public static Comparator<ClassEntry> DeobfuscatedClassEntryComparator; | 42 | public static Comparator<ClassEntry> DeobfuscatedClassEntryComparator; |
| 43 | 43 | ||
| 44 | private static String getClassEntryDisplayName(ClassEntry entry) { | ||
| 45 | if (entry instanceof DecoratedClassEntry) { | ||
| 46 | return ((DecoratedClassEntry)entry).getDecoration() + entry.getName(); | ||
| 47 | } | ||
| 48 | return entry.getName(); | ||
| 49 | } | ||
| 50 | |||
| 44 | static { | 51 | static { |
| 45 | ObfuscatedClassEntryComparator = new Comparator<ClassEntry>() { | 52 | ObfuscatedClassEntryComparator = new Comparator<ClassEntry>() { |
| 46 | @Override | 53 | @Override |
| 47 | public int compare(ClassEntry a, ClassEntry b) { | 54 | public int compare(ClassEntry a, ClassEntry b) { |
| 48 | if (a.getName().length() != b.getName().length()) { | 55 | String aname = getClassEntryDisplayName(a); |
| 49 | return a.getName().length() - b.getName().length(); | 56 | String bname = getClassEntryDisplayName(b); |
| 57 | if (aname.length() != bname.length()) { | ||
| 58 | return aname.length() - bname.length(); | ||
| 50 | } | 59 | } |
| 51 | return a.getName().compareTo(b.getName()); | 60 | return aname.compareTo(bname); |
| 52 | } | 61 | } |
| 53 | }; | 62 | }; |
| 54 | 63 | ||
| 55 | DeobfuscatedClassEntryComparator = new Comparator<ClassEntry>() { | 64 | DeobfuscatedClassEntryComparator = new Comparator<ClassEntry>() { |
| 56 | @Override | 65 | @Override |
| 57 | public int compare(ClassEntry a, ClassEntry b) { | 66 | public int compare(ClassEntry a, ClassEntry b) { |
| 58 | return a.getName().compareTo(b.getName()); | 67 | String aname = getClassEntryDisplayName(a); |
| 68 | String bname = getClassEntryDisplayName(b); | ||
| 69 | return aname.compareTo(bname); | ||
| 59 | } | 70 | } |
| 60 | }; | 71 | }; |
| 61 | } | 72 | } |
diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index 66e931b4..b3d7b899 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java | |||
| @@ -30,6 +30,9 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { | |||
| 30 | 30 | ||
| 31 | @Override | 31 | @Override |
| 32 | public String toString() { | 32 | public String toString() { |
| 33 | if (m_classEntry instanceof DecoratedClassEntry) { | ||
| 34 | return ((DecoratedClassEntry)m_classEntry).getDecoration() + m_classEntry.getSimpleName(); | ||
| 35 | } | ||
| 33 | return m_classEntry.getSimpleName(); | 36 | return m_classEntry.getSimpleName(); |
| 34 | } | 37 | } |
| 35 | } | 38 | } |
diff --git a/src/cuchaz/enigma/gui/DecoratedClassEntry.java b/src/cuchaz/enigma/gui/DecoratedClassEntry.java new file mode 100644 index 00000000..dd8b4fa3 --- /dev/null +++ b/src/cuchaz/enigma/gui/DecoratedClassEntry.java | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 4 | |||
| 5 | |||
| 6 | public class DecoratedClassEntry extends ClassEntry { | ||
| 7 | |||
| 8 | private static final long serialVersionUID = -8798725308554217105L; | ||
| 9 | |||
| 10 | private String m_decoration; | ||
| 11 | |||
| 12 | public DecoratedClassEntry(ClassEntry other, String decoration) { | ||
| 13 | super(other); | ||
| 14 | m_decoration = decoration; | ||
| 15 | } | ||
| 16 | |||
| 17 | public String getDecoration() { | ||
| 18 | return m_decoration; | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 53c767ac..e9dff164 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java | |||
| @@ -1,133 +1,352 @@ | |||
| 1 | package cuchaz.enigma.gui; | 1 | package cuchaz.enigma.gui; |
| 2 | 2 | ||
| 3 | import java.awt.BorderLayout; | ||
| 4 | import java.awt.Container; | ||
| 5 | import java.awt.Dimension; | ||
| 6 | import java.awt.FlowLayout; | ||
| 7 | import java.awt.event.ActionEvent; | ||
| 8 | import java.awt.event.ActionListener; | ||
| 9 | import java.util.ArrayList; | ||
| 10 | import java.util.Collection; | ||
| 11 | import java.util.Collections; | ||
| 12 | import java.util.List; | ||
| 13 | |||
| 14 | import javax.swing.BoxLayout; | ||
| 15 | import javax.swing.ButtonGroup; | ||
| 16 | import javax.swing.JButton; | ||
| 17 | import javax.swing.JEditorPane; | ||
| 18 | import javax.swing.JFrame; | ||
| 19 | import javax.swing.JLabel; | ||
| 20 | import javax.swing.JPanel; | ||
| 21 | import javax.swing.JRadioButton; | ||
| 22 | import javax.swing.JScrollPane; | ||
| 23 | import javax.swing.JSplitPane; | ||
| 24 | import javax.swing.WindowConstants; | ||
| 25 | |||
| 26 | import com.beust.jcommander.internal.Lists; | ||
| 27 | import com.google.common.collect.ArrayListMultimap; | ||
| 28 | import com.google.common.collect.Multimap; | ||
| 29 | |||
| 30 | import cuchaz.enigma.Constants; | ||
| 3 | import cuchaz.enigma.Deobfuscator; | 31 | import cuchaz.enigma.Deobfuscator; |
| 32 | import cuchaz.enigma.convert.ClassIdentifier; | ||
| 33 | import cuchaz.enigma.convert.ClassIdentity; | ||
| 34 | import cuchaz.enigma.convert.ClassMatch; | ||
| 35 | import cuchaz.enigma.convert.ClassNamer; | ||
| 4 | import cuchaz.enigma.convert.Matches; | 36 | import cuchaz.enigma.convert.Matches; |
| 37 | import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; | ||
| 38 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 39 | import de.sciss.syntaxpane.DefaultSyntaxKit; | ||
| 5 | 40 | ||
| 6 | 41 | ||
| 7 | public class MatchingGui { | 42 | public class MatchingGui { |
| 8 | 43 | ||
| 9 | public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 44 | private static enum SourceType { |
| 10 | // TODO Auto-generated constructor stub | 45 | Matched { |
| 46 | |||
| 47 | @Override | ||
| 48 | public Collection<ClassEntry> getSourceClasses(Matches matches) { | ||
| 49 | return matches.getUniqueMatches().keySet(); | ||
| 50 | } | ||
| 51 | }, | ||
| 52 | Unmatched { | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public Collection<ClassEntry> getSourceClasses(Matches matches) { | ||
| 56 | return matches.getUnmatchedSourceClasses(); | ||
| 57 | } | ||
| 58 | }, | ||
| 59 | Ambiguous { | ||
| 60 | |||
| 61 | @Override | ||
| 62 | public Collection<ClassEntry> getSourceClasses(Matches matches) { | ||
| 63 | return matches.getAmbiguouslyMatchedSourceClasses(); | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 67 | public JRadioButton newRadio(ActionListener listener, ButtonGroup group) { | ||
| 68 | JRadioButton button = new JRadioButton(name(), this == getDefault()); | ||
| 69 | button.setActionCommand(name()); | ||
| 70 | button.addActionListener(listener); | ||
| 71 | group.add(button); | ||
| 72 | return button; | ||
| 73 | } | ||
| 74 | |||
| 75 | public abstract Collection<ClassEntry> getSourceClasses(Matches matches); | ||
| 76 | |||
| 77 | public static SourceType getDefault() { | ||
| 78 | return values()[0]; | ||
| 79 | } | ||
| 11 | } | 80 | } |
| 12 | 81 | ||
| 82 | // controls | ||
| 83 | private JFrame m_frame; | ||
| 84 | private ClassSelector m_sourceClasses; | ||
| 85 | private ClassSelector m_destClasses; | ||
| 86 | private JEditorPane m_sourceReader; | ||
| 87 | private JEditorPane m_destReader; | ||
| 88 | private JLabel m_sourceClassLabel; | ||
| 89 | private JLabel m_destClassLabel; | ||
| 90 | private JButton m_matchButton; | ||
| 91 | |||
| 92 | private Matches m_matches; | ||
| 93 | private Deobfuscator m_sourceDeobfuscator; | ||
| 94 | private Deobfuscator m_destDeobfuscator; | ||
| 95 | private ClassEntry m_sourceClass; | ||
| 96 | private ClassEntry m_destClass; | ||
| 13 | 97 | ||
| 14 | /* TODO: see if we can use any of this here | 98 | public MatchingGui(Matches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 15 | public static doTheThings() { | ||
| 16 | 99 | ||
| 17 | // get all the obf class names used in the mappings | 100 | m_matches = matches; |
| 18 | Set<ClassEntry> usedClasses = Sets.newHashSet(); | 101 | m_sourceDeobfuscator = sourceDeobfuscator; |
| 19 | for (String className : mappings.getAllObfClassNames()) { | 102 | m_destDeobfuscator = destDeobfuscator; |
| 20 | usedClasses.add(new ClassEntry(className)); | 103 | |
| 21 | } | 104 | // init frame |
| 22 | System.out.println(String.format("Mappings reference %d/%d classes", | 105 | m_frame = new JFrame(Constants.Name); |
| 23 | usedClasses.size(), sourceIndex.getObfClassEntries().size() | 106 | final Container pane = m_frame.getContentPane(); |
| 24 | )); | 107 | pane.setLayout(new BorderLayout()); |
| 25 | 108 | ||
| 26 | // get the used matches | 109 | // init source side |
| 27 | Collection<ClassMatch> matches = matching.matches(); | 110 | JPanel sourcePanel = new JPanel(); |
| 28 | Matches usedMatches = new Matches(); | 111 | sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS)); |
| 29 | for (ClassMatch match : matching.matches()) { | 112 | sourcePanel.setPreferredSize(new Dimension(200, 0)); |
| 30 | if (!match.intersectSourceClasses(usedClasses).isEmpty()) { | 113 | pane.add(sourcePanel, BorderLayout.WEST); |
| 31 | usedMatches.add(match); | 114 | sourcePanel.add(new JLabel("Source Classes")); |
| 115 | |||
| 116 | // init source type radios | ||
| 117 | JPanel sourceTypePanel = new JPanel(); | ||
| 118 | sourcePanel.add(sourceTypePanel); | ||
| 119 | sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS)); | ||
| 120 | ActionListener sourceTypeListener = new ActionListener() { | ||
| 121 | @Override | ||
| 122 | public void actionPerformed(ActionEvent event) { | ||
| 123 | setSourceType(SourceType.valueOf(event.getActionCommand())); | ||
| 32 | } | 124 | } |
| 125 | }; | ||
| 126 | ButtonGroup sourceTypeButtons = new ButtonGroup(); | ||
| 127 | for (SourceType sourceType : SourceType.values()) { | ||
| 128 | sourceTypePanel.add(sourceType.newRadio(sourceTypeListener, sourceTypeButtons)); | ||
| 33 | } | 129 | } |
| 34 | System.out.println(String.format("Mappings reference %d/%d match groups", | 130 | |
| 35 | usedMatches.size(), matches.size() | 131 | m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); |
| 36 | )); | 132 | m_sourceClasses.setListener(new ClassSelectionListener() { |
| 37 | 133 | @Override | |
| 38 | // see what the used classes map to | 134 | public void onSelectClass(ClassEntry classEntry) { |
| 39 | BiMap<ClassEntry,ClassEntry> uniqueUsedMatches = HashBiMap.create(); | 135 | setSourceClass(classEntry); |
| 40 | Map<ClassEntry,ClassMatch> ambiguousUsedMatches = Maps.newHashMap(); | ||
| 41 | Set<ClassEntry> unmatchedUsedClasses = Sets.newHashSet(); | ||
| 42 | for (ClassMatch match : matching.matches()) { | ||
| 43 | Set<ClassEntry> matchUsedClasses = match.intersectSourceClasses(usedClasses); | ||
| 44 | if (matchUsedClasses.isEmpty()) { | ||
| 45 | continue; | ||
| 46 | } | 136 | } |
| 47 | 137 | }); | |
| 48 | usedMatches.add(match); | 138 | JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); |
| 139 | sourcePanel.add(sourceScroller); | ||
| 140 | |||
| 141 | // init dest side | ||
| 142 | JPanel destPanel = new JPanel(); | ||
| 143 | destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS)); | ||
| 144 | destPanel.setPreferredSize(new Dimension(200, 0)); | ||
| 145 | pane.add(destPanel, BorderLayout.WEST); | ||
| 146 | destPanel.add(new JLabel("Destination Classes")); | ||
| 147 | |||
| 148 | m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); | ||
| 149 | m_destClasses.setListener(new ClassSelectionListener() { | ||
| 150 | @Override | ||
| 151 | public void onSelectClass(ClassEntry classEntry) { | ||
| 152 | setDestClass(classEntry); | ||
| 153 | } | ||
| 154 | }); | ||
| 155 | JScrollPane destScroller = new JScrollPane(m_destClasses); | ||
| 156 | destPanel.add(destScroller); | ||
| 157 | |||
| 158 | // init source panels | ||
| 159 | DefaultSyntaxKit.initKit(); | ||
| 160 | m_sourceReader = new JEditorPane(); | ||
| 161 | m_sourceReader.setEditable(false); | ||
| 162 | m_sourceReader.setContentType("text/java"); | ||
| 163 | m_destReader = new JEditorPane(); | ||
| 164 | m_destReader.setEditable(false); | ||
| 165 | m_destReader.setContentType("text/java"); | ||
| 166 | |||
| 167 | // init all the splits | ||
| 168 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); | ||
| 169 | splitLeft.setResizeWeight(0); // let the right side take all the slack | ||
| 170 | JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel); | ||
| 171 | splitRight.setResizeWeight(1); // let the left side take all the slack | ||
| 172 | JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight); | ||
| 173 | splitCenter.setResizeWeight(0.5); // resize 50:50 | ||
| 174 | pane.add(splitCenter, BorderLayout.CENTER); | ||
| 175 | splitCenter.resetToPreferredSizes(); | ||
| 176 | |||
| 177 | // init bottom panel | ||
| 178 | JPanel bottomPanel = new JPanel(); | ||
| 179 | bottomPanel.setLayout(new FlowLayout()); | ||
| 180 | |||
| 181 | m_sourceClassLabel = new JLabel(); | ||
| 182 | m_sourceClassLabel.setPreferredSize(new Dimension(300, 0)); | ||
| 183 | m_destClassLabel = new JLabel(); | ||
| 184 | m_destClassLabel.setPreferredSize(new Dimension(300, 0)); | ||
| 185 | |||
| 186 | m_matchButton = new JButton(); | ||
| 187 | m_matchButton.addActionListener(new ActionListener() { | ||
| 188 | @Override | ||
| 189 | public void actionPerformed(ActionEvent event) { | ||
| 190 | onMatchClick(); | ||
| 191 | } | ||
| 192 | }); | ||
| 193 | m_matchButton.setPreferredSize(new Dimension(140, 24)); | ||
| 194 | |||
| 195 | bottomPanel.add(m_sourceClassLabel); | ||
| 196 | bottomPanel.add(m_matchButton); | ||
| 197 | bottomPanel.add(m_destClassLabel); | ||
| 198 | pane.add(bottomPanel, BorderLayout.SOUTH); | ||
| 199 | |||
| 200 | // show the frame | ||
| 201 | pane.doLayout(); | ||
| 202 | m_frame.setSize(1024, 576); | ||
| 203 | m_frame.setMinimumSize(new Dimension(640, 480)); | ||
| 204 | m_frame.setVisible(true); | ||
| 205 | m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | ||
| 206 | |||
| 207 | // init state | ||
| 208 | setSourceType(SourceType.getDefault()); | ||
| 209 | updateMatchButton(); | ||
| 210 | } | ||
| 49 | 211 | ||
| 50 | // classify the match | 212 | protected void setSourceType(SourceType val) { |
| 51 | if (!match.isMatched()) { | 213 | // show the source classes |
| 52 | // unmatched | 214 | m_sourceClasses.setClasses(deobfuscateClasses(val.getSourceClasses(m_matches), m_sourceDeobfuscator)); |
| 53 | unmatchedUsedClasses.addAll(matchUsedClasses); | 215 | } |
| 216 | |||
| 217 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { | ||
| 218 | List<ClassEntry> out = Lists.newArrayList(); | ||
| 219 | for (ClassEntry entry : in) { | ||
| 220 | out.add(deobfuscator.deobfuscateEntry(entry)); | ||
| 221 | } | ||
| 222 | return out; | ||
| 223 | } | ||
| 224 | |||
| 225 | protected void setSourceClass(ClassEntry classEntry) { | ||
| 226 | |||
| 227 | // update the current source class | ||
| 228 | m_sourceClass = classEntry; | ||
| 229 | m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : ""); | ||
| 230 | |||
| 231 | if (m_sourceClass != null) { | ||
| 232 | |||
| 233 | // show the dest class(es) | ||
| 234 | ClassMatch match = m_matches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); | ||
| 235 | assert(match != null); | ||
| 236 | if (match.destClasses.isEmpty()) { | ||
| 237 | m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); | ||
| 54 | } else { | 238 | } else { |
| 55 | if (match.isAmbiguous()) { | 239 | m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); |
| 56 | // ambiguously matched | ||
| 57 | for (ClassEntry matchUsedClass : matchUsedClasses) { | ||
| 58 | ambiguousUsedMatches.put(matchUsedClass, match); | ||
| 59 | } | ||
| 60 | } else { | ||
| 61 | // uniquely matched | ||
| 62 | uniqueUsedMatches.put(match.getUniqueSource(), match.getUniqueDest()); | ||
| 63 | } | ||
| 64 | } | 240 | } |
| 241 | m_destClasses.expandRow(0); | ||
| 65 | } | 242 | } |
| 66 | 243 | ||
| 67 | // get unmatched dest classes | 244 | setDestClass(null); |
| 68 | Set<ClassEntry> unmatchedDestClasses = Sets.newHashSet(); | 245 | readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); |
| 69 | for (ClassMatch match : matching.matches()) { | 246 | |
| 70 | if (!match.isMatched()) { | 247 | updateMatchButton(); |
| 71 | unmatchedDestClasses.addAll(match.destClasses); | 248 | } |
| 72 | } | 249 | |
| 250 | private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) { | ||
| 251 | |||
| 252 | ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass); | ||
| 253 | |||
| 254 | // set up identifiers | ||
| 255 | ClassNamer namer = new ClassNamer(m_matches.getUniqueMatches()); | ||
| 256 | ClassIdentifier sourceIdentifier = new ClassIdentifier( | ||
| 257 | m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(), | ||
| 258 | namer.getSourceNamer(), true | ||
| 259 | ); | ||
| 260 | ClassIdentifier destIdentifier = new ClassIdentifier( | ||
| 261 | m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(), | ||
| 262 | namer.getDestNamer(), true | ||
| 263 | ); | ||
| 264 | |||
| 265 | // rank all the unmatched dest classes against the source class | ||
| 266 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); | ||
| 267 | Multimap<Float,ClassEntry> scoredDestClasses = ArrayListMultimap.create(); | ||
| 268 | for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { | ||
| 269 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); | ||
| 270 | float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) | ||
| 271 | /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); | ||
| 272 | scoredDestClasses.put(score, unmatchedDestClass); | ||
| 73 | } | 273 | } |
| 74 | 274 | ||
| 75 | // warn about the ambiguous used matches | 275 | // sort by scores |
| 76 | if (ambiguousUsedMatches.size() > 0) { | 276 | List<Float> scores = new ArrayList<Float>(scoredDestClasses.keySet()); |
| 77 | System.out.println(String.format("%d source classes have ambiguous mappings", ambiguousUsedMatches.size())); | 277 | Collections.sort(scores, Collections.reverseOrder()); |
| 78 | List<ClassMatch> ambiguousMatchesList = Lists.newArrayList(Sets.newHashSet(ambiguousUsedMatches.values())); | 278 | |
| 79 | Collections.sort(ambiguousMatchesList, new Comparator<ClassMatch>() { | 279 | // collect the scored classes in order |
| 80 | @Override | 280 | List<ClassEntry> scoredClasses = Lists.newArrayList(); |
| 81 | public int compare(ClassMatch a, ClassMatch b) { | 281 | for (float score : scores) { |
| 82 | String aName = a.sourceClasses.iterator().next().getName(); | 282 | for (ClassEntry classEntry : scoredDestClasses.get(score)) { |
| 83 | String bName = b.sourceClasses.iterator().next().getName(); | 283 | scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%.0f%% ", score))); |
| 84 | return aName.compareTo(bName); | 284 | if (scoredClasses.size() > 10) { |
| 285 | return scoredClasses; | ||
| 85 | } | 286 | } |
| 86 | }); | ||
| 87 | for (ClassMatch match : ambiguousMatchesList) { | ||
| 88 | System.out.println("Ambiguous matching:"); | ||
| 89 | System.out.println("\tSource: " + getClassNames(match.sourceClasses)); | ||
| 90 | System.out.println("\tDest: " + getClassNames(match.destClasses)); | ||
| 91 | } | 287 | } |
| 92 | } | 288 | } |
| 289 | return scoredClasses; | ||
| 290 | } | ||
| 291 | |||
| 292 | protected void setDestClass(ClassEntry classEntry) { | ||
| 93 | 293 | ||
| 94 | // warn about unmatched used classes | 294 | // update the current source class |
| 95 | for (ClassEntry unmatchedUsedClass : unmatchedUsedClasses) { | 295 | m_destClass = classEntry; |
| 96 | System.out.println("No exact match for source class " + unmatchedUsedClass.getClassEntry()); | 296 | m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); |
| 97 | 297 | ||
| 98 | // rank all the unmatched dest classes against the used class | 298 | readSource(m_destClass, m_destDeobfuscator, m_destReader); |
| 99 | ClassIdentity sourceIdentity = matching.getSourceIdentifier().identify(unmatchedUsedClass); | 299 | |
| 100 | Multimap<Integer,ClassEntry> scoredDestClasses = ArrayListMultimap.create(); | 300 | updateMatchButton(); |
| 101 | for (ClassEntry unmatchedDestClass : unmatchedDestClasses) { | 301 | } |
| 102 | ClassIdentity destIdentity = matching.getDestIdentifier().identify(unmatchedDestClass); | 302 | |
| 103 | scoredDestClasses.put(sourceIdentity.getMatchScore(destIdentity), unmatchedDestClass); | 303 | protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { |
| 104 | } | ||
| 105 | |||
| 106 | List<Integer> scores = new ArrayList<Integer>(scoredDestClasses.keySet()); | ||
| 107 | Collections.sort(scores, Collections.reverseOrder()); | ||
| 108 | printScoredMatches(sourceIdentity.getMaxMatchScore(), scores, scoredDestClasses); | ||
| 109 | } | ||
| 110 | 304 | ||
| 111 | // bail if there were unmatched classes | 305 | if (classEntry == null) { |
| 112 | if (!unmatchedUsedClasses.isEmpty()) { | 306 | reader.setText(null); |
| 113 | throw new Error("There were " + unmatchedUsedClasses.size() + " unmatched classes!"); | 307 | return; |
| 114 | } | 308 | } |
| 309 | |||
| 310 | reader.setText("(decompiling...)"); | ||
| 311 | |||
| 312 | // run decompiler in a separate thread to keep ui responsive | ||
| 313 | new Thread() { | ||
| 314 | @Override | ||
| 315 | public void run() { | ||
| 316 | |||
| 317 | // get the outermost class | ||
| 318 | ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); | ||
| 319 | List<ClassEntry> classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); | ||
| 320 | ClassEntry obfOutermostClassEntry = classChain.get(0); | ||
| 321 | |||
| 322 | // decompile it | ||
| 323 | reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); | ||
| 324 | } | ||
| 325 | }.start(); | ||
| 115 | } | 326 | } |
| 116 | 327 | ||
| 117 | private static void printScoredMatches(int maxScore, List<Integer> scores, Multimap<Integer,ClassEntry> scoredMatches) { | 328 | private void updateMatchButton() { |
| 118 | int numScoredMatchesShown = 0; | 329 | |
| 119 | for (int score : scores) { | 330 | boolean twoSelected = m_sourceClass != null && m_destClass != null; |
| 120 | for (ClassEntry classEntry : scoredMatches.get(score)) { | 331 | boolean isMatched = twoSelected && m_matches.getUniqueMatches().containsKey(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass)); |
| 121 | System.out.println(String.format("\tScore: %3d %3.0f%% %s", | 332 | |
| 122 | score, 100.0 * score / maxScore, classEntry.getName() | 333 | m_matchButton.setEnabled(twoSelected); |
| 123 | )); | 334 | if (twoSelected) { |
| 124 | if (numScoredMatchesShown++ > 10) { | 335 | if (isMatched) { |
| 125 | return; | 336 | m_matchButton.setText("Unmatch"); |
| 126 | } | 337 | } else { |
| 338 | m_matchButton.setText("Match"); | ||
| 127 | } | 339 | } |
| 340 | } else { | ||
| 341 | m_matchButton.setText(""); | ||
| 128 | } | 342 | } |
| 129 | } | 343 | } |
| 130 | 344 | ||
| 345 | protected void onMatchClick() { | ||
| 346 | // TODO | ||
| 347 | } | ||
| 348 | |||
| 349 | /* | ||
| 131 | private static List<String> getClassNames(Collection<ClassEntry> classes) { | 350 | private static List<String> getClassNames(Collection<ClassEntry> classes) { |
| 132 | List<String> out = Lists.newArrayList(); | 351 | List<String> out = Lists.newArrayList(); |
| 133 | for (ClassEntry c : classes) { | 352 | for (ClassEntry c : classes) { |