summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/gui/ClassMatchingGui.java
diff options
context:
space:
mode:
authorGravatar lclc982016-06-30 00:49:21 +1000
committerGravatar GitHub2016-06-30 00:49:21 +1000
commit4be005617b3b8c3578cca07c5d085d12916f0d1d (patch)
treedb163431f38703e26da417ef05eaea2b27a498b9 /src/cuchaz/enigma/gui/ClassMatchingGui.java
parentSome small changes to fix idea importing (diff)
downloadenigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.gz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.xz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.zip
Json format (#2)
* Added new format * Fixed bug * Updated Version
Diffstat (limited to 'src/cuchaz/enigma/gui/ClassMatchingGui.java')
-rw-r--r--src/cuchaz/enigma/gui/ClassMatchingGui.java622
1 files changed, 0 insertions, 622 deletions
diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java
deleted file mode 100644
index 416b01f..0000000
--- a/src/cuchaz/enigma/gui/ClassMatchingGui.java
+++ /dev/null
@@ -1,622 +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 *
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.util.Collection;
20import java.util.Collections;
21import java.util.Comparator;
22import java.util.List;
23import java.util.Map;
24
25import javax.swing.BoxLayout;
26import javax.swing.ButtonGroup;
27import javax.swing.JButton;
28import javax.swing.JCheckBox;
29import javax.swing.JFrame;
30import javax.swing.JLabel;
31import javax.swing.JPanel;
32import javax.swing.JRadioButton;
33import javax.swing.JScrollPane;
34import javax.swing.JSplitPane;
35import javax.swing.SwingConstants;
36import javax.swing.WindowConstants;
37
38import com.google.common.collect.BiMap;
39import com.google.common.collect.Lists;
40import com.google.common.collect.Maps;
41
42import cuchaz.enigma.Constants;
43import cuchaz.enigma.Deobfuscator;
44import cuchaz.enigma.convert.ClassIdentifier;
45import cuchaz.enigma.convert.ClassIdentity;
46import cuchaz.enigma.convert.ClassMatch;
47import cuchaz.enigma.convert.ClassMatches;
48import cuchaz.enigma.convert.ClassMatching;
49import cuchaz.enigma.convert.ClassNamer;
50import cuchaz.enigma.convert.MappingsConverter;
51import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener;
52import cuchaz.enigma.mapping.ClassEntry;
53import cuchaz.enigma.mapping.Mappings;
54import cuchaz.enigma.mapping.MappingsChecker;
55import de.sciss.syntaxpane.DefaultSyntaxKit;
56
57
58public class ClassMatchingGui {
59
60 private static enum SourceType {
61 Matched {
62
63 @Override
64 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
65 return matches.getUniqueMatches().keySet();
66 }
67 },
68 Unmatched {
69
70 @Override
71 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
72 return matches.getUnmatchedSourceClasses();
73 }
74 },
75 Ambiguous {
76
77 @Override
78 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
79 return matches.getAmbiguouslyMatchedSourceClasses();
80 }
81 };
82
83 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
84 JRadioButton button = new JRadioButton(name(), this == getDefault());
85 button.setActionCommand(name());
86 button.addActionListener(listener);
87 group.add(button);
88 return button;
89 }
90
91 public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches);
92
93 public static SourceType getDefault() {
94 return values()[0];
95 }
96 }
97
98 public static interface SaveListener {
99 public void save(ClassMatches matches);
100 }
101
102 // controls
103 private JFrame m_frame;
104 private ClassSelector m_sourceClasses;
105 private ClassSelector m_destClasses;
106 private CodeReader m_sourceReader;
107 private CodeReader m_destReader;
108 private JLabel m_sourceClassLabel;
109 private JLabel m_destClassLabel;
110 private JButton m_matchButton;
111 private Map<SourceType,JRadioButton> m_sourceTypeButtons;
112 private JCheckBox m_advanceCheck;
113 private JCheckBox m_top10Matches;
114
115 private ClassMatches m_classMatches;
116 private Deobfuscator m_sourceDeobfuscator;
117 private Deobfuscator m_destDeobfuscator;
118 private ClassEntry m_sourceClass;
119 private ClassEntry m_destClass;
120 private SourceType m_sourceType;
121 private SaveListener m_saveListener;
122
123 public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
124
125 m_classMatches = matches;
126 m_sourceDeobfuscator = sourceDeobfuscator;
127 m_destDeobfuscator = destDeobfuscator;
128
129 // init frame
130 m_frame = new JFrame(Constants.Name + " - Class Matcher");
131 final Container pane = m_frame.getContentPane();
132 pane.setLayout(new BorderLayout());
133
134 // init source side
135 JPanel sourcePanel = new JPanel();
136 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS));
137 sourcePanel.setPreferredSize(new Dimension(200, 0));
138 pane.add(sourcePanel, BorderLayout.WEST);
139 sourcePanel.add(new JLabel("Source Classes"));
140
141 // init source type radios
142 JPanel sourceTypePanel = new JPanel();
143 sourcePanel.add(sourceTypePanel);
144 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
145 ActionListener sourceTypeListener = new ActionListener() {
146 @Override
147 public void actionPerformed(ActionEvent event) {
148 setSourceType(SourceType.valueOf(event.getActionCommand()));
149 }
150 };
151 ButtonGroup sourceTypeButtons = new ButtonGroup();
152 m_sourceTypeButtons = Maps.newHashMap();
153 for (SourceType sourceType : SourceType.values()) {
154 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
155 m_sourceTypeButtons.put(sourceType, button);
156 sourceTypePanel.add(button);
157 }
158
159 m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator);
160 m_sourceClasses.setListener(new ClassSelectionListener() {
161 @Override
162 public void onSelectClass(ClassEntry classEntry) {
163 setSourceClass(classEntry);
164 }
165 });
166 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
167 sourcePanel.add(sourceScroller);
168
169 // init dest side
170 JPanel destPanel = new JPanel();
171 destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS));
172 destPanel.setPreferredSize(new Dimension(200, 0));
173 pane.add(destPanel, BorderLayout.WEST);
174 destPanel.add(new JLabel("Destination Classes"));
175
176 m_top10Matches = new JCheckBox("Show only top 10 matches");
177 destPanel.add(m_top10Matches);
178 m_top10Matches.addActionListener(new ActionListener() {
179 @Override
180 public void actionPerformed(ActionEvent event) {
181 toggleTop10Matches();
182 }
183 });
184
185 m_destClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator);
186 m_destClasses.setListener(new ClassSelectionListener() {
187 @Override
188 public void onSelectClass(ClassEntry classEntry) {
189 setDestClass(classEntry);
190 }
191 });
192 JScrollPane destScroller = new JScrollPane(m_destClasses);
193 destPanel.add(destScroller);
194
195 JButton autoMatchButton = new JButton("AutoMatch");
196 autoMatchButton.addActionListener(new ActionListener() {
197 @Override
198 public void actionPerformed(ActionEvent event) {
199 autoMatch();
200 }
201 });
202 destPanel.add(autoMatchButton);
203
204 // init source panels
205 DefaultSyntaxKit.initKit();
206 m_sourceReader = new CodeReader();
207 m_destReader = new CodeReader();
208
209 // init all the splits
210 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader));
211 splitLeft.setResizeWeight(0); // let the right side take all the slack
212 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_destReader), destPanel);
213 splitRight.setResizeWeight(1); // let the left side take all the slack
214 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight);
215 splitCenter.setResizeWeight(0.5); // resize 50:50
216 pane.add(splitCenter, BorderLayout.CENTER);
217 splitCenter.resetToPreferredSizes();
218
219 // init bottom panel
220 JPanel bottomPanel = new JPanel();
221 bottomPanel.setLayout(new FlowLayout());
222
223 m_sourceClassLabel = new JLabel();
224 m_sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT);
225 m_destClassLabel = new JLabel();
226 m_destClassLabel.setHorizontalAlignment(SwingConstants.LEFT);
227
228 m_matchButton = new JButton();
229
230 m_advanceCheck = new JCheckBox("Advance to next likely match");
231 m_advanceCheck.addActionListener(new ActionListener() {
232 @Override
233 public void actionPerformed(ActionEvent event) {
234 if (m_advanceCheck.isSelected()) {
235 advance();
236 }
237 }
238 });
239
240 bottomPanel.add(m_sourceClassLabel);
241 bottomPanel.add(m_matchButton);
242 bottomPanel.add(m_destClassLabel);
243 bottomPanel.add(m_advanceCheck);
244 pane.add(bottomPanel, BorderLayout.SOUTH);
245
246 // show the frame
247 pane.doLayout();
248 m_frame.setSize(1024, 576);
249 m_frame.setMinimumSize(new Dimension(640, 480));
250 m_frame.setVisible(true);
251 m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
252
253 // init state
254 updateDestMappings();
255 setSourceType(SourceType.getDefault());
256 updateMatchButton();
257 m_saveListener = null;
258 }
259
260 public void setSaveListener(SaveListener val) {
261 m_saveListener = val;
262 }
263
264 private void updateDestMappings() {
265
266 Mappings newMappings = MappingsConverter.newMappings(
267 m_classMatches,
268 m_sourceDeobfuscator.getMappings(),
269 m_sourceDeobfuscator,
270 m_destDeobfuscator
271 );
272
273 // look for dropped mappings
274 MappingsChecker checker = new MappingsChecker(m_destDeobfuscator.getJarIndex());
275 checker.dropBrokenMappings(newMappings);
276
277 // count them
278 int numDroppedFields = checker.getDroppedFieldMappings().size();
279 int numDroppedMethods = checker.getDroppedMethodMappings().size();
280 System.out.println(String.format(
281 "%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods",
282 numDroppedFields + numDroppedMethods,
283 numDroppedFields,
284 numDroppedMethods
285 ));
286
287 m_destDeobfuscator.setMappings(newMappings);
288 }
289
290 protected void setSourceType(SourceType val) {
291
292 // show the source classes
293 m_sourceType = val;
294 m_sourceClasses.setClasses(deobfuscateClasses(m_sourceType.getSourceClasses(m_classMatches), m_sourceDeobfuscator));
295
296 // update counts
297 for (SourceType sourceType : SourceType.values()) {
298 m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
299 sourceType.name(),
300 sourceType.getSourceClasses(m_classMatches).size()
301 ));
302 }
303 }
304
305 private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) {
306 List<ClassEntry> out = Lists.newArrayList();
307 for (ClassEntry entry : in) {
308
309 ClassEntry deobf = deobfuscator.deobfuscateEntry(entry);
310
311 // make sure we preserve any scores
312 if (entry instanceof ScoredClassEntry) {
313 deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore());
314 }
315
316 out.add(deobf);
317 }
318 return out;
319 }
320
321 protected void setSourceClass(ClassEntry classEntry) {
322
323 Runnable onGetDestClasses = null;
324 if (m_advanceCheck.isSelected()) {
325 onGetDestClasses = new Runnable() {
326 @Override
327 public void run() {
328 pickBestDestClass();
329 }
330 };
331 }
332
333 setSourceClass(classEntry, onGetDestClasses);
334 }
335
336 protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) {
337
338 // update the current source class
339 m_sourceClass = classEntry;
340 m_sourceClassLabel.setText(m_sourceClass != null ? m_sourceClass.getName() : "");
341
342 if (m_sourceClass != null) {
343
344 // show the dest class(es)
345 ClassMatch match = m_classMatches.getMatchBySource(m_sourceDeobfuscator.obfuscateEntry(m_sourceClass));
346 assert(match != null);
347 if (match.destClasses.isEmpty()) {
348
349 m_destClasses.setClasses(null);
350
351 // run in a separate thread to keep ui responsive
352 new Thread() {
353 @Override
354 public void run() {
355 m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator));
356 m_destClasses.expandAll();
357
358 if (onGetDestClasses != null) {
359 onGetDestClasses.run();
360 }
361 }
362 }.start();
363
364 } else {
365
366 m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator));
367 m_destClasses.expandAll();
368
369 if (onGetDestClasses != null) {
370 onGetDestClasses.run();
371 }
372 }
373 }
374
375 setDestClass(null);
376 m_sourceReader.decompileClass(m_sourceClass, m_sourceDeobfuscator, new Runnable() {
377 @Override
378 public void run() {
379 m_sourceReader.navigateToClassDeclaration(m_sourceClass);
380 }
381 });
382
383 updateMatchButton();
384 }
385
386 private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) {
387
388 ClassEntry obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass);
389
390 // set up identifiers
391 ClassNamer namer = new ClassNamer(m_classMatches.getUniqueMatches());
392 ClassIdentifier sourceIdentifier = new ClassIdentifier(
393 m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(),
394 namer.getSourceNamer(), true
395 );
396 ClassIdentifier destIdentifier = new ClassIdentifier(
397 m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(),
398 namer.getDestNamer(), true
399 );
400
401 try {
402
403 // rank all the unmatched dest classes against the source class
404 ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass);
405 List<ClassEntry> scoredDestClasses = Lists.newArrayList();
406 for (ClassEntry unmatchedDestClass : m_classMatches.getUnmatchedDestClasses()) {
407 ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass);
408 float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity))
409 /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore());
410 scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score));
411 }
412
413 if (m_top10Matches.isSelected() && scoredDestClasses.size() > 10) {
414 Collections.sort(scoredDestClasses, new Comparator<ClassEntry>() {
415 @Override
416 public int compare(ClassEntry a, ClassEntry b) {
417 ScoredClassEntry sa = (ScoredClassEntry)a;
418 ScoredClassEntry sb = (ScoredClassEntry)b;
419 return -Float.compare(sa.getScore(), sb.getScore());
420 }
421 });
422 scoredDestClasses = scoredDestClasses.subList(0, 10);
423 }
424
425 return scoredDestClasses;
426
427 } catch (ClassNotFoundException ex) {
428 throw new Error("Unable to find class " + ex.getMessage());
429 }
430 }
431
432 protected void setDestClass(ClassEntry classEntry) {
433
434 // update the current source class
435 m_destClass = classEntry;
436 m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : "");
437
438 m_destReader.decompileClass(m_destClass, m_destDeobfuscator, new Runnable() {
439 @Override
440 public void run() {
441 m_destReader.navigateToClassDeclaration(m_destClass);
442 }
443 });
444
445 updateMatchButton();
446 }
447
448 private void updateMatchButton() {
449
450 ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass);
451 ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass);
452
453 BiMap<ClassEntry,ClassEntry> uniqueMatches = m_classMatches.getUniqueMatches();
454 boolean twoSelected = m_sourceClass != null && m_destClass != null;
455 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest);
456 boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest);
457
458 GuiTricks.deactivateButton(m_matchButton);
459 if (twoSelected) {
460 if (isMatched) {
461 GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() {
462 @Override
463 public void actionPerformed(ActionEvent event) {
464 onUnmatchClick();
465 }
466 });
467 } else if (canMatch) {
468 GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() {
469 @Override
470 public void actionPerformed(ActionEvent event) {
471 onMatchClick();
472 }
473 });
474 }
475 }
476 }
477
478 private void onMatchClick() {
479 // precondition: source and dest classes are set correctly
480
481 ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass);
482 ClassEntry obfDest = m_destDeobfuscator.obfuscateEntry(m_destClass);
483
484 // remove the classes from their match
485 m_classMatches.removeSource(obfSource);
486 m_classMatches.removeDest(obfDest);
487
488 // add them as matched classes
489 m_classMatches.add(new ClassMatch(obfSource, obfDest));
490
491 ClassEntry nextClass = null;
492 if (m_advanceCheck.isSelected()) {
493 nextClass = m_sourceClasses.getNextClass(m_sourceClass);
494 }
495
496 save();
497 updateMatches();
498
499 if (nextClass != null) {
500 advance(nextClass);
501 }
502 }
503
504 private void onUnmatchClick() {
505 // precondition: source and dest classes are set to a unique match
506
507 ClassEntry obfSource = m_sourceDeobfuscator.obfuscateEntry(m_sourceClass);
508
509 // remove the source to break the match, then add the source back as unmatched
510 m_classMatches.removeSource(obfSource);
511 m_classMatches.add(new ClassMatch(obfSource, null));
512
513 save();
514 updateMatches();
515 }
516
517 private void updateMatches() {
518 updateDestMappings();
519 setDestClass(null);
520 m_destClasses.setClasses(null);
521 updateMatchButton();
522
523 // remember where we were in the source tree
524 String packageName = m_sourceClasses.getSelectedPackage();
525
526 setSourceType(m_sourceType);
527
528 m_sourceClasses.expandPackage(packageName);
529 }
530
531 private void save() {
532 if (m_saveListener != null) {
533 m_saveListener.save(m_classMatches);
534 }
535 }
536
537 private void autoMatch() {
538
539 System.out.println("Automatching...");
540
541 // compute a new matching
542 ClassMatching matching = MappingsConverter.computeMatching(
543 m_sourceDeobfuscator.getJar(), m_sourceDeobfuscator.getJarIndex(),
544 m_destDeobfuscator.getJar(), m_destDeobfuscator.getJarIndex(),
545 m_classMatches.getUniqueMatches()
546 );
547 ClassMatches newMatches = new ClassMatches(matching.matches());
548 System.out.println(String.format("Automatch found %d new matches",
549 newMatches.getUniqueMatches().size() - m_classMatches.getUniqueMatches().size()
550 ));
551
552 // update the current matches
553 m_classMatches = newMatches;
554 save();
555 updateMatches();
556 }
557
558 private void advance() {
559 advance(null);
560 }
561
562 private void advance(ClassEntry sourceClass) {
563
564 // make sure we have a source class
565 if (sourceClass == null) {
566 sourceClass = m_sourceClasses.getSelectedClass();
567 if (sourceClass != null) {
568 sourceClass = m_sourceClasses.getNextClass(sourceClass);
569 } else {
570 sourceClass = m_sourceClasses.getFirstClass();
571 }
572 }
573
574 // set the source class
575 setSourceClass(sourceClass, new Runnable() {
576 @Override
577 public void run() {
578 pickBestDestClass();
579 }
580 });
581 m_sourceClasses.setSelectionClass(sourceClass);
582 }
583
584 private void pickBestDestClass() {
585
586 // then, pick the best dest class
587 ClassEntry firstClass = null;
588 ScoredClassEntry bestDestClass = null;
589 for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) {
590 for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) {
591 if (firstClass == null) {
592 firstClass = classNode.getClassEntry();
593 }
594 if (classNode.getClassEntry() instanceof ScoredClassEntry) {
595 ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry();
596 if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) {
597 bestDestClass = scoredClass;
598 }
599 }
600 }
601 }
602
603 // pick the entry to show
604 ClassEntry destClass = null;
605 if (bestDestClass != null) {
606 destClass = bestDestClass;
607 } else if (firstClass != null) {
608 destClass = firstClass;
609 }
610
611 setDestClass(destClass);
612 m_destClasses.setSelectionClass(destClass);
613 }
614
615 private void toggleTop10Matches() {
616 if (m_sourceClass != null) {
617 m_destClasses.clearSelection();
618 m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator));
619 m_destClasses.expandAll();
620 }
621 }
622}