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