diff options
14 files changed, 338 insertions, 37 deletions
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index c56731d..a3a438e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -51,10 +51,7 @@ import cuchaz.enigma.network.packet.RemoveMappingC2SPacket; | |||
| 51 | import cuchaz.enigma.network.packet.RenameC2SPacket; | 51 | import cuchaz.enigma.network.packet.RenameC2SPacket; |
| 52 | import cuchaz.enigma.source.Token; | 52 | import cuchaz.enigma.source.Token; |
| 53 | import cuchaz.enigma.translation.mapping.EntryRemapper; | 53 | import cuchaz.enigma.translation.mapping.EntryRemapper; |
| 54 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 54 | import cuchaz.enigma.translation.representation.entry.*; |
| 55 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 56 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 57 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 58 | import cuchaz.enigma.utils.I18n; | 55 | import cuchaz.enigma.utils.I18n; |
| 59 | import cuchaz.enigma.utils.validation.ParameterizedMessage; | 56 | import cuchaz.enigma.utils.validation.ParameterizedMessage; |
| 60 | import cuchaz.enigma.utils.validation.ValidationContext; | 57 | import cuchaz.enigma.utils.validation.ValidationContext; |
| @@ -82,6 +79,7 @@ public class Gui implements LanguageChangeListener { | |||
| 82 | private JPanel classesPanel; | 79 | private JPanel classesPanel; |
| 83 | private JSplitPane splitClasses; | 80 | private JSplitPane splitClasses; |
| 84 | private IdentifierPanel infoPanel; | 81 | private IdentifierPanel infoPanel; |
| 82 | private StructurePanel structurePanel; | ||
| 85 | private JTree inheritanceTree; | 83 | private JTree inheritanceTree; |
| 86 | private JTree implementationsTree; | 84 | private JTree implementationsTree; |
| 87 | private JTree callsTree; | 85 | private JTree callsTree; |
| @@ -163,6 +161,9 @@ public class Gui implements LanguageChangeListener { | |||
| 163 | // init info panel | 161 | // init info panel |
| 164 | infoPanel = new IdentifierPanel(this); | 162 | infoPanel = new IdentifierPanel(this); |
| 165 | 163 | ||
| 164 | // init structure panel | ||
| 165 | this.structurePanel = new StructurePanel(this); | ||
| 166 | |||
| 166 | // init inheritance panel | 167 | // init inheritance panel |
| 167 | inheritanceTree = new JTree(); | 168 | inheritanceTree = new JTree(); |
| 168 | inheritanceTree.setModel(null); | 169 | inheritanceTree.setModel(null); |
| @@ -287,6 +288,8 @@ public class Gui implements LanguageChangeListener { | |||
| 287 | editorTabPopupMenu.show(openFiles, e.getX(), e.getY(), EditorPanel.byUi(openFiles.getComponentAt(i))); | 288 | editorTabPopupMenu.show(openFiles, e.getX(), e.getY(), EditorPanel.byUi(openFiles.getComponentAt(i))); |
| 288 | } | 289 | } |
| 289 | } | 290 | } |
| 291 | |||
| 292 | showStructure(getActiveEditor()); | ||
| 290 | } | 293 | } |
| 291 | }); | 294 | }); |
| 292 | 295 | ||
| @@ -311,6 +314,7 @@ public class Gui implements LanguageChangeListener { | |||
| 311 | centerPanel.add(openFiles, BorderLayout.CENTER); | 314 | centerPanel.add(openFiles, BorderLayout.CENTER); |
| 312 | tabs = new JTabbedPane(); | 315 | tabs = new JTabbedPane(); |
| 313 | tabs.setPreferredSize(ScaleUtil.getDimension(250, 0)); | 316 | tabs.setPreferredSize(ScaleUtil.getDimension(250, 0)); |
| 317 | tabs.addTab(I18n.translate("info_panel.tree.structure"), structurePanel); | ||
| 314 | tabs.addTab(I18n.translate("info_panel.tree.inheritance"), inheritancePanel); | 318 | tabs.addTab(I18n.translate("info_panel.tree.inheritance"), inheritancePanel); |
| 315 | tabs.addTab(I18n.translate("info_panel.tree.implementations"), implementationsPanel); | 319 | tabs.addTab(I18n.translate("info_panel.tree.implementations"), implementationsPanel); |
| 316 | tabs.addTab(I18n.translate("info_panel.tree.calls"), callPanel); | 320 | tabs.addTab(I18n.translate("info_panel.tree.calls"), callPanel); |
| @@ -482,11 +486,14 @@ public class Gui implements LanguageChangeListener { | |||
| 482 | } | 486 | } |
| 483 | }); | 487 | }); |
| 484 | 488 | ||
| 489 | showStructure(ed); | ||
| 490 | |||
| 485 | return ed; | 491 | return ed; |
| 486 | }); | 492 | }); |
| 487 | if (editorPanel != null) { | 493 | if (editorPanel != null) { |
| 488 | openFiles.setSelectedComponent(editors.get(entry).getUi()); | 494 | openFiles.setSelectedComponent(editors.get(entry).getUi()); |
| 489 | } | 495 | } |
| 496 | |||
| 490 | return editorPanel; | 497 | return editorPanel; |
| 491 | } | 498 | } |
| 492 | 499 | ||
| @@ -506,6 +513,7 @@ public class Gui implements LanguageChangeListener { | |||
| 506 | public void closeEditor(EditorPanel ed) { | 513 | public void closeEditor(EditorPanel ed) { |
| 507 | openFiles.remove(ed.getUi()); | 514 | openFiles.remove(ed.getUi()); |
| 508 | editors.inverse().remove(ed); | 515 | editors.inverse().remove(ed); |
| 516 | showStructure(getActiveEditor()); | ||
| 509 | ed.destroy(); | 517 | ed.destroy(); |
| 510 | } | 518 | } |
| 511 | 519 | ||
| @@ -595,6 +603,32 @@ public class Gui implements LanguageChangeListener { | |||
| 595 | infoPanel.startRenaming(); | 603 | infoPanel.startRenaming(); |
| 596 | } | 604 | } |
| 597 | 605 | ||
| 606 | public void showStructure(EditorPanel editor) { | ||
| 607 | JTree structureTree = this.structurePanel.getStructureTree(); | ||
| 608 | structureTree.setModel(null); | ||
| 609 | |||
| 610 | if (editor == null) { | ||
| 611 | this.structurePanel.getSortingPanel().setVisible(false); | ||
| 612 | return; | ||
| 613 | } | ||
| 614 | |||
| 615 | ClassEntry classEntry = editor.getClassHandle().getRef(); | ||
| 616 | if (classEntry == null) return; | ||
| 617 | |||
| 618 | this.structurePanel.getSortingPanel().setVisible(true); | ||
| 619 | |||
| 620 | // get the class structure | ||
| 621 | StructureTreeNode node = this.controller.getClassStructure(classEntry, this.structurePanel.shouldHideDeobfuscated()); | ||
| 622 | |||
| 623 | // show the tree at the root | ||
| 624 | TreePath path = getPathToRoot(node); | ||
| 625 | structureTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); | ||
| 626 | structureTree.expandPath(path); | ||
| 627 | structureTree.setSelectionRow(structureTree.getRowForPath(path)); | ||
| 628 | |||
| 629 | redraw(); | ||
| 630 | } | ||
| 631 | |||
| 598 | public void showInheritance(EditorPanel editor) { | 632 | public void showInheritance(EditorPanel editor) { |
| 599 | EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); | 633 | EntryReference<Entry<?>, Entry<?>> cursorReference = editor.getCursorReference(); |
| 600 | if (cursorReference == null) return; | 634 | if (cursorReference == null) return; |
| @@ -621,7 +655,7 @@ public class Gui implements LanguageChangeListener { | |||
| 621 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); | 655 | inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); |
| 622 | } | 656 | } |
| 623 | 657 | ||
| 624 | tabs.setSelectedIndex(0); | 658 | tabs.setSelectedIndex(1); |
| 625 | 659 | ||
| 626 | redraw(); | 660 | redraw(); |
| 627 | } | 661 | } |
| @@ -649,7 +683,7 @@ public class Gui implements LanguageChangeListener { | |||
| 649 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); | 683 | implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); |
| 650 | } | 684 | } |
| 651 | 685 | ||
| 652 | tabs.setSelectedIndex(1); | 686 | tabs.setSelectedIndex(2); |
| 653 | 687 | ||
| 654 | redraw(); | 688 | redraw(); |
| 655 | } | 689 | } |
| @@ -669,7 +703,7 @@ public class Gui implements LanguageChangeListener { | |||
| 669 | callsTree.setModel(new DefaultTreeModel(node)); | 703 | callsTree.setModel(new DefaultTreeModel(node)); |
| 670 | } | 704 | } |
| 671 | 705 | ||
| 672 | tabs.setSelectedIndex(2); | 706 | tabs.setSelectedIndex(3); |
| 673 | 707 | ||
| 674 | redraw(); | 708 | redraw(); |
| 675 | } | 709 | } |
| @@ -893,9 +927,10 @@ public class Gui implements LanguageChangeListener { | |||
| 893 | public void retranslateUi() { | 927 | public void retranslateUi() { |
| 894 | this.jarFileChooser.setTitle(I18n.translate("menu.file.jar.open")); | 928 | this.jarFileChooser.setTitle(I18n.translate("menu.file.jar.open")); |
| 895 | this.exportJarFileChooser.setTitle(I18n.translate("menu.file.export.jar")); | 929 | this.exportJarFileChooser.setTitle(I18n.translate("menu.file.export.jar")); |
| 896 | this.tabs.setTitleAt(0, I18n.translate("info_panel.tree.inheritance")); | 930 | this.tabs.setTitleAt(0, I18n.translate("info_panel.tree.structure")); |
| 897 | this.tabs.setTitleAt(1, I18n.translate("info_panel.tree.implementations")); | 931 | this.tabs.setTitleAt(1, I18n.translate("info_panel.tree.inheritance")); |
| 898 | this.tabs.setTitleAt(2, I18n.translate("info_panel.tree.calls")); | 932 | this.tabs.setTitleAt(2, I18n.translate("info_panel.tree.implementations")); |
| 933 | this.tabs.setTitleAt(3, I18n.translate("info_panel.tree.calls")); | ||
| 899 | this.logTabs.setTitleAt(0, I18n.translate("log_panel.users")); | 934 | this.logTabs.setTitleAt(0, I18n.translate("log_panel.users")); |
| 900 | this.logTabs.setTitleAt(1, I18n.translate("log_panel.messages")); | 935 | this.logTabs.setTitleAt(1, I18n.translate("log_panel.messages")); |
| 901 | this.connectionStatusLabel.setText(I18n.translate(connectionState == ConnectionState.NOT_CONNECTED ? "status.disconnected" : "status.connected")); | 936 | this.connectionStatusLabel.setText(I18n.translate(connectionState == ConnectionState.NOT_CONNECTED ? "status.disconnected" : "status.connected")); |
| @@ -907,6 +942,7 @@ public class Gui implements LanguageChangeListener { | |||
| 907 | this.deobfPanel.retranslateUi(); | 942 | this.deobfPanel.retranslateUi(); |
| 908 | this.deobfPanelPopupMenu.retranslateUi(); | 943 | this.deobfPanelPopupMenu.retranslateUi(); |
| 909 | this.infoPanel.retranslateUi(); | 944 | this.infoPanel.retranslateUi(); |
| 945 | this.structurePanel.retranslateUi(); | ||
| 910 | this.editors.values().forEach(EditorPanel::retranslateUi); | 946 | this.editors.values().forEach(EditorPanel::retranslateUi); |
| 911 | } | 947 | } |
| 912 | 948 | ||
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index 4f7819e..4e964da 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -57,10 +57,7 @@ import cuchaz.enigma.translation.mapping.serde.MappingParseException; | |||
| 57 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; | 57 | import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; |
| 58 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 58 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 59 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | 59 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; |
| 60 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 60 | import cuchaz.enigma.translation.representation.entry.*; |
| 61 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 62 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 63 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 64 | import cuchaz.enigma.utils.I18n; | 61 | import cuchaz.enigma.utils.I18n; |
| 65 | import cuchaz.enigma.utils.Utils; | 62 | import cuchaz.enigma.utils.Utils; |
| 66 | import cuchaz.enigma.utils.validation.ValidationContext; | 63 | import cuchaz.enigma.utils.validation.ValidationContext; |
| @@ -395,6 +392,13 @@ public class GuiController implements ClientPacketHandler { | |||
| 395 | chp.invalidateMapped(); | 392 | chp.invalidateMapped(); |
| 396 | } | 393 | } |
| 397 | 394 | ||
| 395 | public StructureTreeNode getClassStructure(ClassEntry entry, boolean hideDeobfuscated) { | ||
| 396 | Translator translator = this.project.getMapper().getDeobfuscator(); | ||
| 397 | StructureTreeNode rootNode = new StructureTreeNode(translator, entry, entry); | ||
| 398 | rootNode.load(this.project.getJarIndex(), hideDeobfuscated); | ||
| 399 | return rootNode; | ||
| 400 | } | ||
| 401 | |||
| 398 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { | 402 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { |
| 399 | Translator translator = project.getMapper().getDeobfuscator(); | 403 | Translator translator = project.getMapper().getDeobfuscator(); |
| 400 | ClassInheritanceTreeNode rootNode = indexTreeBuilder.buildClassInheritance(translator, entry); | 404 | ClassInheritanceTreeNode rootNode = indexTreeBuilder.buildClassInheritance(translator, entry); |
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java new file mode 100644 index 0000000..32f803f --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/panels/StructurePanel.java | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | package cuchaz.enigma.gui.panels; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.StructureTreeNode; | ||
| 4 | import cuchaz.enigma.gui.Gui; | ||
| 5 | import cuchaz.enigma.gui.util.GuiUtil; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.ParentedEntry; | ||
| 10 | import cuchaz.enigma.utils.I18n; | ||
| 11 | |||
| 12 | import javax.swing.*; | ||
| 13 | import javax.swing.tree.TreeCellRenderer; | ||
| 14 | import javax.swing.tree.TreePath; | ||
| 15 | import java.awt.*; | ||
| 16 | import java.awt.event.MouseAdapter; | ||
| 17 | import java.awt.event.MouseEvent; | ||
| 18 | |||
| 19 | public class StructurePanel extends JPanel { | ||
| 20 | private final Gui gui; | ||
| 21 | |||
| 22 | private JPanel sortingPanel; | ||
| 23 | private JCheckBox hideDeobfuscated; | ||
| 24 | |||
| 25 | private JTree structureTree; | ||
| 26 | |||
| 27 | public StructurePanel(Gui gui) { | ||
| 28 | this.gui = gui; | ||
| 29 | |||
| 30 | this.sortingPanel = new JPanel(); | ||
| 31 | this.hideDeobfuscated = new JCheckBox(I18n.translate("info_panel.tree.structure.hide_deobfuscated")); | ||
| 32 | this.hideDeobfuscated.addActionListener(event -> gui.showStructure(gui.getActiveEditor())); | ||
| 33 | this.sortingPanel.add(this.hideDeobfuscated); | ||
| 34 | this.sortingPanel.setVisible(false); | ||
| 35 | |||
| 36 | this.structureTree = new JTree(); | ||
| 37 | this.structureTree.setModel(null); | ||
| 38 | this.structureTree.setCellRenderer(new StructureTreeCellRenderer()); | ||
| 39 | this.structureTree.addMouseListener(new MouseAdapter() { | ||
| 40 | @Override | ||
| 41 | public void mouseClicked(MouseEvent event) { | ||
| 42 | if (event.getClickCount() >= 2) { | ||
| 43 | // get the selected node | ||
| 44 | TreePath path = structureTree.getSelectionPath(); | ||
| 45 | if (path == null) { | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | Object node = path.getLastPathComponent(); | ||
| 50 | if (node instanceof StructureTreeNode) { | ||
| 51 | gui.getController().navigateTo(((StructureTreeNode) node).getEntry()); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | }); | ||
| 56 | |||
| 57 | this.setLayout(new BorderLayout()); | ||
| 58 | this.add(this.sortingPanel, BorderLayout.NORTH); | ||
| 59 | this.add(new JScrollPane(this.structureTree)); | ||
| 60 | } | ||
| 61 | |||
| 62 | public JPanel getSortingPanel() { | ||
| 63 | return this.sortingPanel; | ||
| 64 | } | ||
| 65 | |||
| 66 | public boolean shouldHideDeobfuscated() { | ||
| 67 | return this.hideDeobfuscated.isSelected(); | ||
| 68 | } | ||
| 69 | |||
| 70 | public JTree getStructureTree() { | ||
| 71 | return this.structureTree; | ||
| 72 | } | ||
| 73 | |||
| 74 | public void retranslateUi() { | ||
| 75 | this.hideDeobfuscated.setText(I18n.translate("info_panel.tree.structure.hide_deobfuscated")); | ||
| 76 | } | ||
| 77 | |||
| 78 | class StructureTreeCellRenderer implements TreeCellRenderer { | ||
| 79 | private JLabel label; | ||
| 80 | |||
| 81 | public StructureTreeCellRenderer() { | ||
| 82 | this.label = new JLabel(); | ||
| 83 | } | ||
| 84 | |||
| 85 | @Override | ||
| 86 | public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { | ||
| 87 | ParentedEntry entry = ((StructureTreeNode) value).getEntry(); | ||
| 88 | |||
| 89 | if (entry instanceof ClassEntry) { | ||
| 90 | this.label.setIcon(GuiUtil.CLASS_ICON); | ||
| 91 | } else if (entry instanceof MethodEntry) { | ||
| 92 | this.label.setIcon(GuiUtil.METHOD_ICON); | ||
| 93 | } else if (entry instanceof FieldEntry) { | ||
| 94 | this.label.setIcon(GuiUtil.FIELD_ICON); | ||
| 95 | } | ||
| 96 | |||
| 97 | this.label.setText(value.toString()); | ||
| 98 | |||
| 99 | return this.label; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 7fe942d..666fc0a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java | |||
| @@ -1,24 +1,24 @@ | |||
| 1 | package cuchaz.enigma.gui.util; | 1 | package cuchaz.enigma.gui.util; |
| 2 | 2 | ||
| 3 | import java.awt.Color; | 3 | import cuchaz.enigma.utils.Os; |
| 4 | import java.awt.Cursor; | 4 | |
| 5 | import java.awt.Desktop; | 5 | import javax.imageio.ImageIO; |
| 6 | import java.awt.Font; | 6 | import javax.swing.*; |
| 7 | import java.awt.*; | ||
| 7 | import java.awt.event.MouseAdapter; | 8 | import java.awt.event.MouseAdapter; |
| 8 | import java.awt.event.MouseEvent; | 9 | import java.awt.event.MouseEvent; |
| 9 | import java.awt.font.TextAttribute; | 10 | import java.awt.font.TextAttribute; |
| 10 | import java.io.IOException; | 11 | import java.io.IOException; |
| 12 | import java.io.InputStream; | ||
| 11 | import java.net.URI; | 13 | import java.net.URI; |
| 12 | import java.net.URISyntaxException; | 14 | import java.net.URISyntaxException; |
| 13 | import java.util.Map; | 15 | import java.util.Map; |
| 14 | 16 | ||
| 15 | import javax.swing.JComponent; | ||
| 16 | import javax.swing.JLabel; | ||
| 17 | import javax.swing.ToolTipManager; | ||
| 18 | |||
| 19 | import cuchaz.enigma.utils.Os; | ||
| 20 | |||
| 21 | public class GuiUtil { | 17 | public class GuiUtil { |
| 18 | public static final Icon CLASS_ICON = loadIcon("class"); | ||
| 19 | public static final Icon METHOD_ICON = loadIcon("method"); | ||
| 20 | public static final Icon FIELD_ICON = loadIcon("field"); | ||
| 21 | |||
| 22 | public static void openUrl(String url) { | 22 | public static void openUrl(String url) { |
| 23 | try { | 23 | try { |
| 24 | switch (Os.getOs()) { | 24 | switch (Os.getOs()) { |
| @@ -70,4 +70,15 @@ public class GuiUtil { | |||
| 70 | return link; | 70 | return link; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | public static Icon loadIcon(String name) { | ||
| 74 | try { | ||
| 75 | InputStream inputStream = GuiUtil.class.getResourceAsStream("/icons/" + name + ".png"); | ||
| 76 | Image image = ImageIO.read(inputStream).getScaledInstance(ScaleUtil.scale(16), ScaleUtil.scale(16), Image.SCALE_DEFAULT); | ||
| 77 | return new ImageIcon(image); | ||
| 78 | } catch (IOException e) { | ||
| 79 | e.printStackTrace(); | ||
| 80 | } | ||
| 81 | |||
| 82 | return null; | ||
| 83 | } | ||
| 73 | } | 84 | } |
diff --git a/enigma-swing/src/main/resources/icons/class.png b/enigma-swing/src/main/resources/icons/class.png new file mode 100644 index 0000000..4f7d0f4 --- /dev/null +++ b/enigma-swing/src/main/resources/icons/class.png | |||
| Binary files differ | |||
diff --git a/enigma-swing/src/main/resources/icons/field.png b/enigma-swing/src/main/resources/icons/field.png new file mode 100644 index 0000000..b124dcf --- /dev/null +++ b/enigma-swing/src/main/resources/icons/field.png | |||
| Binary files differ | |||
diff --git a/enigma-swing/src/main/resources/icons/method.png b/enigma-swing/src/main/resources/icons/method.png new file mode 100644 index 0000000..4ed0ccc --- /dev/null +++ b/enigma-swing/src/main/resources/icons/method.png | |||
| Binary files differ | |||
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java new file mode 100644 index 0000000..13f277c --- /dev/null +++ b/enigma/src/main/java/cuchaz/enigma/analysis/StructureTreeNode.java | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 7 | |||
| 8 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 9 | import java.util.List; | ||
| 10 | |||
| 11 | public class StructureTreeNode extends DefaultMutableTreeNode { | ||
| 12 | private final Translator translator; | ||
| 13 | private final ClassEntry parentEntry; | ||
| 14 | private final ParentedEntry entry; | ||
| 15 | |||
| 16 | public StructureTreeNode(Translator translator, ClassEntry parentEntry, ParentedEntry entry) { | ||
| 17 | this.translator = translator; | ||
| 18 | this.parentEntry = parentEntry; | ||
| 19 | this.entry = entry; | ||
| 20 | } | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Returns the parented entry corresponding to this tree node. | ||
| 24 | */ | ||
| 25 | public ParentedEntry getEntry() { | ||
| 26 | return this.entry; | ||
| 27 | } | ||
| 28 | |||
| 29 | public void load(JarIndex jarIndex, boolean hideDeobfuscated) { | ||
| 30 | List<ParentedEntry> children = jarIndex.getChildrenByClass().get(this.parentEntry); | ||
| 31 | |||
| 32 | for (ParentedEntry child : children) { | ||
| 33 | StructureTreeNode childNode = new StructureTreeNode(this.translator, this.parentEntry, child); | ||
| 34 | |||
| 35 | if (child instanceof ClassEntry) { | ||
| 36 | childNode = new StructureTreeNode(this.translator, (ClassEntry) child, child); | ||
| 37 | childNode.load(jarIndex, hideDeobfuscated); | ||
| 38 | } | ||
| 39 | |||
| 40 | // don't add deobfuscated members if hideDeobfuscated is true, unless it's an inner class | ||
| 41 | if (hideDeobfuscated && this.translator.extendedTranslate(child).isDeobfuscated() && !(child instanceof ClassEntry)) { | ||
| 42 | continue; | ||
| 43 | } | ||
| 44 | |||
| 45 | // don't add constructor methods if hideDeobfuscated is true | ||
| 46 | if (hideDeobfuscated && (child instanceof MethodEntry) && ((MethodEntry) child).isConstructor()) { | ||
| 47 | continue; | ||
| 48 | } | ||
| 49 | |||
| 50 | this.add(childNode); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | @Override | ||
| 55 | public String toString() { | ||
| 56 | ParentedEntry translatedEntry = this.translator.extendedTranslate(this.entry).getValue(); | ||
| 57 | String result = translatedEntry.getName(); | ||
| 58 | |||
| 59 | if (this.entry instanceof FieldDefEntry) { | ||
| 60 | FieldDefEntry field = (FieldDefEntry) translatedEntry; | ||
| 61 | String returnType = this.parseDesc(field.getDesc()); | ||
| 62 | |||
| 63 | result = result + ": " + returnType; | ||
| 64 | } else if (this.entry instanceof MethodDefEntry) { | ||
| 65 | MethodDefEntry method = (MethodDefEntry) translatedEntry; | ||
| 66 | String args = this.parseArgs(method.getDesc().getArgumentDescs()); | ||
| 67 | String returnType = this.parseDesc(method.getDesc().getReturnDesc()); | ||
| 68 | |||
| 69 | if (method.isConstructor()) { | ||
| 70 | result = method.getParent().getSimpleName() + args; | ||
| 71 | } else { | ||
| 72 | result = result + args + ": " + returnType; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | return result; | ||
| 77 | } | ||
| 78 | |||
| 79 | private String parseArgs(List<TypeDescriptor> args) { | ||
| 80 | if (args.size() > 0) { | ||
| 81 | String result = "("; | ||
| 82 | |||
| 83 | for (int i = 0; i < args.size(); i++) { | ||
| 84 | if (i > 0) { | ||
| 85 | result += ", "; | ||
| 86 | } | ||
| 87 | |||
| 88 | result += this.parseDesc(args.get(i)); | ||
| 89 | } | ||
| 90 | |||
| 91 | return result + ")"; | ||
| 92 | } | ||
| 93 | |||
| 94 | return "()"; | ||
| 95 | } | ||
| 96 | |||
| 97 | private String parseDesc(TypeDescriptor desc) { | ||
| 98 | if (desc.isVoid()) return "void"; | ||
| 99 | if (desc.isPrimitive()) return desc.getPrimitive().getKeyword(); | ||
| 100 | if (desc.isType()) return desc.getTypeEntry().getSimpleName(); | ||
| 101 | |||
| 102 | if (desc.isArray()) { | ||
| 103 | if (desc.getArrayType().isPrimitive()) return desc.getArrayType().getPrimitive().getKeyword() + "[]"; | ||
| 104 | if (desc.getArrayType().isType()) return desc.getArrayType().getTypeEntry().getSimpleName() + "[]"; | ||
| 105 | } | ||
| 106 | |||
| 107 | return null; | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index b5ad91a..d41731f 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java | |||
| @@ -23,10 +23,7 @@ import cuchaz.enigma.translation.representation.Lambda; | |||
| 23 | import cuchaz.enigma.translation.representation.entry.*; | 23 | import cuchaz.enigma.translation.representation.entry.*; |
| 24 | import cuchaz.enigma.utils.I18n; | 24 | import cuchaz.enigma.utils.I18n; |
| 25 | 25 | ||
| 26 | import java.util.Arrays; | 26 | import java.util.*; |
| 27 | import java.util.Collection; | ||
| 28 | import java.util.HashSet; | ||
| 29 | import java.util.Set; | ||
| 30 | 27 | ||
| 31 | public class JarIndex implements JarIndexer { | 28 | public class JarIndex implements JarIndexer { |
| 32 | private final Set<String> indexedClasses = new HashSet<>(); | 29 | private final Set<String> indexedClasses = new HashSet<>(); |
| @@ -40,6 +37,7 @@ public class JarIndex implements JarIndexer { | |||
| 40 | private final Collection<JarIndexer> indexers; | 37 | private final Collection<JarIndexer> indexers; |
| 41 | 38 | ||
| 42 | private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create(); | 39 | private final Multimap<String, MethodDefEntry> methodImplementations = HashMultimap.create(); |
| 40 | private final Map<ClassEntry, List<ParentedEntry>> childrenByClass; | ||
| 43 | 41 | ||
| 44 | public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, PackageVisibilityIndex packageVisibilityIndex) { | 42 | public JarIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex, BridgeMethodIndex bridgeMethodIndex, PackageVisibilityIndex packageVisibilityIndex) { |
| 45 | this.entryIndex = entryIndex; | 43 | this.entryIndex = entryIndex; |
| @@ -49,6 +47,7 @@ public class JarIndex implements JarIndexer { | |||
| 49 | this.packageVisibilityIndex = packageVisibilityIndex; | 47 | this.packageVisibilityIndex = packageVisibilityIndex; |
| 50 | this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); | 48 | this.indexers = Arrays.asList(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex, packageVisibilityIndex); |
| 51 | this.entryResolver = new IndexEntryResolver(this); | 49 | this.entryResolver = new IndexEntryResolver(this); |
| 50 | this.childrenByClass = new HashMap<>(); | ||
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | public static JarIndex empty() { | 53 | public static JarIndex empty() { |
| @@ -101,6 +100,11 @@ public class JarIndex implements JarIndexer { | |||
| 101 | } | 100 | } |
| 102 | 101 | ||
| 103 | indexers.forEach(indexer -> indexer.indexClass(classEntry)); | 102 | indexers.forEach(indexer -> indexer.indexClass(classEntry)); |
| 103 | childrenByClass.putIfAbsent(classEntry, new ArrayList<>()); | ||
| 104 | if (classEntry.isInnerClass() && !classEntry.getAccess().isSynthetic()) { | ||
| 105 | childrenByClass.putIfAbsent(classEntry.getParent(), new ArrayList<>()); | ||
| 106 | childrenByClass.get(classEntry.getParent()).add(classEntry); | ||
| 107 | } | ||
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | @Override | 110 | @Override |
| @@ -110,6 +114,10 @@ public class JarIndex implements JarIndexer { | |||
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | indexers.forEach(indexer -> indexer.indexField(fieldEntry)); | 116 | indexers.forEach(indexer -> indexer.indexField(fieldEntry)); |
| 117 | if (!fieldEntry.getAccess().isSynthetic()) { | ||
| 118 | childrenByClass.putIfAbsent(fieldEntry.getParent(), new ArrayList<>()); | ||
| 119 | childrenByClass.get(fieldEntry.getParent()).add(fieldEntry); | ||
| 120 | } | ||
| 113 | } | 121 | } |
| 114 | 122 | ||
| 115 | @Override | 123 | @Override |
| @@ -119,6 +127,10 @@ public class JarIndex implements JarIndexer { | |||
| 119 | } | 127 | } |
| 120 | 128 | ||
| 121 | indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); | 129 | indexers.forEach(indexer -> indexer.indexMethod(methodEntry)); |
| 130 | if (!methodEntry.getAccess().isSynthetic() && !methodEntry.getName().equals("<clinit>")) { | ||
| 131 | childrenByClass.putIfAbsent(methodEntry.getParent(), new ArrayList<>()); | ||
| 132 | childrenByClass.get(methodEntry.getParent()).add(methodEntry); | ||
| 133 | } | ||
| 122 | 134 | ||
| 123 | if (!methodEntry.isConstructor()) { | 135 | if (!methodEntry.isConstructor()) { |
| 124 | methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry); | 136 | methodImplementations.put(methodEntry.getParent().getFullName(), methodEntry); |
| @@ -176,6 +188,10 @@ public class JarIndex implements JarIndexer { | |||
| 176 | return entryResolver; | 188 | return entryResolver; |
| 177 | } | 189 | } |
| 178 | 190 | ||
| 191 | public Map<ClassEntry, List<ParentedEntry>> getChildrenByClass() { | ||
| 192 | return this.childrenByClass; | ||
| 193 | } | ||
| 194 | |||
| 179 | public boolean isIndexed(String internalName) { | 195 | public boolean isIndexed(String internalName) { |
| 180 | return indexedClasses.contains(internalName); | 196 | return indexedClasses.contains(internalName); |
| 181 | } | 197 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java index a7dccfc..6a1b82f 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java | |||
| @@ -235,14 +235,14 @@ public class TypeDescriptor implements Translatable { | |||
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | public enum Primitive { | 237 | public enum Primitive { |
| 238 | BYTE('B'), | 238 | BYTE('B', "byte"), |
| 239 | CHARACTER('C'), | 239 | CHARACTER('C', "char"), |
| 240 | SHORT('S'), | 240 | SHORT('S', "short"), |
| 241 | INTEGER('I'), | 241 | INTEGER('I', "int"), |
| 242 | LONG('J'), | 242 | LONG('J', "long"), |
| 243 | FLOAT('F'), | 243 | FLOAT('F', "float"), |
| 244 | DOUBLE('D'), | 244 | DOUBLE('D', "double"), |
| 245 | BOOLEAN('Z'); | 245 | BOOLEAN('Z', "boolean"); |
| 246 | 246 | ||
| 247 | private static final Map<Character, Primitive> lookup; | 247 | private static final Map<Character, Primitive> lookup; |
| 248 | 248 | ||
| @@ -254,9 +254,11 @@ public class TypeDescriptor implements Translatable { | |||
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | private char code; | 256 | private char code; |
| 257 | private String keyword; | ||
| 257 | 258 | ||
| 258 | Primitive(char code) { | 259 | Primitive(char code, String keyword) { |
| 259 | this.code = code; | 260 | this.code = code; |
| 261 | this.keyword = keyword; | ||
| 260 | } | 262 | } |
| 261 | 263 | ||
| 262 | public static Primitive get(char code) { | 264 | public static Primitive get(char code) { |
| @@ -266,5 +268,12 @@ public class TypeDescriptor implements Translatable { | |||
| 266 | public char getCode() { | 268 | public char getCode() { |
| 267 | return this.code; | 269 | return this.code; |
| 268 | } | 270 | } |
| 271 | |||
| 272 | /** | ||
| 273 | * Returns the Java keyword corresponding to this primitive. | ||
| 274 | */ | ||
| 275 | public String getKeyword() { | ||
| 276 | return this.keyword; | ||
| 277 | } | ||
| 269 | } | 278 | } |
| 270 | } | 279 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java index 4a50021..b4a22f1 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java | |||
| @@ -134,6 +134,9 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable< | |||
| 134 | return name; | 134 | return name; |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | /** | ||
| 138 | * Returns whether this class entry has a parent, and therefore is an inner class. | ||
| 139 | */ | ||
| 137 | public boolean isInnerClass() { | 140 | public boolean isInnerClass() { |
| 138 | return parent != null; | 141 | return parent != null; |
| 139 | } | 142 | } |
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java index ff392fe..6fd412a 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java | |||
| @@ -29,6 +29,13 @@ public interface Entry<P extends Entry<?>> extends Translatable { | |||
| 29 | return getName(); | 29 | return getName(); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | /** | ||
| 33 | * Returns the parent entry of this entry. | ||
| 34 | * | ||
| 35 | * <p>The parent entry should be a {@linkplain MethodEntry method} for local variables, | ||
| 36 | * a {@linkplain ClassEntry class} for methods, fields and inner classes, and {@code null} | ||
| 37 | * for other classes.</p> | ||
| 38 | */ | ||
| 32 | @Nullable | 39 | @Nullable |
| 33 | P getParent(); | 40 | P getParent(); |
| 34 | 41 | ||
diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index 9db4e1f..8195bb1 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json | |||
| @@ -101,6 +101,8 @@ | |||
| 101 | "info_panel.identifier.index": "Index", | 101 | "info_panel.identifier.index": "Index", |
| 102 | "info_panel.editor.class.decompiling": "(decompiling...)", | 102 | "info_panel.editor.class.decompiling": "(decompiling...)", |
| 103 | "info_panel.editor.class.not_found": "Unable to find class:", | 103 | "info_panel.editor.class.not_found": "Unable to find class:", |
| 104 | "info_panel.tree.structure": "Structure", | ||
| 105 | "info_panel.tree.structure.hide_deobfuscated": "Hide deobfuscated members", | ||
| 104 | "info_panel.tree.inheritance": "Inheritance", | 106 | "info_panel.tree.inheritance": "Inheritance", |
| 105 | "info_panel.tree.implementations": "Implementations", | 107 | "info_panel.tree.implementations": "Implementations", |
| 106 | "info_panel.tree.calls": "Call Graph", | 108 | "info_panel.tree.calls": "Call Graph", |
diff --git a/enigma/src/main/resources/lang/fr_fr.json b/enigma/src/main/resources/lang/fr_fr.json index 127b9c8..43bea4d 100644 --- a/enigma/src/main/resources/lang/fr_fr.json +++ b/enigma/src/main/resources/lang/fr_fr.json | |||
| @@ -101,6 +101,8 @@ | |||
| 101 | "info_panel.identifier.index": "Index", | 101 | "info_panel.identifier.index": "Index", |
| 102 | "info_panel.editor.class.decompiling": "(décompilation...)", | 102 | "info_panel.editor.class.decompiling": "(décompilation...)", |
| 103 | "info_panel.editor.class.not_found": "Impossible de trouver la classe :", | 103 | "info_panel.editor.class.not_found": "Impossible de trouver la classe :", |
| 104 | "info_panel.tree.structure": "Structure", | ||
| 105 | "info_panel.tree.structure.hide_deobfuscated": "Masquer les membres déobfusqués", | ||
| 104 | "info_panel.tree.inheritance": "Héritage", | 106 | "info_panel.tree.inheritance": "Héritage", |
| 105 | "info_panel.tree.implementations": "Implémentations", | 107 | "info_panel.tree.implementations": "Implémentations", |
| 106 | "info_panel.tree.calls": "Graphique des appels", | 108 | "info_panel.tree.calls": "Graphique des appels", |