summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/gui/MemberMatchingGui.java
diff options
context:
space:
mode:
authorGravatar jeff2015-03-11 11:03:16 -0400
committerGravatar jeff2015-03-11 11:03:16 -0400
commite33b4003a5c423894e7aef575faff359dd1d33b1 (patch)
tree6fa674b5ff8dbc699a44b6423149ad7e6eaae250 /src/cuchaz/enigma/gui/MemberMatchingGui.java
parentnothing of consequence (diff)
downloadenigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.tar.gz
enigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.tar.xz
enigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.zip
generalized field matching
added method matching
Diffstat (limited to 'src/cuchaz/enigma/gui/MemberMatchingGui.java')
-rw-r--r--src/cuchaz/enigma/gui/MemberMatchingGui.java489
1 files changed, 489 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/gui/MemberMatchingGui.java b/src/cuchaz/enigma/gui/MemberMatchingGui.java
new file mode 100644
index 0000000..52545b3
--- /dev/null
+++ b/src/cuchaz/enigma/gui/MemberMatchingGui.java
@@ -0,0 +1,489 @@
1package cuchaz.enigma.gui;
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.awt.event.KeyAdapter;
10import java.awt.event.KeyEvent;
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14
15import javax.swing.BoxLayout;
16import javax.swing.ButtonGroup;
17import javax.swing.JButton;
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;
25import javax.swing.text.Highlighter.HighlightPainter;
26
27import com.beust.jcommander.internal.Lists;
28import com.beust.jcommander.internal.Maps;
29
30import cuchaz.enigma.Constants;
31import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.analysis.EntryReference;
33import cuchaz.enigma.analysis.SourceIndex;
34import cuchaz.enigma.analysis.Token;
35import cuchaz.enigma.convert.ClassMatches;
36import cuchaz.enigma.convert.MemberMatches;
37import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener;
38import cuchaz.enigma.mapping.ClassEntry;
39import cuchaz.enigma.mapping.Entry;
40import de.sciss.syntaxpane.DefaultSyntaxKit;
41
42
43public class MemberMatchingGui<T extends Entry> {
44
45 private static enum SourceType {
46 Matched {
47
48 @Override
49 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
50 return matches.getSourceClassesWithoutUnmatchedEntries();
51 }
52 },
53 Unmatched {
54
55 @Override
56 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
57 return matches.getSourceClassesWithUnmatchedEntries();
58 }
59 };
60
61 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
62 JRadioButton button = new JRadioButton(name(), this == getDefault());
63 button.setActionCommand(name());
64 button.addActionListener(listener);
65 group.add(button);
66 return button;
67 }
68
69 public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches);
70
71 public static SourceType getDefault() {
72 return values()[0];
73 }
74 }
75
76 public static interface SaveListener<T extends Entry> {
77 public void save(MemberMatches<T> matches);
78 }
79
80 // controls
81 private JFrame m_frame;
82 private Map<SourceType,JRadioButton> m_sourceTypeButtons;
83 private ClassSelector m_sourceClasses;
84 private CodeReader m_sourceReader;
85 private CodeReader m_destReader;
86 private JButton m_matchButton;
87 private JButton m_unmatchableButton;
88 private JLabel m_sourceLabel;
89 private JLabel m_destLabel;
90 private HighlightPainter m_unmatchedHighlightPainter;
91 private HighlightPainter m_matchedHighlightPainter;
92
93 private ClassMatches m_classMatches;
94 private MemberMatches<T> m_memberMatches;
95 private Deobfuscator m_sourceDeobfuscator;
96 private Deobfuscator m_destDeobfuscator;
97 private SaveListener<T> m_saveListener;
98 private SourceType m_sourceType;
99 private ClassEntry m_obfSourceClass;
100 private ClassEntry m_obfDestClass;
101 private T m_obfSourceEntry;
102 private T m_obfDestEntry;
103
104 public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
105
106 m_classMatches = classMatches;
107 m_memberMatches = fieldMatches;
108 m_sourceDeobfuscator = sourceDeobfuscator;
109 m_destDeobfuscator = destDeobfuscator;
110
111 // init frame
112 m_frame = new JFrame(Constants.Name + " - Member Matcher");
113 final Container pane = m_frame.getContentPane();
114 pane.setLayout(new BorderLayout());
115
116 // init classes side
117 JPanel classesPanel = new JPanel();
118 classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS));
119 classesPanel.setPreferredSize(new Dimension(200, 0));
120 pane.add(classesPanel, BorderLayout.WEST);
121 classesPanel.add(new JLabel("Classes"));
122
123 // init source type radios
124 JPanel sourceTypePanel = new JPanel();
125 classesPanel.add(sourceTypePanel);
126 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
127 ActionListener sourceTypeListener = new ActionListener() {
128 @Override
129 public void actionPerformed(ActionEvent event) {
130 setSourceType(SourceType.valueOf(event.getActionCommand()));
131 }
132 };
133 ButtonGroup sourceTypeButtons = new ButtonGroup();
134 m_sourceTypeButtons = Maps.newHashMap();
135 for (SourceType sourceType : SourceType.values()) {
136 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
137 m_sourceTypeButtons.put(sourceType, button);
138 sourceTypePanel.add(button);
139 }
140
141 m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator);
142 m_sourceClasses.setListener(new ClassSelectionListener() {
143 @Override
144 public void onSelectClass(ClassEntry classEntry) {
145 setSourceClass(classEntry);
146 }
147 });
148 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
149 classesPanel.add(sourceScroller);
150
151 // init readers
152 DefaultSyntaxKit.initKit();
153 m_sourceReader = new CodeReader();
154 m_sourceReader.setSelectionListener(new CodeReader.SelectionListener() {
155 @Override
156 public void onSelect(EntryReference<Entry,Entry> reference) {
157 if (reference != null) {
158 onSelectSource(reference.entry);
159 } else {
160 onSelectSource(null);
161 }
162 }
163 });
164 m_destReader = new CodeReader();
165 m_destReader.setSelectionListener(new CodeReader.SelectionListener() {
166 @Override
167 public void onSelect(EntryReference<Entry,Entry> reference) {
168 if (reference != null) {
169 onSelectDest(reference.entry);
170 } else {
171 onSelectDest(null);
172 }
173 }
174 });
175
176 // add key bindings
177 KeyAdapter keyListener = new KeyAdapter() {
178 @Override
179 public void keyPressed(KeyEvent event) {
180 switch (event.getKeyCode()) {
181 case KeyEvent.VK_M:
182 m_matchButton.doClick();
183 break;
184 }
185 }
186 };
187 m_sourceReader.addKeyListener(keyListener);
188 m_destReader.addKeyListener(keyListener);
189
190 // init all the splits
191 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader));
192 splitRight.setResizeWeight(0.5); // resize 50:50
193 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight);
194 splitLeft.setResizeWeight(0); // let the right side take all the slack
195 pane.add(splitLeft, BorderLayout.CENTER);
196 splitLeft.resetToPreferredSizes();
197
198 // init bottom panel
199 JPanel bottomPanel = new JPanel();
200 bottomPanel.setLayout(new FlowLayout());
201 pane.add(bottomPanel, BorderLayout.SOUTH);
202
203 m_matchButton = new JButton();
204 m_unmatchableButton = new JButton();
205
206 m_sourceLabel = new JLabel();
207 bottomPanel.add(m_sourceLabel);
208 bottomPanel.add(m_matchButton);
209 bottomPanel.add(m_unmatchableButton);
210 m_destLabel = new JLabel();
211 bottomPanel.add(m_destLabel);
212
213 // show the frame
214 pane.doLayout();
215 m_frame.setSize(1024, 576);
216 m_frame.setMinimumSize(new Dimension(640, 480));
217 m_frame.setVisible(true);
218 m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
219
220 m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter();
221 m_matchedHighlightPainter = new DeobfuscatedHighlightPainter();
222
223 // init state
224 m_saveListener = null;
225 m_obfSourceClass = null;
226 m_obfDestClass = null;
227 m_obfSourceEntry = null;
228 m_obfDestEntry = null;
229 setSourceType(SourceType.getDefault());
230 updateButtons();
231 }
232
233 protected void setSourceType(SourceType val) {
234 m_sourceType = val;
235 updateSourceClasses();
236 }
237
238 public void setSaveListener(SaveListener<T> val) {
239 m_saveListener = val;
240 }
241
242 private void updateSourceClasses() {
243
244 String selectedPackage = m_sourceClasses.getSelectedPackage();
245
246 List<ClassEntry> deobfClassEntries = Lists.newArrayList();
247 for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_memberMatches)) {
248 deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry));
249 }
250 m_sourceClasses.setClasses(deobfClassEntries);
251
252 if (selectedPackage != null) {
253 m_sourceClasses.expandPackage(selectedPackage);
254 }
255
256 for (SourceType sourceType : SourceType.values()) {
257 m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
258 sourceType.name(), sourceType.getObfSourceClasses(m_memberMatches).size()
259 ));
260 }
261 }
262
263 protected void setSourceClass(ClassEntry sourceClass) {
264
265 m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass);
266 m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass);
267 if (m_obfDestClass == null) {
268 throw new Error("No matching dest class for source class: " + m_obfSourceClass);
269 }
270
271 m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, new Runnable() {
272 @Override
273 public void run() {
274 updateSourceHighlights();
275 }
276 });
277 m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, new Runnable() {
278 @Override
279 public void run() {
280 updateDestHighlights();
281 }
282 });
283 }
284
285 protected void updateSourceHighlights() {
286 highlightEntries(m_sourceReader, m_sourceDeobfuscator, m_memberMatches.matches().keySet(), m_memberMatches.getUnmatchedSourceEntries());
287 }
288
289 protected void updateDestHighlights() {
290 highlightEntries(m_destReader, m_destDeobfuscator, m_memberMatches.matches().values(), m_memberMatches.getUnmatchedDestEntries());
291 }
292
293 private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) {
294 reader.clearHighlights();
295 SourceIndex index = reader.getSourceIndex();
296
297 // matched fields
298 for (T obfT : obfMatchedEntries) {
299 T deobfT = deobfuscator.deobfuscateEntry(obfT);
300 Token token = index.getDeclarationToken(deobfT);
301 if (token != null) {
302 reader.setHighlightedToken(token, m_matchedHighlightPainter);
303 }
304 }
305
306 // unmatched fields
307 for (T obfT : obfUnmatchedEntries) {
308 T deobfT = deobfuscator.deobfuscateEntry(obfT);
309 Token token = index.getDeclarationToken(deobfT);
310 if (token != null) {
311 reader.setHighlightedToken(token, m_unmatchedHighlightPainter);
312 }
313 }
314 }
315
316 private boolean isSelectionMatched() {
317 return m_obfSourceEntry != null && m_obfDestEntry != null
318 && m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry);
319 }
320
321 protected void onSelectSource(Entry source) {
322
323 // start with no selection
324 if (isSelectionMatched()) {
325 setDest(null);
326 }
327 setSource(null);
328
329 // then look for a valid source selection
330 if (source != null) {
331
332 // this looks really scary, but it's actually ok
333 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
334 // and MemberMatches.hasSource() will only pass entries that actually match T
335 @SuppressWarnings("unchecked")
336 T sourceEntry = (T)source;
337
338 T obfSourceEntry = m_sourceDeobfuscator.obfuscateEntry(sourceEntry);
339 if (m_memberMatches.hasSource(obfSourceEntry)) {
340 setSource(obfSourceEntry);
341
342 // look for a matched dest too
343 T obfDestEntry = m_memberMatches.matches().get(obfSourceEntry);
344 if (obfDestEntry != null) {
345 setDest(obfDestEntry);
346 }
347 }
348 }
349
350 updateButtons();
351 }
352
353 protected void onSelectDest(Entry dest) {
354
355 // start with no selection
356 if (isSelectionMatched()) {
357 setSource(null);
358 }
359 setDest(null);
360
361 // then look for a valid dest selection
362 if (dest != null) {
363
364 // this looks really scary, but it's actually ok
365 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
366 // and MemberMatches.hasSource() will only pass entries that actually match T
367 @SuppressWarnings("unchecked")
368 T destEntry = (T)dest;
369
370 T obfDestEntry = m_destDeobfuscator.obfuscateEntry(destEntry);
371 if (m_memberMatches.hasDest(obfDestEntry)) {
372 setDest(obfDestEntry);
373
374 // look for a matched source too
375 T obfSourceEntry = m_memberMatches.matches().inverse().get(obfDestEntry);
376 if (obfSourceEntry != null) {
377 setSource(obfSourceEntry);
378 }
379 }
380 }
381
382 updateButtons();
383 }
384
385 private void setSource(T obfEntry) {
386 if (obfEntry == null) {
387 m_obfSourceEntry = obfEntry;
388 m_sourceLabel.setText("");
389 } else {
390 m_obfSourceEntry = obfEntry;
391 m_sourceLabel.setText(getEntryLabel(obfEntry, m_sourceDeobfuscator));
392 }
393 }
394
395 private void setDest(T obfEntry) {
396 if (obfEntry == null) {
397 m_obfDestEntry = obfEntry;
398 m_destLabel.setText("");
399 } else {
400 m_obfDestEntry = obfEntry;
401 m_destLabel.setText(getEntryLabel(obfEntry, m_destDeobfuscator));
402 }
403 }
404
405 private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) {
406 // deobfuscate, then take off the class name
407 T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry);
408 return deobfEntry.toString().substring(deobfEntry.getClassName().length() + 1);
409 }
410
411 private void updateButtons() {
412
413 GuiTricks.deactivateButton(m_matchButton);
414 GuiTricks.deactivateButton(m_unmatchableButton);
415
416 if (m_obfSourceEntry != null && m_obfDestEntry != null) {
417 if (m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry)) {
418 GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() {
419 @Override
420 public void actionPerformed(ActionEvent event) {
421 unmatch();
422 }
423 });
424 } else if (!m_memberMatches.isMatchedSourceEntry(m_obfSourceEntry) && !m_memberMatches.isMatchedDestEntry(m_obfDestEntry)) {
425 GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() {
426 @Override
427 public void actionPerformed(ActionEvent event) {
428 match();
429 }
430 });
431 }
432 } else if (m_obfSourceEntry != null) {
433 GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", new ActionListener() {
434 @Override
435 public void actionPerformed(ActionEvent event) {
436 unmatchable();
437 }
438 });
439 }
440 }
441
442 protected void match() {
443
444 // update the field matches
445 m_memberMatches.makeMatch(m_obfSourceEntry, m_obfDestEntry);
446 save();
447
448 // update the ui
449 onSelectSource(null);
450 onSelectDest(null);
451 updateSourceHighlights();
452 updateDestHighlights();
453 updateSourceClasses();
454 }
455
456 protected void unmatch() {
457
458 // update the field matches
459 m_memberMatches.unmakeMatch(m_obfSourceEntry, m_obfDestEntry);
460 save();
461
462 // update the ui
463 onSelectSource(null);
464 onSelectDest(null);
465 updateSourceHighlights();
466 updateDestHighlights();
467 updateSourceClasses();
468 }
469
470 protected void unmatchable() {
471
472 // update the field matches
473 m_memberMatches.makeSourceUnmatchable(m_obfSourceEntry);
474 save();
475
476 // update the ui
477 onSelectSource(null);
478 onSelectDest(null);
479 updateSourceHighlights();
480 updateDestHighlights();
481 updateSourceClasses();
482 }
483
484 private void save() {
485 if (m_saveListener != null) {
486 m_saveListener.save(m_memberMatches);
487 }
488 }
489}