From 999c64037fb7251f87bd7b105231b3763e003c07 Mon Sep 17 00:00:00 2001 From: hg Date: Sun, 27 Jul 2014 00:52:28 -0400 Subject: made gui responsive to caret position and show identifier info --- src/cuchaz/enigma/Controller.java | 83 ++++++++ src/cuchaz/enigma/Deobfuscator.java | 5 + src/cuchaz/enigma/Main.java | 33 +--- src/cuchaz/enigma/analysis/SourceIndex.java | 30 ++- src/cuchaz/enigma/gui/ClassSelectionHandler.java | 18 -- src/cuchaz/enigma/gui/ClassSelectionListener.java | 18 ++ src/cuchaz/enigma/gui/Gui.java | 228 +++++++++++++++++++--- src/cuchaz/enigma/gui/RenameListener.java | 18 ++ src/cuchaz/enigma/mapping/ArgumentEntry.java | 3 +- src/cuchaz/enigma/mapping/ClassEntry.java | 3 +- src/cuchaz/enigma/mapping/Entry.java | 16 ++ src/cuchaz/enigma/mapping/FieldEntry.java | 3 +- src/cuchaz/enigma/mapping/MethodEntry.java | 3 +- 13 files changed, 379 insertions(+), 82 deletions(-) create mode 100644 src/cuchaz/enigma/Controller.java delete mode 100644 src/cuchaz/enigma/gui/ClassSelectionHandler.java create mode 100644 src/cuchaz/enigma/gui/ClassSelectionListener.java create mode 100644 src/cuchaz/enigma/gui/RenameListener.java create mode 100644 src/cuchaz/enigma/mapping/Entry.java (limited to 'src') diff --git a/src/cuchaz/enigma/Controller.java b/src/cuchaz/enigma/Controller.java new file mode 100644 index 00000000..debc5e38 --- /dev/null +++ b/src/cuchaz/enigma/Controller.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; + +import cuchaz.enigma.analysis.Analyzer; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.gui.ClassSelectionListener; +import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.RenameListener; +import cuchaz.enigma.mapping.Entry; + +public class Controller implements ClassSelectionListener, CaretListener, RenameListener +{ + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + + public Controller( Deobfuscator deobfuscator, Gui gui ) + { + m_deobfuscator = deobfuscator; + m_gui = gui; + m_index = null; + + // update GUI + gui.setTitle( deobfuscator.getJarName() ); + gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); + + // handle events + gui.setClassSelectionListener( this ); + gui.setCaretListener( this ); + gui.setRenameListener( this ); + } + + @Override + public void classSelected( final ClassFile classFile ) + { + m_gui.setSource( "(deobfuscating...)" ); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread( ) + { + @Override + public void run( ) + { + // deobfuscate the bytecode + String source = m_deobfuscator.getSource( classFile ); + m_gui.setSource( source ); + + // index the source file + m_index = Analyzer.analyze( classFile.getName(), source ); + m_gui.highlightTokens( m_index.tokens() ); + } + }.start(); + } + + @Override + public void caretUpdate( CaretEvent event ) + { + if( m_index != null ) + { + int pos = event.getDot(); + m_gui.showEntry( m_index.getEntry( pos ) ); + } + } + + @Override + public void rename( Entry entry, String newName ) + { + // TEMP + System.out.println( "Rename " + entry + " to " + newName ); + } +} diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 83a21719..97c57505 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -62,6 +62,11 @@ public class Deobfuscator m_settings.setShowSyntheticMembers( true ); } + public String getJarName( ) + { + return m_file.getName(); + } + public List getObfuscatedClasses( ) { List classes = new ArrayList(); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index e08c16ed..4842e208 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -12,9 +12,6 @@ package cuchaz.enigma; import java.io.File; -import cuchaz.enigma.analysis.Analyzer; -import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.gui.ClassSelectionHandler; import cuchaz.enigma.gui.Gui; public class Main @@ -28,36 +25,10 @@ public class Main private static void startGui( ) throws Exception { - final Gui gui = new Gui(); - // settings final File jarFile = new File( "/home/jeff/.minecraft/versions/1.7.10/1.7.10.jar" ); - gui.setTitle( jarFile.getName() ); - - // init the deobfuscator - final Deobfuscator deobfuscator = new Deobfuscator( jarFile ); - gui.setObfClasses( deobfuscator.getObfuscatedClasses() ); - // handle events - gui.setClassSelectionHandler( new ClassSelectionHandler( ) - { - @Override - public void classSelected( final ClassFile classFile ) - { - gui.setSource( "(deobfuscating...)" ); - - // run the deobfuscator in a separate thread so we don't block the GUI event queue - new Thread( ) - { - @Override - public void run( ) - { - String source = deobfuscator.getSource( classFile ); - SourceIndex index = Analyzer.analyze( classFile.getName(), source ); - gui.setSource( source, index ); - } - }.start(); - } - } ); + // start the GUI and tie it to the deobfuscator + new Controller( new Deobfuscator( jarFile ), new Gui() ); } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index a4b5329b..ee92d1ed 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -19,10 +19,12 @@ import jsyntaxpane.Token; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -public class SourceIndex implements Iterable> +import cuchaz.enigma.mapping.Entry; + +public class SourceIndex implements Iterable> { - private BiMap m_entryToToken; - private BiMap m_tokenToEntry; + private BiMap m_entryToToken; + private BiMap m_tokenToEntry; public SourceIndex( ) { @@ -30,12 +32,12 @@ public class SourceIndex implements Iterable> m_tokenToEntry = m_entryToToken.inverse(); } - public void add( Object entry, Token token ) + public void add( Entry entry, Token token ) { m_entryToToken.put( entry, token ); } - public Iterator> iterator( ) + public Iterator> iterator( ) { return m_entryToToken.entrySet().iterator(); } @@ -45,12 +47,26 @@ public class SourceIndex implements Iterable> return m_entryToToken.values(); } - public Object getEntry( Token token ) + public Entry getEntry( Token token ) { return m_tokenToEntry.get( token ); } - public Object getToken( Object entry ) + public Entry getEntry( int pos ) + { + // linear search is fast enough for now + for( Map.Entry entry : this ) + { + Token token = entry.getValue(); + if( pos >= token.start && pos <= token.end() ) + { + return entry.getKey(); + } + } + return null; + } + + public Token getToken( Entry entry ) { return m_entryToToken.get( entry ); } diff --git a/src/cuchaz/enigma/gui/ClassSelectionHandler.java b/src/cuchaz/enigma/gui/ClassSelectionHandler.java deleted file mode 100644 index a50cf6a3..00000000 --- a/src/cuchaz/enigma/gui/ClassSelectionHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - * - * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ -package cuchaz.enigma.gui; - -import cuchaz.enigma.ClassFile; - -public interface ClassSelectionHandler -{ - void classSelected( ClassFile classFile ); -} diff --git a/src/cuchaz/enigma/gui/ClassSelectionListener.java b/src/cuchaz/enigma/gui/ClassSelectionListener.java new file mode 100644 index 00000000..4a2aa8d0 --- /dev/null +++ b/src/cuchaz/enigma/gui/ClassSelectionListener.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.ClassFile; + +public interface ClassSelectionListener +{ + void classSelected( ClassFile classFile ); +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index e0d53d83..d1a3cb21 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -12,11 +12,20 @@ package cuchaz.enigma.gui; import java.awt.BorderLayout; import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import java.util.Vector; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; @@ -24,14 +33,21 @@ import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.WindowConstants; +import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.Token; import cuchaz.enigma.ClassFile; import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.mapping.ArgumentEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; public class Gui { @@ -42,11 +58,18 @@ public class Gui private JList m_obfClasses; private JList m_deobfClasses; private JEditorPane m_editor; + private JPanel m_actionPanel; + private JPanel m_renamePanel; + private JLabel m_typeLabel; + private JTextField m_nameField; + private JButton m_renameButton; - // handlers - private ClassSelectionHandler m_classSelectionHandler; + // listeners + private ClassSelectionListener m_classSelectionListener; + private RenameListener m_renameListener; private BoxHighlightPainter m_highlightPainter; + private Entry m_selectedEntry; public Gui( ) { @@ -66,12 +89,12 @@ public class Gui { if( event.getClickCount() == 2 ) { - if( m_classSelectionHandler != null ) + if( m_classSelectionListener != null ) { ClassFile selected = m_obfClasses.getSelectedValue(); if( selected != null ) { - m_classSelectionHandler.classSelected( selected ); + m_classSelectionListener.classSelected( selected ); } } } @@ -93,6 +116,34 @@ public class Gui deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); deobfPanel.add( deobfScroller, BorderLayout.CENTER ); + // init action panel + m_actionPanel = new JPanel(); + m_actionPanel.setLayout( new BoxLayout( m_actionPanel, BoxLayout.Y_AXIS ) ); + m_actionPanel.setPreferredSize( new Dimension( 0, 120 ) ); + m_actionPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); + m_nameField = new JTextField( 26 ); + m_renameButton = new JButton( "Rename" ); + m_renameButton.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + if( m_renameListener != null && m_selectedEntry != null ) + { + m_renameListener.rename( m_selectedEntry, m_nameField.getText() ); + } + } + } ); + m_renamePanel = new JPanel(); + m_renamePanel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); + m_typeLabel = new JLabel( "LongName:", JLabel.RIGHT ); + // NOTE: this looks ridiculous, but it fixes the label size to the size of current text + m_typeLabel.setPreferredSize( m_typeLabel.getPreferredSize() ); + m_renamePanel.add( m_typeLabel ); + m_renamePanel.add( m_nameField ); + m_renamePanel.add( m_renameButton ); + clearEntry(); + // init editor DefaultSyntaxKit.initKit(); m_editor = new JEditorPane(); @@ -102,17 +153,23 @@ public class Gui // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); - JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, sourceScroller ); + JPanel rightPanel = new JPanel(); + rightPanel.setLayout( new BorderLayout() ); + rightPanel.add( m_actionPanel, BorderLayout.NORTH ); + rightPanel.add( sourceScroller, BorderLayout.CENTER ); + JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); pane.add( splitMain, BorderLayout.CENTER ); // show the frame pane.doLayout(); m_frame.setSize( 800, 600 ); + m_frame.setMinimumSize( new Dimension( 640, 480 ) ); m_frame.setVisible( true ); m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); - // init handlers - m_classSelectionHandler = null; + // init listeners + m_classSelectionListener = null; + m_renameListener = null; m_highlightPainter = new BoxHighlightPainter(); } @@ -135,29 +192,156 @@ public class Gui public void setSource( String source, SourceIndex index ) { m_editor.setText( source ); - + } + + public void highlightTokens( Iterable tokens ) + { // remove any old highlighters - m_editor.getHighlighter().removeAllHighlights();; + m_editor.getHighlighter().removeAllHighlights(); - if( index != null ) + if( tokens == null ) { - // color things based on the index - for( Token token : index.tokens() ) + return; + } + + // color things based on the index + for( Token token : tokens ) + { + try { - try - { - m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); - } - catch( BadLocationException ex ) - { - throw new Error( ex ); - } + m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); + } + catch( BadLocationException ex ) + { + throw new IllegalArgumentException( ex ); } } + + redraw(); + } + + public void setClassSelectionListener( ClassSelectionListener val ) + { + m_classSelectionListener = val; + } + + public void setRenameListener( RenameListener val ) + { + m_renameListener = val; + } + + public void setCaretListener( CaretListener listener ) + { + // remove any old listeners + for( CaretListener oldListener : m_editor.getCaretListeners() ) + { + m_editor.removeCaretListener( oldListener ); + } + + m_editor.addCaretListener( listener ); + } + + public void clearEntry( ) + { + m_actionPanel.removeAll(); + JLabel label = new JLabel( "No identifier selected" ); + unboldLabel( label ); + label.setHorizontalAlignment( JLabel.CENTER ); + m_actionPanel.add( label ); + + redraw(); + } + + public void showEntry( Entry entry ) + { + if( entry == null ) + { + clearEntry(); + return; + } + + // layout the action panel + m_actionPanel.removeAll(); + m_actionPanel.add( m_renamePanel ); + m_nameField.setText( entry.getName() ); + + // layout the dynamic section + JPanel dynamicPanel = new JPanel(); + dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); + m_actionPanel.add( dynamicPanel ); + if( entry instanceof ClassEntry ) + { + showEntry( (ClassEntry)entry, dynamicPanel ); + } + else if( entry instanceof FieldEntry ) + { + showEntry( (FieldEntry)entry, dynamicPanel ); + } + else if( entry instanceof MethodEntry ) + { + showEntry( (MethodEntry)entry, dynamicPanel ); + } + else if( entry instanceof ArgumentEntry ) + { + showEntry( (ArgumentEntry)entry, dynamicPanel ); + } + else + { + throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + } + + redraw(); + } + + public void showEntry( ClassEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Class: " ); + } + + public void showEntry( FieldEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Field: " ); + addNameValue( panel, "Class", entry.getClassEntry().getName() ); + } + + public void showEntry( MethodEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Method: " ); + addNameValue( panel, "Class", entry.getClassEntry().getName() ); + addNameValue( panel, "Signature", entry.getSignature() ); + } + + public void showEntry( ArgumentEntry entry, JPanel panel ) + { + m_typeLabel.setText( "Argument: " ); + addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() ); + addNameValue( panel, "Method", entry.getMethodEntry().getName() ); + addNameValue( panel, "Index", Integer.toString( entry.getIndex() ) ); + } + + private void addNameValue( JPanel container, String name, String value ) + { + JPanel panel = new JPanel(); + panel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) ); + container.add( panel ); + + JLabel label = new JLabel( name + ":", JLabel.RIGHT ); + label.setPreferredSize( new Dimension( 80, label.getPreferredSize().height ) ); + panel.add( label ); + + panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); + } + + private JLabel unboldLabel( JLabel label ) + { + Font font = label.getFont(); + label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); + return label; } - public void setClassSelectionHandler( ClassSelectionHandler val ) + private void redraw( ) { - m_classSelectionHandler = val; + m_frame.validate(); + m_frame.repaint(); } } diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java new file mode 100644 index 00000000..58cdadb0 --- /dev/null +++ b/src/cuchaz/enigma/gui/RenameListener.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import cuchaz.enigma.mapping.Entry; + +public interface RenameListener +{ + void rename( Entry entry, String newName ); +} diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java index 6c108d7c..dc3b4df7 100644 --- a/src/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class ArgumentEntry implements Serializable +public class ArgumentEntry implements Entry, Serializable { private static final long serialVersionUID = 4472172468162696006L; @@ -52,6 +52,7 @@ public class ArgumentEntry implements Serializable return m_index; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 7e78d4c0..3a757675 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -13,7 +13,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class ClassEntry implements Serializable +public class ClassEntry implements Entry, Serializable { private static final long serialVersionUID = 4235460580973955811L; @@ -33,6 +33,7 @@ public class ClassEntry implements Serializable m_name = className; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java new file mode 100644 index 00000000..14435324 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Entry.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.mapping; + +public interface Entry +{ + String getName( ); +} diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java index 15a93520..25b665ab 100644 --- a/src/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/cuchaz/enigma/mapping/FieldEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class FieldEntry implements Serializable +public class FieldEntry implements Entry, Serializable { private static final long serialVersionUID = 3004663582802885451L; @@ -41,6 +41,7 @@ public class FieldEntry implements Serializable return m_classEntry; } + @Override public String getName( ) { return m_name; diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java index e71a4664..4afc099b 100644 --- a/src/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/cuchaz/enigma/mapping/MethodEntry.java @@ -14,7 +14,7 @@ import java.io.Serializable; import cuchaz.enigma.Util; -public class MethodEntry implements Serializable +public class MethodEntry implements Entry, Serializable { private static final long serialVersionUID = 4770915224467247458L; @@ -47,6 +47,7 @@ public class MethodEntry implements Serializable return m_classEntry; } + @Override public String getName( ) { return m_name; -- cgit v1.2.3