diff options
Diffstat (limited to 'src/cuchaz/enigma/gui/MatchingGui.java')
| -rw-r--r-- | src/cuchaz/enigma/gui/MatchingGui.java | 208 |
1 files changed, 146 insertions, 62 deletions
diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index f1da25a..1e618d0 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java | |||
| @@ -10,7 +10,6 @@ import java.util.ArrayList; | |||
| 10 | import java.util.Arrays; | 10 | import java.util.Arrays; |
| 11 | import java.util.Collection; | 11 | import java.util.Collection; |
| 12 | import java.util.Collections; | 12 | import java.util.Collections; |
| 13 | import java.util.Enumeration; | ||
| 14 | import java.util.List; | 13 | import java.util.List; |
| 15 | import java.util.Map; | 14 | import java.util.Map; |
| 16 | 15 | ||
| @@ -27,17 +26,18 @@ import javax.swing.JScrollPane; | |||
| 27 | import javax.swing.JSplitPane; | 26 | import javax.swing.JSplitPane; |
| 28 | import javax.swing.SwingConstants; | 27 | import javax.swing.SwingConstants; |
| 29 | import javax.swing.WindowConstants; | 28 | import javax.swing.WindowConstants; |
| 30 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 31 | import javax.swing.tree.TreePath; | ||
| 32 | 29 | ||
| 33 | import com.beust.jcommander.internal.Lists; | 30 | import com.beust.jcommander.internal.Lists; |
| 34 | import com.beust.jcommander.internal.Maps; | 31 | import com.beust.jcommander.internal.Maps; |
| 35 | import com.google.common.collect.ArrayListMultimap; | 32 | import com.google.common.collect.ArrayListMultimap; |
| 36 | import com.google.common.collect.BiMap; | 33 | import com.google.common.collect.BiMap; |
| 37 | import com.google.common.collect.Multimap; | 34 | import com.google.common.collect.Multimap; |
| 35 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 38 | 36 | ||
| 39 | import cuchaz.enigma.Constants; | 37 | import cuchaz.enigma.Constants; |
| 40 | import cuchaz.enigma.Deobfuscator; | 38 | import cuchaz.enigma.Deobfuscator; |
| 39 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 40 | import cuchaz.enigma.analysis.Token; | ||
| 41 | import cuchaz.enigma.convert.ClassIdentifier; | 41 | import cuchaz.enigma.convert.ClassIdentifier; |
| 42 | import cuchaz.enigma.convert.ClassIdentity; | 42 | import cuchaz.enigma.convert.ClassIdentity; |
| 43 | import cuchaz.enigma.convert.ClassMatch; | 43 | import cuchaz.enigma.convert.ClassMatch; |
| @@ -47,6 +47,7 @@ import cuchaz.enigma.convert.MappingsConverter; | |||
| 47 | import cuchaz.enigma.convert.Matches; | 47 | import cuchaz.enigma.convert.Matches; |
| 48 | import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; | 48 | import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; |
| 49 | import cuchaz.enigma.mapping.ClassEntry; | 49 | import cuchaz.enigma.mapping.ClassEntry; |
| 50 | import cuchaz.enigma.mapping.Entry; | ||
| 50 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 51 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 51 | 52 | ||
| 52 | 53 | ||
| @@ -105,6 +106,7 @@ public class MatchingGui { | |||
| 105 | private JButton m_matchButton; | 106 | private JButton m_matchButton; |
| 106 | private Map<SourceType,JRadioButton> m_sourceTypeButtons; | 107 | private Map<SourceType,JRadioButton> m_sourceTypeButtons; |
| 107 | private JCheckBox m_advanceCheck; | 108 | private JCheckBox m_advanceCheck; |
| 109 | private SelectionHighlightPainter m_selectionHighlightPainter; | ||
| 108 | 110 | ||
| 109 | private Matches m_matches; | 111 | private Matches m_matches; |
| 110 | private Deobfuscator m_sourceDeobfuscator; | 112 | private Deobfuscator m_sourceDeobfuscator; |
| @@ -188,12 +190,8 @@ public class MatchingGui { | |||
| 188 | 190 | ||
| 189 | // init source panels | 191 | // init source panels |
| 190 | DefaultSyntaxKit.initKit(); | 192 | DefaultSyntaxKit.initKit(); |
| 191 | m_sourceReader = new JEditorPane(); | 193 | m_sourceReader = makeReader(); |
| 192 | m_sourceReader.setEditable(false); | 194 | m_destReader = makeReader(); |
| 193 | m_sourceReader.setContentType("text/java"); | ||
| 194 | m_destReader = new JEditorPane(); | ||
| 195 | m_destReader.setEditable(false); | ||
| 196 | m_destReader.setContentType("text/java"); | ||
| 197 | 195 | ||
| 198 | // init all the splits | 196 | // init all the splits |
| 199 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); | 197 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); |
| @@ -220,6 +218,14 @@ public class MatchingGui { | |||
| 220 | m_matchButton.setPreferredSize(new Dimension(140, 24)); | 218 | m_matchButton.setPreferredSize(new Dimension(140, 24)); |
| 221 | 219 | ||
| 222 | m_advanceCheck = new JCheckBox("Advance to next likely match"); | 220 | m_advanceCheck = new JCheckBox("Advance to next likely match"); |
| 221 | m_advanceCheck.addActionListener(new ActionListener() { | ||
| 222 | @Override | ||
| 223 | public void actionPerformed(ActionEvent event) { | ||
| 224 | if (m_advanceCheck.isSelected()) { | ||
| 225 | advance(); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | }); | ||
| 223 | 229 | ||
| 224 | bottomPanel.add(m_sourceClassLabel); | 230 | bottomPanel.add(m_sourceClassLabel); |
| 225 | bottomPanel.add(m_matchButton); | 231 | bottomPanel.add(m_matchButton); |
| @@ -234,6 +240,8 @@ public class MatchingGui { | |||
| 234 | m_frame.setVisible(true); | 240 | m_frame.setVisible(true); |
| 235 | m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 241 | m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 236 | 242 | ||
| 243 | m_selectionHighlightPainter = new SelectionHighlightPainter(); | ||
| 244 | |||
| 237 | // init state | 245 | // init state |
| 238 | updateDestMappings(); | 246 | updateDestMappings(); |
| 239 | setSourceType(SourceType.getDefault()); | 247 | setSourceType(SourceType.getDefault()); |
| @@ -241,6 +249,19 @@ public class MatchingGui { | |||
| 241 | m_saveListener = null; | 249 | m_saveListener = null; |
| 242 | } | 250 | } |
| 243 | 251 | ||
| 252 | private JEditorPane makeReader() { | ||
| 253 | |||
| 254 | JEditorPane reader = new JEditorPane(); | ||
| 255 | reader.setEditable(false); | ||
| 256 | reader.setContentType("text/java"); | ||
| 257 | |||
| 258 | // turn off token highlighting (it's wrong most of the time anyway...) | ||
| 259 | DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); | ||
| 260 | kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); | ||
| 261 | |||
| 262 | return reader; | ||
| 263 | } | ||
| 264 | |||
| 244 | public void setSaveListener(SaveListener val) { | 265 | public void setSaveListener(SaveListener val) { |
| 245 | m_saveListener = val; | 266 | m_saveListener = val; |
| 246 | } | 267 | } |
| @@ -272,12 +293,24 @@ public class MatchingGui { | |||
| 272 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { | 293 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { |
| 273 | List<ClassEntry> out = Lists.newArrayList(); | 294 | List<ClassEntry> out = Lists.newArrayList(); |
| 274 | for (ClassEntry entry : in) { | 295 | for (ClassEntry entry : in) { |
| 275 | out.add(deobfuscator.deobfuscateEntry(entry)); | 296 | |
| 297 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); | ||
| 298 | |||
| 299 | // make sure we preserve any scores | ||
| 300 | if (entry instanceof ScoredClassEntry) { | ||
| 301 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore()); | ||
| 302 | } | ||
| 303 | |||
| 304 | out.add(deobf); | ||
| 276 | } | 305 | } |
| 277 | return out; | 306 | return out; |
| 278 | } | 307 | } |
| 279 | 308 | ||
| 280 | protected void setSourceClass(ClassEntry classEntry) { | 309 | protected void setSourceClass(ClassEntry classEntry) { |
| 310 | setSourceClass(classEntry, null); | ||
| 311 | } | ||
| 312 | |||
| 313 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { | ||
| 281 | 314 | ||
| 282 | // update the current source class | 315 | // update the current source class |
| 283 | m_sourceClass = classEntry; | 316 | m_sourceClass = classEntry; |
| @@ -297,20 +330,27 @@ public class MatchingGui { | |||
| 297 | @Override | 330 | @Override |
| 298 | public void run() { | 331 | public void run() { |
| 299 | m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); | 332 | m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); |
| 300 | m_destClasses.expandRow(0); | 333 | m_destClasses.expandAll(); |
| 334 | |||
| 335 | if (onGetDestClasses != null) { | ||
| 336 | onGetDestClasses.run(); | ||
| 337 | } | ||
| 301 | } | 338 | } |
| 302 | }.start(); | 339 | }.start(); |
| 303 | 340 | ||
| 304 | } else { | 341 | } else { |
| 305 | 342 | ||
| 306 | m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); | 343 | m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); |
| 307 | m_destClasses.expandRow(0); | 344 | m_destClasses.expandAll(); |
| 308 | 345 | ||
| 346 | if (onGetDestClasses != null) { | ||
| 347 | onGetDestClasses.run(); | ||
| 348 | } | ||
| 309 | } | 349 | } |
| 310 | } | 350 | } |
| 311 | 351 | ||
| 312 | setDestClass(null); | 352 | setDestClass(null); |
| 313 | readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); | 353 | decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); |
| 314 | 354 | ||
| 315 | updateMatchButton(); | 355 | updateMatchButton(); |
| 316 | } | 356 | } |
| @@ -334,29 +374,14 @@ public class MatchingGui { | |||
| 334 | 374 | ||
| 335 | // rank all the unmatched dest classes against the source class | 375 | // rank all the unmatched dest classes against the source class |
| 336 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); | 376 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); |
| 337 | Multimap<Float,ClassEntry> scoredDestClasses = ArrayListMultimap.create(); | 377 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); |
| 338 | for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { | 378 | for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { |
| 339 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); | 379 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); |
| 340 | float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) | 380 | float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) |
| 341 | /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); | 381 | /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); |
| 342 | scoredDestClasses.put(score, unmatchedDestClass); | 382 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); |
| 343 | } | ||
| 344 | |||
| 345 | // sort by scores | ||
| 346 | List<Float> scores = new ArrayList<Float>(scoredDestClasses.keySet()); | ||
| 347 | Collections.sort(scores, Collections.reverseOrder()); | ||
| 348 | |||
| 349 | // collect the scored classes in order | ||
| 350 | List<ClassEntry> scoredClasses = Lists.newArrayList(); | ||
| 351 | for (float score : scores) { | ||
| 352 | for (ClassEntry classEntry : scoredDestClasses.get(score)) { | ||
| 353 | scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%2.0f%% ", score))); | ||
| 354 | if (scoredClasses.size() > 10) { | ||
| 355 | return scoredClasses; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | 383 | } |
| 359 | return scoredClasses; | 384 | return scoredDestClasses; |
| 360 | 385 | ||
| 361 | } catch (ClassNotFoundException ex) { | 386 | } catch (ClassNotFoundException ex) { |
| 362 | throw new Error("Unable to find class " + ex.getMessage()); | 387 | throw new Error("Unable to find class " + ex.getMessage()); |
| @@ -369,12 +394,12 @@ public class MatchingGui { | |||
| 369 | m_destClass = classEntry; | 394 | m_destClass = classEntry; |
| 370 | m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); | 395 | m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); |
| 371 | 396 | ||
| 372 | readSource(m_destClass, m_destDeobfuscator, m_destReader); | 397 | decompileClass(m_destClass, m_destDeobfuscator, m_destReader); |
| 373 | 398 | ||
| 374 | updateMatchButton(); | 399 | updateMatchButton(); |
| 375 | } | 400 | } |
| 376 | 401 | ||
| 377 | protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { | 402 | protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { |
| 378 | 403 | ||
| 379 | if (classEntry == null) { | 404 | if (classEntry == null) { |
| 380 | reader.setText(null); | 405 | reader.setText(null); |
| @@ -389,12 +414,37 @@ public class MatchingGui { | |||
| 389 | public void run() { | 414 | public void run() { |
| 390 | 415 | ||
| 391 | // get the outermost class | 416 | // get the outermost class |
| 392 | ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); | 417 | ClassEntry outermostClassEntry = classEntry; |
| 393 | List<ClassEntry> classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); | 418 | while (outermostClassEntry.isInnerClass()) { |
| 394 | ClassEntry obfOutermostClassEntry = classChain.get(0); | 419 | outermostClassEntry = outermostClassEntry.getOuterClassEntry(); |
| 420 | } | ||
| 395 | 421 | ||
| 396 | // decompile it | 422 | // decompile it |
| 397 | reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); | 423 | CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); |
| 424 | String source = deobfuscator.getSource(sourceTree); | ||
| 425 | reader.setText(source); | ||
| 426 | SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); | ||
| 427 | |||
| 428 | // navigate to the class declaration | ||
| 429 | Token token = sourceIndex.getDeclarationToken(classEntry); | ||
| 430 | if (token == null) { | ||
| 431 | // couldn't find the class declaration token, might be an anonymous class | ||
| 432 | // look for any declaration in that class instead | ||
| 433 | for (Entry entry : sourceIndex.declarations()) { | ||
| 434 | if (entry.getClassEntry().equals(classEntry)) { | ||
| 435 | token = sourceIndex.getDeclarationToken(entry); | ||
| 436 | break; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | if (token != null) { | ||
| 442 | GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); | ||
| 443 | } else { | ||
| 444 | // couldn't find anything =( | ||
| 445 | System.out.println("Unable to find declaration in source for " + classEntry); | ||
| 446 | } | ||
| 447 | |||
| 398 | } | 448 | } |
| 399 | }.start(); | 449 | }.start(); |
| 400 | } | 450 | } |
| @@ -459,11 +509,16 @@ public class MatchingGui { | |||
| 459 | // add them as matched classes | 509 | // add them as matched classes |
| 460 | m_matches.add(new ClassMatch(obfSource, obfDest)); | 510 | m_matches.add(new ClassMatch(obfSource, obfDest)); |
| 461 | 511 | ||
| 512 | ClassEntry nextClass = null; | ||
| 513 | if (m_advanceCheck.isSelected()) { | ||
| 514 | nextClass = m_sourceClasses.getNextClass(m_sourceClass); | ||
| 515 | } | ||
| 516 | |||
| 462 | save(); | 517 | save(); |
| 463 | updateMatches(); | 518 | updateMatches(); |
| 464 | 519 | ||
| 465 | if (m_advanceCheck.isSelected()) { | 520 | if (nextClass != null) { |
| 466 | advance(); | 521 | advance(nextClass); |
| 467 | } | 522 | } |
| 468 | } | 523 | } |
| 469 | 524 | ||
| @@ -487,31 +542,11 @@ public class MatchingGui { | |||
| 487 | updateMatchButton(); | 542 | updateMatchButton(); |
| 488 | 543 | ||
| 489 | // remember where we were in the source tree | 544 | // remember where we were in the source tree |
| 490 | String packageName = null; | 545 | String packageName = m_sourceClasses.getSelectedPackage(); |
| 491 | if (!m_sourceClasses.isSelectionEmpty()) { | ||
| 492 | packageName = m_sourceClasses.getSelectionPath().getParentPath().getLastPathComponent().toString(); | ||
| 493 | } | ||
| 494 | 546 | ||
| 495 | setSourceType(m_sourceType); | 547 | setSourceType(m_sourceType); |
| 496 | 548 | ||
| 497 | if (packageName != null) { | 549 | m_sourceClasses.expandPackage(packageName); |
| 498 | // find the corresponding path in the new tree | ||
| 499 | TreePath path = null; | ||
| 500 | DefaultMutableTreeNode root = (DefaultMutableTreeNode)m_sourceClasses.getModel().getRoot(); | ||
| 501 | Enumeration<?> children = root.children(); | ||
| 502 | while (children.hasMoreElements()) { | ||
| 503 | Object child = children.nextElement(); | ||
| 504 | if (child.toString().equals(packageName)) { | ||
| 505 | path = new TreePath(new Object[] {root, child}); | ||
| 506 | break; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | if (path != null) { | ||
| 511 | // put the tree back to where it was | ||
| 512 | m_sourceClasses.expandPath(path); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | 550 | } |
| 516 | 551 | ||
| 517 | private void save() { | 552 | private void save() { |
| @@ -542,6 +577,55 @@ public class MatchingGui { | |||
| 542 | } | 577 | } |
| 543 | 578 | ||
| 544 | private void advance() { | 579 | private void advance() { |
| 545 | // TODO: find a likely match | 580 | advance(null); |
| 581 | } | ||
| 582 | |||
| 583 | private void advance(ClassEntry sourceClass) { | ||
| 584 | |||
| 585 | // make sure we have a source class | ||
| 586 | if (sourceClass == null) { | ||
| 587 | sourceClass = m_sourceClasses.getSelectedClass(); | ||
| 588 | if (sourceClass != null) { | ||
| 589 | sourceClass = m_sourceClasses.getNextClass(sourceClass); | ||
| 590 | } else { | ||
| 591 | sourceClass = m_sourceClasses.getFirstClass(); | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | // set the source class | ||
| 596 | setSourceClass(sourceClass, new Runnable() { | ||
| 597 | @Override | ||
| 598 | public void run() { | ||
| 599 | |||
| 600 | // then, pick the best dest class | ||
| 601 | ClassEntry firstClass = null; | ||
| 602 | ScoredClassEntry bestDestClass = null; | ||
| 603 | for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { | ||
| 604 | for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { | ||
| 605 | if (firstClass == null) { | ||
| 606 | firstClass = classNode.getClassEntry(); | ||
| 607 | } | ||
| 608 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { | ||
| 609 | ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); | ||
| 610 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { | ||
| 611 | bestDestClass = scoredClass; | ||
| 612 | } | ||
| 613 | } | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | // pick the entry to show | ||
| 618 | ClassEntry destClass = null; | ||
| 619 | if (bestDestClass != null) { | ||
| 620 | destClass = bestDestClass; | ||
| 621 | } else if (firstClass != null) { | ||
| 622 | destClass = firstClass; | ||
| 623 | } | ||
| 624 | |||
| 625 | setDestClass(destClass); | ||
| 626 | m_destClasses.setSelectionClass(destClass); | ||
| 627 | } | ||
| 628 | }); | ||
| 629 | m_sourceClasses.setSelectionClass(sourceClass); | ||
| 546 | } | 630 | } |
| 547 | } | 631 | } |