summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2015-03-02 01:01:51 -0500
committerGravatar jeff2015-03-02 01:01:51 -0500
commit54d17da93c6708e54c296d63783a60f1c024797b (patch)
treecf42c12c0956786256ff06bbb43c947addc2a573 /src
parentrefactor converter a bit for upcoming convert gui (diff)
downloadenigma-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.java1
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java4
-rw-r--r--src/cuchaz/enigma/convert/MappingsConverter.java61
-rw-r--r--src/cuchaz/enigma/convert/Matches.java26
-rw-r--r--src/cuchaz/enigma/gui/ClassSelector.java19
-rw-r--r--src/cuchaz/enigma/gui/ClassSelectorClassNode.java3
-rw-r--r--src/cuchaz/enigma/gui/DecoratedClassEntry.java20
-rw-r--r--src/cuchaz/enigma/gui/MatchingGui.java413
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 @@
11package cuchaz.enigma.convert; 11package cuchaz.enigma.convert;
12 12
13import java.util.Arrays; 13import java.util.Arrays;
14import java.util.Collections;
14import java.util.Iterator; 15import java.util.Iterator;
15import java.util.LinkedHashMap; 16import java.util.LinkedHashMap;
17import java.util.List;
16import java.util.Map; 18import java.util.Map;
19import java.util.Map.Entry;
17import java.util.jar.JarFile; 20import java.util.jar.JarFile;
18 21
22import com.beust.jcommander.internal.Lists;
19import com.google.common.collect.BiMap; 23import com.google.common.collect.BiMap;
24import com.google.common.collect.HashMultimap;
20import com.google.common.collect.Maps; 25import com.google.common.collect.Maps;
26import com.google.common.collect.Multimap;
21 27
28import cuchaz.enigma.Deobfuscator;
22import cuchaz.enigma.analysis.JarIndex; 29import cuchaz.enigma.analysis.JarIndex;
23import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; 30import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
24import cuchaz.enigma.mapping.ClassEntry; 31import cuchaz.enigma.mapping.ClassEntry;
32import cuchaz.enigma.mapping.ClassMapping;
25import cuchaz.enigma.mapping.Mappings; 33import cuchaz.enigma.mapping.Mappings;
26 34
27public class MappingsConverter { 35public 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;
17public class Matches implements Iterable<ClassMatch> { 17public 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 @@
1package cuchaz.enigma.gui;
2
3import cuchaz.enigma.mapping.ClassEntry;
4
5
6public 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 @@
1package cuchaz.enigma.gui; 1package cuchaz.enigma.gui;
2 2
3import java.awt.BorderLayout;
4import java.awt.Container;
5import java.awt.Dimension;
6import java.awt.FlowLayout;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.List;
13
14import javax.swing.BoxLayout;
15import javax.swing.ButtonGroup;
16import javax.swing.JButton;
17import javax.swing.JEditorPane;
18import javax.swing.JFrame;
19import javax.swing.JLabel;
20import javax.swing.JPanel;
21import javax.swing.JRadioButton;
22import javax.swing.JScrollPane;
23import javax.swing.JSplitPane;
24import javax.swing.WindowConstants;
25
26import com.beust.jcommander.internal.Lists;
27import com.google.common.collect.ArrayListMultimap;
28import com.google.common.collect.Multimap;
29
30import cuchaz.enigma.Constants;
3import cuchaz.enigma.Deobfuscator; 31import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.convert.ClassIdentifier;
33import cuchaz.enigma.convert.ClassIdentity;
34import cuchaz.enigma.convert.ClassMatch;
35import cuchaz.enigma.convert.ClassNamer;
4import cuchaz.enigma.convert.Matches; 36import cuchaz.enigma.convert.Matches;
37import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener;
38import cuchaz.enigma.mapping.ClassEntry;
39import de.sciss.syntaxpane.DefaultSyntaxKit;
5 40
6 41
7public class MatchingGui { 42public 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) {