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