summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2014-08-04 00:26:48 -0400
committerGravatar jeff2014-08-04 00:26:48 -0400
commit57f45b0409d5363782052183bb090175c469f89a (patch)
tree672b32876b420445630e58e16b67d671e45c07b6 /src
parentfixed bugs with saving mappings (diff)
downloadenigma-fork-57f45b0409d5363782052183bb090175c469f89a.tar.gz
enigma-fork-57f45b0409d5363782052183bb090175c469f89a.tar.xz
enigma-fork-57f45b0409d5363782052183bb090175c469f89a.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/ClassFile.java31
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java119
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndex.java4
-rw-r--r--src/cuchaz/enigma/analysis/SourcedAst.java15
-rw-r--r--src/cuchaz/enigma/gui/AboutDialog.java2
-rw-r--r--src/cuchaz/enigma/gui/BoxHighlightPainter.java16
-rw-r--r--src/cuchaz/enigma/gui/BrowserCaret.java50
-rw-r--r--src/cuchaz/enigma/gui/DeobfuscatedClassListCellRenderer.java43
-rw-r--r--src/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java22
-rw-r--r--src/cuchaz/enigma/gui/Gui.java354
-rw-r--r--src/cuchaz/enigma/gui/GuiController.java94
-rw-r--r--src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java (renamed from src/cuchaz/enigma/gui/ClassListCellRenderer.java)8
-rw-r--r--src/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java22
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentMapping.java12
-rw-r--r--src/cuchaz/enigma/mapping/ClassMapping.java27
-rw-r--r--src/cuchaz/enigma/mapping/EntryPair.java6
-rw-r--r--src/cuchaz/enigma/mapping/FieldMapping.java12
-rw-r--r--src/cuchaz/enigma/mapping/IllegalNameException.java29
-rw-r--r--src/cuchaz/enigma/mapping/MappingsWriter.java22
-rw-r--r--src/cuchaz/enigma/mapping/MethodMapping.java12
-rw-r--r--src/cuchaz/enigma/mapping/NameValidator.java52
-rw-r--r--src/cuchaz/enigma/mapping/Renamer.java19
22 files changed, 709 insertions, 262 deletions
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;
13 13
14public class ClassFile 14public class ClassFile
15{ 15{
16 private String m_obfName; 16 private String m_name;
17 private String m_deobfName;
18 17
19 public ClassFile( String obfName ) 18 public ClassFile( String name )
20 { 19 {
21 m_obfName = obfName; 20 if( name.indexOf( '.' ) >= 0 )
22 }
23
24 public String getName( )
25 {
26 if( m_deobfName != null )
27 { 21 {
28 return m_deobfName; 22 throw new IllegalArgumentException( "Class name should be in JVM format!" );
29 } 23 }
30 return m_obfName; 24 m_name = name;
31 }
32
33 public String getObfName( )
34 {
35 return m_obfName;
36 } 25 }
37 26
38 public String getDeobfName( ) 27 public String getName( )
39 {
40 return m_deobfName;
41 }
42 public void setDeobfName( String val )
43 { 28 {
44 m_deobfName = val; 29 return m_name;
45 } 30 }
46 31
47 public String getPath( ) 32 public String getPath( )
48 { 33 {
49 return m_deobfName.replace( ".", "/" ) + ".class"; 34 return m_name.replace( ".", "/" ) + ".class";
50 } 35 }
51} 36}
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;
17import java.io.StringWriter; 17import java.io.StringWriter;
18import java.util.Enumeration; 18import java.util.Enumeration;
19import java.util.List; 19import java.util.List;
20import java.util.Map;
20import java.util.jar.JarEntry; 21import java.util.jar.JarEntry;
21import java.util.jar.JarFile; 22import java.util.jar.JarFile;
22 23
@@ -32,7 +33,6 @@ import cuchaz.enigma.mapping.Entry;
32import cuchaz.enigma.mapping.FieldEntry; 33import cuchaz.enigma.mapping.FieldEntry;
33import cuchaz.enigma.mapping.Mappings; 34import cuchaz.enigma.mapping.Mappings;
34import cuchaz.enigma.mapping.MethodEntry; 35import cuchaz.enigma.mapping.MethodEntry;
35import cuchaz.enigma.mapping.NameValidator;
36import cuchaz.enigma.mapping.Renamer; 36import cuchaz.enigma.mapping.Renamer;
37import cuchaz.enigma.mapping.TranslationDirection; 37import cuchaz.enigma.mapping.TranslationDirection;
38import cuchaz.enigma.mapping.Translator; 38import cuchaz.enigma.mapping.Translator;
@@ -100,26 +100,28 @@ public class Deobfuscator
100 ) ); 100 ) );
101 } 101 }
102 102
103 public void getSortedClasses( List<ClassFile> obfClasses, List<ClassFile> deobfClasses ) 103 public void getSeparatedClasses( List<ClassFile> obfClasses, Map<ClassFile,String> deobfClasses )
104 { 104 {
105 Enumeration<JarEntry> entries = m_jar.entries(); 105 Enumeration<JarEntry> entries = m_jar.entries();
106 while( entries.hasMoreElements() ) 106 while( entries.hasMoreElements() )
107 { 107 {
108 JarEntry entry = entries.nextElement(); 108 JarEntry entry = entries.nextElement();
109 109
110 // get the class name 110 // skip everything but class files
111 String obfName = NameValidator.fileNameToClassName( entry.getName() ); 111 if( !entry.getName().endsWith( ".class" ) )
112 if( obfName == null )
113 { 112 {
114 continue; 113 continue;
115 } 114 }
116 115
117 ClassFile classFile = new ClassFile( obfName ); 116 // get the class name from the file
117 String className = entry.getName().substring( 0, entry.getName().length() - 6 );
118 ClassFile classFile = new ClassFile( className );
119
120 // separate the classes
118 ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() ); 121 ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() );
119 if( classMapping != null ) 122 if( classMapping != null )
120 { 123 {
121 classFile.setDeobfName( classMapping.getDeobfName() ); 124 deobfClasses.put( classFile, classMapping.getDeobfName() );
122 deobfClasses.add( classFile );
123 } 125 }
124 else 126 else
125 { 127 {
@@ -130,84 +132,123 @@ public class Deobfuscator
130 132
131 public String getSource( final ClassFile classFile ) 133 public String getSource( final ClassFile classFile )
132 { 134 {
135 // is this class deobfuscated?
136 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
137 // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name
138 String deobfName = classFile.getName();
139 ClassMapping classMapping = m_mappings.getClassByObf( classFile.getName() );
140 if( classMapping != null )
141 {
142 deobfName = classMapping.getDeobfName();
143 }
144
145 // decompile it!
133 StringWriter buf = new StringWriter(); 146 StringWriter buf = new StringWriter();
134 Decompiler.decompile( classFile.getObfName(), new PlainTextOutput( buf ), m_settings ); 147 Decompiler.decompile( deobfName, new PlainTextOutput( buf ), m_settings );
135 return buf.toString(); 148 return buf.toString();
136 } 149 }
137 150
138 // NOTE: these methods are a bit messy... oh well 151 // NOTE: these methods are a bit messy... oh well
139 152
140 public void rename( Entry entry, String newName ) 153 public void rename( Entry obfEntry, String newName )
141 { 154 {
142 if( entry instanceof ClassEntry ) 155 if( obfEntry instanceof ClassEntry )
143 { 156 {
144 m_renamer.setClassName( (ClassEntry)entry, newName ); 157 m_renamer.setClassName( (ClassEntry)obfEntry, newName );
145 } 158 }
146 else if( entry instanceof FieldEntry ) 159 else if( obfEntry instanceof FieldEntry )
147 { 160 {
148 m_renamer.setFieldName( (FieldEntry)entry, newName ); 161 m_renamer.setFieldName( (FieldEntry)obfEntry, newName );
149 } 162 }
150 else if( entry instanceof MethodEntry ) 163 else if( obfEntry instanceof MethodEntry )
151 { 164 {
152 m_renamer.setMethodName( (MethodEntry)entry, newName ); 165 m_renamer.setMethodName( (MethodEntry)obfEntry, newName );
153 } 166 }
154 else if( entry instanceof ArgumentEntry ) 167 else if( obfEntry instanceof ArgumentEntry )
155 { 168 {
156 m_renamer.setArgumentName( (ArgumentEntry)entry, newName ); 169 m_renamer.setArgumentName( (ArgumentEntry)obfEntry, newName );
157 } 170 }
158 else 171 else
159 { 172 {
160 throw new Error( "Unknown entry type: " + entry.getClass().getName() ); 173 throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() );
161 } 174 }
162 } 175 }
163 176
164 public Entry obfuscate( Entry in ) 177 public Entry obfuscateEntry( Entry deobfEntry )
165 { 178 {
166 Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating ); 179 Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Obfuscating );
167 if( in instanceof ClassEntry ) 180 if( deobfEntry instanceof ClassEntry )
181 {
182 return translator.translateEntry( (ClassEntry)deobfEntry );
183 }
184 else if( deobfEntry instanceof FieldEntry )
185 {
186 return translator.translateEntry( (FieldEntry)deobfEntry );
187 }
188 else if( deobfEntry instanceof MethodEntry )
189 {
190 return translator.translateEntry( (MethodEntry)deobfEntry );
191 }
192 else if( deobfEntry instanceof ArgumentEntry )
193 {
194 return translator.translateEntry( (ArgumentEntry)deobfEntry );
195 }
196 else
197 {
198 throw new Error( "Unknown entry type: " + deobfEntry.getClass().getName() );
199 }
200 }
201
202 public Entry deobfuscateEntry( Entry obfEntry )
203 {
204 Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating );
205 if( obfEntry instanceof ClassEntry )
168 { 206 {
169 return translator.translateEntry( (ClassEntry)in ); 207 return translator.translateEntry( (ClassEntry)obfEntry );
170 } 208 }
171 else if( in instanceof FieldEntry ) 209 else if( obfEntry instanceof FieldEntry )
172 { 210 {
173 return translator.translateEntry( (FieldEntry)in ); 211 return translator.translateEntry( (FieldEntry)obfEntry );
174 } 212 }
175 else if( in instanceof MethodEntry ) 213 else if( obfEntry instanceof MethodEntry )
176 { 214 {
177 return translator.translateEntry( (MethodEntry)in ); 215 return translator.translateEntry( (MethodEntry)obfEntry );
178 } 216 }
179 else if( in instanceof ArgumentEntry ) 217 else if( obfEntry instanceof ArgumentEntry )
180 { 218 {
181 return translator.translateEntry( (ArgumentEntry)in ); 219 return translator.translateEntry( (ArgumentEntry)obfEntry );
182 } 220 }
183 else 221 else
184 { 222 {
185 throw new Error( "Unknown entry type: " + in.getClass().getName() ); 223 throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() );
186 } 224 }
187 } 225 }
188 226
189 public Entry deobfuscate( Entry in ) 227 public boolean hasMapping( Entry obfEntry )
190 { 228 {
191 Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ); 229 Translator translator = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating );
192 if( in instanceof ClassEntry ) 230 if( obfEntry instanceof ClassEntry )
193 { 231 {
194 return translator.translateEntry( (ClassEntry)in ); 232 String deobfName = translator.translate( (ClassEntry)obfEntry );
233 return deobfName != null && !deobfName.equals( obfEntry.getName() );
195 } 234 }
196 else if( in instanceof FieldEntry ) 235 else if( obfEntry instanceof FieldEntry )
197 { 236 {
198 return translator.translateEntry( (FieldEntry)in ); 237 String deobfName = translator.translate( (FieldEntry)obfEntry );
238 return deobfName != null && !deobfName.equals( obfEntry.getName() );
199 } 239 }
200 else if( in instanceof MethodEntry ) 240 else if( obfEntry instanceof MethodEntry )
201 { 241 {
202 return translator.translateEntry( (MethodEntry)in ); 242 String deobfName = translator.translate( (MethodEntry)obfEntry );
243 return deobfName != null && !deobfName.equals( obfEntry.getName() );
203 } 244 }
204 else if( in instanceof ArgumentEntry ) 245 else if( obfEntry instanceof ArgumentEntry )
205 { 246 {
206 return translator.translateEntry( (ArgumentEntry)in ); 247 return translator.translate( (ArgumentEntry)obfEntry ) != null;
207 } 248 }
208 else 249 else
209 { 250 {
210 throw new Error( "Unknown entry type: " + in.getClass().getName() ); 251 throw new Error( "Unknown entry type: " + obfEntry.getClass().getName() );
211 } 252 }
212 } 253 }
213} 254}
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<Map.Entry<Entry,Token>>
52 return m_tokenToEntry.get( token ); 52 return m_tokenToEntry.get( token );
53 } 53 }
54 54
55 public Entry getEntry( int pos ) 55 public Map.Entry<Entry,Token> getEntry( int pos )
56 { 56 {
57 // linear search is fast enough for now 57 // linear search is fast enough for now
58 for( Map.Entry<Entry,Token> entry : this ) 58 for( Map.Entry<Entry,Token> entry : this )
@@ -60,7 +60,7 @@ public class SourceIndex implements Iterable<Map.Entry<Entry,Token>>
60 Token token = entry.getValue(); 60 Token token = entry.getValue();
61 if( pos >= token.start && pos <= token.end() ) 61 if( pos >= token.start && pos <= token.end() )
62 { 62 {
63 return entry.getKey(); 63 return entry;
64 } 64 }
65 } 65 }
66 return null; 66 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
59 } 59 }
60 60
61 // index the self class using the package name 61 // index the self class using the package name
62 String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() ); 62 if( m_tree.getPackageName() != null )
63 for( Tree typeTree : m_tree.getTypeDecls() )
64 { 63 {
65 if( typeTree instanceof ClassTree ) 64 String packageName = Descriptor.toJvmName( m_tree.getPackageName().toString() );
65 for( Tree typeTree : m_tree.getTypeDecls() )
66 { 66 {
67 ClassTree classTree = (ClassTree)typeTree; 67 if( typeTree instanceof ClassTree )
68 String className = classTree.getSimpleName().toString(); 68 {
69 m_classNameIndex.put( className, packageName + "/" + className ); 69 ClassTree classTree = (ClassTree)typeTree;
70 String className = classTree.getSimpleName().toString();
71 m_classNameIndex.put( className, packageName + "/" + className );
72 }
70 } 73 }
71 } 74 }
72 } 75 }
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;
18import java.awt.event.ActionListener; 18import java.awt.event.ActionListener;
19import java.io.IOException; 19import java.io.IOException;
20 20
21import javax.swing.Box;
22import javax.swing.BoxLayout;
23import javax.swing.JButton; 21import javax.swing.JButton;
24import javax.swing.JFrame; 22import javax.swing.JFrame;
25import javax.swing.JLabel; 23import 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;
19import javax.swing.text.Highlighter; 19import javax.swing.text.Highlighter;
20import javax.swing.text.JTextComponent; 20import javax.swing.text.JTextComponent;
21 21
22public class BoxHighlightPainter implements Highlighter.HighlightPainter 22public abstract class BoxHighlightPainter implements Highlighter.HighlightPainter
23{ 23{
24 private static final Color FillColor = new Color( 230, 230, 230 ); 24 private Color m_fillColor;
25 private static final Color BorderColor = new Color( 100, 100, 100 ); 25 private Color m_borderColor;
26
27 protected BoxHighlightPainter( Color fillColor, Color borderColor )
28 {
29 m_fillColor = fillColor;
30 m_borderColor = borderColor;
31 }
26 32
27 @Override 33 @Override
28 public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text ) 34 public void paint( Graphics g, int start, int end, Shape shape, JTextComponent text )
@@ -39,11 +45,11 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter
39 bounds.height -= 2; 45 bounds.height -= 2;
40 46
41 // fill the area 47 // fill the area
42 g.setColor( FillColor ); 48 g.setColor( m_fillColor );
43 g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); 49 g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 );
44 50
45 // draw a box around the area 51 // draw a box around the area
46 g.setColor( BorderColor ); 52 g.setColor( m_borderColor );
47 g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 ); 53 g.drawRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, 4, 4 );
48 } 54 }
49 catch( BadLocationException ex ) 55 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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Graphics;
14import java.awt.Shape;
15
16import javax.swing.text.DefaultCaret;
17import javax.swing.text.Highlighter;
18import javax.swing.text.JTextComponent;
19
20public class BrowserCaret extends DefaultCaret
21{
22 private static final long serialVersionUID = 1158977422507969940L;
23
24 private static final Highlighter.HighlightPainter m_selectionPainter = new Highlighter.HighlightPainter( )
25 {
26 @Override
27 public void paint( Graphics g, int p0, int p1, Shape bounds, JTextComponent c )
28 {
29 // don't paint anything
30 }
31 };
32
33 @Override
34 public boolean isSelectionVisible( )
35 {
36 return false;
37 }
38
39 @Override
40 public boolean isVisible( )
41 {
42 return true;
43 }
44
45 @Override
46 public Highlighter.HighlightPainter getSelectionPainter( )
47 {
48 return m_selectionPainter;
49 }
50}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Component;
14import java.util.Map;
15
16import javassist.bytecode.Descriptor;
17
18import javax.swing.DefaultListCellRenderer;
19import javax.swing.JLabel;
20import javax.swing.JList;
21import javax.swing.ListCellRenderer;
22
23import cuchaz.enigma.ClassFile;
24
25public class DeobfuscatedClassListCellRenderer implements ListCellRenderer<Map.Entry<ClassFile,String>>
26{
27 private DefaultListCellRenderer m_defaultRenderer;
28
29 public DeobfuscatedClassListCellRenderer( )
30 {
31 m_defaultRenderer = new DefaultListCellRenderer();
32 }
33
34 @Override
35 public Component getListCellRendererComponent( JList<? extends Map.Entry<ClassFile,String>> list, Map.Entry<ClassFile,String> entry, int index, boolean isSelected, boolean hasFocus )
36 {
37 JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, entry, index, isSelected, hasFocus );
38
39 label.setText( Descriptor.toJavaName( entry.getValue() ) );
40
41 return label;
42 }
43}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Color;
14
15public class DeobfuscatedHighlightPainter extends BoxHighlightPainter
16{
17 public DeobfuscatedHighlightPainter( )
18 {
19 // green ish
20 super( new Color( 220, 255, 220 ), new Color( 80, 160, 80 ) );
21 }
22}
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 @@
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui;
12 12
13import java.awt.BorderLayout; 13import java.awt.BorderLayout;
14import java.awt.Color;
14import java.awt.Container; 15import java.awt.Container;
15import java.awt.Dimension; 16import java.awt.Dimension;
16import java.awt.FlowLayout; 17import java.awt.FlowLayout;
@@ -18,18 +19,21 @@ import java.awt.Font;
18import java.awt.GridLayout; 19import java.awt.GridLayout;
19import java.awt.event.ActionEvent; 20import java.awt.event.ActionEvent;
20import java.awt.event.ActionListener; 21import java.awt.event.ActionListener;
22import java.awt.event.KeyAdapter;
23import java.awt.event.KeyEvent;
21import java.awt.event.MouseAdapter; 24import java.awt.event.MouseAdapter;
22import java.awt.event.MouseEvent; 25import java.awt.event.MouseEvent;
26import java.awt.event.WindowAdapter;
27import java.awt.event.WindowEvent;
23import java.io.File; 28import java.io.File;
24import java.io.IOException; 29import java.io.IOException;
25import java.util.Collections; 30import java.util.Collections;
26import java.util.Comparator; 31import java.util.Comparator;
27import java.util.List; 32import java.util.List;
33import java.util.Map;
28import java.util.Vector; 34import java.util.Vector;
29 35
30import javax.swing.BorderFactory; 36import javax.swing.BorderFactory;
31import javax.swing.BoxLayout;
32import javax.swing.JButton;
33import javax.swing.JEditorPane; 37import javax.swing.JEditorPane;
34import javax.swing.JFileChooser; 38import javax.swing.JFileChooser;
35import javax.swing.JFrame; 39import javax.swing.JFrame;
@@ -38,7 +42,9 @@ import javax.swing.JList;
38import javax.swing.JMenu; 42import javax.swing.JMenu;
39import javax.swing.JMenuBar; 43import javax.swing.JMenuBar;
40import javax.swing.JMenuItem; 44import javax.swing.JMenuItem;
45import javax.swing.JOptionPane;
41import javax.swing.JPanel; 46import javax.swing.JPanel;
47import javax.swing.JPopupMenu;
42import javax.swing.JScrollPane; 48import javax.swing.JScrollPane;
43import javax.swing.JSplitPane; 49import javax.swing.JSplitPane;
44import javax.swing.JTextField; 50import javax.swing.JTextField;
@@ -47,17 +53,19 @@ import javax.swing.WindowConstants;
47import javax.swing.event.CaretEvent; 53import javax.swing.event.CaretEvent;
48import javax.swing.event.CaretListener; 54import javax.swing.event.CaretListener;
49import javax.swing.text.BadLocationException; 55import javax.swing.text.BadLocationException;
56import javax.swing.text.Highlighter;
50 57
51import jsyntaxpane.DefaultSyntaxKit; 58import jsyntaxpane.DefaultSyntaxKit;
59import jsyntaxpane.SyntaxDocument;
52import jsyntaxpane.Token; 60import jsyntaxpane.Token;
53import cuchaz.enigma.ClassFile; 61import cuchaz.enigma.ClassFile;
54import cuchaz.enigma.Constants; 62import cuchaz.enigma.Constants;
55import cuchaz.enigma.analysis.SourceIndex;
56import cuchaz.enigma.mapping.ArgumentEntry; 63import cuchaz.enigma.mapping.ArgumentEntry;
57import cuchaz.enigma.mapping.ClassEntry; 64import cuchaz.enigma.mapping.ClassEntry;
58import cuchaz.enigma.mapping.Entry; 65import cuchaz.enigma.mapping.Entry;
59import cuchaz.enigma.mapping.EntryPair; 66import cuchaz.enigma.mapping.EntryPair;
60import cuchaz.enigma.mapping.FieldEntry; 67import cuchaz.enigma.mapping.FieldEntry;
68import cuchaz.enigma.mapping.IllegalNameException;
61import cuchaz.enigma.mapping.MethodEntry; 69import cuchaz.enigma.mapping.MethodEntry;
62 70
63public class Gui 71public class Gui
@@ -86,14 +94,11 @@ public class Gui
86 // controls 94 // controls
87 private JFrame m_frame; 95 private JFrame m_frame;
88 private JList<ClassFile> m_obfClasses; 96 private JList<ClassFile> m_obfClasses;
89 private JList<ClassFile> m_deobfClasses; 97 private JList<Map.Entry<ClassFile,String>> m_deobfClasses;
90 private JEditorPane m_editor; 98 private JEditorPane m_editor;
91 private JPanel m_actionPanel; 99 private JPanel m_infoPanel;
92 private JPanel m_renamePanel; 100 private BoxHighlightPainter m_obfuscatedHighlightPainter;
93 private JLabel m_typeLabel; 101 private BoxHighlightPainter m_deobfuscatedHighlightPainter;
94 private JTextField m_nameField;
95 private JButton m_renameButton;
96 private BoxHighlightPainter m_highlightPainter;
97 102
98 // dynamic menu items 103 // dynamic menu items
99 private JMenuItem m_closeJarMenu; 104 private JMenuItem m_closeJarMenu;
@@ -101,6 +106,7 @@ public class Gui
101 private JMenuItem m_saveMappingsMenu; 106 private JMenuItem m_saveMappingsMenu;
102 private JMenuItem m_saveMappingsAsMenu; 107 private JMenuItem m_saveMappingsAsMenu;
103 private JMenuItem m_closeMappingsMenu; 108 private JMenuItem m_closeMappingsMenu;
109 private JMenuItem m_renameMenu;
104 110
105 // state 111 // state
106 private EntryPair<Entry> m_selectedEntryPair; 112 private EntryPair<Entry> m_selectedEntryPair;
@@ -124,7 +130,7 @@ public class Gui
124 m_obfClasses = new JList<ClassFile>(); 130 m_obfClasses = new JList<ClassFile>();
125 m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); 131 m_obfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
126 m_obfClasses.setLayoutOrientation( JList.VERTICAL ); 132 m_obfClasses.setLayoutOrientation( JList.VERTICAL );
127 m_obfClasses.setCellRenderer( new ClassListCellRenderer() ); 133 m_obfClasses.setCellRenderer( new ObfuscatedClassListCellRenderer() );
128 m_obfClasses.addMouseListener( new MouseAdapter() 134 m_obfClasses.addMouseListener( new MouseAdapter()
129 { 135 {
130 public void mouseClicked( MouseEvent event ) 136 public void mouseClicked( MouseEvent event )
@@ -146,20 +152,20 @@ public class Gui
146 obfPanel.add( obfScroller, BorderLayout.CENTER ); 152 obfPanel.add( obfScroller, BorderLayout.CENTER );
147 153
148 // init deobfuscated classes list 154 // init deobfuscated classes list
149 m_deobfClasses = new JList<ClassFile>(); 155 m_deobfClasses = new JList<Map.Entry<ClassFile,String>>();
150 m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); 156 m_deobfClasses.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
151 m_deobfClasses.setLayoutOrientation( JList.VERTICAL ); 157 m_deobfClasses.setLayoutOrientation( JList.VERTICAL );
152 m_deobfClasses.setCellRenderer( new ClassListCellRenderer() ); 158 m_deobfClasses.setCellRenderer( new DeobfuscatedClassListCellRenderer() );
153 m_deobfClasses.addMouseListener( new MouseAdapter() 159 m_deobfClasses.addMouseListener( new MouseAdapter()
154 { 160 {
155 public void mouseClicked( MouseEvent event ) 161 public void mouseClicked( MouseEvent event )
156 { 162 {
157 if( event.getClickCount() == 2 ) 163 if( event.getClickCount() == 2 )
158 { 164 {
159 ClassFile selected = m_deobfClasses.getSelectedValue(); 165 Map.Entry<ClassFile,String> selected = m_deobfClasses.getSelectedValue();
160 if( selected != null ) 166 if( selected != null )
161 { 167 {
162 m_controller.deobfuscateClass( selected ); 168 m_controller.deobfuscateClass( selected.getKey() );
163 } 169 }
164 } 170 }
165 } 171 }
@@ -170,39 +176,20 @@ public class Gui
170 deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH ); 176 deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH );
171 deobfPanel.add( deobfScroller, BorderLayout.CENTER ); 177 deobfPanel.add( deobfScroller, BorderLayout.CENTER );
172 178
173 // init action panel 179 // init info panel
174 m_actionPanel = new JPanel(); 180 m_infoPanel = new JPanel();
175 m_actionPanel.setLayout( new BoxLayout( m_actionPanel, BoxLayout.Y_AXIS ) ); 181 m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) );
176 m_actionPanel.setPreferredSize( new Dimension( 0, 120 ) ); 182 m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) );
177 m_actionPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) ); 183 m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) );
178 m_nameField = new JTextField( 26 );
179 m_renameButton = new JButton( "Rename" );
180 m_renameButton.addActionListener( new ActionListener( )
181 {
182 @Override
183 public void actionPerformed( ActionEvent event )
184 {
185 if( m_selectedEntryPair != null )
186 {
187 m_controller.rename( m_selectedEntryPair.obf, m_nameField.getText() );
188 }
189 }
190 } );
191 m_renamePanel = new JPanel();
192 m_renamePanel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) );
193 m_typeLabel = new JLabel( "LongName:", JLabel.RIGHT );
194 // NOTE: this looks ridiculous, but it fixes the label size to the size of current text
195 m_typeLabel.setPreferredSize( m_typeLabel.getPreferredSize() );
196 m_renamePanel.add( m_typeLabel );
197 m_renamePanel.add( m_nameField );
198 m_renamePanel.add( m_renameButton );
199 clearEntryPair(); 184 clearEntryPair();
200 185
201 // init editor 186 // init editor
202 DefaultSyntaxKit.initKit(); 187 DefaultSyntaxKit.initKit();
203 m_highlightPainter = new BoxHighlightPainter(); 188 m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter();
189 m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter();
204 m_editor = new JEditorPane(); 190 m_editor = new JEditorPane();
205 m_editor.setEditable( false ); 191 m_editor.setEditable( false );
192 m_editor.setCaret( new BrowserCaret() );
206 JScrollPane sourceScroller = new JScrollPane( m_editor ); 193 JScrollPane sourceScroller = new JScrollPane( m_editor );
207 m_editor.setContentType( "text/java" ); 194 m_editor.setContentType( "text/java" );
208 m_editor.addCaretListener( new CaretListener( ) 195 m_editor.addCaretListener( new CaretListener( )
@@ -214,19 +201,55 @@ public class Gui
214 if( m_selectedEntryPair != null ) 201 if( m_selectedEntryPair != null )
215 { 202 {
216 showEntryPair( m_selectedEntryPair ); 203 showEntryPair( m_selectedEntryPair );
204 m_renameMenu.setEnabled( true );
217 } 205 }
218 else 206 else
219 { 207 {
220 clearEntryPair(); 208 clearEntryPair();
209 m_renameMenu.setEnabled( false );
221 } 210 }
222 } 211 }
223 } ); 212 } );
213 m_editor.addKeyListener( new KeyAdapter( )
214 {
215 @Override
216 public void keyPressed( KeyEvent event )
217 {
218 switch( event.getKeyCode() )
219 {
220 case KeyEvent.VK_R:
221 startRename();
222 break;
223 }
224 }
225 } );
226
227 // turn off token highlighting (it's wrong most of the time anyway...)
228 DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit();
229 kit.toggleComponent( m_editor, "jsyntaxpane.components.TokenMarker" );
230
231 // init editor popup menu
232 JPopupMenu popupMenu = new JPopupMenu();
233 m_editor.setComponentPopupMenu( popupMenu );
234 {
235 JMenuItem menu = new JMenuItem( "Rename" );
236 menu.addActionListener( new ActionListener( )
237 {
238 @Override
239 public void actionPerformed( ActionEvent event )
240 {
241 startRename();
242 }
243 } );
244 popupMenu.add( menu );
245 m_renameMenu = menu;
246 }
224 247
225 // layout controls 248 // layout controls
226 JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel ); 249 JSplitPane splitLeft = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel );
227 JPanel rightPanel = new JPanel(); 250 JPanel rightPanel = new JPanel();
228 rightPanel.setLayout( new BorderLayout() ); 251 rightPanel.setLayout( new BorderLayout() );
229 rightPanel.add( m_actionPanel, BorderLayout.NORTH ); 252 rightPanel.add( m_infoPanel, BorderLayout.NORTH );
230 rightPanel.add( sourceScroller, BorderLayout.CENTER ); 253 rightPanel.add( sourceScroller, BorderLayout.CENTER );
231 JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel ); 254 JSplitPane splitMain = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, rightPanel );
232 pane.add( splitMain, BorderLayout.CENTER ); 255 pane.add( splitMain, BorderLayout.CENTER );
@@ -353,6 +376,19 @@ public class Gui
353 } ); 376 } );
354 m_closeMappingsMenu = item; 377 m_closeMappingsMenu = item;
355 } 378 }
379 menu.addSeparator();
380 {
381 JMenuItem item = new JMenuItem( "Exit" );
382 menu.add( item );
383 item.addActionListener( new ActionListener( )
384 {
385 @Override
386 public void actionPerformed( ActionEvent event )
387 {
388 close();
389 }
390 } );
391 }
356 } 392 }
357 { 393 {
358 JMenu menu = new JMenu( "Help" ); 394 JMenu menu = new JMenu( "Help" );
@@ -374,12 +410,21 @@ public class Gui
374 // init state 410 // init state
375 onCloseJar(); 411 onCloseJar();
376 412
413 m_frame.addWindowListener( new WindowAdapter( )
414 {
415 @Override
416 public void windowClosing( WindowEvent event )
417 {
418 close();
419 }
420 } );
421
377 // show the frame 422 // show the frame
378 pane.doLayout(); 423 pane.doLayout();
379 m_frame.setSize( 800, 600 ); 424 m_frame.setSize( 800, 600 );
380 m_frame.setMinimumSize( new Dimension( 640, 480 ) ); 425 m_frame.setMinimumSize( new Dimension( 640, 480 ) );
381 m_frame.setVisible( true ); 426 m_frame.setVisible( true );
382 m_frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); 427 m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );
383 } 428 }
384 429
385 public GuiController getController( ) 430 public GuiController getController( )
@@ -430,15 +475,15 @@ public class Gui
430 } 475 }
431 } 476 }
432 477
433 public void setDeobfClasses( List<ClassFile> classes ) 478 public void setDeobfClasses( Map<ClassFile,String> deobfClasses )
434 { 479 {
435 if( classes != null ) 480 if( deobfClasses != null )
436 { 481 {
437 m_deobfClasses.setListData( new Vector<ClassFile>( classes ) ); 482 m_deobfClasses.setListData( new Vector<Map.Entry<ClassFile,String>>( deobfClasses.entrySet() ) );
438 } 483 }
439 else 484 else
440 { 485 {
441 m_deobfClasses.setListData( new Vector<ClassFile>() ); 486 m_deobfClasses.setListData( new Vector<Map.Entry<ClassFile,String>>() );
442 } 487 }
443 } 488 }
444 489
@@ -450,48 +495,77 @@ public class Gui
450 495
451 public void setSource( String source ) 496 public void setSource( String source )
452 { 497 {
453 setSource( source, null ); 498 setSource( source, 0 );
454 } 499 }
455 500
456 public void setSource( String source, SourceIndex index ) 501 public void setSource( String source, int lineNum )
457 { 502 {
503 // remove any old highlighters
504 m_editor.getHighlighter().removeAllHighlights();
505
458 m_editor.setText( source ); 506 m_editor.setText( source );
459 setHighlightedTokens( null ); 507
508 // count the offset of the target line
509 String text = m_editor.getText();
510 int pos = 0;
511 int numLines = 0;
512 for( ; pos < text.length(); pos++ )
513 {
514 if( numLines == lineNum )
515 {
516 break;
517 }
518 if( text.charAt( pos ) == '\n' )
519 {
520 numLines++;
521 }
522 }
523
524 // put the caret at the line number
525 m_editor.setCaretPosition( pos );
526 m_editor.grabFocus();
460 } 527 }
461 528
462 public void setHighlightedTokens( Iterable<Token> tokens ) 529 public void setHighlightedTokens( Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens )
463 { 530 {
464 // remove any old highlighters 531 // remove any old highlighters
465 m_editor.getHighlighter().removeAllHighlights(); 532 m_editor.getHighlighter().removeAllHighlights();
466 533
467 if( tokens == null ) 534 // color things based on the index
535 if( obfuscatedTokens != null )
468 { 536 {
469 return; 537 setHighlightedTokens( obfuscatedTokens, m_obfuscatedHighlightPainter );
538 }
539 if( deobfuscatedTokens != null )
540 {
541 setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter );
470 } 542 }
471 543
472 // color things based on the index 544 redraw();
545 }
546
547 private void setHighlightedTokens( Iterable<Token> tokens, Highlighter.HighlightPainter painter )
548 {
473 for( Token token : tokens ) 549 for( Token token : tokens )
474 { 550 {
475 try 551 try
476 { 552 {
477 m_editor.getHighlighter().addHighlight( token.start, token.end(), m_highlightPainter ); 553 m_editor.getHighlighter().addHighlight( token.start, token.end(), painter );
478 } 554 }
479 catch( BadLocationException ex ) 555 catch( BadLocationException ex )
480 { 556 {
481 throw new IllegalArgumentException( ex ); 557 throw new IllegalArgumentException( ex );
482 } 558 }
483 } 559 }
484
485 redraw();
486 } 560 }
487 561
488 private void clearEntryPair( ) 562 private void clearEntryPair( )
489 { 563 {
490 m_actionPanel.removeAll(); 564 m_infoPanel.removeAll();
491 JLabel label = new JLabel( "No identifier selected" ); 565 JLabel label = new JLabel( "No identifier selected" );
492 unboldLabel( label ); 566 unboldLabel( label );
493 label.setHorizontalAlignment( JLabel.CENTER ); 567 label.setHorizontalAlignment( JLabel.CENTER );
494 m_actionPanel.add( label ); 568 m_infoPanel.add( label );
495 569
496 redraw(); 570 redraw();
497 } 571 }
@@ -507,30 +581,22 @@ public class Gui
507 581
508 m_selectedEntryPair = pair; 582 m_selectedEntryPair = pair;
509 583
510 // layout the action panel 584 m_infoPanel.removeAll();
511 m_actionPanel.removeAll();
512 m_actionPanel.add( m_renamePanel );
513 m_nameField.setText( pair.deobf.getName() );
514
515 // layout the dynamic section
516 JPanel dynamicPanel = new JPanel();
517 dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) );
518 m_actionPanel.add( dynamicPanel );
519 if( pair.deobf instanceof ClassEntry ) 585 if( pair.deobf instanceof ClassEntry )
520 { 586 {
521 showClassEntryPair( (EntryPair<? extends ClassEntry>)pair, dynamicPanel ); 587 showClassEntryPair( (EntryPair<? extends ClassEntry>)pair );
522 } 588 }
523 else if( pair.deobf instanceof FieldEntry ) 589 else if( pair.deobf instanceof FieldEntry )
524 { 590 {
525 showFieldEntryPair( (EntryPair<? extends FieldEntry>)pair, dynamicPanel ); 591 showFieldEntryPair( (EntryPair<? extends FieldEntry>)pair );
526 } 592 }
527 else if( pair.deobf instanceof MethodEntry ) 593 else if( pair.deobf instanceof MethodEntry )
528 { 594 {
529 showMethodEntryPair( (EntryPair<? extends MethodEntry>)pair, dynamicPanel ); 595 showMethodEntryPair( (EntryPair<? extends MethodEntry>)pair );
530 } 596 }
531 else if( pair.deobf instanceof ArgumentEntry ) 597 else if( pair.deobf instanceof ArgumentEntry )
532 { 598 {
533 showArgumentEntryPair( (EntryPair<? extends ArgumentEntry>)pair, dynamicPanel ); 599 showArgumentEntryPair( (EntryPair<? extends ArgumentEntry>)pair );
534 } 600 }
535 else 601 else
536 { 602 {
@@ -540,30 +606,30 @@ public class Gui
540 redraw(); 606 redraw();
541 } 607 }
542 608
543 private void showClassEntryPair( EntryPair<? extends ClassEntry> pair, JPanel panel ) 609 private void showClassEntryPair( EntryPair<? extends ClassEntry> pair )
544 { 610 {
545 m_typeLabel.setText( "Class: " ); 611 addNameValue( m_infoPanel, "Class", pair.deobf.getName() );
546 } 612 }
547 613
548 private void showFieldEntryPair( EntryPair<? extends FieldEntry> pair, JPanel panel ) 614 private void showFieldEntryPair( EntryPair<? extends FieldEntry> pair )
549 { 615 {
550 m_typeLabel.setText( "Field: " ); 616 addNameValue( m_infoPanel, "Field", pair.deobf.getName() );
551 addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); 617 addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() );
552 } 618 }
553 619
554 private void showMethodEntryPair( EntryPair<? extends MethodEntry> pair, JPanel panel ) 620 private void showMethodEntryPair( EntryPair<? extends MethodEntry> pair )
555 { 621 {
556 m_typeLabel.setText( "Method: " ); 622 addNameValue( m_infoPanel, "Method", pair.deobf.getName() );
557 addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); 623 addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() );
558 addNameValue( panel, "Signature", pair.obf.getSignature() + " <-> " + pair.deobf.getSignature() ); 624 addNameValue( m_infoPanel, "Signature", pair.deobf.getSignature() );
559 } 625 }
560 626
561 private void showArgumentEntryPair( EntryPair<? extends ArgumentEntry> pair, JPanel panel ) 627 private void showArgumentEntryPair( EntryPair<? extends ArgumentEntry> pair )
562 { 628 {
563 m_typeLabel.setText( "Argument: " ); 629 addNameValue( m_infoPanel, "Argument", pair.deobf.getName() );
564 addNameValue( panel, "Class", pair.obf.getClassEntry().getName() + " <-> " + pair.deobf.getClassEntry().getName() ); 630 addNameValue( m_infoPanel, "Class", pair.deobf.getClassEntry().getName() );
565 addNameValue( panel, "Method", pair.obf.getMethodEntry().getName() + " <-> " + pair.deobf.getMethodEntry().getName() ); 631 addNameValue( m_infoPanel, "Method", pair.deobf.getMethodEntry().getName() );
566 addNameValue( panel, "Index", Integer.toString( pair.obf.getIndex() ) ); 632 addNameValue( m_infoPanel, "Index", Integer.toString( pair.deobf.getIndex() ) );
567 } 633 }
568 634
569 private void addNameValue( JPanel container, String name, String value ) 635 private void addNameValue( JPanel container, String name, String value )
@@ -578,6 +644,120 @@ public class Gui
578 644
579 panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) ); 645 panel.add( unboldLabel( new JLabel( value, JLabel.LEFT ) ) );
580 } 646 }
647
648 private void startRename( )
649 {
650 // init the text box
651 final JTextField text = new JTextField();
652 text.setText( m_selectedEntryPair.deobf.getName() );
653 text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) );
654 text.addKeyListener( new KeyAdapter( )
655 {
656 @Override
657 public void keyPressed( KeyEvent event )
658 {
659 switch( event.getKeyCode() )
660 {
661 case KeyEvent.VK_ENTER:
662 finishRename( text, true );
663 break;
664
665 case KeyEvent.VK_ESCAPE:
666 finishRename( text, false );
667 break;
668 }
669 }
670 } );
671
672 // find the label with the name and replace it with the text box
673 JPanel panel = (JPanel)m_infoPanel.getComponent( 0 );
674 panel.remove( panel.getComponentCount() - 1 );
675 panel.add( text );
676 text.grabFocus();
677 text.selectAll();
678
679 redraw();
680 }
681
682 private void finishRename( JTextField text, boolean saveName )
683 {
684 String newName = text.getText();
685 if( saveName && newName != null && newName.length() > 0 )
686 {
687 SyntaxDocument doc = (SyntaxDocument)m_editor.getDocument();
688 int lineNum = doc.getLineNumberAt( m_editor.getCaretPosition() );
689 try
690 {
691 m_controller.rename( m_selectedEntryPair.obf, newName, lineNum );
692 }
693 catch( IllegalNameException ex )
694 {
695 text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) );
696 }
697 return;
698 }
699
700 // abort the rename
701 JPanel panel = (JPanel)m_infoPanel.getComponent( 0 );
702 panel.remove( panel.getComponentCount() - 1 );
703 panel.add( unboldLabel( new JLabel( m_selectedEntryPair.deobf.getName(), JLabel.LEFT ) ) );
704
705 m_editor.grabFocus();
706
707 redraw();
708 }
709
710 private void close( )
711 {
712 if( !m_controller.isDirty() )
713 {
714 // everything is saved, we can exit safely
715 m_frame.dispose();
716 }
717 else
718 {
719 // ask to save before closing
720 String[] options = {
721 "Save and exit",
722 "Discard changes",
723 "Cancel"
724 };
725 int response = JOptionPane.showOptionDialog(
726 m_frame,
727 "Your mappings have not been saved yet. Do you want to save?",
728 "Save your changes?",
729 JOptionPane.YES_NO_CANCEL_OPTION,
730 JOptionPane.QUESTION_MESSAGE,
731 null,
732 options,
733 options[2]
734 );
735 switch( response )
736 {
737 case JOptionPane.YES_OPTION: // save and exit
738 if( m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
739 {
740 try
741 {
742 m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() );
743 m_frame.dispose();
744 }
745 catch( IOException ex )
746 {
747 throw new Error( ex );
748 }
749 }
750 break;
751
752 case JOptionPane.NO_OPTION:
753 // don't save, exit
754 m_frame.dispose();
755 break;
756
757 // cancel means do nothing
758 }
759 }
760 }
581 761
582 private JLabel unboldLabel( JLabel label ) 762 private JLabel unboldLabel( JLabel label )
583 { 763 {
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;
14import java.io.FileReader; 14import java.io.FileReader;
15import java.io.FileWriter; 15import java.io.FileWriter;
16import java.io.IOException; 16import java.io.IOException;
17import java.util.ArrayList;
18import java.util.List; 17import java.util.List;
18import java.util.Map;
19
20import jsyntaxpane.Token;
21
22import com.beust.jcommander.internal.Lists;
23import com.beust.jcommander.internal.Maps;
19 24
20import cuchaz.enigma.ClassFile; 25import cuchaz.enigma.ClassFile;
21import cuchaz.enigma.Deobfuscator; 26import cuchaz.enigma.Deobfuscator;
22import cuchaz.enigma.analysis.Analyzer; 27import cuchaz.enigma.analysis.Analyzer;
23import cuchaz.enigma.analysis.SourceIndex; 28import cuchaz.enigma.analysis.SourceIndex;
24import cuchaz.enigma.mapping.ClassEntry;
25import cuchaz.enigma.mapping.Entry; 29import cuchaz.enigma.mapping.Entry;
26import cuchaz.enigma.mapping.EntryPair; 30import cuchaz.enigma.mapping.EntryPair;
27import cuchaz.enigma.mapping.MappingsReader; 31import cuchaz.enigma.mapping.MappingsReader;
@@ -33,6 +37,7 @@ public class GuiController
33 private Gui m_gui; 37 private Gui m_gui;
34 private SourceIndex m_index; 38 private SourceIndex m_index;
35 private ClassFile m_currentFile; 39 private ClassFile m_currentFile;
40 private boolean m_isDirty;
36 41
37 public GuiController( Gui gui ) 42 public GuiController( Gui gui )
38 { 43 {
@@ -40,6 +45,12 @@ public class GuiController
40 m_deobfuscator = null; 45 m_deobfuscator = null;
41 m_index = null; 46 m_index = null;
42 m_currentFile = null; 47 m_currentFile = null;
48 m_isDirty = false;
49 }
50
51 public boolean isDirty( )
52 {
53 return m_isDirty;
43 } 54 }
44 55
45 public void openJar( File file ) 56 public void openJar( File file )
@@ -62,6 +73,7 @@ public class GuiController
62 FileReader in = new FileReader( file ); 73 FileReader in = new FileReader( file );
63 m_deobfuscator.setMappings( new MappingsReader().read( in ) ); 74 m_deobfuscator.setMappings( new MappingsReader().read( in ) );
64 in.close(); 75 in.close();
76 m_isDirty = false;
65 m_gui.setMappingsFile( file ); 77 m_gui.setMappingsFile( file );
66 refreshClasses(); 78 refreshClasses();
67 refreshOpenFiles(); 79 refreshOpenFiles();
@@ -73,12 +85,14 @@ public class GuiController
73 FileWriter out = new FileWriter( file ); 85 FileWriter out = new FileWriter( file );
74 new MappingsWriter().write( out, m_deobfuscator.getMappings() ); 86 new MappingsWriter().write( out, m_deobfuscator.getMappings() );
75 out.close(); 87 out.close();
88 m_isDirty = false;
76 } 89 }
77 90
78 public void closeMappings( ) 91 public void closeMappings( )
79 { 92 {
80 m_deobfuscator.setMappings( null ); 93 m_deobfuscator.setMappings( null );
81 m_gui.setMappingsFile( null ); 94 m_gui.setMappingsFile( null );
95 refreshClasses();
82 refreshOpenFiles(); 96 refreshOpenFiles();
83 } 97 }
84 98
@@ -95,52 +109,63 @@ public class GuiController
95 return null; 109 return null;
96 } 110 }
97 111
98 Entry deobfEntry = m_index.getEntry( pos ); 112 Map.Entry<Entry,Token> deobfEntryAndToken = m_index.getEntry( pos );
99 if( deobfEntry == null ) 113 if( deobfEntryAndToken == null )
100 { 114 {
101 return null; 115 return null;
102 } 116 }
103 return new EntryPair<Entry>( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ); 117 Entry deobfEntry = deobfEntryAndToken.getKey();
118 Token token = deobfEntryAndToken.getValue();
119 return new EntryPair<Entry>( m_deobfuscator.obfuscateEntry( deobfEntry ), deobfEntry, token );
104 } 120 }
105 121
106 public void rename( Entry obfsEntry, String newName ) 122 public boolean entryHasMapping( int pos )
107 { 123 {
108 m_deobfuscator.rename( obfsEntry, newName ); 124 EntryPair<Entry> pair = getEntryPair( pos );
109 125 if( pair == null || pair.obf == null )
110 // did we rename the current file?
111 if( obfsEntry instanceof ClassEntry )
112 { 126 {
113 ClassEntry classEntry = (ClassEntry)obfsEntry; 127 return false;
114
115 // update the current file
116 if( classEntry.getName().equals( m_currentFile.getName() ) )
117 {
118 m_currentFile = new ClassFile( newName );
119 }
120 } 128 }
121 129 return m_deobfuscator.hasMapping( pair.obf );
122 refreshOpenFiles(); 130 }
131
132 public void rename( Entry obfsEntry, String newName, int lineNum )
133 {
134 m_deobfuscator.rename( obfsEntry, newName );
135 m_isDirty = true;
136 refreshClasses();
137 refreshOpenFiles( lineNum );
123 } 138 }
124 139
125 private void refreshClasses( ) 140 private void refreshClasses( )
126 { 141 {
127 List<ClassFile> obfClasses = new ArrayList<ClassFile>(); 142 List<ClassFile> obfClasses = Lists.newArrayList();
128 List<ClassFile> deobfClasses = new ArrayList<ClassFile>(); 143 Map<ClassFile,String> deobfClasses = Maps.newHashMap();
129 m_deobfuscator.getSortedClasses( obfClasses, deobfClasses ); 144 m_deobfuscator.getSeparatedClasses( obfClasses, deobfClasses );
130 m_gui.setObfClasses( obfClasses ); 145 m_gui.setObfClasses( obfClasses );
131 m_gui.setDeobfClasses( deobfClasses ); 146 m_gui.setDeobfClasses( deobfClasses );
132 } 147 }
133 148
134 private void refreshOpenFiles( ) 149 private void refreshOpenFiles( )
135 { 150 {
151 refreshOpenFiles( 0 );
152 }
153
154 private void refreshOpenFiles( int lineNum )
155 {
136 if( m_currentFile != null ) 156 if( m_currentFile != null )
137 { 157 {
138 deobfuscate( m_currentFile ); 158 deobfuscate( m_currentFile, lineNum );
139 } 159 }
140 } 160 }
141 161
142 private void deobfuscate( final ClassFile classFile ) 162 private void deobfuscate( final ClassFile classFile )
143 { 163 {
164 deobfuscate( classFile, 0 );
165 }
166
167 private void deobfuscate( final ClassFile classFile, final int lineNum )
168 {
144 m_gui.setSource( "(deobfuscating...)" ); 169 m_gui.setSource( "(deobfuscating...)" );
145 170
146 // run the deobfuscator in a separate thread so we don't block the GUI event queue 171 // run the deobfuscator in a separate thread so we don't block the GUI event queue
@@ -149,13 +174,28 @@ public class GuiController
149 @Override 174 @Override
150 public void run( ) 175 public void run( )
151 { 176 {
152 // deobfuscate the bytecode 177 // deobfuscate,decompile the bytecode
153 String source = m_deobfuscator.getSource( classFile ); 178 String source = m_deobfuscator.getSource( classFile );
154 m_gui.setSource( source ); 179 m_gui.setSource( source, lineNum );
155 180
156 // index the source file 181 // index the source file
157 m_index = Analyzer.analyze( classFile.getName(), source ); 182 m_index = Analyzer.analyze( classFile.getName(), source );
158 m_gui.setHighlightedTokens( m_index.tokens() ); 183
184 // set the highlighted tokens
185 List<Token> obfuscatedTokens = Lists.newArrayList();
186 List<Token> deobfuscatedTokens = Lists.newArrayList();
187 for( Token token : m_index.tokens() )
188 {
189 if( entryHasMapping( token.start ) )
190 {
191 deobfuscatedTokens.add( token );
192 }
193 else
194 {
195 obfuscatedTokens.add( token );
196 }
197 }
198 m_gui.setHighlightedTokens( obfuscatedTokens, deobfuscatedTokens );
159 } 199 }
160 }.start(); 200 }.start();
161 } 201 }
diff --git a/src/cuchaz/enigma/gui/ClassListCellRenderer.java b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java
index 302f140..d46e1ae 100644
--- a/src/cuchaz/enigma/gui/ClassListCellRenderer.java
+++ b/src/cuchaz/enigma/gui/ObfuscatedClassListCellRenderer.java
@@ -12,6 +12,8 @@ package cuchaz.enigma.gui;
12 12
13import java.awt.Component; 13import java.awt.Component;
14 14
15import javassist.bytecode.Descriptor;
16
15import javax.swing.DefaultListCellRenderer; 17import javax.swing.DefaultListCellRenderer;
16import javax.swing.JLabel; 18import javax.swing.JLabel;
17import javax.swing.JList; 19import javax.swing.JList;
@@ -19,11 +21,11 @@ import javax.swing.ListCellRenderer;
19 21
20import cuchaz.enigma.ClassFile; 22import cuchaz.enigma.ClassFile;
21 23
22public class ClassListCellRenderer implements ListCellRenderer<ClassFile> 24public class ObfuscatedClassListCellRenderer implements ListCellRenderer<ClassFile>
23{ 25{
24 private DefaultListCellRenderer m_defaultRenderer; 26 private DefaultListCellRenderer m_defaultRenderer;
25 27
26 public ClassListCellRenderer( ) 28 public ObfuscatedClassListCellRenderer( )
27 { 29 {
28 m_defaultRenderer = new DefaultListCellRenderer(); 30 m_defaultRenderer = new DefaultListCellRenderer();
29 } 31 }
@@ -33,7 +35,7 @@ public class ClassListCellRenderer implements ListCellRenderer<ClassFile>
33 { 35 {
34 JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus ); 36 JLabel label = (JLabel)m_defaultRenderer.getListCellRendererComponent( list, classFile, index, isSelected, hasFocus );
35 37
36 label.setText( classFile.getName() ); 38 label.setText( Descriptor.toJavaName( classFile.getName() ) );
37 39
38 return label; 40 return label;
39 } 41 }
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Color;
14
15public class ObfuscatedHighlightPainter extends BoxHighlightPainter
16{
17 public ObfuscatedHighlightPainter( )
18 {
19 // red ish
20 super( new Color( 255, 220, 220 ), new Color( 160, 80, 80 ) );
21 }
22}
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;
12 12
13import java.io.Serializable; 13import java.io.Serializable;
14 14
15public class ArgumentMapping implements Serializable 15public class ArgumentMapping implements Serializable, Comparable<ArgumentMapping>
16{ 16{
17 private static final long serialVersionUID = 8610742471440861315L; 17 private static final long serialVersionUID = 8610742471440861315L;
18 18
@@ -23,7 +23,7 @@ public class ArgumentMapping implements Serializable
23 public ArgumentMapping( int index, String name ) 23 public ArgumentMapping( int index, String name )
24 { 24 {
25 m_index = index; 25 m_index = index;
26 m_name = name; 26 m_name = NameValidator.validateArgumentName( name );
27 } 27 }
28 28
29 public int getIndex( ) 29 public int getIndex( )
@@ -37,6 +37,12 @@ public class ArgumentMapping implements Serializable
37 } 37 }
38 public void setName( String val ) 38 public void setName( String val )
39 { 39 {
40 m_name = val; 40 m_name = NameValidator.validateArgumentName( val );
41 }
42
43 @Override
44 public int compareTo( ArgumentMapping other )
45 {
46 return Integer.compare( m_index, other.m_index );
41 } 47 }
42} 48}
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;
15 15
16import com.beust.jcommander.internal.Maps; 16import com.beust.jcommander.internal.Maps;
17 17
18public class ClassMapping implements Serializable 18public class ClassMapping implements Serializable, Comparable<ClassMapping>
19{ 19{
20 private static final long serialVersionUID = -5148491146902340107L; 20 private static final long serialVersionUID = -5148491146902340107L;
21 21
@@ -30,7 +30,7 @@ public class ClassMapping implements Serializable
30 public ClassMapping( String obfName, String deobfName ) 30 public ClassMapping( String obfName, String deobfName )
31 { 31 {
32 m_obfName = obfName; 32 m_obfName = obfName;
33 m_deobfName = deobfName; 33 m_deobfName = NameValidator.validateClassName( deobfName );
34 m_fieldsByObf = Maps.newHashMap(); 34 m_fieldsByObf = Maps.newHashMap();
35 m_fieldsByDeobf = Maps.newHashMap(); 35 m_fieldsByDeobf = Maps.newHashMap();
36 m_methodsByObf = Maps.newHashMap(); 36 m_methodsByObf = Maps.newHashMap();
@@ -48,7 +48,7 @@ public class ClassMapping implements Serializable
48 } 48 }
49 public void setDeobfName( String val ) 49 public void setDeobfName( String val )
50 { 50 {
51 m_deobfName = val; 51 m_deobfName = NameValidator.validateClassName( val );
52 } 52 }
53 53
54 public Iterable<FieldMapping> fields( ) 54 public Iterable<FieldMapping> fields( )
@@ -97,11 +97,6 @@ public class ClassMapping implements Serializable
97 97
98 public void setFieldName( String obfName, String deobfName ) 98 public void setFieldName( String obfName, String deobfName )
99 { 99 {
100 if( deobfName == null )
101 {
102 throw new IllegalArgumentException( "deobf name cannot be null!" );
103 }
104
105 FieldMapping fieldMapping = m_fieldsByObf.get( obfName ); 100 FieldMapping fieldMapping = m_fieldsByObf.get( obfName );
106 if( fieldMapping == null ) 101 if( fieldMapping == null )
107 { 102 {
@@ -140,11 +135,6 @@ public class ClassMapping implements Serializable
140 135
141 public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature ) 136 public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature )
142 { 137 {
143 if( deobfName == null )
144 {
145 throw new IllegalArgumentException( "deobf name cannot be null!" );
146 }
147
148 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) ); 138 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) );
149 if( methodIndex == null ) 139 if( methodIndex == null )
150 { 140 {
@@ -167,11 +157,6 @@ public class ClassMapping implements Serializable
167 157
168 public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) 158 public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName )
169 { 159 {
170 if( argumentName == null )
171 {
172 throw new IllegalArgumentException( "argument name cannot be null!" );
173 }
174
175 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); 160 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) );
176 if( methodIndex == null ) 161 if( methodIndex == null )
177 { 162 {
@@ -214,4 +199,10 @@ public class ClassMapping implements Serializable
214 } 199 }
215 return buf.toString(); 200 return buf.toString();
216 } 201 }
202
203 @Override
204 public int compareTo( ClassMapping other )
205 {
206 return m_obfName.compareTo( other.m_obfName );
207 }
217} 208}
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 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import jsyntaxpane.Token;
14
13 15
14public class EntryPair<T extends Entry> 16public class EntryPair<T extends Entry>
15{ 17{
16 public T obf; 18 public T obf;
17 public T deobf; 19 public T deobf;
20 public Token token;
18 21
19 public EntryPair( T obf, T deobf ) 22 public EntryPair( T obf, T deobf, Token token )
20 { 23 {
21 this.obf = obf; 24 this.obf = obf;
22 this.deobf = deobf; 25 this.deobf = deobf;
26 this.token = token;
23 } 27 }
24} 28}
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;
12 12
13import java.io.Serializable; 13import java.io.Serializable;
14 14
15public class FieldMapping implements Serializable 15public class FieldMapping implements Serializable, Comparable<FieldMapping>
16{ 16{
17 private static final long serialVersionUID = 8610742471440861315L; 17 private static final long serialVersionUID = 8610742471440861315L;
18 18
@@ -22,7 +22,7 @@ public class FieldMapping implements Serializable
22 public FieldMapping( String obfName, String deobfName ) 22 public FieldMapping( String obfName, String deobfName )
23 { 23 {
24 m_obfName = obfName; 24 m_obfName = obfName;
25 m_deobfName = deobfName; 25 m_deobfName = NameValidator.validateFieldName( deobfName );
26 } 26 }
27 27
28 public String getObfName( ) 28 public String getObfName( )
@@ -36,6 +36,12 @@ public class FieldMapping implements Serializable
36 } 36 }
37 public void setDeobfName( String val ) 37 public void setDeobfName( String val )
38 { 38 {
39 m_deobfName = val; 39 m_deobfName = NameValidator.validateFieldName( val );
40 }
41
42 @Override
43 public int compareTo( FieldMapping other )
44 {
45 return m_obfName.compareTo( other.m_obfName );
40 } 46 }
41} 47}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public class IllegalNameException extends RuntimeException
14{
15 private static final long serialVersionUID = -2279910052561114323L;
16
17 private String m_name;
18
19 public IllegalNameException( String name )
20 {
21 m_name = name;
22 }
23
24 @Override
25 public String getMessage( )
26 {
27 return "Illegal name: " + m_name;
28 }
29}
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;
13import java.io.IOException; 13import java.io.IOException;
14import java.io.PrintWriter; 14import java.io.PrintWriter;
15import java.io.Writer; 15import java.io.Writer;
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.List;
16 19
17public class MappingsWriter 20public class MappingsWriter
18{ 21{
@@ -25,7 +28,7 @@ public class MappingsWriter
25 public void write( PrintWriter out, Mappings mappings ) 28 public void write( PrintWriter out, Mappings mappings )
26 throws IOException 29 throws IOException
27 { 30 {
28 for( ClassMapping classMapping : mappings.classes() ) 31 for( ClassMapping classMapping : sorted( mappings.classes() ) )
29 { 32 {
30 write( out, classMapping ); 33 write( out, classMapping );
31 } 34 }
@@ -36,12 +39,12 @@ public class MappingsWriter
36 { 39 {
37 out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() ); 40 out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() );
38 41
39 for( FieldMapping fieldMapping : classMapping.fields() ) 42 for( FieldMapping fieldMapping : sorted( classMapping.fields() ) )
40 { 43 {
41 write( out, fieldMapping ); 44 write( out, fieldMapping );
42 } 45 }
43 46
44 for( MethodMapping methodMapping : classMapping.methods() ) 47 for( MethodMapping methodMapping : sorted( classMapping.methods() ) )
45 { 48 {
46 write( out, methodMapping ); 49 write( out, methodMapping );
47 } 50 }
@@ -61,7 +64,7 @@ public class MappingsWriter
61 methodMapping.getObfSignature(), methodMapping.getDeobfSignature() 64 methodMapping.getObfSignature(), methodMapping.getDeobfSignature()
62 ); 65 );
63 66
64 for( ArgumentMapping argumentMapping : methodMapping.arguments() ) 67 for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) )
65 { 68 {
66 write( out, argumentMapping ); 69 write( out, argumentMapping );
67 } 70 }
@@ -72,4 +75,15 @@ public class MappingsWriter
72 { 75 {
73 out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() ); 76 out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() );
74 } 77 }
78
79 private <T extends Comparable<T>> List<T> sorted( Iterable<T> classes )
80 {
81 List<T> out = new ArrayList<T>();
82 for( T t : classes )
83 {
84 out.add( t );
85 }
86 Collections.sort( out );
87 return out;
88 }
75} 89}
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;
14import java.util.Map; 14import java.util.Map;
15import java.util.TreeMap; 15import java.util.TreeMap;
16 16
17public class MethodMapping implements Serializable 17public class MethodMapping implements Serializable, Comparable<MethodMapping>
18{ 18{
19 private static final long serialVersionUID = -4409570216084263978L; 19 private static final long serialVersionUID = -4409570216084263978L;
20 20
@@ -28,7 +28,7 @@ public class MethodMapping implements Serializable
28 public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature ) 28 public MethodMapping( String obfName, String deobfName, String obfSignature, String deobfSignature )
29 { 29 {
30 m_obfName = obfName; 30 m_obfName = obfName;
31 m_deobfName = deobfName; 31 m_deobfName = NameValidator.validateMethodName( deobfName );
32 m_obfSignature = obfSignature; 32 m_obfSignature = obfSignature;
33 m_deobfSignature = deobfSignature; 33 m_deobfSignature = deobfSignature;
34 m_arguments = new TreeMap<Integer,ArgumentMapping>(); 34 m_arguments = new TreeMap<Integer,ArgumentMapping>();
@@ -45,7 +45,7 @@ public class MethodMapping implements Serializable
45 } 45 }
46 public void setDeobfName( String val ) 46 public void setDeobfName( String val )
47 { 47 {
48 m_deobfName = val; 48 m_deobfName = NameValidator.validateMethodName( val );
49 } 49 }
50 50
51 public String getObfSignature( ) 51 public String getObfSignature( )
@@ -133,4 +133,10 @@ public class MethodMapping implements Serializable
133 } 133 }
134 return buf.toString(); 134 return buf.toString();
135 } 135 }
136
137 @Override
138 public int compareTo( MethodMapping other )
139 {
140 return m_obfName.compareTo( other.m_obfName );
141 }
136} 142}
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 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.util.Arrays;
14import java.util.List;
13import java.util.regex.Pattern; 15import java.util.regex.Pattern;
14 16
17import javassist.bytecode.Descriptor;
18
15public class NameValidator 19public class NameValidator
16{ 20{
17 private static final String IdentifierPattern; 21 private static final Pattern IdentifierPattern;
18 private static final Pattern ClassPattern; 22 private static final Pattern ClassPattern;
23 private static final List<String> ReservedWords = Arrays.asList(
24 "abstract", "continue", "for", "new", "switch",
25 "assert", "default", "goto", "package", "synchronized",
26 "boolean", "do", "if", "private", "this",
27 "break", "double", "implements", "protected", "throw",
28 "byte", "else", "import", "public", "throws",
29 "case", "enum", "instanceof", "return", "transient",
30 "catch", "extends", "int", "short", "try",
31 "char", "final", "interface", "static", "void",
32 "class", "finally", "long", "strictfp", "volatile",
33 "const", "float", "native", "super", "while"
34 );
19 35
20 static 36 static
21 { 37 {
@@ -34,34 +50,36 @@ public class NameValidator
34 } 50 }
35 } 51 }
36 52
37 IdentifierPattern = String.format( "[\\Q%s\\E][\\Q%s\\E]*", startChars.toString(), partChars.toString() ); 53 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
38 ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", IdentifierPattern, IdentifierPattern ) ); 54 IdentifierPattern = Pattern.compile( identifierRegex );
55 ClassPattern = Pattern.compile( String.format( "^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex ) );
39 } 56 }
40 57
41 public String validateClassName( String name ) 58 public static String validateClassName( String name )
42 { 59 {
43 if( !ClassPattern.matcher( name ).matches() ) 60 if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) )
44 { 61 {
45 throw new IllegalArgumentException( "Illegal name: " + name ); 62 throw new IllegalNameException( name );
46 } 63 }
47 64 return Descriptor.toJvmName( name );
48 return classNameToJavaName( name );
49 } 65 }
50 66
51 public static String fileNameToClassName( String fileName ) 67 public static String validateFieldName( String name )
52 { 68 {
53 final String suffix = ".class"; 69 if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) )
54
55 if( !fileName.endsWith( suffix ) )
56 { 70 {
57 return null; 71 throw new IllegalNameException( name );
58 } 72 }
59 73 return name;
60 return fileName.substring( 0, fileName.length() - suffix.length() ).replace( "/", "." ); 74 }
75
76 public static String validateMethodName( String name )
77 {
78 return validateFieldName( name );
61 } 79 }
62 80
63 public static String classNameToJavaName( String className ) 81 public static String validateArgumentName( String name )
64 { 82 {
65 return className.replace( ".", "/" ); 83 return validateFieldName( name );
66 } 84 }
67} 85}
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
39 m_mappings.m_classesByDeobf.put( deobfName, classMapping ); 39 m_mappings.m_classesByDeobf.put( deobfName, classMapping );
40 40
41 updateDeobfMethodSignatures(); 41 updateDeobfMethodSignatures();
42
43 // TEMP
44 String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf );
45 assert( translatedName != null && translatedName.equals( deobfName ) );
46 } 42 }
47 43
48 public void setFieldName( FieldEntry obf, String deobfName ) 44 public void setFieldName( FieldEntry obf, String deobfName )
@@ -54,11 +50,6 @@ public class Renamer
54 } 50 }
55 51
56 classMapping.setFieldName( obf.getName(), deobfName ); 52 classMapping.setFieldName( obf.getName(), deobfName );
57
58 // TEMP
59 System.out.println( classMapping );
60 String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf );
61 assert( translatedName != null && translatedName.equals( deobfName ) );
62 } 53 }
63 54
64 public void setMethodName( MethodEntry obf, String deobfName ) 55 public void setMethodName( MethodEntry obf, String deobfName )
@@ -73,11 +64,6 @@ public class Renamer
73 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); 64 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature );
74 65
75 // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too 66 // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too
76
77 // TEMP
78 System.out.println( classMapping );
79 String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf );
80 assert( translatedName != null && translatedName.equals( deobfName ) );
81 } 67 }
82 68
83 public void setArgumentName( ArgumentEntry obf, String deobfName ) 69 public void setArgumentName( ArgumentEntry obf, String deobfName )
@@ -89,11 +75,6 @@ public class Renamer
89 } 75 }
90 76
91 classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); 77 classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName );
92
93 // TEMP
94 System.out.println( classMapping );
95 String translatedName = m_mappings.getTranslator( m_ancestries, TranslationDirection.Deobfuscating ).translate( obf );
96 assert( translatedName != null && translatedName.equals( deobfName ) );
97 } 78 }
98 79
99 public void write( OutputStream out ) 80 public void write( OutputStream out )