diff options
| author | 2019-01-30 21:05:32 +0200 | |
|---|---|---|
| committer | 2019-01-30 21:05:32 +0200 | |
| commit | ba7a354efae7d49833c887cf147ac940c975a1fa (patch) | |
| tree | 02e14fda81dd5984e24f2df392c57c6e829fc875 /src/main/java/cuchaz/enigma/gui | |
| parent | Rewrite the Jenkinsfile to use the new declarative pipeline syntax, lets hope... (diff) | |
| download | enigma-fork-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.gz enigma-fork-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.xz enigma-fork-ba7a354efae7d49833c887cf147ac940c975a1fa.zip | |
Remap sources (#106)
* Source remapping beginnings
* Fix navigation to remapped classes
* Translate identifier info reference
* Remap local variables with default names in source
* Caching translator
* Fix lack of highlighting for first opened class
* Fix unicode variable names
* Unicode checker shouldn't be checking just alphanumeric
* Fix package tree being built from obf names
* Don't index `this` as method call for method::reference
* Apply proposed names
* Fix source export issues
* Replace unicode var names at bytecode level uniquely
* Drop imports from editor source
* Class selector fixes
* Delta keep track of base mappings to enable lookup of old names
* Optimize source remapping by remapping source with a StringBuffer instead of copying
* Bump version
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui')
10 files changed, 503 insertions, 400 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java index c3b7288..39d0333 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -17,9 +17,11 @@ import com.google.common.collect.Maps; | |||
| 17 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; | 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; |
| 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; | 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; |
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.throwables.IllegalNameException; | 20 | import cuchaz.enigma.throwables.IllegalNameException; |
| 21 | import cuchaz.enigma.translation.Translator; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 22 | 23 | ||
| 24 | import javax.annotation.Nullable; | ||
| 23 | import javax.swing.*; | 25 | import javax.swing.*; |
| 24 | import javax.swing.event.CellEditorListener; | 26 | import javax.swing.event.CellEditorListener; |
| 25 | import javax.swing.event.ChangeEvent; | 27 | import javax.swing.event.ChangeEvent; |
| @@ -27,21 +29,26 @@ import javax.swing.tree.*; | |||
| 27 | import java.awt.event.MouseAdapter; | 29 | import java.awt.event.MouseAdapter; |
| 28 | import java.awt.event.MouseEvent; | 30 | import java.awt.event.MouseEvent; |
| 29 | import java.util.*; | 31 | import java.util.*; |
| 30 | import java.util.List; | ||
| 31 | 32 | ||
| 32 | public class ClassSelector extends JTree { | 33 | public class ClassSelector extends JTree { |
| 33 | 34 | ||
| 34 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); | 35 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); |
| 36 | |||
| 37 | private final GuiController controller; | ||
| 38 | |||
| 35 | private DefaultMutableTreeNode rootNodes; | 39 | private DefaultMutableTreeNode rootNodes; |
| 36 | private ClassSelectionListener selectionListener; | 40 | private ClassSelectionListener selectionListener; |
| 37 | private RenameSelectionListener renameSelectionListener; | 41 | private RenameSelectionListener renameSelectionListener; |
| 38 | private Comparator<ClassEntry> comparator; | 42 | private Comparator<ClassEntry> comparator; |
| 39 | 43 | ||
| 44 | private final Map<ClassEntry, ClassEntry> displayedObfToDeobf = new HashMap<>(); | ||
| 45 | |||
| 40 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { | 46 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { |
| 41 | this.comparator = comparator; | 47 | this.comparator = comparator; |
| 48 | this.controller = gui.getController(); | ||
| 42 | 49 | ||
| 43 | // configure the tree control | 50 | // configure the tree control |
| 44 | setEditable(gui != null); | 51 | setEditable(true); |
| 45 | setRootVisible(false); | 52 | setRootVisible(false); |
| 46 | setShowsRootHandles(false); | 53 | setShowsRootHandles(false); |
| 47 | setModel(null); | 54 | setModel(null); |
| @@ -55,66 +62,64 @@ public class ClassSelector extends JTree { | |||
| 55 | TreePath path = getSelectionPath(); | 62 | TreePath path = getSelectionPath(); |
| 56 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { | 63 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { |
| 57 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); | 64 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); |
| 58 | selectionListener.onSelectClass(node.getClassEntry()); | 65 | selectionListener.onSelectClass(node.getObfEntry()); |
| 59 | } | 66 | } |
| 60 | } | 67 | } |
| 61 | } | 68 | } |
| 62 | }); | 69 | }); |
| 63 | 70 | ||
| 64 | if (gui != null) { | 71 | final JTree tree = this; |
| 65 | final JTree tree = this; | ||
| 66 | 72 | ||
| 67 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, | 73 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, |
| 68 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { | 74 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { |
| 69 | @Override | 75 | @Override |
| 70 | public boolean isCellEditable(EventObject event) { | 76 | public boolean isCellEditable(EventObject event) { |
| 71 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); | 77 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); |
| 72 | } | 78 | } |
| 73 | }; | 79 | }; |
| 74 | this.setCellEditor(editor); | 80 | this.setCellEditor(editor); |
| 75 | editor.addCellEditorListener(new CellEditorListener() { | 81 | editor.addCellEditorListener(new CellEditorListener() { |
| 76 | @Override | 82 | @Override |
| 77 | public void editingStopped(ChangeEvent e) { | 83 | public void editingStopped(ChangeEvent e) { |
| 78 | String data = editor.getCellEditorValue().toString(); | 84 | String data = editor.getCellEditorValue().toString(); |
| 79 | TreePath path = getSelectionPath(); | 85 | TreePath path = getSelectionPath(); |
| 80 | 86 | ||
| 81 | Object realPath = path.getLastPathComponent(); | 87 | Object realPath = path.getLastPathComponent(); |
| 82 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { | 88 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { |
| 83 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; | 89 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; |
| 84 | TreeNode parentNode = node.getParent(); | 90 | TreeNode parentNode = node.getParent(); |
| 85 | if (parentNode == null) | 91 | if (parentNode == null) |
| 86 | return; | 92 | return; |
| 87 | boolean allowEdit = true; | 93 | boolean allowEdit = true; |
| 88 | for (int i = 0; i < parentNode.getChildCount(); i++) { | 94 | for (int i = 0; i < parentNode.getChildCount(); i++) { |
| 89 | TreeNode childNode = parentNode.getChildAt(i); | 95 | TreeNode childNode = parentNode.getChildAt(i); |
| 90 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { | 96 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { |
| 91 | allowEdit = false; | 97 | allowEdit = false; |
| 92 | break; | 98 | break; |
| 93 | } | ||
| 94 | } | 99 | } |
| 95 | if (allowEdit && renameSelectionListener != null) { | ||
| 96 | Object prevData = node.getUserObject(); | ||
| 97 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; | ||
| 98 | try { | ||
| 99 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | ||
| 100 | node.setUserObject(objectData); // Make sure that it's modified | ||
| 101 | } catch (IllegalNameException ex) { | ||
| 102 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | ||
| 103 | JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK"); | ||
| 104 | editor.cancelCellEditing(); | ||
| 105 | } | ||
| 106 | } else | ||
| 107 | editor.cancelCellEditing(); | ||
| 108 | } | 100 | } |
| 109 | 101 | if (allowEdit && renameSelectionListener != null) { | |
| 102 | Object prevData = node.getUserObject(); | ||
| 103 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; | ||
| 104 | try { | ||
| 105 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | ||
| 106 | node.setUserObject(objectData); // Make sure that it's modified | ||
| 107 | } catch (IllegalNameException ex) { | ||
| 108 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | ||
| 109 | JOptionPane.ERROR_MESSAGE, null, new String[]{"Ok"}, "OK"); | ||
| 110 | editor.cancelCellEditing(); | ||
| 111 | } | ||
| 112 | } else | ||
| 113 | editor.cancelCellEditing(); | ||
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | @Override | 116 | } |
| 113 | public void editingCanceled(ChangeEvent e) { | 117 | |
| 114 | // NOP | 118 | @Override |
| 115 | } | 119 | public void editingCanceled(ChangeEvent e) { |
| 116 | }); | 120 | // NOP |
| 117 | } | 121 | } |
| 122 | }); | ||
| 118 | // init defaults | 123 | // init defaults |
| 119 | this.selectionListener = null; | 124 | this.selectionListener = null; |
| 120 | this.renameSelectionListener = null; | 125 | this.renameSelectionListener = null; |
| @@ -142,16 +147,21 @@ public class ClassSelector extends JTree { | |||
| 142 | } | 147 | } |
| 143 | 148 | ||
| 144 | public void setClasses(Collection<ClassEntry> classEntries) { | 149 | public void setClasses(Collection<ClassEntry> classEntries) { |
| 150 | displayedObfToDeobf.clear(); | ||
| 151 | |||
| 145 | List<StateEntry> state = getExpansionState(this); | 152 | List<StateEntry> state = getExpansionState(this); |
| 146 | if (classEntries == null) { | 153 | if (classEntries == null) { |
| 147 | setModel(null); | 154 | setModel(null); |
| 148 | return; | 155 | return; |
| 149 | } | 156 | } |
| 150 | 157 | ||
| 158 | Translator translator = controller.getDeobfuscator().getMapper().getDeobfuscator(); | ||
| 159 | |||
| 151 | // build the package names | 160 | // build the package names |
| 152 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); | 161 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); |
| 153 | for (ClassEntry classEntry : classEntries) { | 162 | for (ClassEntry obfClass : classEntries) { |
| 154 | packages.put(classEntry.getPackageName(), null); | 163 | ClassEntry deobfClass = translator.translate(obfClass); |
| 164 | packages.put(deobfClass.getPackageName(), null); | ||
| 155 | } | 165 | } |
| 156 | 166 | ||
| 157 | // sort the packages | 167 | // sort the packages |
| @@ -191,20 +201,24 @@ public class ClassSelector extends JTree { | |||
| 191 | 201 | ||
| 192 | // put the classes into packages | 202 | // put the classes into packages |
| 193 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); | 203 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); |
| 194 | for (ClassEntry classEntry : classEntries) { | 204 | for (ClassEntry obfClass : classEntries) { |
| 195 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); | 205 | ClassEntry deobfClass = translator.translate(obfClass); |
| 206 | packagedClassEntries.put(deobfClass.getPackageName(), obfClass); | ||
| 196 | } | 207 | } |
| 197 | 208 | ||
| 198 | // build the class nodes | 209 | // build the class nodes |
| 199 | for (String packageName : packagedClassEntries.keySet()) { | 210 | for (String packageName : packagedClassEntries.keySet()) { |
| 200 | // sort the class entries | 211 | // sort the class entries |
| 201 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); | 212 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); |
| 202 | classEntriesInPackage.sort(this.comparator); | 213 | classEntriesInPackage.sort((o1, o2) -> comparator.compare(translator.translate(o1), translator.translate(o2))); |
| 203 | 214 | ||
| 204 | // create the nodes in order | 215 | // create the nodes in order |
| 205 | for (ClassEntry classEntry : classEntriesInPackage) { | 216 | for (ClassEntry obfClass : classEntriesInPackage) { |
| 217 | ClassEntry deobfClass = translator.translate(obfClass); | ||
| 206 | ClassSelectorPackageNode node = packages.get(packageName); | 218 | ClassSelectorPackageNode node = packages.get(packageName); |
| 207 | node.add(new ClassSelectorClassNode(classEntry)); | 219 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfClass, deobfClass); |
| 220 | displayedObfToDeobf.put(obfClass, deobfClass); | ||
| 221 | node.add(classNode); | ||
| 208 | } | 222 | } |
| 209 | } | 223 | } |
| 210 | 224 | ||
| @@ -324,7 +338,7 @@ public class ClassSelector extends JTree { | |||
| 324 | } | 338 | } |
| 325 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 339 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 326 | if (packageNode.getPackageName().equals(packageName)) { | 340 | if (packageNode.getPackageName().equals(packageName)) { |
| 327 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); | 341 | expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); |
| 328 | return; | 342 | return; |
| 329 | } | 343 | } |
| 330 | } | 344 | } |
| @@ -332,14 +346,13 @@ public class ClassSelector extends JTree { | |||
| 332 | 346 | ||
| 333 | public void expandAll() { | 347 | public void expandAll() { |
| 334 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 348 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 335 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); | 349 | expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); |
| 336 | } | 350 | } |
| 337 | } | 351 | } |
| 338 | 352 | ||
| 339 | public ClassEntry getFirstClass() { | 353 | public ClassEntry getFirstClass() { |
| 340 | ClassSelectorPackageNode packageNode = packageNodes().get(0); | 354 | ClassSelectorPackageNode packageNode = packageNodes().get(0); |
| 341 | if (packageNode != null) | 355 | if (packageNode != null) { |
| 342 | { | ||
| 343 | ClassSelectorClassNode classNode = classNodes(packageNode).get(0); | 356 | ClassSelectorClassNode classNode = classNodes(packageNode).get(0); |
| 344 | if (classNode != null) { | 357 | if (classNode != null) { |
| 345 | return classNode.getClassEntry(); | 358 | return classNode.getClassEntry(); |
| @@ -350,7 +363,7 @@ public class ClassSelector extends JTree { | |||
| 350 | 363 | ||
| 351 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { | 364 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { |
| 352 | String packageName = entry.getPackageName(); | 365 | String packageName = entry.getPackageName(); |
| 353 | if (packageName == null){ | 366 | if (packageName == null) { |
| 354 | packageName = "(none)"; | 367 | packageName = "(none)"; |
| 355 | } | 368 | } |
| 356 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 369 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| @@ -361,6 +374,11 @@ public class ClassSelector extends JTree { | |||
| 361 | return null; | 374 | return null; |
| 362 | } | 375 | } |
| 363 | 376 | ||
| 377 | @Nullable | ||
| 378 | public ClassEntry getDisplayedDeobf(ClassEntry obfEntry) { | ||
| 379 | return displayedObfToDeobf.get(obfEntry); | ||
| 380 | } | ||
| 381 | |||
| 364 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { | 382 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { |
| 365 | ClassSelectorPackageNode packageNode = getPackageNode(entry); | 383 | ClassSelectorPackageNode packageNode = getPackageNode(entry); |
| 366 | 384 | ||
| @@ -402,7 +420,7 @@ public class ClassSelector extends JTree { | |||
| 402 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 420 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 403 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 421 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 404 | if (classNode.getClassEntry().equals(classEntry)) { | 422 | if (classNode.getClassEntry().equals(classEntry)) { |
| 405 | setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode })); | 423 | setSelectionPath(new TreePath(new Object[]{getModel().getRoot(), packageNode, classNode})); |
| 406 | } | 424 | } |
| 407 | } | 425 | } |
| 408 | } | 426 | } |
| @@ -418,6 +436,9 @@ public class ClassSelector extends JTree { | |||
| 418 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); | 436 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); |
| 419 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { | 437 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { |
| 420 | model.removeNodeFromParent(childNode); | 438 | model.removeNodeFromParent(childNode); |
| 439 | if (childNode instanceof ClassSelectorClassNode) { | ||
| 440 | displayedObfToDeobf.remove(((ClassSelectorClassNode) childNode).getObfEntry()); | ||
| 441 | } | ||
| 421 | break; | 442 | break; |
| 422 | } | 443 | } |
| 423 | } | 444 | } |
| @@ -428,13 +449,25 @@ public class ClassSelector extends JTree { | |||
| 428 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); | 449 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); |
| 429 | } | 450 | } |
| 430 | 451 | ||
| 431 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) { | 452 | public void moveClassIn(ClassEntry classEntry) { |
| 432 | if (otherSelector == null) | 453 | removeEntry(classEntry); |
| 433 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); | 454 | insertNode(classEntry); |
| 434 | insertNode(getOrCreate(newClassEntry), newClassEntry); | ||
| 435 | } | 455 | } |
| 436 | 456 | ||
| 437 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) { | 457 | public void moveClassOut(ClassEntry classEntry) { |
| 458 | removeEntry(classEntry); | ||
| 459 | } | ||
| 460 | |||
| 461 | private void removeEntry(ClassEntry classEntry) { | ||
| 462 | ClassEntry previousDeobf = displayedObfToDeobf.get(classEntry); | ||
| 463 | if (previousDeobf != null) { | ||
| 464 | ClassSelectorPackageNode packageNode = getPackageNode(previousDeobf); | ||
| 465 | removeNode(packageNode, previousDeobf); | ||
| 466 | removeNodeIfEmpty(packageNode); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | public ClassSelectorPackageNode getOrCreatePackage(ClassEntry entry) { | ||
| 438 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 471 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 439 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); | 472 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); |
| 440 | if (newPackageNode == null) { | 473 | if (newPackageNode == null) { |
| @@ -444,10 +477,15 @@ public class ClassSelector extends JTree { | |||
| 444 | return newPackageNode; | 477 | return newPackageNode; |
| 445 | } | 478 | } |
| 446 | 479 | ||
| 447 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { | 480 | public void insertNode(ClassEntry obfEntry) { |
| 481 | ClassEntry deobfEntry = controller.getDeobfuscator().deobfuscate(obfEntry); | ||
| 482 | ClassSelectorPackageNode packageNode = getOrCreatePackage(deobfEntry); | ||
| 483 | |||
| 448 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 484 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 449 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); | 485 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfEntry, deobfEntry); |
| 450 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); | 486 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); |
| 487 | |||
| 488 | displayedObfToDeobf.put(obfEntry, deobfEntry); | ||
| 451 | } | 489 | } |
| 452 | 490 | ||
| 453 | public void reload() { | 491 | public void reload() { |
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java index 0810043..e119640 100644 --- a/src/main/java/cuchaz/enigma/gui/CodeReader.java +++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java | |||
| @@ -11,58 +11,27 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 15 | import cuchaz.enigma.Deobfuscator; | ||
| 16 | import cuchaz.enigma.analysis.EntryReference; | ||
| 17 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 18 | import cuchaz.enigma.analysis.Token; | 14 | import cuchaz.enigma.analysis.Token; |
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 21 | import de.sciss.syntaxpane.DefaultSyntaxKit; | ||
| 22 | 15 | ||
| 23 | import javax.swing.*; | 16 | import javax.swing.*; |
| 24 | import javax.swing.text.BadLocationException; | 17 | import javax.swing.text.BadLocationException; |
| 18 | import javax.swing.text.Document; | ||
| 25 | import javax.swing.text.Highlighter.HighlightPainter; | 19 | import javax.swing.text.Highlighter.HighlightPainter; |
| 26 | import java.awt.*; | 20 | import java.awt.*; |
| 27 | import java.awt.event.ActionEvent; | 21 | import java.awt.event.ActionEvent; |
| 28 | import java.awt.event.ActionListener; | 22 | import java.awt.event.ActionListener; |
| 29 | 23 | ||
| 30 | public class CodeReader extends JEditorPane { | 24 | public class CodeReader extends JEditorPane { |
| 31 | |||
| 32 | private static final long serialVersionUID = 3673180950485748810L; | 25 | private static final long serialVersionUID = 3673180950485748810L; |
| 33 | 26 | ||
| 34 | private static final Object lock = new Object(); | ||
| 35 | private SourceIndex sourceIndex; | ||
| 36 | private SelectionListener selectionListener; | ||
| 37 | |||
| 38 | public CodeReader() { | ||
| 39 | |||
| 40 | setEditable(false); | ||
| 41 | setContentType("text/java"); | ||
| 42 | |||
| 43 | // turn off token highlighting (it's wrong most of the time anyway...) | ||
| 44 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); | ||
| 45 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | ||
| 46 | |||
| 47 | // hook events | ||
| 48 | addCaretListener(event -> | ||
| 49 | { | ||
| 50 | if (selectionListener != null && sourceIndex != null) { | ||
| 51 | Token token = sourceIndex.getReferenceToken(event.getDot()); | ||
| 52 | if (token != null) { | ||
| 53 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); | ||
| 54 | } else { | ||
| 55 | selectionListener.onSelect(null); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }); | ||
| 59 | } | ||
| 60 | |||
| 61 | // HACKHACK: someday we can update the main GUI to use this code reader | 27 | // HACKHACK: someday we can update the main GUI to use this code reader |
| 62 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { | 28 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { |
| 63 | 29 | ||
| 64 | // set the caret position to the token | 30 | // set the caret position to the token |
| 65 | editor.setCaretPosition(token.start); | 31 | Document document = editor.getDocument(); |
| 32 | int clampedPosition = Math.min(Math.max(token.start, 0), document.getLength()); | ||
| 33 | |||
| 34 | editor.setCaretPosition(clampedPosition); | ||
| 66 | editor.grabFocus(); | 35 | editor.grabFocus(); |
| 67 | 36 | ||
| 68 | try { | 37 | try { |
| @@ -101,57 +70,4 @@ public class CodeReader extends JEditorPane { | |||
| 101 | }); | 70 | }); |
| 102 | timer.start(); | 71 | timer.start(); |
| 103 | } | 72 | } |
| 104 | |||
| 105 | public void setSelectionListener(SelectionListener val) { | ||
| 106 | selectionListener = val; | ||
| 107 | } | ||
| 108 | |||
| 109 | public void setCode(String code) { | ||
| 110 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls | ||
| 111 | synchronized (lock) { | ||
| 112 | setText(code); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | public SourceIndex getSourceIndex() { | ||
| 117 | return sourceIndex; | ||
| 118 | } | ||
| 119 | |||
| 120 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { | ||
| 121 | decompileClass(classEntry, deobfuscator, null); | ||
| 122 | } | ||
| 123 | |||
| 124 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { | ||
| 125 | decompileClass(classEntry, deobfuscator, null, callback); | ||
| 126 | } | ||
| 127 | |||
| 128 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { | ||
| 129 | |||
| 130 | if (classEntry == null) { | ||
| 131 | setCode(null); | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | |||
| 135 | setCode("(decompiling...)"); | ||
| 136 | |||
| 137 | // run decompilation in a separate thread to keep ui responsive | ||
| 138 | new Thread(() -> | ||
| 139 | { | ||
| 140 | |||
| 141 | // decompile it | ||
| 142 | |||
| 143 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getName()); | ||
| 144 | String source = deobfuscator.getSource(sourceTree); | ||
| 145 | setCode(source); | ||
| 146 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); | ||
| 147 | |||
| 148 | if (callback != null) { | ||
| 149 | callback.run(); | ||
| 150 | } | ||
| 151 | }).start(); | ||
| 152 | } | ||
| 153 | |||
| 154 | public interface SelectionListener { | ||
| 155 | void onSelect(EntryReference<Entry<?>, Entry<?>> reference); | ||
| 156 | } | ||
| 157 | } | 73 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java new file mode 100644 index 0000000..03f76c9 --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.Deobfuscator; | ||
| 4 | import cuchaz.enigma.analysis.EntryReference; | ||
| 5 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 6 | import cuchaz.enigma.analysis.Token; | ||
| 7 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 8 | import cuchaz.enigma.gui.highlight.TokenHighlightType; | ||
| 9 | import cuchaz.enigma.translation.LocalNameGenerator; | ||
| 10 | import cuchaz.enigma.translation.Translator; | ||
| 11 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; | ||
| 16 | |||
| 17 | import javax.annotation.Nullable; | ||
| 18 | import java.util.*; | ||
| 19 | |||
| 20 | public class DecompiledClassSource { | ||
| 21 | private final ClassEntry classEntry; | ||
| 22 | private final Deobfuscator deobfuscator; | ||
| 23 | |||
| 24 | private final SourceIndex obfuscatedIndex; | ||
| 25 | private SourceIndex remappedIndex; | ||
| 26 | |||
| 27 | private final Map<TokenHighlightType, Collection<Token>> highlightedTokens = new EnumMap<>(TokenHighlightType.class); | ||
| 28 | |||
| 29 | public DecompiledClassSource(ClassEntry classEntry, Deobfuscator deobfuscator, SourceIndex index) { | ||
| 30 | this.classEntry = classEntry; | ||
| 31 | this.deobfuscator = deobfuscator; | ||
| 32 | this.obfuscatedIndex = index; | ||
| 33 | this.remappedIndex = index; | ||
| 34 | } | ||
| 35 | |||
| 36 | public void remapSource(Translator translator) { | ||
| 37 | highlightedTokens.clear(); | ||
| 38 | |||
| 39 | SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens()); | ||
| 40 | |||
| 41 | SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(token, movedToken, translator)); | ||
| 42 | remappedIndex = obfuscatedIndex.remapTo(remapResult); | ||
| 43 | } | ||
| 44 | |||
| 45 | private String remapToken(Token token, Token movedToken, Translator translator) { | ||
| 46 | EntryReference<Entry<?>, Entry<?>> reference = obfuscatedIndex.getReference(token); | ||
| 47 | |||
| 48 | if (deobfuscator.isRenamable(reference)) { | ||
| 49 | Entry<?> entry = reference.getNameableEntry(); | ||
| 50 | Entry<?> translatedEntry = translator.translate(entry); | ||
| 51 | |||
| 52 | if (isDeobfuscated(entry, translatedEntry)) { | ||
| 53 | highlightToken(movedToken, TokenHighlightType.DEOBFUSCATED); | ||
| 54 | return translatedEntry.getSourceRemapName(); | ||
| 55 | } else { | ||
| 56 | String proposedName = proposeName(entry); | ||
| 57 | if (proposedName != null) { | ||
| 58 | highlightToken(movedToken, TokenHighlightType.PROPOSED); | ||
| 59 | return proposedName; | ||
| 60 | } | ||
| 61 | |||
| 62 | highlightToken(movedToken, TokenHighlightType.OBFUSCATED); | ||
| 63 | |||
| 64 | String defaultName = generateDefaultName(translatedEntry); | ||
| 65 | if (defaultName != null) { | ||
| 66 | return defaultName; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | return null; | ||
| 72 | } | ||
| 73 | |||
| 74 | @Nullable | ||
| 75 | private String proposeName(Entry<?> entry) { | ||
| 76 | if (entry instanceof FieldEntry) { | ||
| 77 | for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { | ||
| 78 | String owner = entry.getContainingClass().getFullName(); | ||
| 79 | String proposal = plugin.proposeFieldName(owner, entry.getName(), ((FieldEntry) entry).getDesc().toString()); | ||
| 80 | if (proposal != null) { | ||
| 81 | return proposal; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | return null; | ||
| 86 | } | ||
| 87 | |||
| 88 | @Nullable | ||
| 89 | private String generateDefaultName(Entry<?> entry) { | ||
| 90 | if (entry instanceof LocalVariableDefEntry) { | ||
| 91 | LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry; | ||
| 92 | |||
| 93 | int index = localVariable.getIndex(); | ||
| 94 | if (localVariable.isArgument()) { | ||
| 95 | List<TypeDescriptor> arguments = localVariable.getParent().getDesc().getArgumentDescs(); | ||
| 96 | return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments); | ||
| 97 | } else { | ||
| 98 | return LocalNameGenerator.generateLocalVariableName(index, localVariable.getDesc()); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | return null; | ||
| 103 | } | ||
| 104 | |||
| 105 | private boolean isDeobfuscated(Entry<?> entry, Entry<?> translatedEntry) { | ||
| 106 | return !entry.getName().equals(translatedEntry.getName()); | ||
| 107 | } | ||
| 108 | |||
| 109 | public ClassEntry getEntry() { | ||
| 110 | return classEntry; | ||
| 111 | } | ||
| 112 | |||
| 113 | public SourceIndex getIndex() { | ||
| 114 | return remappedIndex; | ||
| 115 | } | ||
| 116 | |||
| 117 | public Map<TokenHighlightType, Collection<Token>> getHighlightedTokens() { | ||
| 118 | return highlightedTokens; | ||
| 119 | } | ||
| 120 | |||
| 121 | private void highlightToken(Token token, TokenHighlightType highlightType) { | ||
| 122 | highlightedTokens.computeIfAbsent(highlightType, t -> new ArrayList<>()).add(token); | ||
| 123 | } | ||
| 124 | |||
| 125 | @Override | ||
| 126 | public String toString() { | ||
| 127 | return remappedIndex.getSource(); | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index d119735..a6e20a2 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -24,7 +24,7 @@ import cuchaz.enigma.gui.filechooser.FileChooserAny; | |||
| 24 | import cuchaz.enigma.gui.filechooser.FileChooserFolder; | 24 | import cuchaz.enigma.gui.filechooser.FileChooserFolder; |
| 25 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; | 25 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; |
| 26 | import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; | 26 | import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; |
| 27 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; | 27 | import cuchaz.enigma.gui.highlight.TokenHighlightType; |
| 28 | import cuchaz.enigma.gui.panels.PanelDeobf; | 28 | import cuchaz.enigma.gui.panels.PanelDeobf; |
| 29 | import cuchaz.enigma.gui.panels.PanelEditor; | 29 | import cuchaz.enigma.gui.panels.PanelEditor; |
| 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; | 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; |
| @@ -44,10 +44,9 @@ import javax.swing.tree.TreeNode; | |||
| 44 | import javax.swing.tree.TreePath; | 44 | import javax.swing.tree.TreePath; |
| 45 | import java.awt.*; | 45 | import java.awt.*; |
| 46 | import java.awt.event.*; | 46 | import java.awt.event.*; |
| 47 | import java.io.IOException; | ||
| 48 | import java.nio.file.Path; | 47 | import java.nio.file.Path; |
| 49 | import java.util.*; | ||
| 50 | import java.util.List; | 48 | import java.util.List; |
| 49 | import java.util.*; | ||
| 51 | import java.util.function.Function; | 50 | import java.util.function.Function; |
| 52 | 51 | ||
| 53 | public class Gui { | 52 | public class Gui { |
| @@ -71,7 +70,7 @@ public class Gui { | |||
| 71 | private JPanel classesPanel; | 70 | private JPanel classesPanel; |
| 72 | private JSplitPane splitClasses; | 71 | private JSplitPane splitClasses; |
| 73 | private PanelIdentifier infoPanel; | 72 | private PanelIdentifier infoPanel; |
| 74 | public Map<String, BoxHighlightPainter> boxHighlightPainters; | 73 | public Map<TokenHighlightType, BoxHighlightPainter> boxHighlightPainters; |
| 75 | private SelectionHighlightPainter selectionHighlightPainter; | 74 | private SelectionHighlightPainter selectionHighlightPainter; |
| 76 | private JTree inheritanceTree; | 75 | private JTree inheritanceTree; |
| 77 | private JTree implementationsTree; | 76 | private JTree implementationsTree; |
| @@ -320,7 +319,7 @@ public class Gui { | |||
| 320 | this.frame.setTitle(Constants.NAME + " - " + jarName); | 319 | this.frame.setTitle(Constants.NAME + " - " + jarName); |
| 321 | this.classesPanel.removeAll(); | 320 | this.classesPanel.removeAll(); |
| 322 | this.classesPanel.add(splitClasses); | 321 | this.classesPanel.add(splitClasses); |
| 323 | setSource(null); | 322 | setEditorText(null); |
| 324 | 323 | ||
| 325 | // update menu | 324 | // update menu |
| 326 | this.menuBar.closeJarMenu.setEnabled(true); | 325 | this.menuBar.closeJarMenu.setEnabled(true); |
| @@ -342,7 +341,7 @@ public class Gui { | |||
| 342 | this.frame.setTitle(Constants.NAME); | 341 | this.frame.setTitle(Constants.NAME); |
| 343 | setObfClasses(null); | 342 | setObfClasses(null); |
| 344 | setDeobfClasses(null); | 343 | setDeobfClasses(null); |
| 345 | setSource(null); | 344 | setEditorText(null); |
| 346 | this.classesPanel.removeAll(); | 345 | this.classesPanel.removeAll(); |
| 347 | 346 | ||
| 348 | // update menu | 347 | // update menu |
| @@ -373,11 +372,16 @@ public class Gui { | |||
| 373 | this.menuBar.saveMappingsMenu.setEnabled(path != null); | 372 | this.menuBar.saveMappingsMenu.setEnabled(path != null); |
| 374 | } | 373 | } |
| 375 | 374 | ||
| 376 | public void setSource(String source) { | 375 | public void setEditorText(String source) { |
| 377 | this.editor.getHighlighter().removeAllHighlights(); | 376 | this.editor.getHighlighter().removeAllHighlights(); |
| 378 | this.editor.setText(source); | 377 | this.editor.setText(source); |
| 379 | } | 378 | } |
| 380 | 379 | ||
| 380 | public void setSource(DecompiledClassSource source) { | ||
| 381 | editor.setText(source.toString()); | ||
| 382 | setHighlightedTokens(source.getHighlightedTokens()); | ||
| 383 | } | ||
| 384 | |||
| 381 | public void showToken(final Token token) { | 385 | public void showToken(final Token token) { |
| 382 | if (token == null) { | 386 | if (token == null) { |
| 383 | throw new IllegalArgumentException("Token cannot be null!"); | 387 | throw new IllegalArgumentException("Token cannot be null!"); |
| @@ -401,15 +405,15 @@ public class Gui { | |||
| 401 | showToken(sortedTokens.get(0)); | 405 | showToken(sortedTokens.get(0)); |
| 402 | } | 406 | } |
| 403 | 407 | ||
| 404 | public void setHighlightedTokens(Map<String, Iterable<Token>> tokens) { | 408 | public void setHighlightedTokens(Map<TokenHighlightType, Collection<Token>> tokens) { |
| 405 | // remove any old highlighters | 409 | // remove any old highlighters |
| 406 | this.editor.getHighlighter().removeAllHighlights(); | 410 | this.editor.getHighlighter().removeAllHighlights(); |
| 407 | 411 | ||
| 408 | if (boxHighlightPainters != null) { | 412 | if (boxHighlightPainters != null) { |
| 409 | for (String s : tokens.keySet()) { | 413 | for (TokenHighlightType type : tokens.keySet()) { |
| 410 | BoxHighlightPainter painter = boxHighlightPainters.get(s); | 414 | BoxHighlightPainter painter = boxHighlightPainters.get(type); |
| 411 | if (painter != null) { | 415 | if (painter != null) { |
| 412 | setHighlightedTokens(tokens.get(s), painter); | 416 | setHighlightedTokens(tokens.get(type), painter); |
| 413 | } | 417 | } |
| 414 | } | 418 | } |
| 415 | } | 419 | } |
| @@ -435,17 +439,19 @@ public class Gui { | |||
| 435 | 439 | ||
| 436 | this.reference = reference; | 440 | this.reference = reference; |
| 437 | 441 | ||
| 442 | EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); | ||
| 443 | |||
| 438 | infoPanel.removeAll(); | 444 | infoPanel.removeAll(); |
| 439 | if (reference.entry instanceof ClassEntry) { | 445 | if (translatedReference.entry instanceof ClassEntry) { |
| 440 | showClassEntry((ClassEntry) this.reference.entry); | 446 | showClassEntry((ClassEntry) translatedReference.entry); |
| 441 | } else if (this.reference.entry instanceof FieldEntry) { | 447 | } else if (translatedReference.entry instanceof FieldEntry) { |
| 442 | showFieldEntry((FieldEntry) this.reference.entry); | 448 | showFieldEntry((FieldEntry) translatedReference.entry); |
| 443 | } else if (this.reference.entry instanceof MethodEntry) { | 449 | } else if (translatedReference.entry instanceof MethodEntry) { |
| 444 | showMethodEntry((MethodEntry) this.reference.entry); | 450 | showMethodEntry((MethodEntry) translatedReference.entry); |
| 445 | } else if (this.reference.entry instanceof LocalVariableEntry) { | 451 | } else if (translatedReference.entry instanceof LocalVariableEntry) { |
| 446 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); | 452 | showLocalVariableEntry((LocalVariableEntry) translatedReference.entry); |
| 447 | } else { | 453 | } else { |
| 448 | throw new Error("Unknown entry desc: " + this.reference.entry.getClass().getName()); | 454 | throw new Error("Unknown entry desc: " + translatedReference.entry.getClass().getName()); |
| 449 | } | 455 | } |
| 450 | 456 | ||
| 451 | redraw(); | 457 | redraw(); |
| @@ -519,7 +525,7 @@ public class Gui { | |||
| 519 | Token token = this.controller.getToken(pos); | 525 | Token token = this.controller.getToken(pos); |
| 520 | boolean isToken = token != null; | 526 | boolean isToken = token != null; |
| 521 | 527 | ||
| 522 | reference = this.controller.getDeobfReference(token); | 528 | reference = this.controller.getReference(token); |
| 523 | 529 | ||
| 524 | Entry<?> referenceEntry = reference != null ? reference.entry : null; | 530 | Entry<?> referenceEntry = reference != null ? reference.entry : null; |
| 525 | boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry; | 531 | boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry; |
| @@ -527,7 +533,7 @@ public class Gui { | |||
| 527 | boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); | 533 | boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); |
| 528 | boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); | 534 | boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); |
| 529 | boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); | 535 | boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); |
| 530 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | 536 | boolean isRenameable = isToken && this.controller.getDeobfuscator().isRenamable(reference); |
| 531 | 537 | ||
| 532 | if (isToken) { | 538 | if (isToken) { |
| 533 | showReference(reference); | 539 | showReference(reference); |
| @@ -544,7 +550,7 @@ public class Gui { | |||
| 544 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); | 550 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); |
| 545 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | 551 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); |
| 546 | 552 | ||
| 547 | if (isToken && this.controller.entryHasDeobfuscatedName(referenceEntry)) { | 553 | if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) { |
| 548 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); | 554 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); |
| 549 | } else { | 555 | } else { |
| 550 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); | 556 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); |
| @@ -576,7 +582,10 @@ public class Gui { | |||
| 576 | 582 | ||
| 577 | // init the text box | 583 | // init the text box |
| 578 | final JTextField text = new JTextField(); | 584 | final JTextField text = new JTextField(); |
| 579 | text.setText(reference.getNameableName()); | 585 | |
| 586 | EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); | ||
| 587 | text.setText(translatedReference.getNameableName()); | ||
| 588 | |||
| 580 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); | 589 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); |
| 581 | text.addKeyListener(new KeyAdapter() { | 590 | text.addKeyListener(new KeyAdapter() { |
| 582 | @Override | 591 | @Override |
| @@ -603,7 +612,7 @@ public class Gui { | |||
| 603 | 612 | ||
| 604 | int offset = text.getText().lastIndexOf('/') + 1; | 613 | int offset = text.getText().lastIndexOf('/') + 1; |
| 605 | // If it's a class and isn't in the default package, assume that it's deobfuscated. | 614 | // If it's a class and isn't in the default package, assume that it's deobfuscated. |
| 606 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) | 615 | if (translatedReference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) |
| 607 | text.select(offset, text.getText().length()); | 616 | text.select(offset, text.getText().length()); |
| 608 | else | 617 | else |
| 609 | text.selectAll(); | 618 | text.selectAll(); |
| @@ -719,7 +728,7 @@ public class Gui { | |||
| 719 | } | 728 | } |
| 720 | 729 | ||
| 721 | public void toggleMapping() { | 730 | public void toggleMapping() { |
| 722 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { | 731 | if (this.controller.getDeobfuscator().isRemapped(reference.entry)) { |
| 723 | this.controller.removeMapping(reference); | 732 | this.controller.removeMapping(reference); |
| 724 | } else { | 733 | } else { |
| 725 | this.controller.markAsDeobfuscated(reference); | 734 | this.controller.markAsDeobfuscated(reference); |
| @@ -743,7 +752,7 @@ public class Gui { | |||
| 743 | callback.apply(response); | 752 | callback.apply(response); |
| 744 | } | 753 | } |
| 745 | 754 | ||
| 746 | public void saveMapping() throws IOException { | 755 | public void saveMapping() { |
| 747 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) | 756 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) |
| 748 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); | 757 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); |
| 749 | } | 758 | } |
| @@ -757,13 +766,8 @@ public class Gui { | |||
| 757 | // ask to save before closing | 766 | // ask to save before closing |
| 758 | showDiscardDiag((response) -> { | 767 | showDiscardDiag((response) -> { |
| 759 | if (response == JOptionPane.YES_OPTION) { | 768 | if (response == JOptionPane.YES_OPTION) { |
| 760 | try { | 769 | this.saveMapping(); |
| 761 | this.saveMapping(); | 770 | this.frame.dispose(); |
| 762 | this.frame.dispose(); | ||
| 763 | |||
| 764 | } catch (IOException ex) { | ||
| 765 | throw new Error(ex); | ||
| 766 | } | ||
| 767 | } else if (response == JOptionPane.NO_OPTION) | 771 | } else if (response == JOptionPane.NO_OPTION) |
| 768 | this.frame.dispose(); | 772 | this.frame.dispose(); |
| 769 | 773 | ||
| @@ -796,47 +800,39 @@ public class Gui { | |||
| 796 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); | 800 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); |
| 797 | } | 801 | } |
| 798 | 802 | ||
| 799 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName) { | 803 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, String newName) { |
| 800 | String oldEntry = deobfReference.entry.getContainingClass().getPackageName(); | 804 | String oldEntry = obfReference.entry.getContainingClass().getPackageName(); |
| 801 | String newEntry = new ClassEntry(newName).getPackageName(); | 805 | String newEntry = new ClassEntry(newName).getPackageName(); |
| 802 | moveClassTree(deobfReference, newName, oldEntry == null, | 806 | moveClassTree(obfReference, oldEntry == null, newEntry == null); |
| 803 | newEntry == null); | ||
| 804 | } | 807 | } |
| 805 | 808 | ||
| 806 | // TODO: getExpansionState will *not* actually update itself based on name changes! | 809 | // TODO: getExpansionState will *not* actually update itself based on name changes! |
| 807 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { | 810 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, boolean isOldOb, boolean isNewOb) { |
| 808 | ClassEntry oldEntry = deobfReference.entry.getContainingClass(); | 811 | ClassEntry classEntry = obfReference.entry.getContainingClass(); |
| 809 | ClassEntry newEntry = new ClassEntry(newName); | ||
| 810 | 812 | ||
| 811 | // Ob -> deob | 813 | // Ob -> deob |
| 812 | List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses); | 814 | List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses); |
| 813 | List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses); | 815 | List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses); |
| 814 | 816 | ||
| 815 | if (isOldOb && !isNewOb) { | 817 | if (isOldOb && !isNewOb) { |
| 816 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | 818 | this.deobfPanel.deobfClasses.moveClassIn(classEntry); |
| 817 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); | 819 | this.obfPanel.obfClasses.moveClassOut(classEntry); |
| 818 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); | ||
| 819 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); | ||
| 820 | this.deobfPanel.deobfClasses.reload(); | 820 | this.deobfPanel.deobfClasses.reload(); |
| 821 | this.obfPanel.obfClasses.reload(); | 821 | this.obfPanel.obfClasses.reload(); |
| 822 | } | 822 | } |
| 823 | // Deob -> ob | 823 | // Deob -> ob |
| 824 | else if (isNewOb && !isOldOb) { | 824 | else if (isNewOb && !isOldOb) { |
| 825 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); | 825 | this.obfPanel.obfClasses.moveClassIn(classEntry); |
| 826 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); | 826 | this.deobfPanel.deobfClasses.moveClassOut(classEntry); |
| 827 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); | ||
| 828 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); | ||
| 829 | this.deobfPanel.deobfClasses.reload(); | 827 | this.deobfPanel.deobfClasses.reload(); |
| 830 | this.obfPanel.obfClasses.reload(); | 828 | this.obfPanel.obfClasses.reload(); |
| 831 | } | 829 | } |
| 832 | // Local move | 830 | // Local move |
| 833 | else if (isOldOb) { | 831 | else if (isOldOb) { |
| 834 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); | 832 | this.obfPanel.obfClasses.moveClassIn(classEntry); |
| 835 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); | ||
| 836 | this.obfPanel.obfClasses.reload(); | 833 | this.obfPanel.obfClasses.reload(); |
| 837 | } else { | 834 | } else { |
| 838 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); | 835 | this.deobfPanel.deobfClasses.moveClassIn(classEntry); |
| 839 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); | ||
| 840 | this.deobfPanel.deobfClasses.reload(); | 836 | this.deobfPanel.deobfClasses.reload(); |
| 841 | } | 837 | } |
| 842 | 838 | ||
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index fd9e7f0..03e1768 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -11,13 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.ImmutableMap; | ||
| 15 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Queues; | 15 | import com.google.common.collect.Queues; |
| 16 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | ||
| 17 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 17 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 18 | import cuchaz.enigma.Deobfuscator; | 18 | import cuchaz.enigma.Deobfuscator; |
| 19 | import cuchaz.enigma.SourceProvider; | ||
| 19 | import cuchaz.enigma.analysis.*; | 20 | import cuchaz.enigma.analysis.*; |
| 20 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 21 | import cuchaz.enigma.config.Config; | 21 | import cuchaz.enigma.config.Config; |
| 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; | 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; |
| 23 | import cuchaz.enigma.throwables.MappingParseException; | 23 | import cuchaz.enigma.throwables.MappingParseException; |
| @@ -36,16 +36,20 @@ import java.awt.event.ItemEvent; | |||
| 36 | import java.io.File; | 36 | import java.io.File; |
| 37 | import java.io.IOException; | 37 | import java.io.IOException; |
| 38 | import java.nio.file.Path; | 38 | import java.nio.file.Path; |
| 39 | import java.util.*; | 39 | import java.util.Collection; |
| 40 | import java.util.Deque; | ||
| 41 | import java.util.List; | ||
| 42 | import java.util.concurrent.ExecutorService; | ||
| 43 | import java.util.concurrent.Executors; | ||
| 40 | import java.util.jar.JarFile; | 44 | import java.util.jar.JarFile; |
| 41 | import java.util.stream.Collectors; | 45 | import java.util.stream.Collectors; |
| 42 | 46 | ||
| 43 | public class GuiController { | 47 | public class GuiController { |
| 48 | private static final ExecutorService DECOMPILER_SERVICE = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("decompiler-thread").build()); | ||
| 44 | 49 | ||
| 45 | private Deobfuscator deobfuscator; | 50 | private Deobfuscator deobfuscator; |
| 46 | private Gui gui; | 51 | private Gui gui; |
| 47 | private SourceIndex index; | 52 | private DecompiledClassSource currentSource; |
| 48 | private ClassEntry currentObfClass; | ||
| 49 | private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; | 53 | private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; |
| 50 | 54 | ||
| 51 | private Path loadedMappingPath; | 55 | private Path loadedMappingPath; |
| @@ -54,8 +58,7 @@ public class GuiController { | |||
| 54 | public GuiController(Gui gui) { | 58 | public GuiController(Gui gui) { |
| 55 | this.gui = gui; | 59 | this.gui = gui; |
| 56 | this.deobfuscator = null; | 60 | this.deobfuscator = null; |
| 57 | this.index = null; | 61 | this.currentSource = null; |
| 58 | this.currentObfClass = null; | ||
| 59 | this.referenceStack = Queues.newArrayDeque(); | 62 | this.referenceStack = Queues.newArrayDeque(); |
| 60 | } | 63 | } |
| 61 | 64 | ||
| @@ -93,7 +96,7 @@ public class GuiController { | |||
| 93 | public void saveMappings(MappingFormat format, Path path) { | 96 | public void saveMappings(MappingFormat format, Path path) { |
| 94 | EntryRemapper mapper = deobfuscator.getMapper(); | 97 | EntryRemapper mapper = deobfuscator.getMapper(); |
| 95 | 98 | ||
| 96 | MappingDelta delta = mapper.takeMappingDelta(); | 99 | MappingDelta<EntryMapping> delta = mapper.takeMappingDelta(); |
| 97 | boolean saveAll = !path.equals(loadedMappingPath); | 100 | boolean saveAll = !path.equals(loadedMappingPath); |
| 98 | 101 | ||
| 99 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> { | 102 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> { |
| @@ -116,189 +119,167 @@ public class GuiController { | |||
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | public void exportSource(final File dirOut) { | 121 | public void exportSource(final File dirOut) { |
| 119 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); | 122 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut.toPath(), progress)); |
| 120 | } | 123 | } |
| 121 | 124 | ||
| 122 | public void exportJar(final File fileOut) { | 125 | public void exportJar(final File fileOut) { |
| 123 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); | 126 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeTransformedJar(fileOut, progress)); |
| 124 | } | 127 | } |
| 125 | 128 | ||
| 126 | public Token getToken(int pos) { | 129 | public Token getToken(int pos) { |
| 127 | if (this.index == null) { | 130 | if (this.currentSource == null) { |
| 128 | return null; | 131 | return null; |
| 129 | } | 132 | } |
| 130 | return this.index.getReferenceToken(pos); | 133 | return this.currentSource.getIndex().getReferenceToken(pos); |
| 131 | } | 134 | } |
| 132 | 135 | ||
| 133 | @Nullable | 136 | @Nullable |
| 134 | public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { | 137 | public EntryReference<Entry<?>, Entry<?>> getReference(Token token) { |
| 135 | if (this.index == null) { | 138 | if (this.currentSource == null) { |
| 136 | return null; | 139 | return null; |
| 137 | } | 140 | } |
| 138 | return this.index.getDeobfReference(token); | 141 | return this.currentSource.getIndex().getReference(token); |
| 139 | } | 142 | } |
| 140 | 143 | ||
| 141 | public ReadableToken getReadableToken(Token token) { | 144 | public ReadableToken getReadableToken(Token token) { |
| 142 | if (this.index == null) { | 145 | if (this.currentSource == null) { |
| 143 | return null; | 146 | return null; |
| 144 | } | 147 | } |
| 148 | SourceIndex index = this.currentSource.getIndex(); | ||
| 145 | return new ReadableToken( | 149 | return new ReadableToken( |
| 146 | this.index.getLineNumber(token.start), | 150 | index.getLineNumber(token.start), |
| 147 | this.index.getColumnNumber(token.start), | 151 | index.getColumnNumber(token.start), |
| 148 | this.index.getColumnNumber(token.end) | 152 | index.getColumnNumber(token.end) |
| 149 | ); | 153 | ); |
| 150 | } | 154 | } |
| 151 | 155 | ||
| 152 | public boolean entryHasDeobfuscatedName(Entry<?> deobfEntry) { | 156 | public boolean entryIsInJar(Entry<?> entry) { |
| 153 | EntryResolver resolver = this.deobfuscator.getMapper().getDeobfResolver(); | 157 | if (entry == null) return false; |
| 154 | Entry<?> resolvedEntry = resolver.resolveFirstEntry(deobfEntry, ResolutionStrategy.RESOLVE_ROOT); | 158 | return this.deobfuscator.isRenamable(entry); |
| 155 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.getMapper().obfuscate(resolvedEntry)); | ||
| 156 | } | 159 | } |
| 157 | 160 | ||
| 158 | public boolean entryIsInJar(Entry<?> deobfEntry) { | 161 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { |
| 159 | if (deobfEntry == null) return false; | ||
| 160 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.getMapper().obfuscate(deobfEntry)); | ||
| 161 | } | ||
| 162 | |||
| 163 | public boolean referenceIsRenameable(EntryReference<Entry<?>, Entry<?>> deobfReference) { | ||
| 164 | if (deobfReference == null) return false; | ||
| 165 | return this.deobfuscator.isRenameable(this.deobfuscator.getMapper().obfuscate(deobfReference)); | ||
| 166 | } | ||
| 167 | |||
| 168 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | ||
| 169 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 170 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 162 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 171 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, obfClassEntry); | 163 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, entry); |
| 172 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); | 164 | return ClassInheritanceTreeNode.findNode(rootNode, entry); |
| 173 | } | 165 | } |
| 174 | 166 | ||
| 175 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | 167 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry entry) { |
| 176 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 177 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 168 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 178 | return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, obfClassEntry); | 169 | return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, entry); |
| 179 | } | 170 | } |
| 180 | 171 | ||
| 181 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | 172 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry entry) { |
| 182 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 183 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 173 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 184 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, obfMethodEntry); | 174 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, entry); |
| 185 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); | 175 | return MethodInheritanceTreeNode.findNode(rootNode, entry); |
| 186 | } | 176 | } |
| 187 | 177 | ||
| 188 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | 178 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { |
| 189 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 190 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 179 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 191 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, obfMethodEntry); | 180 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, entry); |
| 192 | if (rootNodes.isEmpty()) { | 181 | if (rootNodes.isEmpty()) { |
| 193 | return null; | 182 | return null; |
| 194 | } | 183 | } |
| 195 | if (rootNodes.size() > 1) { | 184 | if (rootNodes.size() > 1) { |
| 196 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); | 185 | System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); |
| 197 | } | 186 | } |
| 198 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); | 187 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); |
| 199 | } | 188 | } |
| 200 | 189 | ||
| 201 | public ClassReferenceTreeNode getClassReferences(ClassEntry deobfClassEntry) { | 190 | public ClassReferenceTreeNode getClassReferences(ClassEntry entry) { |
| 202 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 203 | Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); | 191 | Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 204 | ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, obfClassEntry); | 192 | ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, entry); |
| 205 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 193 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 206 | return rootNode; | 194 | return rootNode; |
| 207 | } | 195 | } |
| 208 | 196 | ||
| 209 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | 197 | public FieldReferenceTreeNode getFieldReferences(FieldEntry entry) { |
| 210 | FieldEntry obfFieldEntry = this.deobfuscator.getMapper().obfuscate(deobfFieldEntry); | ||
| 211 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 198 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 212 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, obfFieldEntry); | 199 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, entry); |
| 213 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 200 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 214 | return rootNode; | 201 | return rootNode; |
| 215 | } | 202 | } |
| 216 | 203 | ||
| 217 | public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry, boolean recursive) { | 204 | public MethodReferenceTreeNode getMethodReferences(MethodEntry entry, boolean recursive) { |
| 218 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 219 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 205 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 220 | MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, obfMethodEntry); | 206 | MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, entry); |
| 221 | rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); | 207 | rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); |
| 222 | return rootNode; | 208 | return rootNode; |
| 223 | } | 209 | } |
| 224 | 210 | ||
| 225 | public void rename(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean refreshClassTree) { | 211 | public void rename(EntryReference<Entry<?>, Entry<?>> reference, String newName, boolean refreshClassTree) { |
| 226 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 212 | this.deobfuscator.rename(reference.getNameableEntry(), newName); |
| 227 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName); | ||
| 228 | |||
| 229 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | ||
| 230 | this.gui.moveClassTree(deobfReference, newName); | ||
| 231 | refreshCurrentClass(obfReference); | ||
| 232 | 213 | ||
| 214 | if (refreshClassTree && reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) | ||
| 215 | this.gui.moveClassTree(reference, newName); | ||
| 216 | refreshCurrentClass(reference); | ||
| 233 | } | 217 | } |
| 234 | 218 | ||
| 235 | public void removeMapping(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 219 | public void removeMapping(EntryReference<Entry<?>, Entry<?>> reference) { |
| 236 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 220 | this.deobfuscator.removeMapping(reference.getNameableEntry()); |
| 237 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); | 221 | if (reference.entry instanceof ClassEntry) |
| 238 | if (deobfReference.entry instanceof ClassEntry) | 222 | this.gui.moveClassTree(reference, false, true); |
| 239 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); | 223 | refreshCurrentClass(reference); |
| 240 | refreshCurrentClass(obfReference); | ||
| 241 | } | 224 | } |
| 242 | 225 | ||
| 243 | public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 226 | public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> reference) { |
| 244 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 227 | this.deobfuscator.markAsDeobfuscated(reference.getNameableEntry()); |
| 245 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); | 228 | if (reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) |
| 246 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 229 | this.gui.moveClassTree(reference, true, false); |
| 247 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); | 230 | refreshCurrentClass(reference); |
| 248 | refreshCurrentClass(obfReference); | ||
| 249 | } | 231 | } |
| 250 | 232 | ||
| 251 | public void openDeclaration(Entry<?> deobfEntry) { | 233 | public void openDeclaration(Entry<?> entry) { |
| 252 | if (deobfEntry == null) { | 234 | if (entry == null) { |
| 253 | throw new IllegalArgumentException("Entry cannot be null!"); | 235 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 254 | } | 236 | } |
| 255 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); | 237 | openReference(new EntryReference<>(entry, entry.getName())); |
| 256 | } | 238 | } |
| 257 | 239 | ||
| 258 | public void openReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 240 | public void openReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 259 | if (deobfReference == null) { | 241 | if (reference == null) { |
| 260 | throw new IllegalArgumentException("Reference cannot be null!"); | 242 | throw new IllegalArgumentException("Reference cannot be null!"); |
| 261 | } | 243 | } |
| 262 | 244 | ||
| 263 | // get the reference target class | 245 | // get the reference target class |
| 264 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 246 | ClassEntry classEntry = reference.getLocationClassEntry(); |
| 265 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry(); | 247 | if (!this.deobfuscator.isRenamable(classEntry)) { |
| 266 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { | 248 | throw new IllegalArgumentException("Obfuscated class " + classEntry + " was not found in the jar!"); |
| 267 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); | ||
| 268 | } | 249 | } |
| 269 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { | 250 | |
| 251 | if (this.currentSource == null || !this.currentSource.getEntry().equals(classEntry)) { | ||
| 270 | // deobfuscate the class, then navigate to the reference | 252 | // deobfuscate the class, then navigate to the reference |
| 271 | this.currentObfClass = obfClassEntry; | 253 | loadClass(classEntry, () -> showReference(reference)); |
| 272 | deobfuscate(this.currentObfClass, obfReference); | ||
| 273 | } else { | 254 | } else { |
| 274 | showReference(obfReference); | 255 | showReference(reference); |
| 275 | } | 256 | } |
| 276 | } | 257 | } |
| 277 | 258 | ||
| 278 | private void showReference(EntryReference<Entry<?>, Entry<?>> obfReference) { | 259 | private void showReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 279 | EntryRemapper mapper = this.deobfuscator.getMapper(); | 260 | EntryRemapper mapper = this.deobfuscator.getMapper(); |
| 280 | 261 | ||
| 281 | Collection<Token> tokens = mapper.getObfResolver().resolveReference(obfReference, ResolutionStrategy.RESOLVE_ROOT) | 262 | SourceIndex index = this.currentSource.getIndex(); |
| 263 | Collection<Token> tokens = mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_ROOT) | ||
| 282 | .stream() | 264 | .stream() |
| 283 | .map(mapper::deobfuscate) | 265 | .flatMap(r -> index.getReferenceTokens(r).stream()) |
| 284 | .flatMap(reference -> index.getReferenceTokens(reference).stream()) | ||
| 285 | .collect(Collectors.toList()); | 266 | .collect(Collectors.toList()); |
| 286 | 267 | ||
| 287 | if (tokens.isEmpty()) { | 268 | if (tokens.isEmpty()) { |
| 288 | // DEBUG | 269 | // DEBUG |
| 289 | System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentObfClass)); | 270 | System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentSource.getEntry())); |
| 290 | } else { | 271 | } else { |
| 291 | this.gui.showTokens(tokens); | 272 | this.gui.showTokens(tokens); |
| 292 | } | 273 | } |
| 293 | } | 274 | } |
| 294 | 275 | ||
| 295 | public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 276 | public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 296 | this.referenceStack.push(this.deobfuscator.getMapper().obfuscate(deobfReference)); | 277 | this.referenceStack.push(reference); |
| 297 | } | 278 | } |
| 298 | 279 | ||
| 299 | public void openPreviousReference() { | 280 | public void openPreviousReference() { |
| 300 | if (hasPreviousLocation()) { | 281 | if (hasPreviousLocation()) { |
| 301 | openReference(this.deobfuscator.getMapper().deobfuscate(this.referenceStack.pop())); | 282 | openReference(this.referenceStack.pop()); |
| 302 | } | 283 | } |
| 303 | } | 284 | } |
| 304 | 285 | ||
| @@ -318,97 +299,65 @@ public class GuiController { | |||
| 318 | refreshCurrentClass(null); | 299 | refreshCurrentClass(null); |
| 319 | } | 300 | } |
| 320 | 301 | ||
| 321 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> obfReference) { | 302 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) { |
| 322 | if (this.currentObfClass != null) { | 303 | if (currentSource != null) { |
| 323 | deobfuscate(this.currentObfClass, obfReference); | 304 | loadClass(currentSource.getEntry(), () -> { |
| 305 | if (reference != null) { | ||
| 306 | showReference(reference); | ||
| 307 | } | ||
| 308 | }); | ||
| 324 | } | 309 | } |
| 325 | } | 310 | } |
| 326 | 311 | ||
| 327 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry<?>, Entry<?>> obfReference) { | 312 | private void loadClass(ClassEntry classEntry, Runnable callback) { |
| 313 | ClassEntry targetClass = classEntry.getOutermostClass(); | ||
| 328 | 314 | ||
| 329 | this.gui.setSource("(deobfuscating...)"); | 315 | boolean requiresDecompile = currentSource == null || !currentSource.getEntry().equals(targetClass); |
| 316 | if (requiresDecompile) { | ||
| 317 | gui.setEditorText("(decompiling...)"); | ||
| 318 | } | ||
| 330 | 319 | ||
| 331 | // run the deobfuscator in a separate thread so we don't block the GUI event queue | 320 | DECOMPILER_SERVICE.submit(() -> { |
| 332 | new Thread(() -> | 321 | try { |
| 333 | { | 322 | if (requiresDecompile) { |
| 334 | // decompile,deobfuscate the bytecode | 323 | decompileSource(targetClass, deobfuscator.getObfSourceProvider()); |
| 335 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClass().getFullName()); | ||
| 336 | if (sourceTree == null) { | ||
| 337 | // decompilation of this class is not supported | ||
| 338 | gui.setSource("Unable to find class: " + classEntry); | ||
| 339 | return; | ||
| 340 | } | ||
| 341 | String source = deobfuscator.getSource(sourceTree); | ||
| 342 | index = deobfuscator.getSourceIndex(sourceTree, source); | ||
| 343 | |||
| 344 | String sourceString = index.getSource(); | ||
| 345 | |||
| 346 | // set the highlighted tokens | ||
| 347 | List<Token> obfuscatedTokens = Lists.newArrayList(); | ||
| 348 | List<Token> proposedTokens = Lists.newArrayList(); | ||
| 349 | List<Token> deobfuscatedTokens = Lists.newArrayList(); | ||
| 350 | List<Token> otherTokens = Lists.newArrayList(); | ||
| 351 | |||
| 352 | int offset = 0; | ||
| 353 | Map<Token, Token> tokenRemap = new HashMap<>(); | ||
| 354 | boolean remapped = false; | ||
| 355 | |||
| 356 | for (Token inToken : index.referenceTokens()) { | ||
| 357 | Token token = inToken.move(offset); | ||
| 358 | |||
| 359 | EntryReference<Entry<?>, Entry<?>> reference = index.getDeobfReference(inToken); | ||
| 360 | if (referenceIsRenameable(reference)) { | ||
| 361 | boolean added = false; | ||
| 362 | |||
| 363 | if (!entryHasDeobfuscatedName(reference.getNameableEntry())) { | ||
| 364 | Entry<?> obfEntry = deobfuscator.getMapper().obfuscate(reference.getNameableEntry()); | ||
| 365 | if (obfEntry instanceof FieldEntry) { | ||
| 366 | for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { | ||
| 367 | String owner = obfEntry.getContainingClass().getFullName(); | ||
| 368 | String proposal = plugin.proposeFieldName(owner, obfEntry.getName(), ((FieldEntry) obfEntry).getDesc().toString()); | ||
| 369 | if (proposal != null) { | ||
| 370 | proposedTokens.add(token); | ||
| 371 | offset += token.getRenameOffset(proposal); | ||
| 372 | sourceString = token.rename(sourceString, proposal); | ||
| 373 | added = true; | ||
| 374 | remapped = true; | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | if (!added) { | ||
| 382 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { | ||
| 383 | deobfuscatedTokens.add(token); | ||
| 384 | } else { | ||
| 385 | obfuscatedTokens.add(token); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | } else { | ||
| 389 | otherTokens.add(token); | ||
| 390 | } | 324 | } |
| 391 | 325 | ||
| 392 | tokenRemap.put(inToken, token); | 326 | remapSource(deobfuscator.getMapper().getDeobfuscator()); |
| 327 | callback.run(); | ||
| 328 | } catch (Throwable t) { | ||
| 329 | System.err.println("An exception was thrown while decompiling class " + classEntry.getFullName()); | ||
| 330 | t.printStackTrace(System.err); | ||
| 393 | } | 331 | } |
| 332 | }); | ||
| 333 | } | ||
| 394 | 334 | ||
| 395 | if (remapped) { | 335 | private void decompileSource(ClassEntry targetClass, SourceProvider sourceProvider) { |
| 396 | index.remap(sourceString, tokenRemap); | 336 | CompilationUnit sourceTree = sourceProvider.getSources(targetClass.getFullName()); |
| 397 | } | 337 | if (sourceTree == null) { |
| 338 | gui.setEditorText("Unable to find class: " + targetClass); | ||
| 339 | return; | ||
| 340 | } | ||
| 398 | 341 | ||
| 399 | gui.setSource(sourceString); | 342 | DropImportAstTransform.INSTANCE.run(sourceTree); |
| 400 | if (obfReference != null) { | 343 | |
| 401 | showReference(obfReference); | 344 | String sourceString = sourceProvider.writeSourceToString(sourceTree); |
| 402 | } | 345 | |
| 346 | SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true); | ||
| 347 | index.resolveReferences(deobfuscator.getMapper().getObfResolver()); | ||
| 348 | |||
| 349 | currentSource = new DecompiledClassSource(targetClass, deobfuscator, index); | ||
| 350 | } | ||
| 351 | |||
| 352 | private void remapSource(Translator translator) { | ||
| 353 | if (currentSource == null) { | ||
| 354 | return; | ||
| 355 | } | ||
| 356 | |||
| 357 | currentSource.remapSource(translator); | ||
| 403 | 358 | ||
| 404 | gui.setEditorTheme(Config.getInstance().lookAndFeel); | 359 | gui.setEditorTheme(Config.getInstance().lookAndFeel); |
| 405 | gui.setHighlightedTokens(ImmutableMap.of( | 360 | gui.setSource(currentSource); |
| 406 | "obfuscated", obfuscatedTokens, | ||
| 407 | "proposed", proposedTokens, | ||
| 408 | "deobfuscated", deobfuscatedTokens, | ||
| 409 | "other", otherTokens | ||
| 410 | )); | ||
| 411 | }).start(); | ||
| 412 | } | 361 | } |
| 413 | 362 | ||
| 414 | public Deobfuscator getDeobfuscator() { | 363 | public Deobfuscator getDeobfuscator() { |
diff --git a/src/main/java/cuchaz/enigma/gui/SourceRemapper.java b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java new file mode 100644 index 0000000..f38f44e --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.Token; | ||
| 4 | |||
| 5 | import java.util.HashMap; | ||
| 6 | import java.util.Map; | ||
| 7 | |||
| 8 | public class SourceRemapper { | ||
| 9 | private final String source; | ||
| 10 | private final Iterable<Token> tokens; | ||
| 11 | |||
| 12 | public SourceRemapper(String source, Iterable<Token> tokens) { | ||
| 13 | this.source = source; | ||
| 14 | this.tokens = tokens; | ||
| 15 | } | ||
| 16 | |||
| 17 | public Result remap(Remapper remapper) { | ||
| 18 | StringBuffer remappedSource = new StringBuffer(source); | ||
| 19 | Map<Token, Token> remappedTokens = new HashMap<>(); | ||
| 20 | |||
| 21 | int accumulatedOffset = 0; | ||
| 22 | for (Token token : tokens) { | ||
| 23 | Token movedToken = token.move(accumulatedOffset); | ||
| 24 | |||
| 25 | String remappedName = remapper.remap(token, movedToken); | ||
| 26 | if (remappedName != null) { | ||
| 27 | accumulatedOffset += movedToken.getRenameOffset(remappedName); | ||
| 28 | movedToken.rename(remappedSource, remappedName); | ||
| 29 | } | ||
| 30 | |||
| 31 | if (!token.equals(movedToken)) { | ||
| 32 | remappedTokens.put(token, movedToken); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return new Result(remappedSource.toString(), remappedTokens); | ||
| 37 | } | ||
| 38 | |||
| 39 | public static class Result { | ||
| 40 | private final String remappedSource; | ||
| 41 | private final Map<Token, Token> remappedTokens; | ||
| 42 | |||
| 43 | Result(String remappedSource, Map<Token, Token> remappedTokens) { | ||
| 44 | this.remappedSource = remappedSource; | ||
| 45 | this.remappedTokens = remappedTokens; | ||
| 46 | } | ||
| 47 | |||
| 48 | public String getSource() { | ||
| 49 | return remappedSource; | ||
| 50 | } | ||
| 51 | |||
| 52 | public Token getRemappedToken(Token token) { | ||
| 53 | return remappedTokens.getOrDefault(token, token); | ||
| 54 | } | ||
| 55 | |||
| 56 | public boolean isEmpty() { | ||
| 57 | return remappedTokens.isEmpty(); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | public interface Remapper { | ||
| 62 | String remap(Token token, Token movedToken); | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index f4f0277..dfbfa65 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java | |||
| @@ -152,12 +152,8 @@ public class MenuBar extends JMenuBar { | |||
| 152 | if (this.gui.getController().isDirty()) { | 152 | if (this.gui.getController().isDirty()) { |
| 153 | this.gui.showDiscardDiag((response -> { | 153 | this.gui.showDiscardDiag((response -> { |
| 154 | if (response == JOptionPane.YES_OPTION) { | 154 | if (response == JOptionPane.YES_OPTION) { |
| 155 | try { | 155 | gui.saveMapping(); |
| 156 | gui.saveMapping(); | 156 | this.gui.getController().closeMappings(); |
| 157 | this.gui.getController().closeMappings(); | ||
| 158 | } catch (IOException e) { | ||
| 159 | throw new Error(e); | ||
| 160 | } | ||
| 161 | } else if (response == JOptionPane.NO_OPTION) | 157 | } else if (response == JOptionPane.NO_OPTION) |
| 162 | this.gui.getController().closeMappings(); | 158 | this.gui.getController().closeMappings(); |
| 163 | return null; | 159 | return null; |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 10366ce..cef6494 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java | |||
| @@ -34,7 +34,9 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter { | |||
| 34 | public static Rectangle getBounds(JTextComponent text, int start, int end) { | 34 | public static Rectangle getBounds(JTextComponent text, int start, int end) { |
| 35 | try { | 35 | try { |
| 36 | // determine the bounds of the text | 36 | // determine the bounds of the text |
| 37 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | 37 | Rectangle startRect = text.modelToView(start); |
| 38 | Rectangle endRect = text.modelToView(end); | ||
| 39 | Rectangle bounds = startRect.union(endRect); | ||
| 38 | 40 | ||
| 39 | // adjust the box so it looks nice | 41 | // adjust the box so it looks nice |
| 40 | bounds.x -= 2; | 42 | bounds.x -= 2; |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java new file mode 100644 index 0000000..ae23f32 --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | package cuchaz.enigma.gui.highlight; | ||
| 2 | |||
| 3 | public enum TokenHighlightType { | ||
| 4 | OBFUSCATED, | ||
| 5 | DEOBFUSCATED, | ||
| 6 | PROPOSED | ||
| 7 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index bf6b178..922f8f2 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java | |||
| @@ -17,13 +17,19 @@ import javax.swing.tree.DefaultMutableTreeNode; | |||
| 17 | 17 | ||
| 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { |
| 19 | 19 | ||
| 20 | private final ClassEntry obfEntry; | ||
| 20 | private ClassEntry classEntry; | 21 | private ClassEntry classEntry; |
| 21 | 22 | ||
| 22 | public ClassSelectorClassNode(ClassEntry classEntry) { | 23 | public ClassSelectorClassNode(ClassEntry obfEntry, ClassEntry classEntry) { |
| 24 | this.obfEntry = obfEntry; | ||
| 23 | this.classEntry = classEntry; | 25 | this.classEntry = classEntry; |
| 24 | this.setUserObject(classEntry); | 26 | this.setUserObject(classEntry); |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 29 | public ClassEntry getObfEntry() { | ||
| 30 | return obfEntry; | ||
| 31 | } | ||
| 32 | |||
| 27 | public ClassEntry getClassEntry() { | 33 | public ClassEntry getClassEntry() { |
| 28 | return this.classEntry; | 34 | return this.classEntry; |
| 29 | } | 35 | } |