diff options
| author | 2015-03-08 13:49:44 -0400 | |
|---|---|---|
| committer | 2015-03-08 13:49:44 -0400 | |
| commit | 61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b (patch) | |
| tree | 9d10cec3141887ebf1a387ad4f669b1b45e4dc8e /src/cuchaz/enigma | |
| parent | Closing branch: default (diff) | |
| download | enigma-fork-61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b.tar.gz enigma-fork-61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b.tar.xz enigma-fork-61eb14f65e73a9b3d0ea6eca6b04da804a4ff61b.zip | |
lots of small tweaks and improvements
Diffstat (limited to 'src/cuchaz/enigma')
| -rw-r--r-- | src/cuchaz/enigma/analysis/SourceIndex.java | 4 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/ClassIdentity.java | 19 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/ClassSelector.java | 142 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/ClassSelectorClassNode.java | 4 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/DecoratedClassEntry.java | 20 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/Gui.java | 50 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/GuiTricks.java | 58 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/MatchingGui.java | 208 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/ScoredClassEntry.java | 20 |
9 files changed, 378 insertions, 147 deletions
diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index e31b803..b3fb751 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java | |||
| @@ -146,6 +146,10 @@ public class SourceIndex { | |||
| 146 | return m_declarationToToken.values(); | 146 | return m_declarationToToken.values(); |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | public Iterable<Entry> declarations() { | ||
| 150 | return m_declarationToToken.keySet(); | ||
| 151 | } | ||
| 152 | |||
| 149 | public Token getDeclarationToken(Entry deobfEntry) { | 153 | public Token getDeclarationToken(Entry deobfEntry) { |
| 150 | return m_declarationToToken.get(deobfEntry); | 154 | return m_declarationToToken.get(deobfEntry); |
| 151 | } | 155 | } |
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 3736a53..d07e0a4 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java | |||
| @@ -69,6 +69,7 @@ public class ClassIdentity { | |||
| 69 | private Multiset<String> m_implements; | 69 | private Multiset<String> m_implements; |
| 70 | private Multiset<String> m_implementations; | 70 | private Multiset<String> m_implementations; |
| 71 | private Multiset<String> m_references; | 71 | private Multiset<String> m_references; |
| 72 | private String m_outer; | ||
| 72 | 73 | ||
| 73 | private final ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { | 74 | private final ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { |
| 74 | 75 | ||
| @@ -167,6 +168,8 @@ public class ClassIdentity { | |||
| 167 | } | 168 | } |
| 168 | } | 169 | } |
| 169 | } | 170 | } |
| 171 | |||
| 172 | m_outer = EntryFactory.getClassEntry(c).getOuterClassName(); | ||
| 170 | } | 173 | } |
| 171 | 174 | ||
| 172 | private void addReference(EntryReference<? extends Entry,BehaviorEntry> reference) { | 175 | private void addReference(EntryReference<? extends Entry,BehaviorEntry> reference) { |
| @@ -234,6 +237,9 @@ public class ClassIdentity { | |||
| 234 | buf.append(reference); | 237 | buf.append(reference); |
| 235 | buf.append("\n"); | 238 | buf.append("\n"); |
| 236 | } | 239 | } |
| 240 | buf.append("\touter "); | ||
| 241 | buf.append(m_outer); | ||
| 242 | buf.append("\n"); | ||
| 237 | return buf.toString(); | 243 | return buf.toString(); |
| 238 | } | 244 | } |
| 239 | 245 | ||
| @@ -402,13 +408,15 @@ public class ClassIdentity { | |||
| 402 | } | 408 | } |
| 403 | 409 | ||
| 404 | public int getMatchScore(ClassIdentity other) { | 410 | public int getMatchScore(ClassIdentity other) { |
| 405 | return getNumMatches(m_fields, other.m_fields) | 411 | return 2*getNumMatches(m_extends, other.m_extends) |
| 412 | + 2*getNumMatches(m_outer, other.m_outer) | ||
| 413 | + getNumMatches(m_fields, other.m_fields) | ||
| 406 | + getNumMatches(m_methods, other.m_methods) | 414 | + getNumMatches(m_methods, other.m_methods) |
| 407 | + getNumMatches(m_constructors, other.m_constructors); | 415 | + getNumMatches(m_constructors, other.m_constructors); |
| 408 | } | 416 | } |
| 409 | 417 | ||
| 410 | public int getMaxMatchScore() { | 418 | public int getMaxMatchScore() { |
| 411 | return m_fields.size() + m_methods.size() + m_constructors.size(); | 419 | return 2 + 2 + m_fields.size() + m_methods.size() + m_constructors.size(); |
| 412 | } | 420 | } |
| 413 | 421 | ||
| 414 | public boolean matches(CtClass c) { | 422 | public boolean matches(CtClass c) { |
| @@ -427,4 +435,11 @@ public class ClassIdentity { | |||
| 427 | } | 435 | } |
| 428 | return numMatches; | 436 | return numMatches; |
| 429 | } | 437 | } |
| 438 | |||
| 439 | private int getNumMatches(String a, String b) { | ||
| 440 | if (a.equals(b)) { | ||
| 441 | return 1; | ||
| 442 | } | ||
| 443 | return 0; | ||
| 444 | } | ||
| 430 | } | 445 | } |
diff --git a/src/cuchaz/enigma/gui/ClassSelector.java b/src/cuchaz/enigma/gui/ClassSelector.java index e5f550b..2a63675 100644 --- a/src/cuchaz/enigma/gui/ClassSelector.java +++ b/src/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -15,6 +15,7 @@ import java.awt.event.MouseEvent; | |||
| 15 | import java.util.Collection; | 15 | import java.util.Collection; |
| 16 | import java.util.Collections; | 16 | import java.util.Collections; |
| 17 | import java.util.Comparator; | 17 | import java.util.Comparator; |
| 18 | import java.util.Enumeration; | ||
| 18 | import java.util.List; | 19 | import java.util.List; |
| 19 | import java.util.Map; | 20 | import java.util.Map; |
| 20 | 21 | ||
| @@ -41,19 +42,12 @@ public class ClassSelector extends JTree { | |||
| 41 | public static Comparator<ClassEntry> ObfuscatedClassEntryComparator; | 42 | public static Comparator<ClassEntry> ObfuscatedClassEntryComparator; |
| 42 | public static Comparator<ClassEntry> DeobfuscatedClassEntryComparator; | 43 | public static Comparator<ClassEntry> DeobfuscatedClassEntryComparator; |
| 43 | 44 | ||
| 44 | private static String getClassEntryDisplayName(ClassEntry entry) { | ||
| 45 | if (entry instanceof DecoratedClassEntry) { | ||
| 46 | return ((DecoratedClassEntry)entry).getDecoration() + entry.getName(); | ||
| 47 | } | ||
| 48 | return entry.getName(); | ||
| 49 | } | ||
| 50 | |||
| 51 | static { | 45 | static { |
| 52 | ObfuscatedClassEntryComparator = new Comparator<ClassEntry>() { | 46 | ObfuscatedClassEntryComparator = new Comparator<ClassEntry>() { |
| 53 | @Override | 47 | @Override |
| 54 | public int compare(ClassEntry a, ClassEntry b) { | 48 | public int compare(ClassEntry a, ClassEntry b) { |
| 55 | String aname = getClassEntryDisplayName(a); | 49 | String aname = a.getName(); |
| 56 | String bname = getClassEntryDisplayName(b); | 50 | String bname = a.getName(); |
| 57 | if (aname.length() != bname.length()) { | 51 | if (aname.length() != bname.length()) { |
| 58 | return aname.length() - bname.length(); | 52 | return aname.length() - bname.length(); |
| 59 | } | 53 | } |
| @@ -64,9 +58,13 @@ public class ClassSelector extends JTree { | |||
| 64 | DeobfuscatedClassEntryComparator = new Comparator<ClassEntry>() { | 58 | DeobfuscatedClassEntryComparator = new Comparator<ClassEntry>() { |
| 65 | @Override | 59 | @Override |
| 66 | public int compare(ClassEntry a, ClassEntry b) { | 60 | public int compare(ClassEntry a, ClassEntry b) { |
| 67 | String aname = getClassEntryDisplayName(a); | 61 | if (a instanceof ScoredClassEntry && b instanceof ScoredClassEntry) { |
| 68 | String bname = getClassEntryDisplayName(b); | 62 | return Float.compare( |
| 69 | return aname.compareTo(bname); | 63 | ((ScoredClassEntry)b).getScore(), |
| 64 | ((ScoredClassEntry)a).getScore() | ||
| 65 | ); | ||
| 66 | } | ||
| 67 | return a.getName().compareTo(b.getName()); | ||
| 70 | } | 68 | } |
| 71 | }; | 69 | }; |
| 72 | } | 70 | } |
| @@ -172,4 +170,124 @@ public class ClassSelector extends JTree { | |||
| 172 | // finally, update the tree control | 170 | // finally, update the tree control |
| 173 | setModel(new DefaultTreeModel(root)); | 171 | setModel(new DefaultTreeModel(root)); |
| 174 | } | 172 | } |
| 173 | |||
| 174 | public ClassEntry getSelectedClass() { | ||
| 175 | if (!isSelectionEmpty()) { | ||
| 176 | Object selectedNode = getSelectionPath().getLastPathComponent(); | ||
| 177 | if (selectedNode instanceof ClassSelectorClassNode) { | ||
| 178 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | ||
| 179 | return classNode.getClassEntry(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | return null; | ||
| 183 | } | ||
| 184 | |||
| 185 | public String getSelectedPackage() { | ||
| 186 | if (!isSelectionEmpty()) { | ||
| 187 | Object selectedNode = getSelectionPath().getLastPathComponent(); | ||
| 188 | if (selectedNode instanceof ClassSelectorPackageNode) { | ||
| 189 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)selectedNode; | ||
| 190 | return packageNode.getPackageName(); | ||
| 191 | } else if (selectedNode instanceof ClassSelectorClassNode) { | ||
| 192 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)selectedNode; | ||
| 193 | return classNode.getClassEntry().getPackageName(); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | return null; | ||
| 197 | } | ||
| 198 | |||
| 199 | public Iterable<ClassSelectorPackageNode> packageNodes() { | ||
| 200 | List<ClassSelectorPackageNode> nodes = Lists.newArrayList(); | ||
| 201 | DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); | ||
| 202 | Enumeration<?> children = root.children(); | ||
| 203 | while (children.hasMoreElements()) { | ||
| 204 | ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode)children.nextElement(); | ||
| 205 | nodes.add(packageNode); | ||
| 206 | } | ||
| 207 | return nodes; | ||
| 208 | } | ||
| 209 | |||
| 210 | public Iterable<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { | ||
| 211 | List<ClassSelectorClassNode> nodes = Lists.newArrayList(); | ||
| 212 | Enumeration<?> children = packageNode.children(); | ||
| 213 | while (children.hasMoreElements()) { | ||
| 214 | ClassSelectorClassNode classNode = (ClassSelectorClassNode)children.nextElement(); | ||
| 215 | nodes.add(classNode); | ||
| 216 | } | ||
| 217 | return nodes; | ||
| 218 | } | ||
| 219 | |||
| 220 | public void expandPackage(String packageName) { | ||
| 221 | if (packageName == null) { | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 225 | if (packageNode.getPackageName().equals(packageName)) { | ||
| 226 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | ||
| 227 | return; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | public void expandAll() { | ||
| 233 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 234 | expandPath(new TreePath(new Object[] {getModel().getRoot(), packageNode})); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | public ClassEntry getFirstClass() { | ||
| 239 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 240 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | ||
| 241 | return classNode.getClassEntry(); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | return null; | ||
| 245 | } | ||
| 246 | |||
| 247 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { | ||
| 248 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 249 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | ||
| 250 | return packageNode; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | return null; | ||
| 254 | } | ||
| 255 | |||
| 256 | public ClassEntry getNextClass(ClassEntry entry) { | ||
| 257 | boolean foundIt = false; | ||
| 258 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 259 | if (!foundIt) { | ||
| 260 | // skip to the package with our target in it | ||
| 261 | if (packageNode.getPackageName().equals(entry.getPackageName())) { | ||
| 262 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | ||
| 263 | if (!foundIt) { | ||
| 264 | if (classNode.getClassEntry().equals(entry)) { | ||
| 265 | foundIt = true; | ||
| 266 | } | ||
| 267 | } else { | ||
| 268 | // return the next class | ||
| 269 | return classNode.getClassEntry(); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } else { | ||
| 274 | // return the next class | ||
| 275 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | ||
| 276 | return classNode.getClassEntry(); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | } | ||
| 280 | return null; | ||
| 281 | } | ||
| 282 | |||
| 283 | public void setSelectionClass(ClassEntry classEntry) { | ||
| 284 | expandPackage(classEntry.getPackageName()); | ||
| 285 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | ||
| 286 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | ||
| 287 | if (classNode.getClassEntry().equals(classEntry)) { | ||
| 288 | setSelectionPath(new TreePath(new Object[] {getModel().getRoot(), packageNode, classNode})); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 175 | } | 293 | } |
diff --git a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java index f054188..6c1c9a0 100644 --- a/src/cuchaz/enigma/gui/ClassSelectorClassNode.java +++ b/src/cuchaz/enigma/gui/ClassSelectorClassNode.java | |||
| @@ -30,8 +30,8 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { | |||
| 30 | 30 | ||
| 31 | @Override | 31 | @Override |
| 32 | public String toString() { | 32 | public String toString() { |
| 33 | if (m_classEntry instanceof DecoratedClassEntry) { | 33 | if (m_classEntry instanceof ScoredClassEntry) { |
| 34 | return ((DecoratedClassEntry)m_classEntry).getDecoration() + m_classEntry.getSimpleName(); | 34 | return String.format("%d%% %s", (int)((ScoredClassEntry)m_classEntry).getScore(), m_classEntry.getSimpleName()); |
| 35 | } | 35 | } |
| 36 | return m_classEntry.getSimpleName(); | 36 | return m_classEntry.getSimpleName(); |
| 37 | } | 37 | } |
diff --git a/src/cuchaz/enigma/gui/DecoratedClassEntry.java b/src/cuchaz/enigma/gui/DecoratedClassEntry.java deleted file mode 100644 index dd8b4fa..0000000 --- a/src/cuchaz/enigma/gui/DecoratedClassEntry.java +++ /dev/null | |||
| @@ -1,20 +0,0 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 4 | |||
| 5 | |||
| 6 | public class DecoratedClassEntry extends ClassEntry { | ||
| 7 | |||
| 8 | private static final long serialVersionUID = -8798725308554217105L; | ||
| 9 | |||
| 10 | private String m_decoration; | ||
| 11 | |||
| 12 | public DecoratedClassEntry(ClassEntry other, String decoration) { | ||
| 13 | super(other); | ||
| 14 | m_decoration = decoration; | ||
| 15 | } | ||
| 16 | |||
| 17 | public String getDecoration() { | ||
| 18 | return m_decoration; | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index a214243..38a6ee9 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java | |||
| @@ -16,7 +16,6 @@ import java.awt.Container; | |||
| 16 | import java.awt.Dimension; | 16 | import java.awt.Dimension; |
| 17 | import java.awt.FlowLayout; | 17 | import java.awt.FlowLayout; |
| 18 | import java.awt.GridLayout; | 18 | import java.awt.GridLayout; |
| 19 | import java.awt.Rectangle; | ||
| 20 | import java.awt.event.ActionEvent; | 19 | import java.awt.event.ActionEvent; |
| 21 | import java.awt.event.ActionListener; | 20 | import java.awt.event.ActionListener; |
| 22 | import java.awt.event.InputEvent; | 21 | import java.awt.event.InputEvent; |
| @@ -54,8 +53,6 @@ import javax.swing.JTextField; | |||
| 54 | import javax.swing.JTree; | 53 | import javax.swing.JTree; |
| 55 | import javax.swing.KeyStroke; | 54 | import javax.swing.KeyStroke; |
| 56 | import javax.swing.ListSelectionModel; | 55 | import javax.swing.ListSelectionModel; |
| 57 | import javax.swing.SwingUtilities; | ||
| 58 | import javax.swing.Timer; | ||
| 59 | import javax.swing.WindowConstants; | 56 | import javax.swing.WindowConstants; |
| 60 | import javax.swing.event.CaretEvent; | 57 | import javax.swing.event.CaretEvent; |
| 61 | import javax.swing.event.CaretListener; | 58 | import javax.swing.event.CaretListener; |
| @@ -740,52 +737,7 @@ public class Gui { | |||
| 740 | if (token == null) { | 737 | if (token == null) { |
| 741 | throw new IllegalArgumentException("Token cannot be null!"); | 738 | throw new IllegalArgumentException("Token cannot be null!"); |
| 742 | } | 739 | } |
| 743 | 740 | GuiTricks.navigateToToken(m_editor, token, m_selectionHighlightPainter); | |
| 744 | // set the caret position to the token | ||
| 745 | m_editor.setCaretPosition(token.start); | ||
| 746 | m_editor.grabFocus(); | ||
| 747 | |||
| 748 | try { | ||
| 749 | // make sure the token is visible in the scroll window | ||
| 750 | Rectangle start = m_editor.modelToView(token.start); | ||
| 751 | Rectangle end = m_editor.modelToView(token.end); | ||
| 752 | final Rectangle show = start.union(end); | ||
| 753 | show.grow(start.width * 10, start.height * 6); | ||
| 754 | SwingUtilities.invokeLater(new Runnable() { | ||
| 755 | @Override | ||
| 756 | public void run() { | ||
| 757 | m_editor.scrollRectToVisible(show); | ||
| 758 | } | ||
| 759 | }); | ||
| 760 | } catch (BadLocationException ex) { | ||
| 761 | throw new Error(ex); | ||
| 762 | } | ||
| 763 | |||
| 764 | // highlight the token momentarily | ||
| 765 | final Timer timer = new Timer(200, new ActionListener() { | ||
| 766 | private int m_counter = 0; | ||
| 767 | private Object m_highlight = null; | ||
| 768 | |||
| 769 | @Override | ||
| 770 | public void actionPerformed(ActionEvent event) { | ||
| 771 | if (m_counter % 2 == 0) { | ||
| 772 | try { | ||
| 773 | m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter); | ||
| 774 | } catch (BadLocationException ex) { | ||
| 775 | // don't care | ||
| 776 | } | ||
| 777 | } else if (m_highlight != null) { | ||
| 778 | m_editor.getHighlighter().removeHighlight(m_highlight); | ||
| 779 | } | ||
| 780 | |||
| 781 | if (m_counter++ > 6) { | ||
| 782 | Timer timer = (Timer)event.getSource(); | ||
| 783 | timer.stop(); | ||
| 784 | } | ||
| 785 | } | ||
| 786 | }); | ||
| 787 | timer.start(); | ||
| 788 | |||
| 789 | redraw(); | 741 | redraw(); |
| 790 | } | 742 | } |
| 791 | 743 | ||
diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index df9e221..5a3a01d 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java | |||
| @@ -11,11 +11,21 @@ | |||
| 11 | package cuchaz.enigma.gui; | 11 | package cuchaz.enigma.gui; |
| 12 | 12 | ||
| 13 | import java.awt.Font; | 13 | import java.awt.Font; |
| 14 | import java.awt.Rectangle; | ||
| 15 | import java.awt.event.ActionEvent; | ||
| 16 | import java.awt.event.ActionListener; | ||
| 14 | import java.awt.event.MouseEvent; | 17 | import java.awt.event.MouseEvent; |
| 15 | 18 | ||
| 16 | import javax.swing.JComponent; | 19 | import javax.swing.JComponent; |
| 20 | import javax.swing.JEditorPane; | ||
| 17 | import javax.swing.JLabel; | 21 | import javax.swing.JLabel; |
| 22 | import javax.swing.SwingUtilities; | ||
| 23 | import javax.swing.Timer; | ||
| 18 | import javax.swing.ToolTipManager; | 24 | import javax.swing.ToolTipManager; |
| 25 | import javax.swing.text.BadLocationException; | ||
| 26 | import javax.swing.text.Highlighter.HighlightPainter; | ||
| 27 | |||
| 28 | import cuchaz.enigma.analysis.Token; | ||
| 19 | 29 | ||
| 20 | public class GuiTricks { | 30 | public class GuiTricks { |
| 21 | 31 | ||
| @@ -33,4 +43,52 @@ public class GuiTricks { | |||
| 33 | manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); | 43 | manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); |
| 34 | manager.setInitialDelay(oldDelay); | 44 | manager.setInitialDelay(oldDelay); |
| 35 | } | 45 | } |
| 46 | |||
| 47 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { | ||
| 48 | |||
| 49 | // set the caret position to the token | ||
| 50 | editor.setCaretPosition(token.start); | ||
| 51 | editor.grabFocus(); | ||
| 52 | |||
| 53 | try { | ||
| 54 | // make sure the token is visible in the scroll window | ||
| 55 | Rectangle start = editor.modelToView(token.start); | ||
| 56 | Rectangle end = editor.modelToView(token.end); | ||
| 57 | final Rectangle show = start.union(end); | ||
| 58 | show.grow(start.width * 10, start.height * 6); | ||
| 59 | SwingUtilities.invokeLater(new Runnable() { | ||
| 60 | @Override | ||
| 61 | public void run() { | ||
| 62 | editor.scrollRectToVisible(show); | ||
| 63 | } | ||
| 64 | }); | ||
| 65 | } catch (BadLocationException ex) { | ||
| 66 | throw new Error(ex); | ||
| 67 | } | ||
| 68 | |||
| 69 | // highlight the token momentarily | ||
| 70 | final Timer timer = new Timer(200, new ActionListener() { | ||
| 71 | private int m_counter = 0; | ||
| 72 | private Object m_highlight = null; | ||
| 73 | |||
| 74 | @Override | ||
| 75 | public void actionPerformed(ActionEvent event) { | ||
| 76 | if (m_counter % 2 == 0) { | ||
| 77 | try { | ||
| 78 | m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter); | ||
| 79 | } catch (BadLocationException ex) { | ||
| 80 | // don't care | ||
| 81 | } | ||
| 82 | } else if (m_highlight != null) { | ||
| 83 | editor.getHighlighter().removeHighlight(m_highlight); | ||
| 84 | } | ||
| 85 | |||
| 86 | if (m_counter++ > 6) { | ||
| 87 | Timer timer = (Timer)event.getSource(); | ||
| 88 | timer.stop(); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | }); | ||
| 92 | timer.start(); | ||
| 93 | } | ||
| 36 | } | 94 | } |
diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index f1da25a..1e618d0 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java | |||
| @@ -10,7 +10,6 @@ import java.util.ArrayList; | |||
| 10 | import java.util.Arrays; | 10 | import java.util.Arrays; |
| 11 | import java.util.Collection; | 11 | import java.util.Collection; |
| 12 | import java.util.Collections; | 12 | import java.util.Collections; |
| 13 | import java.util.Enumeration; | ||
| 14 | import java.util.List; | 13 | import java.util.List; |
| 15 | import java.util.Map; | 14 | import java.util.Map; |
| 16 | 15 | ||
| @@ -27,17 +26,18 @@ import javax.swing.JScrollPane; | |||
| 27 | import javax.swing.JSplitPane; | 26 | import javax.swing.JSplitPane; |
| 28 | import javax.swing.SwingConstants; | 27 | import javax.swing.SwingConstants; |
| 29 | import javax.swing.WindowConstants; | 28 | import javax.swing.WindowConstants; |
| 30 | import javax.swing.tree.DefaultMutableTreeNode; | ||
| 31 | import javax.swing.tree.TreePath; | ||
| 32 | 29 | ||
| 33 | import com.beust.jcommander.internal.Lists; | 30 | import com.beust.jcommander.internal.Lists; |
| 34 | import com.beust.jcommander.internal.Maps; | 31 | import com.beust.jcommander.internal.Maps; |
| 35 | import com.google.common.collect.ArrayListMultimap; | 32 | import com.google.common.collect.ArrayListMultimap; |
| 36 | import com.google.common.collect.BiMap; | 33 | import com.google.common.collect.BiMap; |
| 37 | import com.google.common.collect.Multimap; | 34 | import com.google.common.collect.Multimap; |
| 35 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 38 | 36 | ||
| 39 | import cuchaz.enigma.Constants; | 37 | import cuchaz.enigma.Constants; |
| 40 | import cuchaz.enigma.Deobfuscator; | 38 | import cuchaz.enigma.Deobfuscator; |
| 39 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 40 | import cuchaz.enigma.analysis.Token; | ||
| 41 | import cuchaz.enigma.convert.ClassIdentifier; | 41 | import cuchaz.enigma.convert.ClassIdentifier; |
| 42 | import cuchaz.enigma.convert.ClassIdentity; | 42 | import cuchaz.enigma.convert.ClassIdentity; |
| 43 | import cuchaz.enigma.convert.ClassMatch; | 43 | import cuchaz.enigma.convert.ClassMatch; |
| @@ -47,6 +47,7 @@ import cuchaz.enigma.convert.MappingsConverter; | |||
| 47 | import cuchaz.enigma.convert.Matches; | 47 | import cuchaz.enigma.convert.Matches; |
| 48 | import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; | 48 | import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener; |
| 49 | import cuchaz.enigma.mapping.ClassEntry; | 49 | import cuchaz.enigma.mapping.ClassEntry; |
| 50 | import cuchaz.enigma.mapping.Entry; | ||
| 50 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 51 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 51 | 52 | ||
| 52 | 53 | ||
| @@ -105,6 +106,7 @@ public class MatchingGui { | |||
| 105 | private JButton m_matchButton; | 106 | private JButton m_matchButton; |
| 106 | private Map<SourceType,JRadioButton> m_sourceTypeButtons; | 107 | private Map<SourceType,JRadioButton> m_sourceTypeButtons; |
| 107 | private JCheckBox m_advanceCheck; | 108 | private JCheckBox m_advanceCheck; |
| 109 | private SelectionHighlightPainter m_selectionHighlightPainter; | ||
| 108 | 110 | ||
| 109 | private Matches m_matches; | 111 | private Matches m_matches; |
| 110 | private Deobfuscator m_sourceDeobfuscator; | 112 | private Deobfuscator m_sourceDeobfuscator; |
| @@ -188,12 +190,8 @@ public class MatchingGui { | |||
| 188 | 190 | ||
| 189 | // init source panels | 191 | // init source panels |
| 190 | DefaultSyntaxKit.initKit(); | 192 | DefaultSyntaxKit.initKit(); |
| 191 | m_sourceReader = new JEditorPane(); | 193 | m_sourceReader = makeReader(); |
| 192 | m_sourceReader.setEditable(false); | 194 | m_destReader = makeReader(); |
| 193 | m_sourceReader.setContentType("text/java"); | ||
| 194 | m_destReader = new JEditorPane(); | ||
| 195 | m_destReader.setEditable(false); | ||
| 196 | m_destReader.setContentType("text/java"); | ||
| 197 | 195 | ||
| 198 | // init all the splits | 196 | // init all the splits |
| 199 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); | 197 | JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(m_sourceReader)); |
| @@ -220,6 +218,14 @@ public class MatchingGui { | |||
| 220 | m_matchButton.setPreferredSize(new Dimension(140, 24)); | 218 | m_matchButton.setPreferredSize(new Dimension(140, 24)); |
| 221 | 219 | ||
| 222 | m_advanceCheck = new JCheckBox("Advance to next likely match"); | 220 | m_advanceCheck = new JCheckBox("Advance to next likely match"); |
| 221 | m_advanceCheck.addActionListener(new ActionListener() { | ||
| 222 | @Override | ||
| 223 | public void actionPerformed(ActionEvent event) { | ||
| 224 | if (m_advanceCheck.isSelected()) { | ||
| 225 | advance(); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | }); | ||
| 223 | 229 | ||
| 224 | bottomPanel.add(m_sourceClassLabel); | 230 | bottomPanel.add(m_sourceClassLabel); |
| 225 | bottomPanel.add(m_matchButton); | 231 | bottomPanel.add(m_matchButton); |
| @@ -234,6 +240,8 @@ public class MatchingGui { | |||
| 234 | m_frame.setVisible(true); | 240 | m_frame.setVisible(true); |
| 235 | m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | 241 | m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| 236 | 242 | ||
| 243 | m_selectionHighlightPainter = new SelectionHighlightPainter(); | ||
| 244 | |||
| 237 | // init state | 245 | // init state |
| 238 | updateDestMappings(); | 246 | updateDestMappings(); |
| 239 | setSourceType(SourceType.getDefault()); | 247 | setSourceType(SourceType.getDefault()); |
| @@ -241,6 +249,19 @@ public class MatchingGui { | |||
| 241 | m_saveListener = null; | 249 | m_saveListener = null; |
| 242 | } | 250 | } |
| 243 | 251 | ||
| 252 | private JEditorPane makeReader() { | ||
| 253 | |||
| 254 | JEditorPane reader = new JEditorPane(); | ||
| 255 | reader.setEditable(false); | ||
| 256 | reader.setContentType("text/java"); | ||
| 257 | |||
| 258 | // turn off token highlighting (it's wrong most of the time anyway...) | ||
| 259 | DefaultSyntaxKit kit = (DefaultSyntaxKit)reader.getEditorKit(); | ||
| 260 | kit.toggleComponent(reader, "de.sciss.syntaxpane.components.TokenMarker"); | ||
| 261 | |||
| 262 | return reader; | ||
| 263 | } | ||
| 264 | |||
| 244 | public void setSaveListener(SaveListener val) { | 265 | public void setSaveListener(SaveListener val) { |
| 245 | m_saveListener = val; | 266 | m_saveListener = val; |
| 246 | } | 267 | } |
| @@ -272,12 +293,24 @@ public class MatchingGui { | |||
| 272 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { | 293 | private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) { |
| 273 | List<ClassEntry> out = Lists.newArrayList(); | 294 | List<ClassEntry> out = Lists.newArrayList(); |
| 274 | for (ClassEntry entry : in) { | 295 | for (ClassEntry entry : in) { |
| 275 | out.add(deobfuscator.deobfuscateEntry(entry)); | 296 | |
| 297 | ClassEntry deobf = deobfuscator.deobfuscateEntry(entry); | ||
| 298 | |||
| 299 | // make sure we preserve any scores | ||
| 300 | if (entry instanceof ScoredClassEntry) { | ||
| 301 | deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry)entry).getScore()); | ||
| 302 | } | ||
| 303 | |||
| 304 | out.add(deobf); | ||
| 276 | } | 305 | } |
| 277 | return out; | 306 | return out; |
| 278 | } | 307 | } |
| 279 | 308 | ||
| 280 | protected void setSourceClass(ClassEntry classEntry) { | 309 | protected void setSourceClass(ClassEntry classEntry) { |
| 310 | setSourceClass(classEntry, null); | ||
| 311 | } | ||
| 312 | |||
| 313 | protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { | ||
| 281 | 314 | ||
| 282 | // update the current source class | 315 | // update the current source class |
| 283 | m_sourceClass = classEntry; | 316 | m_sourceClass = classEntry; |
| @@ -297,20 +330,27 @@ public class MatchingGui { | |||
| 297 | @Override | 330 | @Override |
| 298 | public void run() { | 331 | public void run() { |
| 299 | m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); | 332 | m_destClasses.setClasses(deobfuscateClasses(getLikelyMatches(m_sourceClass), m_destDeobfuscator)); |
| 300 | m_destClasses.expandRow(0); | 333 | m_destClasses.expandAll(); |
| 334 | |||
| 335 | if (onGetDestClasses != null) { | ||
| 336 | onGetDestClasses.run(); | ||
| 337 | } | ||
| 301 | } | 338 | } |
| 302 | }.start(); | 339 | }.start(); |
| 303 | 340 | ||
| 304 | } else { | 341 | } else { |
| 305 | 342 | ||
| 306 | m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); | 343 | m_destClasses.setClasses(deobfuscateClasses(match.destClasses, m_destDeobfuscator)); |
| 307 | m_destClasses.expandRow(0); | 344 | m_destClasses.expandAll(); |
| 308 | 345 | ||
| 346 | if (onGetDestClasses != null) { | ||
| 347 | onGetDestClasses.run(); | ||
| 348 | } | ||
| 309 | } | 349 | } |
| 310 | } | 350 | } |
| 311 | 351 | ||
| 312 | setDestClass(null); | 352 | setDestClass(null); |
| 313 | readSource(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); | 353 | decompileClass(m_sourceClass, m_sourceDeobfuscator, m_sourceReader); |
| 314 | 354 | ||
| 315 | updateMatchButton(); | 355 | updateMatchButton(); |
| 316 | } | 356 | } |
| @@ -334,29 +374,14 @@ public class MatchingGui { | |||
| 334 | 374 | ||
| 335 | // rank all the unmatched dest classes against the source class | 375 | // rank all the unmatched dest classes against the source class |
| 336 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); | 376 | ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass); |
| 337 | Multimap<Float,ClassEntry> scoredDestClasses = ArrayListMultimap.create(); | 377 | List<ClassEntry> scoredDestClasses = Lists.newArrayList(); |
| 338 | for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { | 378 | for (ClassEntry unmatchedDestClass : m_matches.getUnmatchedDestClasses()) { |
| 339 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); | 379 | ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass); |
| 340 | float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) | 380 | float score = 100.0f*(sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity)) |
| 341 | /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); | 381 | /(sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore()); |
| 342 | scoredDestClasses.put(score, unmatchedDestClass); | 382 | scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score)); |
| 343 | } | ||
| 344 | |||
| 345 | // sort by scores | ||
| 346 | List<Float> scores = new ArrayList<Float>(scoredDestClasses.keySet()); | ||
| 347 | Collections.sort(scores, Collections.reverseOrder()); | ||
| 348 | |||
| 349 | // collect the scored classes in order | ||
| 350 | List<ClassEntry> scoredClasses = Lists.newArrayList(); | ||
| 351 | for (float score : scores) { | ||
| 352 | for (ClassEntry classEntry : scoredDestClasses.get(score)) { | ||
| 353 | scoredClasses.add(new DecoratedClassEntry(classEntry, String.format("%2.0f%% ", score))); | ||
| 354 | if (scoredClasses.size() > 10) { | ||
| 355 | return scoredClasses; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | 383 | } |
| 359 | return scoredClasses; | 384 | return scoredDestClasses; |
| 360 | 385 | ||
| 361 | } catch (ClassNotFoundException ex) { | 386 | } catch (ClassNotFoundException ex) { |
| 362 | throw new Error("Unable to find class " + ex.getMessage()); | 387 | throw new Error("Unable to find class " + ex.getMessage()); |
| @@ -369,12 +394,12 @@ public class MatchingGui { | |||
| 369 | m_destClass = classEntry; | 394 | m_destClass = classEntry; |
| 370 | m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); | 395 | m_destClassLabel.setText(m_destClass != null ? m_destClass.getName() : ""); |
| 371 | 396 | ||
| 372 | readSource(m_destClass, m_destDeobfuscator, m_destReader); | 397 | decompileClass(m_destClass, m_destDeobfuscator, m_destReader); |
| 373 | 398 | ||
| 374 | updateMatchButton(); | 399 | updateMatchButton(); |
| 375 | } | 400 | } |
| 376 | 401 | ||
| 377 | protected void readSource(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { | 402 | protected void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final JEditorPane reader) { |
| 378 | 403 | ||
| 379 | if (classEntry == null) { | 404 | if (classEntry == null) { |
| 380 | reader.setText(null); | 405 | reader.setText(null); |
| @@ -389,12 +414,37 @@ public class MatchingGui { | |||
| 389 | public void run() { | 414 | public void run() { |
| 390 | 415 | ||
| 391 | // get the outermost class | 416 | // get the outermost class |
| 392 | ClassEntry obfClassEntry = deobfuscator.obfuscateEntry(classEntry); | 417 | ClassEntry outermostClassEntry = classEntry; |
| 393 | List<ClassEntry> classChain = deobfuscator.getJarIndex().getObfClassChain(obfClassEntry); | 418 | while (outermostClassEntry.isInnerClass()) { |
| 394 | ClassEntry obfOutermostClassEntry = classChain.get(0); | 419 | outermostClassEntry = outermostClassEntry.getOuterClassEntry(); |
| 420 | } | ||
| 395 | 421 | ||
| 396 | // decompile it | 422 | // decompile it |
| 397 | reader.setText(deobfuscator.getSource(deobfuscator.getSourceTree(obfOutermostClassEntry.getName()))); | 423 | CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); |
| 424 | String source = deobfuscator.getSource(sourceTree); | ||
| 425 | reader.setText(source); | ||
| 426 | SourceIndex sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); | ||
| 427 | |||
| 428 | // navigate to the class declaration | ||
| 429 | Token token = sourceIndex.getDeclarationToken(classEntry); | ||
| 430 | if (token == null) { | ||
| 431 | // couldn't find the class declaration token, might be an anonymous class | ||
| 432 | // look for any declaration in that class instead | ||
| 433 | for (Entry entry : sourceIndex.declarations()) { | ||
| 434 | if (entry.getClassEntry().equals(classEntry)) { | ||
| 435 | token = sourceIndex.getDeclarationToken(entry); | ||
| 436 | break; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | if (token != null) { | ||
| 442 | GuiTricks.navigateToToken(reader, token, m_selectionHighlightPainter); | ||
| 443 | } else { | ||
| 444 | // couldn't find anything =( | ||
| 445 | System.out.println("Unable to find declaration in source for " + classEntry); | ||
| 446 | } | ||
| 447 | |||
| 398 | } | 448 | } |
| 399 | }.start(); | 449 | }.start(); |
| 400 | } | 450 | } |
| @@ -459,11 +509,16 @@ public class MatchingGui { | |||
| 459 | // add them as matched classes | 509 | // add them as matched classes |
| 460 | m_matches.add(new ClassMatch(obfSource, obfDest)); | 510 | m_matches.add(new ClassMatch(obfSource, obfDest)); |
| 461 | 511 | ||
| 512 | ClassEntry nextClass = null; | ||
| 513 | if (m_advanceCheck.isSelected()) { | ||
| 514 | nextClass = m_sourceClasses.getNextClass(m_sourceClass); | ||
| 515 | } | ||
| 516 | |||
| 462 | save(); | 517 | save(); |
| 463 | updateMatches(); | 518 | updateMatches(); |
| 464 | 519 | ||
| 465 | if (m_advanceCheck.isSelected()) { | 520 | if (nextClass != null) { |
| 466 | advance(); | 521 | advance(nextClass); |
| 467 | } | 522 | } |
| 468 | } | 523 | } |
| 469 | 524 | ||
| @@ -487,31 +542,11 @@ public class MatchingGui { | |||
| 487 | updateMatchButton(); | 542 | updateMatchButton(); |
| 488 | 543 | ||
| 489 | // remember where we were in the source tree | 544 | // remember where we were in the source tree |
| 490 | String packageName = null; | 545 | String packageName = m_sourceClasses.getSelectedPackage(); |
| 491 | if (!m_sourceClasses.isSelectionEmpty()) { | ||
| 492 | packageName = m_sourceClasses.getSelectionPath().getParentPath().getLastPathComponent().toString(); | ||
| 493 | } | ||
| 494 | 546 | ||
| 495 | setSourceType(m_sourceType); | 547 | setSourceType(m_sourceType); |
| 496 | 548 | ||
| 497 | if (packageName != null) { | 549 | m_sourceClasses.expandPackage(packageName); |
| 498 | // find the corresponding path in the new tree | ||
| 499 | TreePath path = null; | ||
| 500 | DefaultMutableTreeNode root = (DefaultMutableTreeNode)m_sourceClasses.getModel().getRoot(); | ||
| 501 | Enumeration<?> children = root.children(); | ||
| 502 | while (children.hasMoreElements()) { | ||
| 503 | Object child = children.nextElement(); | ||
| 504 | if (child.toString().equals(packageName)) { | ||
| 505 | path = new TreePath(new Object[] {root, child}); | ||
| 506 | break; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | if (path != null) { | ||
| 511 | // put the tree back to where it was | ||
| 512 | m_sourceClasses.expandPath(path); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | 550 | } |
| 516 | 551 | ||
| 517 | private void save() { | 552 | private void save() { |
| @@ -542,6 +577,55 @@ public class MatchingGui { | |||
| 542 | } | 577 | } |
| 543 | 578 | ||
| 544 | private void advance() { | 579 | private void advance() { |
| 545 | // TODO: find a likely match | 580 | advance(null); |
| 581 | } | ||
| 582 | |||
| 583 | private void advance(ClassEntry sourceClass) { | ||
| 584 | |||
| 585 | // make sure we have a source class | ||
| 586 | if (sourceClass == null) { | ||
| 587 | sourceClass = m_sourceClasses.getSelectedClass(); | ||
| 588 | if (sourceClass != null) { | ||
| 589 | sourceClass = m_sourceClasses.getNextClass(sourceClass); | ||
| 590 | } else { | ||
| 591 | sourceClass = m_sourceClasses.getFirstClass(); | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | // set the source class | ||
| 596 | setSourceClass(sourceClass, new Runnable() { | ||
| 597 | @Override | ||
| 598 | public void run() { | ||
| 599 | |||
| 600 | // then, pick the best dest class | ||
| 601 | ClassEntry firstClass = null; | ||
| 602 | ScoredClassEntry bestDestClass = null; | ||
| 603 | for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { | ||
| 604 | for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { | ||
| 605 | if (firstClass == null) { | ||
| 606 | firstClass = classNode.getClassEntry(); | ||
| 607 | } | ||
| 608 | if (classNode.getClassEntry() instanceof ScoredClassEntry) { | ||
| 609 | ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); | ||
| 610 | if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { | ||
| 611 | bestDestClass = scoredClass; | ||
| 612 | } | ||
| 613 | } | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | // pick the entry to show | ||
| 618 | ClassEntry destClass = null; | ||
| 619 | if (bestDestClass != null) { | ||
| 620 | destClass = bestDestClass; | ||
| 621 | } else if (firstClass != null) { | ||
| 622 | destClass = firstClass; | ||
| 623 | } | ||
| 624 | |||
| 625 | setDestClass(destClass); | ||
| 626 | m_destClasses.setSelectionClass(destClass); | ||
| 627 | } | ||
| 628 | }); | ||
| 629 | m_sourceClasses.setSelectionClass(sourceClass); | ||
| 546 | } | 630 | } |
| 547 | } | 631 | } |
diff --git a/src/cuchaz/enigma/gui/ScoredClassEntry.java b/src/cuchaz/enigma/gui/ScoredClassEntry.java new file mode 100644 index 0000000..dd7ba61 --- /dev/null +++ b/src/cuchaz/enigma/gui/ScoredClassEntry.java | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 4 | |||
| 5 | |||
| 6 | public class ScoredClassEntry extends ClassEntry { | ||
| 7 | |||
| 8 | private static final long serialVersionUID = -8798725308554217105L; | ||
| 9 | |||
| 10 | private float m_score; | ||
| 11 | |||
| 12 | public ScoredClassEntry(ClassEntry other, float score) { | ||
| 13 | super(other); | ||
| 14 | m_score = score; | ||
| 15 | } | ||
| 16 | |||
| 17 | public float getScore() { | ||
| 18 | return m_score; | ||
| 19 | } | ||
| 20 | } | ||