From 57f45b0409d5363782052183bb090175c469f89a Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 4 Aug 2014 00:26:48 -0400 Subject: added stable save order for mappings to hopefully help with merging added color-coding for source identifiers redesigned rename GUI customized editor pane, added popup menu finished name validation added last-chance save on window close --- src/cuchaz/enigma/ClassFile.java | 31 +- src/cuchaz/enigma/Deobfuscator.java | 119 ++++--- src/cuchaz/enigma/analysis/SourceIndex.java | 4 +- src/cuchaz/enigma/analysis/SourcedAst.java | 15 +- src/cuchaz/enigma/gui/AboutDialog.java | 2 - src/cuchaz/enigma/gui/BoxHighlightPainter.java | 16 +- src/cuchaz/enigma/gui/BrowserCaret.java | 50 +++ src/cuchaz/enigma/gui/ClassListCellRenderer.java | 40 --- .../gui/DeobfuscatedClassListCellRenderer.java | 43 +++ .../enigma/gui/DeobfuscatedHighlightPainter.java | 22 ++ src/cuchaz/enigma/gui/Gui.java | 354 ++++++++++++++++----- src/cuchaz/enigma/gui/GuiController.java | 94 ++++-- .../gui/ObfuscatedClassListCellRenderer.java | 42 +++ .../enigma/gui/ObfuscatedHighlightPainter.java | 22 ++ src/cuchaz/enigma/mapping/ArgumentMapping.java | 12 +- src/cuchaz/enigma/mapping/ClassMapping.java | 27 +- src/cuchaz/enigma/mapping/EntryPair.java | 6 +- src/cuchaz/enigma/mapping/FieldMapping.java | 12 +- .../enigma/mapping/IllegalNameException.java | 29 ++ src/cuchaz/enigma/mapping/MappingsWriter.java | 22 +- src/cuchaz/enigma/mapping/MethodMapping.java | 12 +- src/cuchaz/enigma/mapping/NameValidator.java | 52 ++- src/cuchaz/enigma/mapping/Renamer.java | 19 -- 23 files changed, 746 insertions(+), 299 deletions(-) create mode 100644 src/cuchaz/enigma/gui/BrowserCaret.java delete mode 100644 src/cuchaz/enigma/gui/ClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java create mode 100644 src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java create mode 100644 src/cuchaz/enigma/mapping/IllegalNameException.java (limited to 'src/cuchaz') diff --git a/src/cuchaz/enigma/ClassFile.java b/src/cuchaz/enigma/ClassFile.java index c3c72a4..613b379 100644 --- a/src/cuchaz/enigma/ClassFile.java +++ b/src/cuchaz/enigma/ClassFile.java @@ -13,39 +13,24 @@ package cuchaz.enigma; public class ClassFile { - private String m_obfName; - private String m_deobfName; + private String m_name; - public ClassFile( String obfName ) + public ClassFile( String name ) { - m_obfName = obfName; - } - - public String getName( ) - { - if( m_deobfName != null ) + if( name.indexOf( '.' ) >= 0 ) { - return m_deobfName; + throw new IllegalArgumentException( "Class name should be in JVM format!" ); } - return m_obfName; - } - - public String getObfName( ) - { - return m_obfName; + m_name = name; } - public String getDeobfName( ) - { - return m_deobfName; - } - public void setDeobfName( String val ) + public String getName( ) { - m_deobfName = val; + return m_name; } public String getPath( ) { - return m_deobfName.replace( ".", "/" ) + ".class"; + return m_name.replace( ".", "/" ) + ".class"; } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index edc29e1..a3937b4 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.io.StringWriter; import java.util.Enumeration; import java.util.List; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -32,7 +33,6 @@ import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MethodEntry; -import cuchaz.enigma.mapping.NameValidator; import cuchaz.enigma.mapping.Renamer; import cuchaz.enigma.mapping.TranslationDirection; import cuchaz.enigma.mapping.Translator; @@ -100,26 +100,28 @@ public class Deobfuscator ) ); } - public void getSortedClasses( List obfClasses, List deobfClasses ) + public void getSeparatedClasses( List obfClasses, Map deobfClasses ) { Enumeration entries = m_jar.entries(); while( entries.hasMoreElements() ) { JarEntry entry = entries.nextElement(); - // get the class name - String obfName = NameValidator.fileNameToClassName( entry.getName() ); - if( obfName == null ) + // skip everything but class files + if( !entry.getName().endsWith( ".class" ) ) { continue; } - ClassFile classFile = new ClassFile( obfName ); + // get the class name from the file + String className = entry.getName().substring( 0, entry.getName().length() - 6 ); + ClassFile classFile = new ClassFile( className ); + + // separate the classes ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); if( classMapping != null ) { - classFile.setDeobfName( classMapping.getDeobfName() ); - deobfClasses.add( classFile ); + deobfClasses.put( classFile, classMapping.getDeobfName() ); } else { @@ -130,84 +132,123 @@ public class Deobfuscator public String getSource( final ClassFile classFile ) { + // is this class deobfuscated? + // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out + // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name + String deobfName = classFile.getName(); + ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); + if( classMapping != null ) + { + deobfName = classMapping.getDeobfName(); + } + + // decompile it! StringWriter buf = new StringWriter(); - Decompiler.decompile( classFile.getObfName(), new PlainTextOutput( buf ), m_settings ); + Decompiler.decompile( deobfName, new PlainTextOutput( buf ), m_settings ); return buf.toString(); } // NOTE: these methods are a bit messy... oh well - public void rename( Entry entry, String newName ) + public void rename( Entry obfEntry, String newName ) { - if( entry instanceof ClassEntry ) + if( obfEntry instanceof ClassEntry ) { - m_renamer.setClassName( (ClassEntry)entry, newName ); + m_renamer.setClassName( (ClassEntry)obfEntry, newName ); } - else if( entry instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - m_renamer.setFieldName( (FieldEntry)entry, newName ); + m_renamer.setFieldName( (FieldEntry)obfEntry, newName ); } - else if( entry instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - m_renamer.setMethodName( (MethodEntry)entry, newName ); + m_renamer.setMethodName( (MethodEntry)obfEntry, newName ); } - else if( entry instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - m_renamer.setArgumentName( (ArgumentEntry)entry, newName ); + m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName ); } else { - throw new Error( "Unknown entry type: " + entry.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } - public Entry obfuscate( Entry in ) + public Entry obfuscateEntry( Entry deobfEntry ) { Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ); - if( in instanceof ClassEntry ) + if( deobfEntry instanceof ClassEntry ) + { + return translator.translateEntry( (ClassEntry)deobfEntry ); + } + else if( deobfEntry instanceof FieldEntry ) + { + return translator.translateEntry( (FieldEntry)deobfEntry ); + } + else if( deobfEntry instanceof MethodEntry ) + { + return translator.translateEntry( (MethodEntry)deobfEntry ); + } + else if( deobfEntry instanceof ArgumentEntry ) + { + return translator.translateEntry( (ArgumentEntry)deobfEntry ); + } + else + { + throw new Error( "Unknown entry type: " + deobfEntry.getClass().getName() ); + } + } + + public Entry deobfuscateEntry( Entry obfEntry ) + { + Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); + if( obfEntry instanceof ClassEntry ) { - return translator.translateEntry( (ClassEntry)in ); + return translator.translateEntry( (ClassEntry)obfEntry ); } - else if( in instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - return translator.translateEntry( (FieldEntry)in ); + return translator.translateEntry( (FieldEntry)obfEntry ); } - else if( in instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - return translator.translateEntry( (MethodEntry)in ); + return translator.translateEntry( (MethodEntry)obfEntry ); } - else if( in instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - return translator.translateEntry( (ArgumentEntry)in ); + return translator.translateEntry( (ArgumentEntry)obfEntry ); } else { - throw new Error( "Unknown entry type: " + in.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } - public Entry deobfuscate( Entry in ) + public boolean hasMapping( Entry obfEntry ) { Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); - if( in instanceof ClassEntry ) + if( obfEntry instanceof ClassEntry ) { - return translator.translateEntry( (ClassEntry)in ); + String deobfName = translator.translate( (ClassEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof FieldEntry ) + else if( obfEntry instanceof FieldEntry ) { - return translator.translateEntry( (FieldEntry)in ); + String deobfName = translator.translate( (FieldEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof MethodEntry ) + else if( obfEntry instanceof MethodEntry ) { - return translator.translateEntry( (MethodEntry)in ); + String deobfName = translator.translate( (MethodEntry)obfEntry ); + return deobfName != null && !deobfName.equals( obfEntry.getName() ); } - else if( in instanceof ArgumentEntry ) + else if( obfEntry instanceof ArgumentEntry ) { - return translator.translateEntry( (ArgumentEntry)in ); + return translator.translate( (ArgumentEntry)obfEntry ) != null; } else { - throw new Error( "Unknown entry type: " + in.getClass().getName() ); + throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() ); } } } diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java index ee92d1e..61c833c 100644 --- a/src/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/cuchaz/enigma/analysis/SourceIndex.java @@ -52,7 +52,7 @@ public class SourceIndex implements Iterable> return m_tokenToEntry.get( token ); } - public Entry getEntry( int pos ) + public Map.Entry getEntry( int pos ) { // linear search is fast enough for now for( Map.Entry entry : this ) @@ -60,7 +60,7 @@ public class SourceIndex implements Iterable> Token token = entry.getValue(); if( pos >= token.start && pos <= token.end() ) { - return entry.getKey(); + return entry; } } return null; diff --git a/src/cuchaz/enigma/analysis/SourcedAst.java b/src/cuchaz/enigma/analysis/SourcedAst.java index 52a3453..968c880 100644 --- a/src/cuchaz/enigma/analysis/SourcedAst.java +++ b/src/cuchaz/enigma/analysis/SourcedAst.java @@ -59,14 +59,17 @@ public class SourcedAst } // index the self class using the package name - String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); - for( Tree typeTree : m_tree.getTypeDecls() ) + if( m_tree.getPackageName() != null ) { - if( typeTree instanceof ClassTree ) + String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); + for( Tree typeTree : m_tree.getTypeDecls() ) { - ClassTree classTree = (ClassTree)typeTree; - String className = classTree.getSimpleName().toString(); - m_classNameIndex.put( className, packageName + "/" + className ); + if( typeTree instanceof ClassTree ) + { + ClassTree classTree = (ClassTree)typeTree; + String className = classTree.getSimpleName().toString(); + m_classNameIndex.put( className, packageName + "/" + className ); + } } } } diff --git a/src/cuchaz/enigma/gui/AboutDialog.java b/src/cuchaz/enigma/gui/AboutDialog.java index 79b7543..a245956 100644 --- a/src/cuchaz/enigma/gui/AboutDialog.java +++ b/src/cuchaz/enigma/gui/AboutDialog.java @@ -18,8 +18,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; -import javax.swing.Box; -import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; diff --git a/src/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/cuchaz/enigma/gui/BoxHighlightPainter.java index 22db28b..b9474ff 100644 --- a/src/cuchaz/enigma/gui/BoxHighlightPainter.java +++ b/src/cuchaz/enigma/gui/BoxHighlightPainter.java @@ -19,10 +19,16 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; -public class BoxHighlightPainter implements Highlighter.HighlightPainter +public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter { - private static final Color FillColor = new Color( 230, 230, 230 ); - private static final Color BorderColor = new Color( 100, 100, 100 ); + private Color m_fillColor; + private Color m_borderColor; + + protected BoxHighlightPainter( Color fillColor, Color borderColor ) + { + m_fillColor = fillColor; + m_borderColor = borderColor; + } @Override public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) @@ -39,11 +45,11 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter bounds.height -= 2; // fill the area - g.setColor( FillColor ); + g.setColor( m_fillColor ); g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); // draw a box around the area - g.setColor( BorderColor ); + g.setColor( m_borderColor ); g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); } catch( BadLocationException ex ) diff --git a/src/cuchaz/enigma/gui/BrowserCaret.java b/src/cuchaz/enigma/gui/BrowserCaret.java new file mode 100644 index 0000000..f7e608b --- /dev/null +++ b/src/cuchaz/enigma/gui/BrowserCaret.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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 java.awt.Graphics; +import java.awt.Shape; + +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +public class BrowserCaret extends DefaultCaret +{ + private static final long serialVersionUID = 1158977422507969940L; + + private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter( ) + { + @Override + public void paint( Graphics g, int p0, int p1, Shape bounds, JTextComponent c ) + { + // don't paint anything + } + }; + + @Override + public boolean isSelectionVisible( ) + { + return false; + } + + @Override + public boolean isVisible( ) + { + return true; + } + + @Override + public Highlighter.HighlightPainter getSelectionPainter( ) + { + return m_selectionPainter; + } +} diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ClassListCellRenderer.java deleted file mode 100644 index 302f140..0000000 --- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java +++ /dev/null @@ -1,40 +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 java.awt.Component; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -import cuchaz.enigma.ClassFile; - -public class ClassListCellRenderer implements ListCellRenderer -{ - private DefaultListCellRenderer m_defaultRenderer; - - public ClassListCellRenderer( ) - { - m_defaultRenderer = new DefaultListCellRenderer(); - } - - @Override - public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) - { - JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); - - label.setText( classFile.getName() ); - - return label; - } -} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java new file mode 100644 index 0000000..3a8729d --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 java.awt.Component; +import java.util.Map; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class DeobfuscatedClassListCellRenderer implements ListCellRenderer> +{ + private DefaultListCellRenderer m_defaultRenderer; + + public DeobfuscatedClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList> list, Map.Entry entry, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, entry, index, isSelected, hasFocus ); + + label.setText( Descriptor.toJavaName( entry.getValue() ) ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java new file mode 100644 index 0000000..6a42884 --- /dev/null +++ b/src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * 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 java.awt.Color; + +public class DeobfuscatedHighlightPainter extends BoxHighlightPainter +{ + public DeobfuscatedHighlightPainter( ) + { + // green ish + super( new Color( 220, 255, 220 ), new Color( 80, 160, 80 ) ); + } +} diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index 3f46b6e..87b9308 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java @@ -11,6 +11,7 @@ package cuchaz.enigma.gui; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; @@ -18,18 +19,21 @@ import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Vector; import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JFrame; @@ -38,7 +42,9 @@ import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; @@ -47,17 +53,19 @@ import javax.swing.WindowConstants; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.SyntaxDocument; import jsyntaxpane.Token; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Constants; -import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.IllegalNameException; import cuchaz.enigma.mapping.MethodEntry; public class Gui @@ -86,14 +94,11 @@ public class Gui // controls private JFrame m_frame; private JList m_obfClasses; - private JList m_deobfClasses; + 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; - private BoxHighlightPainter m_highlightPainter; + private JPanel m_infoPanel; + private BoxHighlightPainter m_obfuscatedHighlightPainter; + private BoxHighlightPainter m_deobfuscatedHighlightPainter; // dynamic menu items private JMenuItem m_closeJarMenu; @@ -101,6 +106,7 @@ public class Gui private JMenuItem m_saveMappingsMenu; private JMenuItem m_saveMappingsAsMenu; private JMenuItem m_closeMappingsMenu; + private JMenuItem m_renameMenu; // state private EntryPair m_selectedEntryPair; @@ -124,7 +130,7 @@ public class Gui m_obfClasses = new JList(); m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_obfClasses.setLayoutOrientation( JList.VERTICAL ); - m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); + m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() ); m_obfClasses.addMouseListener( new MouseAdapter() { public void mouseClicked( MouseEvent event ) @@ -146,20 +152,20 @@ public class Gui obfPanel.add( obfScroller, BorderLayout.CENTER ); // init deobfuscated classes list - m_deobfClasses = new JList(); + m_deobfClasses = new JList>(); m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); - m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); + m_deobfClasses.setCellRenderer( new DeobfuscatedClassListCellRenderer() ); m_deobfClasses.addMouseListener( new MouseAdapter() { public void mouseClicked( MouseEvent event ) { if( event.getClickCount() == 2 ) { - ClassFile selected = m_deobfClasses.getSelectedValue(); + Map.Entry selected = m_deobfClasses.getSelectedValue(); if( selected != null ) { - m_controller.deobfuscateClass( selected ); + m_controller.deobfuscateClass( selected.getKey() ); } } } @@ -170,39 +176,20 @@ 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_selectedEntryPair != null ) - { - m_controller.rename( m_selectedEntryPair.obf, 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 ); + // init info panel + m_infoPanel = new JPanel(); + m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) ); + m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) ); + m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); clearEntryPair(); // init editor DefaultSyntaxKit.initKit(); - m_highlightPainter = new BoxHighlightPainter(); + m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); + m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); m_editor = new JEditorPane(); m_editor.setEditable( false ); + m_editor.setCaret( new BrowserCaret() ); JScrollPane sourceScroller = new JScrollPane( m_editor ); m_editor.setContentType( "text/java" ); m_editor.addCaretListener( new CaretListener( ) @@ -214,19 +201,55 @@ public class Gui if( m_selectedEntryPair != null ) { showEntryPair( m_selectedEntryPair ); + m_renameMenu.setEnabled( true ); } else { clearEntryPair(); + m_renameMenu.setEnabled( false ); } } } ); + m_editor.addKeyListener( new KeyAdapter( ) + { + @Override + public void keyPressed( KeyEvent event ) + { + switch( event.getKeyCode() ) + { + case KeyEvent.VK_R: + startRename(); + break; + } + } + } ); + + // turn off token highlighting (it's wrong most of the time anyway...) + DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit(); + kit.toggleComponent( m_editor, "jsyntaxpane.components.TokenMarker" ); + + // init editor popup menu + JPopupMenu popupMenu = new JPopupMenu(); + m_editor.setComponentPopupMenu( popupMenu ); + { + JMenuItem menu = new JMenuItem( "Rename" ); + menu.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + startRename(); + } + } ); + popupMenu.add( menu ); + m_renameMenu = menu; + } // layout controls JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); JPanel rightPanel = new JPanel(); rightPanel.setLayout( new BorderLayout() ); - rightPanel.add( m_actionPanel, BorderLayout.NORTH ); + rightPanel.add( m_infoPanel, BorderLayout.NORTH ); rightPanel.add( sourceScroller, BorderLayout.CENTER ); JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); pane.add( splitMain, BorderLayout.CENTER ); @@ -353,6 +376,19 @@ public class Gui } ); m_closeMappingsMenu = item; } + menu.addSeparator(); + { + JMenuItem item = new JMenuItem( "Exit" ); + menu.add( item ); + item.addActionListener( new ActionListener( ) + { + @Override + public void actionPerformed( ActionEvent event ) + { + close(); + } + } ); + } } { JMenu menu = new JMenu( "Help" ); @@ -374,12 +410,21 @@ public class Gui // init state onCloseJar(); + m_frame.addWindowListener( new WindowAdapter( ) + { + @Override + public void windowClosing( WindowEvent event ) + { + close(); + } + } ); + // 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 ); + m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); } public GuiController getController( ) @@ -430,15 +475,15 @@ public class Gui } } - public void setDeobfClasses( List classes ) + public void setDeobfClasses( Map deobfClasses ) { - if( classes != null ) + if( deobfClasses != null ) { - m_deobfClasses.setListData( new Vector( classes ) ); + m_deobfClasses.setListData( new Vector>( deobfClasses.entrySet() ) ); } else { - m_deobfClasses.setListData( new Vector() ); + m_deobfClasses.setListData( new Vector>() ); } } @@ -450,48 +495,77 @@ public class Gui public void setSource( String source ) { - setSource( source, null ); + setSource( source, 0 ); } - public void setSource( String source, SourceIndex index ) + public void setSource( String source, int lineNum ) { + // remove any old highlighters + m_editor.getHighlighter().removeAllHighlights(); + m_editor.setText( source ); - setHighlightedTokens( null ); + + // count the offset of the target line + String text = m_editor.getText(); + int pos = 0; + int numLines = 0; + for( ; pos < text.length(); pos++ ) + { + if( numLines == lineNum ) + { + break; + } + if( text.charAt( pos ) == '\n' ) + { + numLines++; + } + } + + // put the caret at the line number + m_editor.setCaretPosition( pos ); + m_editor.grabFocus(); } - public void setHighlightedTokens( Iterable tokens ) + public void setHighlightedTokens( Iterable obfuscatedTokens, Iterable deobfuscatedTokens ) { // remove any old highlighters m_editor.getHighlighter().removeAllHighlights(); - if( tokens == null ) + // color things based on the index + if( obfuscatedTokens != null ) { - return; + setHighlightedTokens( obfuscatedTokens, m_obfuscatedHighlightPainter ); + } + if( deobfuscatedTokens != null ) + { + setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter ); } - // color things based on the index + redraw(); + } + + private void setHighlightedTokens( Iterable tokens, Highlighter.HighlightPainter painter ) + { for( Token token : tokens ) { try { - m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); + m_editor.getHighlighter().addHighlight( token.start, token.end(), painter ); } catch( BadLocationException ex ) { throw new IllegalArgumentException( ex ); } } - - redraw(); } private void clearEntryPair( ) { - m_actionPanel.removeAll(); + m_infoPanel.removeAll(); JLabel label = new JLabel( "No identifier selected" ); unboldLabel( label ); label.setHorizontalAlignment( JLabel.CENTER ); - m_actionPanel.add( label ); + m_infoPanel.add( label ); redraw(); } @@ -507,30 +581,22 @@ public class Gui m_selectedEntryPair = pair; - // layout the action panel - m_actionPanel.removeAll(); - m_actionPanel.add( m_renamePanel ); - m_nameField.setText( pair.deobf.getName() ); - - // layout the dynamic section - JPanel dynamicPanel = new JPanel(); - dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); - m_actionPanel.add( dynamicPanel ); + m_infoPanel.removeAll(); if( pair.deobf instanceof ClassEntry ) { - showClassEntryPair( (EntryPair)pair, dynamicPanel ); + showClassEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof FieldEntry ) { - showFieldEntryPair( (EntryPair)pair, dynamicPanel ); + showFieldEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof MethodEntry ) { - showMethodEntryPair( (EntryPair)pair, dynamicPanel ); + showMethodEntryPair( (EntryPair)pair ); } else if( pair.deobf instanceof ArgumentEntry ) { - showArgumentEntryPair( (EntryPair)pair, dynamicPanel ); + showArgumentEntryPair( (EntryPair)pair ); } else { @@ -540,30 +606,30 @@ public class Gui redraw(); } - private void showClassEntryPair( EntryPair pair, JPanel panel ) + private void showClassEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Class: " ); + addNameValue( m_infoPanel, "Class", pair.deobf.getName() ); } - private void showFieldEntryPair( EntryPair pair, JPanel panel ) + private void showFieldEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Field: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Field", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); } - private void showMethodEntryPair( EntryPair pair, JPanel panel ) + private void showMethodEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Method: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); - addNameValue( panel, "Signature", pair.obf.getSignature() + " <-> " + pair.deobf.getSignature() ); + addNameValue( m_infoPanel, "Method", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() ); } - private void showArgumentEntryPair( EntryPair pair, JPanel panel ) + private void showArgumentEntryPair( EntryPair pair ) { - m_typeLabel.setText( "Argument: " ); - addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); - addNameValue( panel, "Method", pair.obf.getMethodEntry().getName() + " <-> " + pair.deobf.getMethodEntry().getName() ); - addNameValue( panel, "Index", Integer.toString( pair.obf.getIndex() ) ); + addNameValue( m_infoPanel, "Argument", pair.deobf.getName() ); + addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() ); + addNameValue( m_infoPanel, "Method", pair.deobf.getMethodEntry().getName() ); + addNameValue( m_infoPanel, "Index", Integer.toString( pair.deobf.getIndex() ) ); } private void addNameValue( JPanel container, String name, String value ) @@ -578,6 +644,120 @@ public class Gui panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); } + + private void startRename( ) + { + // init the text box + final JTextField text = new JTextField(); + text.setText( m_selectedEntryPair.deobf.getName() ); + text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); + text.addKeyListener( new KeyAdapter( ) + { + @Override + public void keyPressed( KeyEvent event ) + { + switch( event.getKeyCode() ) + { + case KeyEvent.VK_ENTER: + finishRename( text, true ); + break; + + case KeyEvent.VK_ESCAPE: + finishRename( text, false ); + break; + } + } + } ); + + // find the label with the name and replace it with the text box + JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); + panel.remove( panel.getComponentCount() - 1 ); + panel.add( text ); + text.grabFocus(); + text.selectAll(); + + redraw(); + } + + private void finishRename( JTextField text, boolean saveName ) + { + String newName = text.getText(); + if( saveName && newName != null && newName.length() > 0 ) + { + SyntaxDocument doc = (SyntaxDocument)m_editor.getDocument(); + int lineNum = doc.getLineNumberAt( m_editor.getCaretPosition() ); + try + { + m_controller.rename( m_selectedEntryPair.obf, newName, lineNum ); + } + catch( IllegalNameException ex ) + { + text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); + } + return; + } + + // abort the rename + JPanel panel = (JPanel)m_infoPanel.getComponent( 0 ); + panel.remove( panel.getComponentCount() - 1 ); + panel.add( unboldLabel( new JLabel( m_selectedEntryPair.deobf.getName(), JLabel.LEFT ) ) ); + + m_editor.grabFocus(); + + redraw(); + } + + private void close( ) + { + if( !m_controller.isDirty() ) + { + // everything is saved, we can exit safely + m_frame.dispose(); + } + else + { + // ask to save before closing + String[] options = { + "Save and exit", + "Discard changes", + "Cancel" + }; + int response = JOptionPane.showOptionDialog( + m_frame, + "Your mappings have not been saved yet. Do you want to save?", + "Save your changes?", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[2] + ); + switch( response ) + { + case JOptionPane.YES_OPTION: // save and exit + if( m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION ) + { + try + { + m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() ); + m_frame.dispose(); + } + catch( IOException ex ) + { + throw new Error( ex ); + } + } + break; + + case JOptionPane.NO_OPTION: + // don't save, exit + m_frame.dispose(); + break; + + // cancel means do nothing + } + } + } private JLabel unboldLabel( JLabel label ) { diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index 6704ef8..e1ba49a 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -14,14 +14,18 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import jsyntaxpane.Token; + +import com.beust.jcommander.internal.Lists; +import com.beust.jcommander.internal.Maps; import cuchaz.enigma.ClassFile; import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.Analyzer; import cuchaz.enigma.analysis.SourceIndex; -import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Entry; import cuchaz.enigma.mapping.EntryPair; import cuchaz.enigma.mapping.MappingsReader; @@ -33,6 +37,7 @@ public class GuiController private Gui m_gui; private SourceIndex m_index; private ClassFile m_currentFile; + private boolean m_isDirty; public GuiController( Gui gui ) { @@ -40,6 +45,12 @@ public class GuiController m_deobfuscator = null; m_index = null; m_currentFile = null; + m_isDirty = false; + } + + public boolean isDirty( ) + { + return m_isDirty; } public void openJar( File file ) @@ -62,6 +73,7 @@ public class GuiController FileReader in = new FileReader( file ); m_deobfuscator.setMappings( new MappingsReader().read( in ) ); in.close(); + m_isDirty = false; m_gui.setMappingsFile( file ); refreshClasses(); refreshOpenFiles(); @@ -73,12 +85,14 @@ public class GuiController FileWriter out = new FileWriter( file ); new MappingsWriter().write( out, m_deobfuscator.getMappings() ); out.close(); + m_isDirty = false; } public void closeMappings( ) { m_deobfuscator.setMappings( null ); m_gui.setMappingsFile( null ); + refreshClasses(); refreshOpenFiles(); } @@ -95,51 +109,62 @@ public class GuiController return null; } - Entry deobfEntry = m_index.getEntry( pos ); - if( deobfEntry == null ) + Map.Entry deobfEntryAndToken = m_index.getEntry( pos ); + if( deobfEntryAndToken == null ) { return null; } - return new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); + Entry deobfEntry = deobfEntryAndToken.getKey(); + Token token = deobfEntryAndToken.getValue(); + return new EntryPair( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry, token ); } - public void rename( Entry obfsEntry, String newName ) + public boolean entryHasMapping( int pos ) { - m_deobfuscator.rename( obfsEntry, newName ); - - // did we rename the current file? - if( obfsEntry instanceof ClassEntry ) + EntryPair pair = getEntryPair( pos ); + if( pair == null || pair.obf == null ) { - ClassEntry classEntry = (ClassEntry)obfsEntry; - - // update the current file - if( classEntry.getName().equals( m_currentFile.getName() ) ) - { - m_currentFile = new ClassFile( newName ); - } + return false; } - - refreshOpenFiles(); + return m_deobfuscator.hasMapping( pair.obf ); + } + + public void rename( Entry obfsEntry, String newName, int lineNum ) + { + m_deobfuscator.rename( obfsEntry, newName ); + m_isDirty = true; + refreshClasses(); + refreshOpenFiles( lineNum ); } private void refreshClasses( ) { - List obfClasses = new ArrayList(); - List deobfClasses = new ArrayList(); - m_deobfuscator.getSortedClasses( obfClasses, deobfClasses ); + List obfClasses = Lists.newArrayList(); + Map deobfClasses = Maps.newHashMap(); + m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses ); m_gui.setObfClasses( obfClasses ); m_gui.setDeobfClasses( deobfClasses ); } - + private void refreshOpenFiles( ) + { + refreshOpenFiles( 0 ); + } + + private void refreshOpenFiles( int lineNum ) { if( m_currentFile != null ) { - deobfuscate( m_currentFile ); + deobfuscate( m_currentFile, lineNum ); } } private void deobfuscate( final ClassFile classFile ) + { + deobfuscate( classFile, 0 ); + } + + private void deobfuscate( final ClassFile classFile, final int lineNum ) { m_gui.setSource( "(deobfuscating...)" ); @@ -149,13 +174,28 @@ public class GuiController @Override public void run( ) { - // deobfuscate the bytecode + // deobfuscate,decompile the bytecode String source = m_deobfuscator.getSource( classFile ); - m_gui.setSource( source ); + m_gui.setSource( source, lineNum ); // index the source file m_index = Analyzer.analyze( classFile.getName(), source ); - m_gui.setHighlightedTokens( m_index.tokens() ); + + // set the highlighted tokens + List obfuscatedTokens = Lists.newArrayList(); + List deobfuscatedTokens = Lists.newArrayList(); + for( Token token : m_index.tokens() ) + { + if( entryHasMapping( token.start ) ) + { + deobfuscatedTokens.add( token ); + } + else + { + obfuscatedTokens.add( token ); + } + } + m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens ); } }.start(); } diff --git a/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java new file mode 100644 index 0000000..d46e1ae --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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 java.awt.Component; + +import javassist.bytecode.Descriptor; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +import cuchaz.enigma.ClassFile; + +public class ObfuscatedClassListCellRenderer implements ListCellRenderer +{ + private DefaultListCellRenderer m_defaultRenderer; + + public ObfuscatedClassListCellRenderer( ) + { + m_defaultRenderer = new DefaultListCellRenderer(); + } + + @Override + public Component getListCellRendererComponent( JList list, ClassFile classFile, int index, boolean isSelected, boolean hasFocus ) + { + JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); + + label.setText( Descriptor.toJavaName( classFile.getName() ) ); + + return label; + } +} diff --git a/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java new file mode 100644 index 0000000..724be34 --- /dev/null +++ b/src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * 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 java.awt.Color; + +public class ObfuscatedHighlightPainter extends BoxHighlightPainter +{ + public ObfuscatedHighlightPainter( ) + { + // red ish + super( new Color( 255, 220, 220 ), new Color( 160, 80, 80 ) ); + } +} diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index d5e020a..168306a 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -12,7 +12,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class ArgumentMapping implements Serializable +public class ArgumentMapping implements Serializable, Comparable { private static final long serialVersionUID = 8610742471440861315L; @@ -23,7 +23,7 @@ public class ArgumentMapping implements Serializable public ArgumentMapping( int index, String name ) { m_index = index; - m_name = name; + m_name = NameValidator.validateArgumentName( name ); } public int getIndex( ) @@ -37,6 +37,12 @@ public class ArgumentMapping implements Serializable } public void setName( String val ) { - m_name = val; + m_name = NameValidator.validateArgumentName( val ); + } + + @Override + public int compareTo( ArgumentMapping other ) + { + return Integer.compare( m_index, other.m_index ); } } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 3ba3569..a1cc775 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -15,7 +15,7 @@ import java.util.Map; import com.beust.jcommander.internal.Maps; -public class ClassMapping implements Serializable +public class ClassMapping implements Serializable, Comparable { private static final long serialVersionUID = -5148491146902340107L; @@ -30,7 +30,7 @@ public class ClassMapping implements Serializable public ClassMapping( String obfName, String deobfName ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateClassName( deobfName ); m_fieldsByObf = Maps.newHashMap(); m_fieldsByDeobf = Maps.newHashMap(); m_methodsByObf = Maps.newHashMap(); @@ -48,7 +48,7 @@ public class ClassMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateClassName( val ); } public Iterable fields( ) @@ -97,11 +97,6 @@ public class ClassMapping implements Serializable public void setFieldName( String obfName, String deobfName ) { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); if( fieldMapping == null ) { @@ -140,11 +135,6 @@ public class ClassMapping implements Serializable public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) { - if( deobfName == null ) - { - throw new IllegalArgumentException( "deobf name cannot be null!" ); - } - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); if( methodIndex == null ) { @@ -167,11 +157,6 @@ public class ClassMapping implements Serializable public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) { - if( argumentName == null ) - { - throw new IllegalArgumentException( "argument name cannot be null!" ); - } - MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); if( methodIndex == null ) { @@ -214,4 +199,10 @@ public class ClassMapping implements Serializable } return buf.toString(); } + + @Override + public int compareTo( ClassMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); + } } diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java index e3325b3..1bf9be0 100644 --- a/src/cuchaz/enigma/mapping/EntryPair.java +++ b/src/cuchaz/enigma/mapping/EntryPair.java @@ -10,15 +10,19 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import jsyntaxpane.Token; + public class EntryPair { public T obf; public T deobf; + public Token token; - public EntryPair( T obf, T deobf ) + public EntryPair( T obf, T deobf, Token token ) { this.obf = obf; this.deobf = deobf; + this.token = token; } } diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 618f45c..ae0855a 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -12,7 +12,7 @@ package cuchaz.enigma.mapping; import java.io.Serializable; -public class FieldMapping implements Serializable +public class FieldMapping implements Serializable, Comparable { private static final long serialVersionUID = 8610742471440861315L; @@ -22,7 +22,7 @@ public class FieldMapping implements Serializable public FieldMapping( String obfName, String deobfName ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateFieldName( deobfName ); } public String getObfName( ) @@ -36,6 +36,12 @@ public class FieldMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateFieldName( val ); + } + + @Override + public int compareTo( FieldMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); } } diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java new file mode 100644 index 0000000..560e5d9 --- /dev/null +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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 class IllegalNameException extends RuntimeException +{ + private static final long serialVersionUID = -2279910052561114323L; + + private String m_name; + + public IllegalNameException( String name ) + { + m_name = name; + } + + @Override + public String getMessage( ) + { + return "Illegal name: " + m_name; + } +} diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index 2086368..a97052f 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -13,6 +13,9 @@ package cuchaz.enigma.mapping; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class MappingsWriter { @@ -25,7 +28,7 @@ public class MappingsWriter public void write( PrintWriter out, Mappings mappings ) throws IOException { - for( ClassMapping classMapping : mappings.classes() ) + for( ClassMapping classMapping : sorted( mappings.classes() ) ) { write( out, classMapping ); } @@ -36,12 +39,12 @@ public class MappingsWriter { out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() ); - for( FieldMapping fieldMapping : classMapping.fields() ) + for( FieldMapping fieldMapping : sorted( classMapping.fields() ) ) { write( out, fieldMapping ); } - for( MethodMapping methodMapping : classMapping.methods() ) + for( MethodMapping methodMapping : sorted( classMapping.methods() ) ) { write( out, methodMapping ); } @@ -61,7 +64,7 @@ public class MappingsWriter methodMapping.getObfSignature(), methodMapping.getDeobfSignature() ); - for( ArgumentMapping argumentMapping : methodMapping.arguments() ) + for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) ) { write( out, argumentMapping ); } @@ -72,4 +75,15 @@ public class MappingsWriter { out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() ); } + + private > List sorted( Iterable classes ) + { + List out = new ArrayList(); + for( T t : classes ) + { + out.add( t ); + } + Collections.sort( out ); + return out; + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 1cdc38a..7857ea7 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -14,7 +14,7 @@ import java.io.Serializable; import java.util.Map; import java.util.TreeMap; -public class MethodMapping implements Serializable +public class MethodMapping implements Serializable, Comparable { private static final long serialVersionUID = -4409570216084263978L; @@ -28,7 +28,7 @@ public class MethodMapping implements Serializable public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature ) { m_obfName = obfName; - m_deobfName = deobfName; + m_deobfName = NameValidator.validateMethodName( deobfName ); m_obfSignature = obfSignature; m_deobfSignature = deobfSignature; m_arguments = new TreeMap(); @@ -45,7 +45,7 @@ public class MethodMapping implements Serializable } public void setDeobfName( String val ) { - m_deobfName = val; + m_deobfName = NameValidator.validateMethodName( val ); } public String getObfSignature( ) @@ -133,4 +133,10 @@ public class MethodMapping implements Serializable } return buf.toString(); } + + @Override + public int compareTo( MethodMapping other ) + { + return m_obfName.compareTo( other.m_obfName ); + } } diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index 30982dc..a8421fa 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java @@ -10,12 +10,28 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import java.util.Arrays; +import java.util.List; import java.util.regex.Pattern; +import javassist.bytecode.Descriptor; + public class NameValidator { - private static final String IdentifierPattern; + private static final Pattern IdentifierPattern; private static final Pattern ClassPattern; + private static final List ReservedWords = Arrays.asList( + "abstract", "continue", "for", "new", "switch", + "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", + "break", "double", "implements", "protected", "throw", + "byte", "else", "import", "public", "throws", + "case", "enum", "instanceof", "return", "transient", + "catch", "extends", "int", "short", "try", + "char", "final", "interface", "static", "void", + "class", "finally", "long", "strictfp", "volatile", + "const", "float", "native", "super", "while" + ); static { @@ -34,34 +50,36 @@ public class NameValidator } } - IdentifierPattern = String.format( "[\\Q%s\\E][\\Q%s\\E]*", startChars.toString(), partChars.toString() ); - ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", IdentifierPattern, IdentifierPattern ) ); + String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; + IdentifierPattern = Pattern.compile( identifierRegex ); + ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex ) ); } - public String validateClassName( String name ) + public static String validateClassName( String name ) { - if( !ClassPattern.matcher( name ).matches() ) + if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalArgumentException( "Illegal name: " + name ); + throw new IllegalNameException( name ); } - - return classNameToJavaName( name ); + return Descriptor.toJvmName( name ); } - public static String fileNameToClassName( String fileName ) + public static String validateFieldName( String name ) { - final String suffix = ".class"; - - if( !fileName.endsWith( suffix ) ) + if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - return null; + throw new IllegalNameException( name ); } - - return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); + return name; + } + + public static String validateMethodName( String name ) + { + return validateFieldName( name ); } - public static String classNameToJavaName( String className ) + public static String validateArgumentName( String name ) { - return className.replace( ".", "/" ); + return validateFieldName( name ); } } diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java index 4a648ad..42ac17d 100644 --- a/src/cuchaz/enigma/mapping/Renamer.java +++ b/src/cuchaz/enigma/mapping/Renamer.java @@ -39,10 +39,6 @@ public class Renamer m_mappings.m_classesByDeobf.put( deobfName, classMapping ); updateDeobfMethodSignatures(); - - // TEMP - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setFieldName( FieldEntry obf, String deobfName ) @@ -54,11 +50,6 @@ public class Renamer } classMapping.setFieldName( obf.getName(), deobfName ); - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setMethodName( MethodEntry obf, String deobfName ) @@ -73,11 +64,6 @@ public class Renamer classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void setArgumentName( ArgumentEntry obf, String deobfName ) @@ -89,11 +75,6 @@ public class Renamer } classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); - - // TEMP - System.out.println( classMapping ); - String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf ); - assert( translatedName != null && translatedName.equals( deobfName ) ); } public void write( OutputStream out ) -- cgit v1.2.3