summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar hg2014-08-17 10:56:17 -0400
committerGravatar hg2014-08-17 10:56:17 -0400
commit6c4440ac1133bfaa7871d1049d174528a289ef30 (patch)
treefe1142b285c5e43dbd3afe8dd3eb0189f027c6a6 /src
parenttrying to get inner/anonymous classes working... I have a working heuristic i... (diff)
downloadenigma-6c4440ac1133bfaa7871d1049d174528a289ef30.tar.gz
enigma-6c4440ac1133bfaa7871d1049d174528a289ef30.tar.xz
enigma-6c4440ac1133bfaa7871d1049d174528a289ef30.zip
added support for automatic reconstruction of inner and anonymous classes
also added class to restore bridge method flags taken out by the obfuscator
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java51
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java56
-rw-r--r--src/cuchaz/enigma/analysis/Ancestries.java20
-rw-r--r--src/cuchaz/enigma/analysis/BridgeFixer.java91
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java155
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndex.java31
-rw-r--r--src/cuchaz/enigma/analysis/TreeDumpVisitor.java45
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java64
-rw-r--r--src/cuchaz/enigma/gui/Gui.java16
-rw-r--r--src/cuchaz/enigma/gui/GuiController.java5
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java28
-rw-r--r--src/cuchaz/enigma/mapping/ClassMapping.java97
-rw-r--r--src/cuchaz/enigma/mapping/MappingParseException.java31
-rw-r--r--src/cuchaz/enigma/mapping/MappingsReader.java76
-rw-r--r--src/cuchaz/enigma/mapping/MappingsWriter.java40
-rw-r--r--src/cuchaz/enigma/mapping/Renamer.java64
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java104
17 files changed, 743 insertions, 231 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 127a0d98..9a0ec132 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -62,7 +62,6 @@ public class Deobfuscator
62 62
63 // config the decompiler 63 // config the decompiler
64 m_settings = DecompilerSettings.javaDefaults(); 64 m_settings = DecompilerSettings.javaDefaults();
65 m_settings.setShowSyntheticMembers( true );
66 65
67 // init mappings 66 // init mappings
68 setMappings( new Mappings() ); 67 setMappings( new Mappings() );
@@ -109,9 +108,15 @@ public class Deobfuscator
109 { 108 {
110 for( String obfClassName : m_jarIndex.getObfClassNames() ) 109 for( String obfClassName : m_jarIndex.getObfClassNames() )
111 { 110 {
111 // skip inner classes
112 if( m_jarIndex.getOuterClass( obfClassName ) != null )
113 {
114 continue;
115 }
116
112 // separate the classes 117 // separate the classes
113 ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); 118 ClassMapping classMapping = m_mappings.getClassByObf( obfClassName );
114 if( classMapping != null ) 119 if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) )
115 { 120 {
116 deobfClasses.add( classMapping.getDeobfName() ); 121 deobfClasses.add( classMapping.getDeobfName() );
117 } 122 }
@@ -151,6 +156,7 @@ public class Deobfuscator
151 // render the AST into source 156 // render the AST into source
152 StringWriter buf = new StringWriter(); 157 StringWriter buf = new StringWriter();
153 root.acceptVisitor( new InsertParenthesesVisitor(), null ); 158 root.acceptVisitor( new InsertParenthesesVisitor(), null );
159 //root.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
154 root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null ); 160 root.acceptVisitor( new JavaOutputVisitor( new PlainTextOutput( buf ), m_settings ), null );
155 161
156 // build the source index 162 // build the source index
@@ -281,33 +287,30 @@ public class Deobfuscator
281 } 287 }
282 } 288 }
283 289
284 public boolean entryIsObfuscatedIdenfitier( Entry obfEntry ) 290 public boolean isObfuscatedIdentifier( Entry obfEntry )
285 { 291 {
286 if( obfEntry instanceof ClassEntry ) 292 if( obfEntry instanceof ClassEntry )
287 { 293 {
288 // obf classes must be in the list 294 if( obfEntry.getName().indexOf( '$' ) >= 0 )
289 return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); 295 {
290 } 296 String[] parts = obfEntry.getName().split( "\\$" );
291 else if( obfEntry instanceof FieldEntry ) 297 assert( parts.length == 2 ); // not supporting recursively-nested classes
292 { 298 String outerClassName = parts[0];
293 return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); 299 String innerClassName = parts[1];
294 } 300
295 else if( obfEntry instanceof MethodEntry ) 301 // both classes must be in the list
296 { 302 return m_jarIndex.getObfClassNames().contains( outerClassName )
297 return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); 303 && m_jarIndex.getObfClassNames().contains( innerClassName );
298 } 304 }
299 else if( obfEntry instanceof ConstructorEntry ) 305 else
300 { 306 {
301 return m_jarIndex.getObfClassNames().contains( obfEntry.getClassName() ); 307 // class must be in the list
308 return m_jarIndex.getObfClassNames().contains( obfEntry.getName() );
309 }
302 } 310 }
303 else if( obfEntry instanceof ArgumentEntry ) 311 else
304 { 312 {
305 // arguments only appear in method declarations 313 return isObfuscatedIdentifier( obfEntry.getClassEntry() );
306 // since we only show declarations for obf classes, these are always obfuscated
307 return true;
308 } 314 }
309
310 // assume everything else is not obfuscated
311 return false;
312 } 315 }
313} 316}
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
index fdfcea0f..c1d96ae3 100644
--- a/src/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/cuchaz/enigma/TranslatingTypeLoader.java
@@ -26,6 +26,7 @@ import javassist.bytecode.Descriptor;
26import com.strobel.assembler.metadata.Buffer; 26import com.strobel.assembler.metadata.Buffer;
27import com.strobel.assembler.metadata.ITypeLoader; 27import com.strobel.assembler.metadata.ITypeLoader;
28 28
29import cuchaz.enigma.analysis.BridgeFixer;
29import cuchaz.enigma.analysis.JarIndex; 30import cuchaz.enigma.analysis.JarIndex;
30import cuchaz.enigma.bytecode.ClassTranslator; 31import cuchaz.enigma.bytecode.ClassTranslator;
31import cuchaz.enigma.bytecode.InnerClassWriter; 32import cuchaz.enigma.bytecode.InnerClassWriter;
@@ -50,12 +51,6 @@ public class TranslatingTypeLoader implements ITypeLoader
50 @Override 51 @Override
51 public boolean tryLoadType( String deobfClassName, Buffer out ) 52 public boolean tryLoadType( String deobfClassName, Buffer out )
52 { 53 {
53 // TEMP
54 if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) )
55 {
56 System.out.println( "Looking for: " + deobfClassName );
57 }
58
59 // what class file should we actually load? 54 // what class file should we actually load?
60 String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); 55 String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName );
61 if( obfClassName == null ) 56 if( obfClassName == null )
@@ -64,26 +59,12 @@ public class TranslatingTypeLoader implements ITypeLoader
64 } 59 }
65 String classFileName = obfClassName; 60 String classFileName = obfClassName;
66 61
67 // is this a properly-referenced inner class? 62 // is this an inner class?
68 boolean isInnerClass = deobfClassName.indexOf( '$' ) >= 0; 63 if( obfClassName.indexOf( '$' ) >= 0 )
69 if( isInnerClass )
70 {
71 // get just the bare inner class name
72 String[] parts = deobfClassName.split( "\\$" );
73 String deobfClassFileName = parts[parts.length - 1];
74
75 // make sure the bare inner class name is obfuscated
76 classFileName = m_obfuscatingTranslator.translateClass( deobfClassFileName );
77 if( classFileName == null )
78 {
79 classFileName = deobfClassFileName;
80 }
81 }
82
83 // TEMP
84 if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) )
85 { 64 {
86 System.out.println( "\tLooking at class file: " + classFileName ); 65 // the file name is the bare inner class name
66 String[] parts = obfClassName.split( "\\$" );
67 classFileName = parts[parts.length - 1];
87 } 68 }
88 69
89 // get the jar entry 70 // get the jar entry
@@ -118,26 +99,20 @@ public class TranslatingTypeLoader implements ITypeLoader
118 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); 99 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) );
119 CtClass c = classPool.get( javaClassFileName ); 100 CtClass c = classPool.get( javaClassFileName );
120 101
121 if( isInnerClass ) 102 // do all kinds of deobfuscating transformations on the class
122 { 103 new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).write( c );
123 // rename the class to what procyon expects 104 new BridgeFixer().fixBridges( c );
124 c.setName( deobfClassName );
125 }
126 else
127 {
128 // maybe it's an outer class
129 new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).writeInnerClasses( c );
130 }
131
132 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); 105 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c );
133 new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); 106 new ClassTranslator( m_deobfuscatingTranslator ).translate( c );
134 107
135 assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ); 108 // sanity checking
136 assert( c.getClassFile().getName().equals( deobfClassName ) ); 109 assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) )
137 110 : String.format( "%s is not %s", Descriptor.toJvmName( c.getName() ), deobfClassName );
138 buf = c.toBytecode(); 111 assert( Descriptor.toJvmName( c.getClassFile().getName() ).equals( deobfClassName ) )
112 : String.format( "%s is not %s", Descriptor.toJvmName( c.getClassFile().getName() ), deobfClassName );
139 113
140 // pass the transformed class along to the decompiler 114 // pass the transformed class along to the decompiler
115 buf = c.toBytecode();
141 out.reset( buf.length ); 116 out.reset( buf.length );
142 System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); 117 System.arraycopy( buf, 0, out.array(), out.position(), buf.length );
143 out.position( 0 ); 118 out.position( 0 );
@@ -149,5 +124,4 @@ public class TranslatingTypeLoader implements ITypeLoader
149 throw new Error( ex ); 124 throw new Error( ex );
150 } 125 }
151 } 126 }
152
153} 127}
diff --git a/src/cuchaz/enigma/analysis/Ancestries.java b/src/cuchaz/enigma/analysis/Ancestries.java
index 83c239cd..b9d8cbf4 100644
--- a/src/cuchaz/enigma/analysis/Ancestries.java
+++ b/src/cuchaz/enigma/analysis/Ancestries.java
@@ -47,6 +47,26 @@ public class Ancestries implements Serializable
47 } 47 }
48 } 48 }
49 49
50 public void renameClasses( Map<String,String> renames )
51 {
52 Map<String,String> newSuperclasses = Maps.newHashMap();
53 for( Map.Entry<String,String> entry : m_superclasses.entrySet() )
54 {
55 String subclass = renames.get( entry.getKey() );
56 if( subclass == null )
57 {
58 subclass = entry.getKey();
59 }
60 String superclass = renames.get( entry.getValue() );
61 if( superclass == null )
62 {
63 superclass = entry.getValue();
64 }
65 newSuperclasses.put( subclass, superclass );
66 }
67 m_superclasses = newSuperclasses;
68 }
69
50 public String getSuperclassName( String className ) 70 public String getSuperclassName( String className )
51 { 71 {
52 return m_superclasses.get( className ); 72 return m_superclasses.get( className );
diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java
new file mode 100644
index 00000000..db441d2b
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/BridgeFixer.java
@@ -0,0 +1,91 @@
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.analysis;
12
13import java.util.List;
14
15import javassist.CannotCompileException;
16import javassist.CtClass;
17import javassist.CtMethod;
18import javassist.NotFoundException;
19import javassist.bytecode.AccessFlag;
20import javassist.expr.ExprEditor;
21import javassist.expr.MethodCall;
22
23import com.beust.jcommander.internal.Lists;
24
25public class BridgeFixer
26{
27 public void fixBridges( CtClass c )
28 {
29 // bridge methods are scrubbed and marked as synthetic methods by the obfuscator
30 // need to figure out which synthetics are bridge methods and restore them
31 for( CtMethod method : c.getDeclaredMethods() )
32 {
33 // skip non-synthetic methods
34 if( ( method.getModifiers() & AccessFlag.SYNTHETIC ) == 0 )
35 {
36 continue;
37 }
38
39 CtMethod bridgedMethod = getBridgedMethod( method );
40 if( bridgedMethod != null )
41 {
42 bridgedMethod.setName( method.getName() );
43 method.setModifiers( method.getModifiers() | AccessFlag.BRIDGE );
44 }
45 }
46 }
47
48 private CtMethod getBridgedMethod( CtMethod method )
49 {
50 // bridge methods just call another method, cast it to the return type, and return the result
51 // let's see if we can detect this scenario
52
53 // get all the called methods
54 final List<MethodCall> methodCalls = Lists.newArrayList();
55 try
56 {
57 method.instrument( new ExprEditor( )
58 {
59 @Override
60 public void edit( MethodCall call )
61 {
62 methodCalls.add( call );
63 }
64 } );
65 }
66 catch( CannotCompileException ex )
67 {
68 // this is stupid... we're not even compiling anything
69 throw new Error( ex );
70 }
71
72 // is there just one?
73 if( methodCalls.size() != 1 )
74 {
75 return null;
76 }
77 MethodCall call = methodCalls.get( 0 );
78
79 try
80 {
81 // we have a bridge method!
82 return call.getMethod();
83 }
84 catch( NotFoundException ex )
85 {
86 // can't find the type? not a bridge method
87 ex.printStackTrace( System.err );
88 return null;
89 }
90 }
91}
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 9962bfaa..34e8986f 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -10,7 +10,9 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.util.AbstractMap;
13import java.util.Collection; 14import java.util.Collection;
15import java.util.Iterator;
14import java.util.List; 16import java.util.List;
15import java.util.Map; 17import java.util.Map;
16import java.util.Set; 18import java.util.Set;
@@ -37,6 +39,7 @@ import com.google.common.collect.Maps;
37import com.google.common.collect.Multimap; 39import com.google.common.collect.Multimap;
38import com.google.common.collect.Sets; 40import com.google.common.collect.Sets;
39 41
42import cuchaz.enigma.mapping.ArgumentEntry;
40import cuchaz.enigma.mapping.ClassEntry; 43import cuchaz.enigma.mapping.ClassEntry;
41import cuchaz.enigma.mapping.ConstructorEntry; 44import cuchaz.enigma.mapping.ConstructorEntry;
42import cuchaz.enigma.mapping.Entry; 45import cuchaz.enigma.mapping.Entry;
@@ -53,6 +56,7 @@ public class JarIndex
53 private Multimap<FieldEntry,Entry> m_fieldCalls; 56 private Multimap<FieldEntry,Entry> m_fieldCalls;
54 private Multimap<String,String> m_innerClasses; 57 private Multimap<String,String> m_innerClasses;
55 private Map<String,String> m_outerClasses; 58 private Map<String,String> m_outerClasses;
59 private Set<String> m_anonymousClasses;
56 60
57 public JarIndex( ) 61 public JarIndex( )
58 { 62 {
@@ -63,6 +67,7 @@ public class JarIndex
63 m_fieldCalls = HashMultimap.create(); 67 m_fieldCalls = HashMultimap.create();
64 m_innerClasses = HashMultimap.create(); 68 m_innerClasses = HashMultimap.create();
65 m_outerClasses = Maps.newHashMap(); 69 m_outerClasses = Maps.newHashMap();
70 m_anonymousClasses = Sets.newHashSet();
66 } 71 }
67 72
68 public void indexJar( JarFile jar ) 73 public void indexJar( JarFile jar )
@@ -84,7 +89,7 @@ public class JarIndex
84 } 89 }
85 } 90 }
86 91
87 // pass 2: index inner classes 92 // pass 2: index inner classes and anonymous classes
88 for( CtClass c : JarClassIterator.classes( jar ) ) 93 for( CtClass c : JarClassIterator.classes( jar ) )
89 { 94 {
90 String outerClassName = isInnerClass( c ); 95 String outerClassName = isInnerClass( c );
@@ -93,8 +98,21 @@ public class JarIndex
93 String innerClassName = Descriptor.toJvmName( c.getName() ); 98 String innerClassName = Descriptor.toJvmName( c.getName() );
94 m_innerClasses.put( outerClassName, innerClassName ); 99 m_innerClasses.put( outerClassName, innerClassName );
95 m_outerClasses.put( innerClassName, outerClassName ); 100 m_outerClasses.put( innerClassName, outerClassName );
101
102 if( isAnonymousClass( c, outerClassName ) )
103 {
104 m_anonymousClasses.add( innerClassName );
105 }
96 } 106 }
97 } 107 }
108
109 // step 3: update other indicies with inner class info
110 Map<String,String> renames = Maps.newHashMap();
111 for( Map.Entry<String,String> entry : m_outerClasses.entrySet() )
112 {
113 renames.put( entry.getKey(), entry.getValue() + "$" + entry.getKey() );
114 }
115 renameClasses( renames );
98 } 116 }
99 117
100 private void indexBehavior( CtBehavior behavior ) 118 private void indexBehavior( CtBehavior behavior )
@@ -186,14 +204,10 @@ public class JarIndex
186 @SuppressWarnings( "unchecked" ) 204 @SuppressWarnings( "unchecked" )
187 private String isInnerClass( CtClass c ) 205 private String isInnerClass( CtClass c )
188 { 206 {
189 String innerClassName = Descriptor.toJvmName( c.getName() ); 207 // inner classes:
190
191 // first, is this an anonymous class?
192 // for anonymous classes:
193 // the outer class is always a synthetic field 208 // the outer class is always a synthetic field
194 // there's at least one constructor with the type of the synthetic field as an argument 209 // there's at least one constructor with the type of the synthetic field as an argument
195 // this constructor is called exactly once by the class of the synthetic field 210
196
197 for( FieldInfo field : (List<FieldInfo>)c.getClassFile().getFields() ) 211 for( FieldInfo field : (List<FieldInfo>)c.getClassFile().getFields() )
198 { 212 {
199 boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; 213 boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
@@ -230,16 +244,8 @@ public class JarIndex
230 String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); 244 String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) );
231 if( argumentClassName.equals( outerClassName ) ) 245 if( argumentClassName.equals( outerClassName ) )
232 { 246 {
233 // is this constructor called exactly once? 247 targetConstructor = constructor;
234 ConstructorEntry constructorEntry = new ConstructorEntry( 248 break;
235 new ClassEntry( innerClassName ),
236 constructor.getMethodInfo().getDescriptor()
237 );
238 if( this.getMethodCallers( constructorEntry ).size() == 1 )
239 {
240 targetConstructor = constructor;
241 break;
242 }
243 } 249 }
244 } 250 }
245 } 251 }
@@ -255,6 +261,30 @@ public class JarIndex
255 return null; 261 return null;
256 } 262 }
257 263
264 private boolean isAnonymousClass( CtClass c, String outerClassName )
265 {
266 String innerClassName = Descriptor.toJvmName( c.getName() );
267
268 // anonymous classes:
269 // have only one constructor
270 // it's called exactly once by the outer class
271 // type of inner class not referenced anywhere in outer class
272
273 // is there exactly one constructor?
274 if( c.getDeclaredConstructors().length != 1 )
275 {
276 return false;
277 }
278 CtConstructor constructor = c.getDeclaredConstructors()[0];
279
280 // is this constructor called exactly once?
281 ConstructorEntry constructorEntry = new ConstructorEntry(
282 new ClassEntry( innerClassName ),
283 constructor.getMethodInfo().getDescriptor()
284 );
285 return getMethodCallers( constructorEntry ).size() == 1;
286 }
287
258 public Set<String> getObfClassNames( ) 288 public Set<String> getObfClassNames( )
259 { 289 {
260 return m_obfClassNames; 290 return m_obfClassNames;
@@ -343,4 +373,95 @@ public class JarIndex
343 { 373 {
344 return m_outerClasses.get( obfInnerClassName ); 374 return m_outerClasses.get( obfInnerClassName );
345 } 375 }
376
377 public boolean isAnonymousClass( String obfInnerClassName )
378 {
379 return m_anonymousClasses.contains( obfInnerClassName );
380 }
381
382 private void renameClasses( Map<String,String> renames )
383 {
384 m_ancestries.renameClasses( renames );
385 renameMultimap( renames, m_methodImplementations );
386 renameMultimap( renames, m_methodCalls );
387 renameMultimap( renames, m_fieldCalls );
388 }
389
390 private <T,U> void renameMultimap( Map<String,String> renames, Multimap<T,U> map )
391 {
392 // for each key/value pair...
393 Set<Map.Entry<T,U>> entriesToAdd = Sets.newHashSet();
394 Iterator<Map.Entry<T,U>> iter = map.entries().iterator();
395 while( iter.hasNext() )
396 {
397 Map.Entry<T,U> entry = iter.next();
398 iter.remove();
399 entriesToAdd.add( new AbstractMap.SimpleEntry<T,U>(
400 renameEntry( renames, entry.getKey() ),
401 renameEntry( renames, entry.getValue() )
402 ) );
403 }
404 for( Map.Entry<T,U> entry : entriesToAdd )
405 {
406 map.put( entry.getKey(), entry.getValue() );
407 }
408 }
409
410 @SuppressWarnings( "unchecked" )
411 private <T> T renameEntry( Map<String,String> renames, T entry )
412 {
413 if( entry instanceof String )
414 {
415 String stringEntry = (String)entry;
416 if( renames.containsKey( stringEntry ) )
417 {
418 return (T)renames.get( stringEntry );
419 }
420 }
421 else if( entry instanceof ClassEntry )
422 {
423 ClassEntry classEntry = (ClassEntry)entry;
424 return (T)new ClassEntry( renameEntry( renames, classEntry.getClassName() ) );
425 }
426 else if( entry instanceof FieldEntry )
427 {
428 FieldEntry fieldEntry = (FieldEntry)entry;
429 return (T)new FieldEntry(
430 renameEntry( renames, fieldEntry.getClassEntry() ),
431 fieldEntry.getName()
432 );
433 }
434 else if( entry instanceof ConstructorEntry )
435 {
436 ConstructorEntry constructorEntry = (ConstructorEntry)entry;
437 return (T)new ConstructorEntry(
438 renameEntry( renames, constructorEntry.getClassEntry() ),
439 constructorEntry.getSignature()
440 );
441 }
442 else if( entry instanceof MethodEntry )
443 {
444 MethodEntry methodEntry = (MethodEntry)entry;
445 return (T)new MethodEntry(
446 renameEntry( renames, methodEntry.getClassEntry() ),
447 methodEntry.getName(),
448 methodEntry.getSignature()
449 );
450 }
451 else if( entry instanceof ArgumentEntry )
452 {
453 ArgumentEntry argumentEntry = (ArgumentEntry)entry;
454 return (T)new ArgumentEntry(
455 renameEntry( renames, argumentEntry.getMethodEntry() ),
456 argumentEntry.getIndex(),
457 argumentEntry.getName()
458 );
459 }
460 else
461 {
462 throw new Error( "Not an entry: " + entry );
463 }
464
465 return entry;
466 }
346} 467}
diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java
index 531f3e04..645a71da 100644
--- a/src/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/cuchaz/enigma/analysis/SourceIndex.java
@@ -17,7 +17,7 @@ import java.util.TreeMap;
17import com.google.common.collect.Lists; 17import com.google.common.collect.Lists;
18import com.google.common.collect.Maps; 18import com.google.common.collect.Maps;
19import com.strobel.decompiler.languages.Region; 19import com.strobel.decompiler.languages.Region;
20import com.strobel.decompiler.languages.java.ast.AstNode; 20import com.strobel.decompiler.languages.java.ast.Identifier;
21 21
22import cuchaz.enigma.mapping.Entry; 22import cuchaz.enigma.mapping.Entry;
23 23
@@ -51,19 +51,27 @@ public class SourceIndex
51 return m_source; 51 return m_source;
52 } 52 }
53 53
54 public Token getToken( AstNode node ) 54 public Token getToken( Identifier node )
55 { 55 {
56 // get a token for this node's region 56 // get a token for this node's region
57 Region region = node.getRegion(); 57 Region region = node.getRegion();
58 if( region.getBeginLine() == 0 || region.getEndLine() == 0 ) 58 if( region.getBeginLine() == 0 || region.getEndLine() == 0 )
59 { 59 {
60 throw new IllegalArgumentException( "Invalid region: " + region ); 60 System.err.println( "WARNING: " + node.getNodeType() + " node has invalid region: " + region );
61 return null;
61 } 62 }
62 Token token = new Token( 63 Token token = new Token(
63 toPos( region.getBeginLine(), region.getBeginColumn() ), 64 toPos( region.getBeginLine(), region.getBeginColumn() ),
64 toPos( region.getEndLine(), region.getEndColumn() ) 65 toPos( region.getEndLine(), region.getEndColumn() )
65 ); 66 );
66 67
68 // for tokens representing inner classes, make sure we only get the simple name
69 int pos = node.getName().lastIndexOf( '$' );
70 if( pos >= 0 )
71 {
72 token.end -= pos + 1;
73 }
74
67 // HACKHACK: sometimes node regions are off by one 75 // HACKHACK: sometimes node regions are off by one
68 // I think this is a bug in Procyon, but it's easy to work around 76 // I think this is a bug in Procyon, but it's easy to work around
69 if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) 77 if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) )
@@ -79,16 +87,23 @@ public class SourceIndex
79 return token; 87 return token;
80 } 88 }
81 89
82 public void add( AstNode node, Entry deobfEntry ) 90 public void add( Identifier node, Entry deobfEntry )
83 { 91 {
84 m_tokens.put( getToken( node ), deobfEntry ); 92 Token token = getToken( node );
93 if( token != null )
94 {
95 m_tokens.put( token, deobfEntry );
96 }
85 } 97 }
86 98
87 public void addDeclaration( AstNode node, Entry deobfEntry ) 99 public void addDeclaration( Identifier node, Entry deobfEntry )
88 { 100 {
89 Token token = getToken( node ); 101 Token token = getToken( node );
90 m_tokens.put( token, deobfEntry ); 102 if( token != null )
91 m_declarations.put( deobfEntry, token ); 103 {
104 m_tokens.put( token, deobfEntry );
105 m_declarations.put( deobfEntry, token );
106 }
92 } 107 }
93 108
94 public Token getToken( int pos ) 109 public Token getToken( int pos )
diff --git a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java
index 05d0e6be..ac3e92db 100644
--- a/src/cuchaz/enigma/analysis/TreeDumpVisitor.java
+++ b/src/cuchaz/enigma/analysis/TreeDumpVisitor.java
@@ -10,6 +10,11 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.io.File;
14import java.io.FileWriter;
15import java.io.IOException;
16import java.io.Writer;
17
13import com.strobel.componentmodel.Key; 18import com.strobel.componentmodel.Key;
14import com.strobel.decompiler.languages.java.ast.Annotation; 19import com.strobel.decompiler.languages.java.ast.Annotation;
15import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; 20import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression;
@@ -87,10 +92,42 @@ import com.strobel.decompiler.patterns.Pattern;
87 92
88public class TreeDumpVisitor implements IAstVisitor<Void, Void> 93public class TreeDumpVisitor implements IAstVisitor<Void, Void>
89{ 94{
95 private File m_file;
96 private Writer m_out;
97
98 public TreeDumpVisitor( File file )
99 {
100 m_file = file;
101 m_out = null;
102 }
103
104 @Override
105 public Void visitCompilationUnit( CompilationUnit node, Void ignored )
106 {
107 try
108 {
109 m_out = new FileWriter( m_file );
110 recurse( node, ignored );
111 m_out.close();
112 return null;
113 }
114 catch( IOException ex )
115 {
116 throw new Error( ex );
117 }
118 }
119
90 private Void recurse( AstNode node, Void ignored ) 120 private Void recurse( AstNode node, Void ignored )
91 { 121 {
92 // show the tree 122 // show the tree
93 System.out.println( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() ); 123 try
124 {
125 m_out.write( getIndent( node ) + node.getClass().getSimpleName() + dumpUserData( node ) + " " + node.getRegion() + "\n" );
126 }
127 catch( IOException ex )
128 {
129 throw new Error( ex );
130 }
94 131
95 // recurse 132 // recurse
96 for( final AstNode child : node.getChildren() ) 133 for( final AstNode child : node.getChildren() )
@@ -379,12 +416,6 @@ public class TreeDumpVisitor implements IAstVisitor<Void, Void>
379 } 416 }
380 417
381 @Override 418 @Override
382 public Void visitCompilationUnit( CompilationUnit node, Void ignored )
383 {
384 return recurse( node, ignored );
385 }
386
387 @Override
388 public Void visitPackageDeclaration( PackageDeclaration node, Void ignored ) 419 public Void visitPackageDeclaration( PackageDeclaration node, Void ignored )
389 { 420 {
390 return recurse( node, ignored ); 421 return recurse( node, ignored );
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
index d4abe4ea..b0e33ac2 100644
--- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java
+++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -14,6 +14,7 @@ import java.util.Collection;
14 14
15import javassist.CtClass; 15import javassist.CtClass;
16import javassist.bytecode.AccessFlag; 16import javassist.bytecode.AccessFlag;
17import javassist.bytecode.ConstPool;
17import javassist.bytecode.Descriptor; 18import javassist.bytecode.Descriptor;
18import javassist.bytecode.InnerClassesAttribute; 19import javassist.bytecode.InnerClassesAttribute;
19import cuchaz.enigma.analysis.JarIndex; 20import cuchaz.enigma.analysis.JarIndex;
@@ -29,21 +30,33 @@ public class InnerClassWriter
29 m_deobfuscatingTranslator = deobfuscatingTranslator; 30 m_deobfuscatingTranslator = deobfuscatingTranslator;
30 m_jarIndex = jarIndex; 31 m_jarIndex = jarIndex;
31 } 32 }
32 33
33 public void writeInnerClasses( CtClass c ) 34 public void write( CtClass c )
34 { 35 {
35 // is this an outer class with inner classes? 36 // get the outer class name
36 String obfOuterClassName = Descriptor.toJvmName( c.getName() ); 37 String obfClassName = Descriptor.toJvmName( c.getName() );
38 String obfOuterClassName = m_jarIndex.getOuterClass( obfClassName );
39 if( obfOuterClassName == null )
40 {
41 // this is an outer class
42 obfOuterClassName = obfClassName;
43 }
44 else
45 {
46 // this is an inner class, rename it to outer$inner
47 c.setName( obfOuterClassName + "$" + obfClassName );
48 }
49
50 // write the inner classes if needed
37 Collection<String> obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName ); 51 Collection<String> obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName );
38 if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() ) 52 if( obfInnerClassNames != null )
39 { 53 {
40 writeInnerClasses( c, obfInnerClassNames ); 54 writeInnerClasses( c, obfOuterClassName, obfInnerClassNames );
41 } 55 }
42 } 56 }
43 57
44 private void writeInnerClasses( CtClass c, Collection<String> obfInnerClassNames ) 58 private void writeInnerClasses( CtClass c, String obfOuterClassName, Collection<String> obfInnerClassNames )
45 { 59 {
46 String obfOuterClassName = Descriptor.toJvmName( c.getName() );
47 InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() ); 60 InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() );
48 c.getClassFile().addAttribute( attr ); 61 c.getClassFile().addAttribute( attr );
49 for( String obfInnerClassName : obfInnerClassNames ) 62 for( String obfInnerClassName : obfInnerClassNames )
@@ -54,26 +67,37 @@ public class InnerClassWriter
54 { 67 {
55 deobfOuterClassName = obfOuterClassName; 68 deobfOuterClassName = obfOuterClassName;
56 } 69 }
57 String deobfInnerClassName = m_deobfuscatingTranslator.translateClass( obfInnerClassName ); 70 String obfOuterInnerClassName = obfOuterClassName + "$" + obfInnerClassName;
58 if( deobfInnerClassName == null ) 71 String deobfOuterInnerClassName = m_deobfuscatingTranslator.translateClass( obfOuterInnerClassName );
72 if( deobfOuterInnerClassName == null )
73 {
74 deobfOuterInnerClassName = obfOuterInnerClassName;
75 }
76 String deobfInnerClassName = deobfOuterInnerClassName.substring( deobfOuterInnerClassName.lastIndexOf( '$' ) + 1 );
77
78 // here's what the JVM spec says about the InnerClasses attribute
79 // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags );
80
81 // update the attribute with this inner class
82 ConstPool constPool = c.getClassFile().getConstPool();
83 int innerClassIndex = constPool.addClassInfo( deobfOuterInnerClassName );
84 int outerClassIndex = 0;
85 int innerClassSimpleNameIndex = 0;
86 if( !m_jarIndex.isAnonymousClass( obfInnerClassName ) )
59 { 87 {
60 deobfInnerClassName = obfInnerClassName; 88 outerClassIndex = constPool.addClassInfo( deobfOuterClassName );
89 innerClassSimpleNameIndex = constPool.addUtf8Info( deobfInnerClassName );
61 } 90 }
62 91
63 // update the attribute
64 String deobfOuterInnerClassName = deobfOuterClassName + "$" + deobfInnerClassName;
65 attr.append( 92 attr.append(
66 deobfOuterInnerClassName, 93 innerClassIndex,
67 deobfOuterClassName, 94 outerClassIndex,
68 deobfInnerClassName, 95 innerClassSimpleNameIndex,
69 c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER 96 c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER
70 ); 97 );
71 98
72 // make sure the outer class references only the new inner class names 99 // make sure the outer class references only the new inner class names
73 c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName ); 100 c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName );
74
75 // TEMP
76 System.out.println( "\tInner " + obfInnerClassName + " -> " + deobfOuterInnerClassName );
77 } 101 }
78 } 102 }
79} 103}
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java
index 3b67a329..9ed6dd86 100644
--- a/src/cuchaz/enigma/gui/Gui.java
+++ b/src/cuchaz/enigma/gui/Gui.java
@@ -78,6 +78,7 @@ import cuchaz.enigma.mapping.Entry;
78import cuchaz.enigma.mapping.EntryPair; 78import cuchaz.enigma.mapping.EntryPair;
79import cuchaz.enigma.mapping.FieldEntry; 79import cuchaz.enigma.mapping.FieldEntry;
80import cuchaz.enigma.mapping.IllegalNameException; 80import cuchaz.enigma.mapping.IllegalNameException;
81import cuchaz.enigma.mapping.MappingParseException;
81import cuchaz.enigma.mapping.MethodEntry; 82import cuchaz.enigma.mapping.MethodEntry;
82 83
83public class Gui 84public class Gui
@@ -508,6 +509,10 @@ public class Gui
508 { 509 {
509 throw new Error( ex ); 510 throw new Error( ex );
510 } 511 }
512 catch( MappingParseException ex )
513 {
514 JOptionPane.showMessageDialog( m_frame, ex.getMessage() );
515 }
511 } 516 }
512 } 517 }
513 } ); 518 } );
@@ -862,9 +867,17 @@ public class Gui
862 867
863 private void startRename( ) 868 private void startRename( )
864 { 869 {
870 // get the class name
871 String className = m_selectedEntryPair.deobf.getName();
872 int pos = className.lastIndexOf( '$' );
873 if( pos >= 0 )
874 {
875 className = className.substring( pos + 1 );
876 }
877
865 // init the text box 878 // init the text box
866 final JTextField text = new JTextField(); 879 final JTextField text = new JTextField();
867 text.setText( m_selectedEntryPair.deobf.getName() ); 880 text.setText( className );
868 text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) ); 881 text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) );
869 text.addKeyListener( new KeyAdapter( ) 882 text.addKeyListener( new KeyAdapter( )
870 { 883 {
@@ -905,6 +918,7 @@ public class Gui
905 } 918 }
906 catch( IllegalNameException ex ) 919 catch( IllegalNameException ex )
907 { 920 {
921 ex.printStackTrace( System.err );
908 text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); 922 text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) );
909 } 923 }
910 return; 924 return;
diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java
index 534b0cc5..ffeb99aa 100644
--- a/src/cuchaz/enigma/gui/GuiController.java
+++ b/src/cuchaz/enigma/gui/GuiController.java
@@ -31,6 +31,7 @@ import cuchaz.enigma.mapping.ConstructorEntry;
31import cuchaz.enigma.mapping.Entry; 31import cuchaz.enigma.mapping.Entry;
32import cuchaz.enigma.mapping.EntryPair; 32import cuchaz.enigma.mapping.EntryPair;
33import cuchaz.enigma.mapping.FieldEntry; 33import cuchaz.enigma.mapping.FieldEntry;
34import cuchaz.enigma.mapping.MappingParseException;
34import cuchaz.enigma.mapping.MappingsReader; 35import cuchaz.enigma.mapping.MappingsReader;
35import cuchaz.enigma.mapping.MappingsWriter; 36import cuchaz.enigma.mapping.MappingsWriter;
36import cuchaz.enigma.mapping.MethodEntry; 37import cuchaz.enigma.mapping.MethodEntry;
@@ -75,7 +76,7 @@ public class GuiController
75 } 76 }
76 77
77 public void openMappings( File file ) 78 public void openMappings( File file )
78 throws IOException 79 throws IOException, MappingParseException
79 { 80 {
80 FileReader in = new FileReader( file ); 81 FileReader in = new FileReader( file );
81 m_deobfuscator.setMappings( new MappingsReader().read( in ) ); 82 m_deobfuscator.setMappings( new MappingsReader().read( in ) );
@@ -135,7 +136,7 @@ public class GuiController
135 136
136 public boolean entryIsObfuscatedIdenfitier( Entry deobfEntry ) 137 public boolean entryIsObfuscatedIdenfitier( Entry deobfEntry )
137 { 138 {
138 return m_deobfuscator.entryIsObfuscatedIdenfitier( m_deobfuscator.obfuscateEntry( deobfEntry ) ); 139 return m_deobfuscator.isObfuscatedIdentifier( m_deobfuscator.obfuscateEntry( deobfEntry ) );
139 } 140 }
140 141
141 public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry ) 142 public ClassInheritanceTreeNode getClassInheritance( ClassEntry obfClassEntry )
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java
index 738e8e3b..dad6da90 100644
--- a/src/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/cuchaz/enigma/mapping/ClassEntry.java
@@ -82,4 +82,32 @@ public class ClassEntry implements Entry, Serializable
82 { 82 {
83 return m_name; 83 return m_name;
84 } 84 }
85
86 public boolean isInnerClass( )
87 {
88 return m_name.lastIndexOf( '$' ) >= 0;
89 }
90
91 public String getOuterClassName( )
92 {
93 if( isInnerClass() )
94 {
95 return m_name.substring( 0, m_name.lastIndexOf( '$' ) );
96 }
97 return m_name;
98 }
99
100 public String getInnerClassName( )
101 {
102 if( !isInnerClass() )
103 {
104 throw new Error( "This is not an inner class!" );
105 }
106 return m_name.substring( m_name.lastIndexOf( '$' ) + 1 );
107 }
108
109 public ClassEntry getOuterClassEntry( )
110 {
111 return new ClassEntry( getOuterClassName() );
112 }
85} 113}
diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java
index c6826f31..c7f930c6 100644
--- a/src/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/cuchaz/enigma/mapping/ClassMapping.java
@@ -21,6 +21,8 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
21 21
22 private String m_obfName; 22 private String m_obfName;
23 private String m_deobfName; 23 private String m_deobfName;
24 private Map<String,ClassMapping> m_innerClassesByObf;
25 private Map<String,ClassMapping> m_innerClassesByDeobf;
24 private Map<String,FieldMapping> m_fieldsByObf; 26 private Map<String,FieldMapping> m_fieldsByObf;
25 private Map<String,FieldMapping> m_fieldsByDeobf; 27 private Map<String,FieldMapping> m_fieldsByDeobf;
26 private Map<String,MethodMapping> m_methodsByObf; 28 private Map<String,MethodMapping> m_methodsByObf;
@@ -31,6 +33,8 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
31 { 33 {
32 m_obfName = obfName; 34 m_obfName = obfName;
33 m_deobfName = NameValidator.validateClassName( deobfName ); 35 m_deobfName = NameValidator.validateClassName( deobfName );
36 m_innerClassesByObf = Maps.newHashMap();
37 m_innerClassesByDeobf = Maps.newHashMap();
34 m_fieldsByObf = Maps.newHashMap(); 38 m_fieldsByObf = Maps.newHashMap();
35 m_fieldsByDeobf = Maps.newHashMap(); 39 m_fieldsByDeobf = Maps.newHashMap();
36 m_methodsByObf = Maps.newHashMap(); 40 m_methodsByObf = Maps.newHashMap();
@@ -51,6 +55,72 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
51 m_deobfName = NameValidator.validateClassName( val ); 55 m_deobfName = NameValidator.validateClassName( val );
52 } 56 }
53 57
58 //// INNER CLASSES ////////
59
60 public Iterable<ClassMapping> innerClasses( )
61 {
62 assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() );
63 return m_innerClassesByObf.values();
64 }
65
66 protected void addInnerClassMapping( ClassMapping classMapping )
67 {
68 m_innerClassesByObf.put( classMapping.getObfName(), classMapping );
69 m_innerClassesByDeobf.put( classMapping.getDeobfName(), classMapping );
70 }
71
72 public ClassMapping getOrCreateInnerClass( String obfName )
73 {
74 ClassMapping classMapping = m_innerClassesByObf.get( obfName );
75 if( classMapping == null )
76 {
77 classMapping = new ClassMapping( obfName, obfName );
78 m_innerClassesByObf.put( obfName, classMapping );
79 m_innerClassesByDeobf.put( obfName, classMapping );
80 }
81 return classMapping;
82 }
83
84 public ClassMapping getInnerClassByObf( String obfName )
85 {
86 return m_innerClassesByObf.get( obfName );
87 }
88
89 public ClassMapping getInnerClassByDeobf( String deobfName )
90 {
91 return m_innerClassesByDeobf.get( deobfName );
92 }
93
94 public String getObfInnerClassName( String deobfName )
95 {
96 ClassMapping classMapping = m_innerClassesByDeobf.get( deobfName );
97 if( classMapping != null )
98 {
99 return classMapping.getObfName();
100 }
101 return null;
102 }
103
104 public String getDeobfInnerClassName( String obfName )
105 {
106 ClassMapping classMapping = m_innerClassesByObf.get( obfName );
107 if( classMapping != null )
108 {
109 return classMapping.getDeobfName();
110 }
111 return null;
112 }
113
114 public void setInnerClassName( String obfName, String deobfName )
115 {
116 ClassMapping classMapping = getOrCreateInnerClass( obfName );
117 m_innerClassesByDeobf.remove( classMapping.getDeobfName() );
118 classMapping.setDeobfName( deobfName );
119 m_innerClassesByDeobf.put( deobfName, classMapping );
120 }
121
122 //// FIELDS ////////
123
54 public Iterable<FieldMapping> fields( ) 124 public Iterable<FieldMapping> fields( )
55 { 125 {
56 assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() ); 126 assert( m_fieldsByObf.size() == m_fieldsByDeobf.size() );
@@ -62,18 +132,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
62 m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping ); 132 m_fieldsByObf.put( fieldMapping.getObfName(), fieldMapping );
63 m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping ); 133 m_fieldsByDeobf.put( fieldMapping.getDeobfName(), fieldMapping );
64 } 134 }
65
66 public Iterable<MethodMapping> methods( )
67 {
68 assert( m_methodsByObf.size() == m_methodsByDeobf.size() );
69 return m_methodsByObf.values();
70 }
71 135
72 protected void addMethodMapping( MethodMapping methodMapping )
73 {
74 m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping );
75 m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping );
76 }
77 136
78 public String getObfFieldName( String deobfName ) 137 public String getObfFieldName( String deobfName )
79 { 138 {
@@ -110,6 +169,20 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
110 m_fieldsByDeobf.put( deobfName, fieldMapping ); 169 m_fieldsByDeobf.put( deobfName, fieldMapping );
111 } 170 }
112 171
172 //// METHODS ////////
173
174 public Iterable<MethodMapping> methods( )
175 {
176 assert( m_methodsByObf.size() == m_methodsByDeobf.size() );
177 return m_methodsByObf.values();
178 }
179
180 protected void addMethodMapping( MethodMapping methodMapping )
181 {
182 m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping );
183 m_methodsByDeobf.put( getMethodKey( methodMapping.getDeobfName(), methodMapping.getDeobfSignature() ), methodMapping );
184 }
185
113 public MethodMapping getMethodByObf( String obfName, String signature ) 186 public MethodMapping getMethodByObf( String obfName, String signature )
114 { 187 {
115 return m_methodsByObf.get( getMethodKey( obfName, signature ) ); 188 return m_methodsByObf.get( getMethodKey( obfName, signature ) );
@@ -155,6 +228,8 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
155 } 228 }
156 } 229 }
157 230
231 //// ARGUMENTS ////////
232
158 public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName ) 233 public void setArgumentName( String obfMethodName, String obfMethodSignature, int argumentIndex, String argumentName )
159 { 234 {
160 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) ); 235 MethodMapping methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) );
diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java
new file mode 100644
index 00000000..4fcc1f18
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MappingParseException.java
@@ -0,0 +1,31 @@
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 MappingParseException extends Exception
14{
15 private static final long serialVersionUID = -5487280332892507236L;
16
17 private int m_line;
18 private String m_message;
19
20 public MappingParseException( int line, String message )
21 {
22 m_line = line;
23 m_message = message;
24 }
25
26 @Override
27 public String getMessage( )
28 {
29 return "Line " + m_line + ": " + m_message;
30 }
31}
diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java
index b0394090..4cebb3a4 100644
--- a/src/cuchaz/enigma/mapping/MappingsReader.java
+++ b/src/cuchaz/enigma/mapping/MappingsReader.java
@@ -13,25 +13,27 @@ package cuchaz.enigma.mapping;
13import java.io.BufferedReader; 13import java.io.BufferedReader;
14import java.io.IOException; 14import java.io.IOException;
15import java.io.Reader; 15import java.io.Reader;
16import java.util.Deque;
16import java.util.NoSuchElementException; 17import java.util.NoSuchElementException;
17import java.util.Scanner; 18import java.util.Scanner;
18 19
20import com.google.common.collect.Queues;
21
19import cuchaz.enigma.Util; 22import cuchaz.enigma.Util;
20 23
21public class MappingsReader 24public class MappingsReader
22{ 25{
23 public Mappings read( Reader in ) 26 public Mappings read( Reader in )
24 throws IOException 27 throws IOException, MappingParseException
25 { 28 {
26 return read( new BufferedReader( in ) ); 29 return read( new BufferedReader( in ) );
27 } 30 }
28 31
29 public Mappings read( BufferedReader in ) 32 public Mappings read( BufferedReader in )
30 throws IOException 33 throws IOException, MappingParseException
31 { 34 {
32 Mappings mappings = new Mappings(); 35 Mappings mappings = new Mappings();
33 ClassMapping classMapping = null; 36 Deque<Object> mappingStack = Queues.newArrayDeque();
34 MethodMapping methodMapping = null;
35 37
36 int lineNumber = 0; 38 int lineNumber = 0;
37 String line = null; 39 String line = null;
@@ -47,12 +49,28 @@ public class MappingsReader
47 } 49 }
48 50
49 // skip blank lines 51 // skip blank lines
50 line = line.trim(); 52 if( line.trim().length() <= 0 )
51 if( line.length() <= 0 )
52 { 53 {
53 continue; 54 continue;
54 } 55 }
55 56
57 // get the indent of this line
58 int indent = 0;
59 for( int i=0; i<line.length(); i++ )
60 {
61 if( line.charAt( i ) != '\t' )
62 {
63 break;
64 }
65 indent++;
66 }
67
68 // handle stack pops
69 while( indent < mappingStack.size() )
70 {
71 mappingStack.pop();
72 }
73
56 Scanner scanner = new Scanner( line ); 74 Scanner scanner = new Scanner( line );
57 try 75 try
58 { 76 {
@@ -63,40 +81,58 @@ public class MappingsReader
63 81
64 if( token.equalsIgnoreCase( "CLASS" ) ) 82 if( token.equalsIgnoreCase( "CLASS" ) )
65 { 83 {
66 classMapping = readClass( scanner ); 84 ClassMapping classMapping = readClass( scanner );
67 mappings.addClassMapping( classMapping ); 85 if( indent == 0 )
68 methodMapping = null; 86 {
87 // outer class
88 mappings.addClassMapping( classMapping );
89 }
90 else if( indent == 1 )
91 {
92 // inner class
93 if( !( mappingStack.getFirst() instanceof ClassMapping ) )
94 {
95 throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" );
96 }
97 ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping( classMapping );
98 }
99 else
100 {
101 throw new MappingParseException( lineNumber, "Unexpected CLASS entry here!" );
102 }
103 mappingStack.push( classMapping );
69 } 104 }
70 else if( token.equalsIgnoreCase( "FIELD" ) ) 105 else if( token.equalsIgnoreCase( "FIELD" ) )
71 { 106 {
72 if( classMapping == null ) 107 if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) )
73 { 108 {
74 throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected FIELD entry here!" ); 109 throw new MappingParseException( lineNumber, "Unexpected FIELD entry here!" );
75 } 110 }
76 classMapping.addFieldMapping( readField( scanner ) ); 111 ((ClassMapping)mappingStack.getFirst()).addFieldMapping( readField( scanner ) );
77 } 112 }
78 else if( token.equalsIgnoreCase( "METHOD" ) ) 113 else if( token.equalsIgnoreCase( "METHOD" ) )
79 { 114 {
80 if( classMapping == null ) 115 if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof ClassMapping) )
81 { 116 {
82 throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected METHOD entry here!" ); 117 throw new MappingParseException( lineNumber, "Unexpected METHOD entry here!" );
83 } 118 }
84 methodMapping = readMethod( scanner ); 119 MethodMapping methodMapping = readMethod( scanner );
85 classMapping.addMethodMapping( methodMapping ); 120 ((ClassMapping)mappingStack.getFirst()).addMethodMapping( methodMapping );
121 mappingStack.push( methodMapping );
86 } 122 }
87 else if( token.equalsIgnoreCase( "ARG" ) ) 123 else if( token.equalsIgnoreCase( "ARG" ) )
88 { 124 {
89 if( classMapping == null || methodMapping == null ) 125 if( mappingStack.isEmpty() || !(mappingStack.getFirst() instanceof MethodMapping) )
90 { 126 {
91 throw new IllegalArgumentException( "Line " + lineNumber + ": Unexpected ARG entry here!" ); 127 throw new MappingParseException( lineNumber, "Unexpected ARG entry here!" );
92 } 128 }
93 methodMapping.addArgumentMapping( readArgument( scanner ) ); 129 ((MethodMapping)mappingStack.getFirst()).addArgumentMapping( readArgument( scanner ) );
94 } 130 }
95 } 131 }
96 } 132 }
97 catch( NoSuchElementException ex ) 133 catch( NoSuchElementException ex )
98 { 134 {
99 throw new IllegalArgumentException( "Line " + lineNumber + ": malformed line!" ); 135 throw new MappingParseException( lineNumber, "Malformed line!" );
100 } 136 }
101 finally 137 finally
102 { 138 {
diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java
index a97052fa..62035713 100644
--- a/src/cuchaz/enigma/mapping/MappingsWriter.java
+++ b/src/cuchaz/enigma/mapping/MappingsWriter.java
@@ -30,50 +30,56 @@ public class MappingsWriter
30 { 30 {
31 for( ClassMapping classMapping : sorted( mappings.classes() ) ) 31 for( ClassMapping classMapping : sorted( mappings.classes() ) )
32 { 32 {
33 write( out, classMapping ); 33 write( out, classMapping, 0 );
34 } 34 }
35 } 35 }
36 36
37 public void write( PrintWriter out, ClassMapping classMapping ) 37 private void write( PrintWriter out, ClassMapping classMapping, int depth )
38 throws IOException 38 throws IOException
39 { 39 {
40 out.format( "CLASS %s %s\n", classMapping.getObfName(), classMapping.getDeobfName() ); 40 out.format( "%sCLASS %s %s\n", getIndent( depth ), classMapping.getObfName(), classMapping.getDeobfName() );
41
42 for( ClassMapping innerClassMapping : sorted( classMapping.innerClasses() ) )
43 {
44 write( out, innerClassMapping, depth + 1 );
45 }
41 46
42 for( FieldMapping fieldMapping : sorted( classMapping.fields() ) ) 47 for( FieldMapping fieldMapping : sorted( classMapping.fields() ) )
43 { 48 {
44 write( out, fieldMapping ); 49 write( out, fieldMapping, depth + 1 );
45 } 50 }
46 51
47 for( MethodMapping methodMapping : sorted( classMapping.methods() ) ) 52 for( MethodMapping methodMapping : sorted( classMapping.methods() ) )
48 { 53 {
49 write( out, methodMapping ); 54 write( out, methodMapping, depth + 1 );
50 } 55 }
51 } 56 }
52 57
53 public void write( PrintWriter out, FieldMapping fieldMapping ) 58 private void write( PrintWriter out, FieldMapping fieldMapping, int depth )
54 throws IOException 59 throws IOException
55 { 60 {
56 out.format( "\tFIELD %s %s\n", fieldMapping.getObfName(), fieldMapping.getDeobfName() ); 61 out.format( "%sFIELD %s %s\n", getIndent( depth ), fieldMapping.getObfName(), fieldMapping.getDeobfName() );
57 } 62 }
58 63
59 public void write( PrintWriter out, MethodMapping methodMapping ) 64 private void write( PrintWriter out, MethodMapping methodMapping, int depth )
60 throws IOException 65 throws IOException
61 { 66 {
62 out.format( "\tMETHOD %s %s %s %s\n", 67 out.format( "%sMETHOD %s %s %s %s\n",
68 getIndent( depth ),
63 methodMapping.getObfName(), methodMapping.getDeobfName(), 69 methodMapping.getObfName(), methodMapping.getDeobfName(),
64 methodMapping.getObfSignature(), methodMapping.getDeobfSignature() 70 methodMapping.getObfSignature(), methodMapping.getDeobfSignature()
65 ); 71 );
66 72
67 for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) ) 73 for( ArgumentMapping argumentMapping : sorted( methodMapping.arguments() ) )
68 { 74 {
69 write( out, argumentMapping ); 75 write( out, argumentMapping, depth + 1 );
70 } 76 }
71 } 77 }
72 78
73 public void write( PrintWriter out, ArgumentMapping argumentMapping ) 79 private void write( PrintWriter out, ArgumentMapping argumentMapping, int depth )
74 throws IOException 80 throws IOException
75 { 81 {
76 out.format( "\t\tARG %d %s\n", argumentMapping.getIndex(), argumentMapping.getName() ); 82 out.format( "%sARG %d %s\n", getIndent( depth ), argumentMapping.getIndex(), argumentMapping.getName() );
77 } 83 }
78 84
79 private <T extends Comparable<T>> List<T> sorted( Iterable<T> classes ) 85 private <T extends Comparable<T>> List<T> sorted( Iterable<T> classes )
@@ -86,4 +92,14 @@ public class MappingsWriter
86 Collections.sort( out ); 92 Collections.sort( out );
87 return out; 93 return out;
88 } 94 }
95
96 private String getIndent( int depth )
97 {
98 StringBuilder buf = new StringBuilder();
99 for( int i=0; i<depth; i++ )
100 {
101 buf.append( "\t" );
102 }
103 return buf.toString();
104 }
89} 105}
diff --git a/src/cuchaz/enigma/mapping/Renamer.java b/src/cuchaz/enigma/mapping/Renamer.java
index d372575e..0bb8dc12 100644
--- a/src/cuchaz/enigma/mapping/Renamer.java
+++ b/src/cuchaz/enigma/mapping/Renamer.java
@@ -32,15 +32,18 @@ public class Renamer
32 public void setClassName( ClassEntry obf, String deobfName ) 32 public void setClassName( ClassEntry obf, String deobfName )
33 { 33 {
34 deobfName = NameValidator.validateClassName( deobfName ); 34 deobfName = NameValidator.validateClassName( deobfName );
35 ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getName() ); 35 ClassMapping classMapping = getOrCreateClassMapping( obf );
36 if( classMapping == null ) 36
37 if( obf.isInnerClass() )
37 { 38 {
38 classMapping = createClassMapping( obf ); 39 classMapping.setInnerClassName( obf.getInnerClassName(), deobfName );
40 }
41 else
42 {
43 m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() );
44 classMapping.setDeobfName( deobfName );
45 m_mappings.m_classesByDeobf.put( deobfName, classMapping );
39 } 46 }
40
41 m_mappings.m_classesByDeobf.remove( classMapping.getDeobfName() );
42 classMapping.setDeobfName( deobfName );
43 m_mappings.m_classesByDeobf.put( deobfName, classMapping );
44 47
45 updateDeobfMethodSignatures(); 48 updateDeobfMethodSignatures();
46 } 49 }
@@ -48,12 +51,7 @@ public class Renamer
48 public void setFieldName( FieldEntry obf, String deobfName ) 51 public void setFieldName( FieldEntry obf, String deobfName )
49 { 52 {
50 deobfName = NameValidator.validateFieldName( deobfName ); 53 deobfName = NameValidator.validateFieldName( deobfName );
51 ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); 54 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() );
52 if( classMapping == null )
53 {
54 classMapping = createClassMapping( obf.getClassEntry() );
55 }
56
57 classMapping.setFieldName( obf.getName(), deobfName ); 55 classMapping.setFieldName( obf.getName(), deobfName );
58 } 56 }
59 57
@@ -84,28 +82,15 @@ public class Renamer
84 public void setMethodName( MethodEntry obf, String deobfName ) 82 public void setMethodName( MethodEntry obf, String deobfName )
85 { 83 {
86 deobfName = NameValidator.validateMethodName( deobfName ); 84 deobfName = NameValidator.validateMethodName( deobfName );
87 ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); 85 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() );
88 if( classMapping == null )
89 {
90 classMapping = createClassMapping( obf.getClassEntry() );
91 }
92
93 String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() ); 86 String deobfSignature = m_mappings.getTranslator( m_index.getAncestries(), TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() );
94 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature ); 87 classMapping.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature );
95
96 // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too
97
98 } 88 }
99 89
100 public void setArgumentName( ArgumentEntry obf, String deobfName ) 90 public void setArgumentName( ArgumentEntry obf, String deobfName )
101 { 91 {
102 deobfName = NameValidator.validateArgumentName( deobfName ); 92 deobfName = NameValidator.validateArgumentName( deobfName );
103 ClassMapping classMapping = m_mappings.m_classesByObf.get( obf.getClassName() ); 93 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping( obf.getClassEntry() );
104 if( classMapping == null )
105 {
106 classMapping = createClassMapping( obf.getClassEntry() );
107 }
108
109 classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName ); 94 classMapping.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName );
110 } 95 }
111 96
@@ -119,11 +104,26 @@ public class Renamer
119 gzipout.finish(); 104 gzipout.finish();
120 } 105 }
121 106
122 private ClassMapping createClassMapping( ClassEntry obf ) 107 private ClassMapping getOrCreateClassMapping( ClassEntry obfClassEntry )
108 {
109 String obfClassName = obfClassEntry.getOuterClassName();
110 ClassMapping classMapping = m_mappings.m_classesByObf.get( obfClassName );
111 if( classMapping == null )
112 {
113 classMapping = new ClassMapping( obfClassName, obfClassName );
114 m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping );
115 m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping );
116 }
117 return classMapping;
118 }
119
120 private ClassMapping getOrCreateClassMappingOrInnerClassMapping( ClassEntry obfClassEntry )
123 { 121 {
124 ClassMapping classMapping = new ClassMapping( obf.getName(), obf.getName() ); 122 ClassMapping classMapping = getOrCreateClassMapping( obfClassEntry );
125 m_mappings.m_classesByObf.put( classMapping.getObfName(), classMapping ); 123 if( obfClassEntry.isInnerClass() )
126 m_mappings.m_classesByDeobf.put( classMapping.getDeobfName(), classMapping ); 124 {
125 classMapping = classMapping.getOrCreateInnerClass( obfClassEntry.getInnerClassName() );
126 }
127 return classMapping; 127 return classMapping;
128 } 128 }
129 129
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
index 50433214..76f45cd6 100644
--- a/src/cuchaz/enigma/mapping/Translator.java
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -20,7 +20,7 @@ import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
20public class Translator 20public class Translator
21{ 21{
22 private TranslationDirection m_direction; 22 private TranslationDirection m_direction;
23 /* TEMP */ public Map<String,ClassMapping> m_classes; 23 public Map<String,ClassMapping> m_classes;
24 private Ancestries m_ancestries; 24 private Ancestries m_ancestries;
25 25
26 protected Translator( TranslationDirection direction, Map<String,ClassMapping> classes, Ancestries ancestries ) 26 protected Translator( TranslationDirection direction, Map<String,ClassMapping> classes, Ancestries ancestries )
@@ -30,22 +30,42 @@ public class Translator
30 m_ancestries = ancestries; 30 m_ancestries = ancestries;
31 } 31 }
32 32
33 public String translate( ClassEntry in ) 33 public String translateClass( String className )
34 { 34 {
35 return translateClass( in.getName() ); 35 return translate( new ClassEntry( className ) );
36 } 36 }
37 37
38 public String translateClass( String in ) 38 public String translate( ClassEntry in )
39 { 39 {
40 ClassMapping classIndex = m_classes.get( in ); 40 ClassMapping classMapping = m_classes.get( in.getOuterClassName() );
41 if( classIndex != null ) 41 if( classMapping != null )
42 { 42 {
43 return m_direction.choose( 43 if( in.isInnerClass() )
44 classIndex.getDeobfName(), 44 {
45 classIndex.getObfName() 45 // look for the inner class
46 ); 46 String translatedInnerClassName = m_direction.choose(
47 classMapping.getDeobfInnerClassName( in.getInnerClassName() ),
48 classMapping.getObfInnerClassName( in.getInnerClassName() )
49 );
50 if( translatedInnerClassName != null )
51 {
52 // return outer$inner
53 String translatedOuterClassName = m_direction.choose(
54 classMapping.getDeobfName(),
55 classMapping.getObfName()
56 );
57 return translatedOuterClassName + "$" + translatedInnerClassName;
58 }
59 }
60 else
61 {
62 // just return outer
63 return m_direction.choose(
64 classMapping.getDeobfName(),
65 classMapping.getObfName()
66 );
67 }
47 } 68 }
48
49 return null; 69 return null;
50 } 70 }
51 71
@@ -64,21 +84,20 @@ public class Translator
64 for( String className : getSelfAndAncestors( in.getClassName() ) ) 84 for( String className : getSelfAndAncestors( in.getClassName() ) )
65 { 85 {
66 // look for the class 86 // look for the class
67 ClassMapping classIndex = m_classes.get( className ); 87 ClassMapping classMapping = findClassMapping( new ClassEntry( className ) );
68 if( classIndex != null ) 88 if( classMapping != null )
69 { 89 {
70 // look for the field 90 // look for the field
71 String deobfName = m_direction.choose( 91 String translatedName = m_direction.choose(
72 classIndex.getDeobfFieldName( in.getName() ), 92 classMapping.getDeobfFieldName( in.getName() ),
73 classIndex.getObfFieldName( in.getName() ) 93 classMapping.getObfFieldName( in.getName() )
74 ); 94 );
75 if( deobfName != null ) 95 if( translatedName != null )
76 { 96 {
77 return deobfName; 97 return translatedName;
78 } 98 }
79 } 99 }
80 } 100 }
81
82 return null; 101 return null;
83 } 102 }
84 103
@@ -99,20 +118,20 @@ public class Translator
99 { 118 {
100 for( String className : getSelfAndAncestors( in.getClassName() ) ) 119 for( String className : getSelfAndAncestors( in.getClassName() ) )
101 { 120 {
102 // look for the class 121 // look for class
103 ClassMapping classIndex = m_classes.get( className ); 122 ClassMapping classMapping = findClassMapping( new ClassEntry( className ) );
104 if( classIndex != null ) 123 if( classMapping != null )
105 { 124 {
106 // look for the method 125 // look for the method
107 MethodMapping methodIndex = m_direction.choose( 126 MethodMapping methodMapping = m_direction.choose(
108 classIndex.getMethodByObf( in.getName(), in.getSignature() ), 127 classMapping.getMethodByObf( in.getName(), in.getSignature() ),
109 classIndex.getMethodByDeobf( in.getName(), in.getSignature() ) 128 classMapping.getMethodByDeobf( in.getName(), in.getSignature() )
110 ); 129 );
111 if( methodIndex != null ) 130 if( methodMapping != null )
112 { 131 {
113 return m_direction.choose( 132 return m_direction.choose(
114 methodIndex.getDeobfName(), 133 methodMapping.getDeobfName(),
115 methodIndex.getObfName() 134 methodMapping.getObfName()
116 ); 135 );
117 } 136 }
118 } 137 }
@@ -148,19 +167,19 @@ public class Translator
148 for( String className : getSelfAndAncestors( in.getClassName() ) ) 167 for( String className : getSelfAndAncestors( in.getClassName() ) )
149 { 168 {
150 // look for the class 169 // look for the class
151 ClassMapping classIndex = m_classes.get( className ); 170 ClassMapping classMapping = findClassMapping( new ClassEntry( className ) );
152 if( classIndex != null ) 171 if( classMapping != null )
153 { 172 {
154 // look for the method 173 // look for the method
155 MethodMapping methodIndex = m_direction.choose( 174 MethodMapping methodMapping = m_direction.choose(
156 classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ), 175 classMapping.getMethodByObf( in.getMethodName(), in.getMethodSignature() ),
157 classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() ) 176 classMapping.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() )
158 ); 177 );
159 if( methodIndex != null ) 178 if( methodMapping != null )
160 { 179 {
161 return m_direction.choose( 180 return m_direction.choose(
162 methodIndex.getDeobfArgumentName( in.getIndex() ), 181 methodMapping.getDeobfArgumentName( in.getIndex() ),
163 methodIndex.getObfArgumentName( in.getIndex() ) 182 methodMapping.getObfArgumentName( in.getIndex() )
164 ); 183 );
165 } 184 }
166 } 185 }
@@ -207,4 +226,17 @@ public class Translator
207 ancestry.addAll( m_ancestries.getAncestry( className ) ); 226 ancestry.addAll( m_ancestries.getAncestry( className ) );
208 return ancestry; 227 return ancestry;
209 } 228 }
229
230 private ClassMapping findClassMapping( ClassEntry classEntry )
231 {
232 ClassMapping classMapping = m_classes.get( classEntry.getOuterClassName() );
233 if( classMapping != null && classEntry.isInnerClass() )
234 {
235 classMapping = m_direction.choose(
236 classMapping.getInnerClassByObf( classEntry.getInnerClassName() ),
237 classMapping.getInnerClassByDeobf( classEntry.getInnerClassName() )
238 );
239 }
240 return classMapping;
241 }
210} 242}