diff options
| author | 2014-08-17 10:56:17 -0400 | |
|---|---|---|
| committer | 2014-08-17 10:56:17 -0400 | |
| commit | 6c4440ac1133bfaa7871d1049d174528a289ef30 (patch) | |
| tree | fe1142b285c5e43dbd3afe8dd3eb0189f027c6a6 /src | |
| parent | trying to get inner/anonymous classes working... I have a working heuristic i... (diff) | |
| download | enigma-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.java | 51 | ||||
| -rw-r--r-- | src/cuchaz/enigma/TranslatingTypeLoader.java | 56 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/Ancestries.java | 20 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/BridgeFixer.java | 91 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 155 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/SourceIndex.java | 31 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/TreeDumpVisitor.java | 45 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/InnerClassWriter.java | 64 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/Gui.java | 16 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/GuiController.java | 5 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ClassEntry.java | 28 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ClassMapping.java | 97 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingParseException.java | 31 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsReader.java | 76 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsWriter.java | 40 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Renamer.java | 64 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Translator.java | 104 |
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; | |||
| 26 | import com.strobel.assembler.metadata.Buffer; | 26 | import com.strobel.assembler.metadata.Buffer; |
| 27 | import com.strobel.assembler.metadata.ITypeLoader; | 27 | import com.strobel.assembler.metadata.ITypeLoader; |
| 28 | 28 | ||
| 29 | import cuchaz.enigma.analysis.BridgeFixer; | ||
| 29 | import cuchaz.enigma.analysis.JarIndex; | 30 | import cuchaz.enigma.analysis.JarIndex; |
| 30 | import cuchaz.enigma.bytecode.ClassTranslator; | 31 | import cuchaz.enigma.bytecode.ClassTranslator; |
| 31 | import cuchaz.enigma.bytecode.InnerClassWriter; | 32 | import 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.analysis; | ||
| 12 | |||
| 13 | import java.util.List; | ||
| 14 | |||
| 15 | import javassist.CannotCompileException; | ||
| 16 | import javassist.CtClass; | ||
| 17 | import javassist.CtMethod; | ||
| 18 | import javassist.NotFoundException; | ||
| 19 | import javassist.bytecode.AccessFlag; | ||
| 20 | import javassist.expr.ExprEditor; | ||
| 21 | import javassist.expr.MethodCall; | ||
| 22 | |||
| 23 | import com.beust.jcommander.internal.Lists; | ||
| 24 | |||
| 25 | public 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 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.analysis; | 11 | package cuchaz.enigma.analysis; |
| 12 | 12 | ||
| 13 | import java.util.AbstractMap; | ||
| 13 | import java.util.Collection; | 14 | import java.util.Collection; |
| 15 | import java.util.Iterator; | ||
| 14 | import java.util.List; | 16 | import java.util.List; |
| 15 | import java.util.Map; | 17 | import java.util.Map; |
| 16 | import java.util.Set; | 18 | import java.util.Set; |
| @@ -37,6 +39,7 @@ import com.google.common.collect.Maps; | |||
| 37 | import com.google.common.collect.Multimap; | 39 | import com.google.common.collect.Multimap; |
| 38 | import com.google.common.collect.Sets; | 40 | import com.google.common.collect.Sets; |
| 39 | 41 | ||
| 42 | import cuchaz.enigma.mapping.ArgumentEntry; | ||
| 40 | import cuchaz.enigma.mapping.ClassEntry; | 43 | import cuchaz.enigma.mapping.ClassEntry; |
| 41 | import cuchaz.enigma.mapping.ConstructorEntry; | 44 | import cuchaz.enigma.mapping.ConstructorEntry; |
| 42 | import cuchaz.enigma.mapping.Entry; | 45 | import 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; | |||
| 17 | import com.google.common.collect.Lists; | 17 | import com.google.common.collect.Lists; |
| 18 | import com.google.common.collect.Maps; | 18 | import com.google.common.collect.Maps; |
| 19 | import com.strobel.decompiler.languages.Region; | 19 | import com.strobel.decompiler.languages.Region; |
| 20 | import com.strobel.decompiler.languages.java.ast.AstNode; | 20 | import com.strobel.decompiler.languages.java.ast.Identifier; |
| 21 | 21 | ||
| 22 | import cuchaz.enigma.mapping.Entry; | 22 | import 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 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.analysis; | 11 | package cuchaz.enigma.analysis; |
| 12 | 12 | ||
| 13 | import java.io.File; | ||
| 14 | import java.io.FileWriter; | ||
| 15 | import java.io.IOException; | ||
| 16 | import java.io.Writer; | ||
| 17 | |||
| 13 | import com.strobel.componentmodel.Key; | 18 | import com.strobel.componentmodel.Key; |
| 14 | import com.strobel.decompiler.languages.java.ast.Annotation; | 19 | import com.strobel.decompiler.languages.java.ast.Annotation; |
| 15 | import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; | 20 | import com.strobel.decompiler.languages.java.ast.AnonymousObjectCreationExpression; |
| @@ -87,10 +92,42 @@ import com.strobel.decompiler.patterns.Pattern; | |||
| 87 | 92 | ||
| 88 | public class TreeDumpVisitor implements IAstVisitor<Void, Void> | 93 | public 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 | ||
| 15 | import javassist.CtClass; | 15 | import javassist.CtClass; |
| 16 | import javassist.bytecode.AccessFlag; | 16 | import javassist.bytecode.AccessFlag; |
| 17 | import javassist.bytecode.ConstPool; | ||
| 17 | import javassist.bytecode.Descriptor; | 18 | import javassist.bytecode.Descriptor; |
| 18 | import javassist.bytecode.InnerClassesAttribute; | 19 | import javassist.bytecode.InnerClassesAttribute; |
| 19 | import cuchaz.enigma.analysis.JarIndex; | 20 | import 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; | |||
| 78 | import cuchaz.enigma.mapping.EntryPair; | 78 | import cuchaz.enigma.mapping.EntryPair; |
| 79 | import cuchaz.enigma.mapping.FieldEntry; | 79 | import cuchaz.enigma.mapping.FieldEntry; |
| 80 | import cuchaz.enigma.mapping.IllegalNameException; | 80 | import cuchaz.enigma.mapping.IllegalNameException; |
| 81 | import cuchaz.enigma.mapping.MappingParseException; | ||
| 81 | import cuchaz.enigma.mapping.MethodEntry; | 82 | import cuchaz.enigma.mapping.MethodEntry; |
| 82 | 83 | ||
| 83 | public class Gui | 84 | public 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; | |||
| 31 | import cuchaz.enigma.mapping.Entry; | 31 | import cuchaz.enigma.mapping.Entry; |
| 32 | import cuchaz.enigma.mapping.EntryPair; | 32 | import cuchaz.enigma.mapping.EntryPair; |
| 33 | import cuchaz.enigma.mapping.FieldEntry; | 33 | import cuchaz.enigma.mapping.FieldEntry; |
| 34 | import cuchaz.enigma.mapping.MappingParseException; | ||
| 34 | import cuchaz.enigma.mapping.MappingsReader; | 35 | import cuchaz.enigma.mapping.MappingsReader; |
| 35 | import cuchaz.enigma.mapping.MappingsWriter; | 36 | import cuchaz.enigma.mapping.MappingsWriter; |
| 36 | import cuchaz.enigma.mapping.MethodEntry; | 37 | import 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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; | |||
| 13 | import java.io.BufferedReader; | 13 | import java.io.BufferedReader; |
| 14 | import java.io.IOException; | 14 | import java.io.IOException; |
| 15 | import java.io.Reader; | 15 | import java.io.Reader; |
| 16 | import java.util.Deque; | ||
| 16 | import java.util.NoSuchElementException; | 17 | import java.util.NoSuchElementException; |
| 17 | import java.util.Scanner; | 18 | import java.util.Scanner; |
| 18 | 19 | ||
| 20 | import com.google.common.collect.Queues; | ||
| 21 | |||
| 19 | import cuchaz.enigma.Util; | 22 | import cuchaz.enigma.Util; |
| 20 | 23 | ||
| 21 | public class MappingsReader | 24 | public 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; | |||
| 20 | public class Translator | 20 | public 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 | } |