diff options
| author | 2014-08-23 23:43:31 -0400 | |
|---|---|---|
| committer | 2014-08-23 23:43:31 -0400 | |
| commit | 8fa1741b621644ef84a9395a4c395d4ff3a89207 (patch) | |
| tree | 9cc054e2636dd13a32950ad68dba212275d33026 /src | |
| parent | added export command with progress bar (diff) | |
| download | enigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.tar.gz enigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.tar.xz enigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.zip | |
moved all classes from the default package into a package called "default" so they can be properly imported by other classes
Diffstat (limited to 'src')
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 49 | ||||
| -rw-r--r-- | src/cuchaz/enigma/TranslatingTypeLoader.java | 19 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarClassIterator.java | 45 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 40 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassRenamer.java | 115 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassTranslator.java | 64 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/Gui.java | 3 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/GuiController.java | 9 | ||||
| -rw-r--r-- | src/cuchaz/enigma/gui/GuiTricks.java | 18 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ClassEntry.java | 25 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/IllegalNameException.java | 22 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/NameValidator.java | 8 |
13 files changed, 298 insertions, 125 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5d87ad0..ee414fa 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -17,6 +17,8 @@ import java.io.StringWriter; | |||
| 17 | import java.util.List; | 17 | import java.util.List; |
| 18 | import java.util.jar.JarFile; | 18 | import java.util.jar.JarFile; |
| 19 | 19 | ||
| 20 | import javassist.bytecode.Descriptor; | ||
| 21 | |||
| 20 | import com.strobel.assembler.metadata.MetadataSystem; | 22 | import com.strobel.assembler.metadata.MetadataSystem; |
| 21 | import com.strobel.assembler.metadata.TypeDefinition; | 23 | import com.strobel.assembler.metadata.TypeDefinition; |
| 22 | import com.strobel.decompiler.DecompilerContext; | 24 | import com.strobel.decompiler.DecompilerContext; |
| @@ -118,28 +120,30 @@ public class Deobfuscator | |||
| 118 | 120 | ||
| 119 | public void getSeparatedClasses( List<String> obfClasses, List<String> deobfClasses ) | 121 | public void getSeparatedClasses( List<String> obfClasses, List<String> deobfClasses ) |
| 120 | { | 122 | { |
| 121 | for( String obfClassName : m_jarIndex.getObfClassNames() ) | 123 | for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) |
| 122 | { | 124 | { |
| 123 | // skip inner classes | 125 | // skip inner classes |
| 124 | if( m_jarIndex.getOuterClass( obfClassName ) != null ) | 126 | if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) |
| 125 | { | 127 | { |
| 126 | continue; | 128 | continue; |
| 127 | } | 129 | } |
| 128 | 130 | ||
| 129 | // separate the classes | 131 | // separate the classes |
| 130 | ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); | 132 | ClassEntry deobfClassEntry = deobfuscateEntry( obfClassEntry ); |
| 131 | if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) ) | 133 | if( !deobfClassEntry.equals( obfClassEntry ) ) |
| 132 | { | 134 | { |
| 133 | deobfClasses.add( classMapping.getDeobfName() ); | 135 | // if the class has a mapping, clearly it's deobfuscated |
| 136 | deobfClasses.add( deobfClassEntry.getName() ); | ||
| 134 | } | 137 | } |
| 135 | else if( obfClassName.indexOf( '/' ) >= 0 ) | 138 | else if( !obfClassEntry.getPackageName().equals( "default" ) ) |
| 136 | { | 139 | { |
| 137 | // this class is in a package and therefore is not obfuscated | 140 | // also call it deobufscated if it's not in the "default" package |
| 138 | deobfClasses.add( obfClassName ); | 141 | deobfClasses.add( obfClassEntry.getName() ); |
| 139 | } | 142 | } |
| 140 | else | 143 | else |
| 141 | { | 144 | { |
| 142 | obfClasses.add( obfClassName ); | 145 | // otherwise, assume it's still obfuscated |
| 146 | obfClasses.add( obfClassEntry.getName() ); | ||
| 143 | } | 147 | } |
| 144 | } | 148 | } |
| 145 | } | 149 | } |
| @@ -198,7 +202,7 @@ public class Deobfuscator | |||
| 198 | public void writeSources( File dirOut, ProgressListener progress ) | 202 | public void writeSources( File dirOut, ProgressListener progress ) |
| 199 | throws IOException | 203 | throws IOException |
| 200 | { | 204 | { |
| 201 | int numClasses = m_jarIndex.getObfClassNames().size(); | 205 | int numClasses = m_jarIndex.getObfClassEntries().size(); |
| 202 | if( progress != null ) | 206 | if( progress != null ) |
| 203 | { | 207 | { |
| 204 | progress.init( numClasses ); | 208 | progress.init( numClasses ); |
| @@ -206,22 +210,22 @@ public class Deobfuscator | |||
| 206 | int i = 0; | 210 | int i = 0; |
| 207 | 211 | ||
| 208 | // DEOBFUSCATE ALL THE THINGS!! @_@ | 212 | // DEOBFUSCATE ALL THE THINGS!! @_@ |
| 209 | for( String obfClassName : m_jarIndex.getObfClassNames() ) | 213 | for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) |
| 210 | { | 214 | { |
| 211 | // skip inner classes | 215 | // skip inner classes |
| 212 | if( m_jarIndex.getOuterClass( obfClassName ) != null ) | 216 | if( obfClassEntry.isInnerClass() ) |
| 213 | { | 217 | { |
| 214 | continue; | 218 | continue; |
| 215 | } | 219 | } |
| 216 | 220 | ||
| 217 | ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassName ) ); | 221 | ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); |
| 218 | if( progress != null ) | 222 | if( progress != null ) |
| 219 | { | 223 | { |
| 220 | progress.onProgress( i++, deobfClassEntry.toString() ); | 224 | progress.onProgress( i++, deobfClassEntry.toString() ); |
| 221 | } | 225 | } |
| 222 | 226 | ||
| 223 | // get the source | 227 | // get the source |
| 224 | String source = getSource( getSourceTree( obfClassName ) ); | 228 | String source = getSource( getSourceTree( obfClassEntry.getName() ) ); |
| 225 | 229 | ||
| 226 | // write the file | 230 | // write the file |
| 227 | File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); | 231 | File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); |
| @@ -284,7 +288,7 @@ public class Deobfuscator | |||
| 284 | { | 288 | { |
| 285 | if( obfEntry instanceof ClassEntry ) | 289 | if( obfEntry instanceof ClassEntry ) |
| 286 | { | 290 | { |
| 287 | m_renamer.setClassName( (ClassEntry)obfEntry, newName ); | 291 | m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); |
| 288 | } | 292 | } |
| 289 | else if( obfEntry instanceof FieldEntry ) | 293 | else if( obfEntry instanceof FieldEntry ) |
| 290 | { | 294 | { |
| @@ -348,21 +352,18 @@ public class Deobfuscator | |||
| 348 | { | 352 | { |
| 349 | if( obfEntry instanceof ClassEntry ) | 353 | if( obfEntry instanceof ClassEntry ) |
| 350 | { | 354 | { |
| 351 | if( obfEntry.getName().indexOf( '$' ) >= 0 ) | 355 | ClassEntry obfClassEntry = (ClassEntry)obfEntry; |
| 356 | if( obfClassEntry.isInnerClass() ) | ||
| 352 | { | 357 | { |
| 353 | String[] parts = obfEntry.getName().split( "\\$" ); | ||
| 354 | assert( parts.length == 2 ); // not supporting recursively-nested classes | ||
| 355 | String outerClassName = parts[0]; | ||
| 356 | String innerClassName = parts[1]; | ||
| 357 | |||
| 358 | // both classes must be in the list | 358 | // both classes must be in the list |
| 359 | return m_jarIndex.getObfClassNames().contains( outerClassName ) | 359 | return m_jarIndex.getObfClassEntries().contains( obfClassEntry.getOuterClassEntry() ) |
| 360 | && m_jarIndex.getObfClassNames().contains( innerClassName ); | 360 | && m_jarIndex.getObfClassEntries().contains( obfClassEntry.getInnerClassName() ); |
| 361 | // TODO: make sure this works for the inner class!! | ||
| 361 | } | 362 | } |
| 362 | else | 363 | else |
| 363 | { | 364 | { |
| 364 | // class must be in the list | 365 | // class must be in the list |
| 365 | return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); | 366 | return m_jarIndex.getObfClassEntries().contains( obfEntry ); |
| 366 | } | 367 | } |
| 367 | } | 368 | } |
| 368 | else | 369 | else |
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index cc86364..e7e941a 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java | |||
| @@ -13,6 +13,7 @@ package cuchaz.enigma; | |||
| 13 | import java.io.ByteArrayOutputStream; | 13 | import java.io.ByteArrayOutputStream; |
| 14 | import java.io.IOException; | 14 | import java.io.IOException; |
| 15 | import java.io.InputStream; | 15 | import java.io.InputStream; |
| 16 | import java.util.Arrays; | ||
| 16 | import java.util.Map; | 17 | import java.util.Map; |
| 17 | import java.util.jar.JarEntry; | 18 | import java.util.jar.JarEntry; |
| 18 | import java.util.jar.JarFile; | 19 | import java.util.jar.JarFile; |
| @@ -30,6 +31,7 @@ import com.strobel.assembler.metadata.ITypeLoader; | |||
| 30 | 31 | ||
| 31 | import cuchaz.enigma.analysis.BridgeFixer; | 32 | import cuchaz.enigma.analysis.BridgeFixer; |
| 32 | import cuchaz.enigma.analysis.JarIndex; | 33 | import cuchaz.enigma.analysis.JarIndex; |
| 34 | import cuchaz.enigma.bytecode.ClassRenamer; | ||
| 33 | import cuchaz.enigma.bytecode.ClassTranslator; | 35 | import cuchaz.enigma.bytecode.ClassTranslator; |
| 34 | import cuchaz.enigma.bytecode.InnerClassWriter; | 36 | import cuchaz.enigma.bytecode.InnerClassWriter; |
| 35 | import cuchaz.enigma.bytecode.MethodParameterWriter; | 37 | import cuchaz.enigma.bytecode.MethodParameterWriter; |
| @@ -99,22 +101,29 @@ public class TranslatingTypeLoader implements ITypeLoader | |||
| 99 | return null; | 101 | return null; |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | /* DEBUG | 104 | // DEBUG |
| 103 | if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) | 105 | if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) |
| 104 | { | 106 | { |
| 105 | System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); | 107 | System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); |
| 106 | } | 108 | } |
| 107 | */ | 109 | // |
| 108 | 110 | ||
| 109 | // get the jar entry | 111 | // get the jar entry |
| 110 | String classFileName; | 112 | String classFileName; |
| 111 | if( obfClassEntry.isInnerClass() ) | 113 | if( obfClassEntry.isInnerClass() ) |
| 112 | { | 114 | { |
| 115 | // use just the inner class simple name for inner classes | ||
| 113 | classFileName = obfClassEntry.getInnerClassName(); | 116 | classFileName = obfClassEntry.getInnerClassName(); |
| 114 | } | 117 | } |
| 118 | else if( obfClassEntry.getPackageName().equals( "default" ) ) | ||
| 119 | { | ||
| 120 | // use the outer class simple name for classes in the "default" package | ||
| 121 | classFileName = obfClassEntry.getSimpleName(); | ||
| 122 | } | ||
| 115 | else | 123 | else |
| 116 | { | 124 | { |
| 117 | classFileName = obfClassEntry.getOuterClassName(); | 125 | // otherwise, just use the class name (ie for classes in packages) |
| 126 | classFileName = obfClassEntry.getName(); | ||
| 118 | } | 127 | } |
| 119 | JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); | 128 | JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); |
| 120 | if( entry == null ) | 129 | if( entry == null ) |
| @@ -147,6 +156,10 @@ public class TranslatingTypeLoader implements ITypeLoader | |||
| 147 | classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); | 156 | classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); |
| 148 | CtClass c = classPool.get( javaClassFileName ); | 157 | CtClass c = classPool.get( javaClassFileName ); |
| 149 | 158 | ||
| 159 | // we moved a lot of classes out of the default package into the "default" package | ||
| 160 | // make sure all the class references are consistent | ||
| 161 | ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); | ||
| 162 | |||
| 150 | // reconstruct inner classes | 163 | // reconstruct inner classes |
| 151 | new InnerClassWriter( m_jarIndex ).write( c ); | 164 | new InnerClassWriter( m_jarIndex ).write( c ); |
| 152 | 165 | ||
diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java index cf6df80..6c9f124 100644 --- a/src/cuchaz/enigma/analysis/JarClassIterator.java +++ b/src/cuchaz/enigma/analysis/JarClassIterator.java | |||
| @@ -28,6 +28,7 @@ import javassist.bytecode.Descriptor; | |||
| 28 | import com.beust.jcommander.internal.Lists; | 28 | import com.beust.jcommander.internal.Lists; |
| 29 | 29 | ||
| 30 | import cuchaz.enigma.Constants; | 30 | import cuchaz.enigma.Constants; |
| 31 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 31 | 32 | ||
| 32 | public class JarClassIterator implements Iterator<CtClass> | 33 | public class JarClassIterator implements Iterator<CtClass> |
| 33 | { | 34 | { |
| @@ -36,13 +37,22 @@ public class JarClassIterator implements Iterator<CtClass> | |||
| 36 | 37 | ||
| 37 | public JarClassIterator( JarFile jar ) | 38 | public JarClassIterator( JarFile jar ) |
| 38 | { | 39 | { |
| 39 | this( jar, getClassEntries( jar ) ); | ||
| 40 | } | ||
| 41 | |||
| 42 | public JarClassIterator( JarFile jar, List<JarEntry> entries ) | ||
| 43 | { | ||
| 44 | m_jar = jar; | 40 | m_jar = jar; |
| 45 | m_iter = entries.iterator(); | 41 | |
| 42 | // get the jar entries that correspond to classes | ||
| 43 | List<JarEntry> classEntries = Lists.newArrayList(); | ||
| 44 | Enumeration<JarEntry> entries = m_jar.entries(); | ||
| 45 | while( entries.hasMoreElements() ) | ||
| 46 | { | ||
| 47 | JarEntry entry = entries.nextElement(); | ||
| 48 | |||
| 49 | // is this a class file? | ||
| 50 | if( entry.getName().endsWith( ".class" ) ) | ||
| 51 | { | ||
| 52 | classEntries.add( entry ); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | m_iter = classEntries.iterator(); | ||
| 46 | } | 56 | } |
| 47 | 57 | ||
| 48 | @Override | 58 | @Override |
| @@ -79,19 +89,13 @@ public class JarClassIterator implements Iterator<CtClass> | |||
| 79 | } | 89 | } |
| 80 | } | 90 | } |
| 81 | 91 | ||
| 82 | // determine the class name (ie chop off the ".class") | ||
| 83 | String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); | ||
| 84 | |||
| 85 | // get a javassist handle for the class | 92 | // get a javassist handle for the class |
| 93 | String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); | ||
| 86 | ClassPool classPool = new ClassPool(); | 94 | ClassPool classPool = new ClassPool(); |
| 87 | classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); | 95 | classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); |
| 88 | return classPool.get( className ); | 96 | return classPool.get( className ); |
| 89 | } | 97 | } |
| 90 | catch( IOException ex ) | 98 | catch( IOException | NotFoundException ex ) |
| 91 | { | ||
| 92 | throw new Error( "Unable to read class: " + entry.getName() ); | ||
| 93 | } | ||
| 94 | catch( NotFoundException ex ) | ||
| 95 | { | 99 | { |
| 96 | throw new Error( "Unable to load class: " + entry.getName() ); | 100 | throw new Error( "Unable to load class: " + entry.getName() ); |
| 97 | } | 101 | } |
| @@ -103,9 +107,9 @@ public class JarClassIterator implements Iterator<CtClass> | |||
| 103 | throw new UnsupportedOperationException(); | 107 | throw new UnsupportedOperationException(); |
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | public static List<JarEntry> getClassEntries( JarFile jar ) | 110 | public static List<ClassEntry> getClassEntries( JarFile jar ) |
| 107 | { | 111 | { |
| 108 | List<JarEntry> classes = Lists.newArrayList(); | 112 | List<ClassEntry> classEntries = Lists.newArrayList(); |
| 109 | Enumeration<JarEntry> entries = jar.entries(); | 113 | Enumeration<JarEntry> entries = jar.entries(); |
| 110 | while( entries.hasMoreElements() ) | 114 | while( entries.hasMoreElements() ) |
| 111 | { | 115 | { |
| @@ -114,10 +118,10 @@ public class JarClassIterator implements Iterator<CtClass> | |||
| 114 | // is this a class file? | 118 | // is this a class file? |
| 115 | if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) | 119 | if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) |
| 116 | { | 120 | { |
| 117 | classes.add( entry ); | 121 | classEntries.add( getClassEntry( entry ) ); |
| 118 | } | 122 | } |
| 119 | } | 123 | } |
| 120 | return classes; | 124 | return classEntries; |
| 121 | } | 125 | } |
| 122 | 126 | ||
| 123 | public static Iterable<CtClass> classes( final JarFile jar ) | 127 | public static Iterable<CtClass> classes( final JarFile jar ) |
| @@ -131,4 +135,9 @@ public class JarClassIterator implements Iterator<CtClass> | |||
| 131 | } | 135 | } |
| 132 | }; | 136 | }; |
| 133 | } | 137 | } |
| 138 | |||
| 139 | private static ClassEntry getClassEntry( JarEntry entry ) | ||
| 140 | { | ||
| 141 | return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); | ||
| 142 | } | ||
| 134 | } | 143 | } |
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 315a286..cdaca37 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -17,7 +17,6 @@ import java.util.Iterator; | |||
| 17 | import java.util.List; | 17 | import java.util.List; |
| 18 | import java.util.Map; | 18 | import java.util.Map; |
| 19 | import java.util.Set; | 19 | import java.util.Set; |
| 20 | import java.util.jar.JarEntry; | ||
| 21 | import java.util.jar.JarFile; | 20 | import java.util.jar.JarFile; |
| 22 | 21 | ||
| 23 | import javassist.CannotCompileException; | 22 | import javassist.CannotCompileException; |
| @@ -35,13 +34,12 @@ import javassist.expr.MethodCall; | |||
| 35 | import javassist.expr.NewExpr; | 34 | import javassist.expr.NewExpr; |
| 36 | 35 | ||
| 37 | import com.google.common.collect.HashMultimap; | 36 | import com.google.common.collect.HashMultimap; |
| 38 | import com.google.common.collect.HashMultiset; | ||
| 39 | import com.google.common.collect.Lists; | 37 | import com.google.common.collect.Lists; |
| 40 | import com.google.common.collect.Maps; | 38 | import com.google.common.collect.Maps; |
| 41 | import com.google.common.collect.Multimap; | 39 | import com.google.common.collect.Multimap; |
| 42 | import com.google.common.collect.Multiset; | ||
| 43 | import com.google.common.collect.Sets; | 40 | import com.google.common.collect.Sets; |
| 44 | 41 | ||
| 42 | import cuchaz.enigma.bytecode.ClassRenamer; | ||
| 45 | import cuchaz.enigma.mapping.ArgumentEntry; | 43 | import cuchaz.enigma.mapping.ArgumentEntry; |
| 46 | import cuchaz.enigma.mapping.BehaviorEntry; | 44 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 47 | import cuchaz.enigma.mapping.ClassEntry; | 45 | import cuchaz.enigma.mapping.ClassEntry; |
| @@ -53,7 +51,7 @@ import cuchaz.enigma.mapping.Translator; | |||
| 53 | 51 | ||
| 54 | public class JarIndex | 52 | public class JarIndex |
| 55 | { | 53 | { |
| 56 | private Set<String> m_obfClassNames; | 54 | private Set<ClassEntry> m_obfClassEntries; |
| 57 | private Ancestries m_ancestries; | 55 | private Ancestries m_ancestries; |
| 58 | private Multimap<String,MethodEntry> m_methodImplementations; | 56 | private Multimap<String,MethodEntry> m_methodImplementations; |
| 59 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; | 57 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; |
| @@ -64,7 +62,7 @@ public class JarIndex | |||
| 64 | 62 | ||
| 65 | public JarIndex( ) | 63 | public JarIndex( ) |
| 66 | { | 64 | { |
| 67 | m_obfClassNames = Sets.newHashSet(); | 65 | m_obfClassEntries = Sets.newHashSet(); |
| 68 | m_ancestries = new Ancestries(); | 66 | m_ancestries = new Ancestries(); |
| 69 | m_methodImplementations = HashMultimap.create(); | 67 | m_methodImplementations = HashMultimap.create(); |
| 70 | m_behaviorReferences = HashMultimap.create(); | 68 | m_behaviorReferences = HashMultimap.create(); |
| @@ -77,15 +75,20 @@ public class JarIndex | |||
| 77 | public void indexJar( JarFile jar ) | 75 | public void indexJar( JarFile jar ) |
| 78 | { | 76 | { |
| 79 | // pass 1: read the class names | 77 | // pass 1: read the class names |
| 80 | for( JarEntry entry : JarClassIterator.getClassEntries( jar ) ) | 78 | for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) |
| 81 | { | 79 | { |
| 82 | String className = entry.getName().substring( 0, entry.getName().length() - 6 ); | 80 | if( classEntry.isInDefaultPackage() ) |
| 83 | m_obfClassNames.add( Descriptor.toJvmName( className ) ); | 81 | { |
| 82 | // move out of default package | ||
| 83 | classEntry = new ClassEntry( "default/" + classEntry.getName() ); | ||
| 84 | } | ||
| 85 | m_obfClassEntries.add( classEntry ); | ||
| 84 | } | 86 | } |
| 85 | 87 | ||
| 86 | // pass 2: index the types, methods | 88 | // pass 2: index the types, methods |
| 87 | for( CtClass c : JarClassIterator.classes( jar ) ) | 89 | for( CtClass c : JarClassIterator.classes( jar ) ) |
| 88 | { | 90 | { |
| 91 | fixClass( c ); | ||
| 89 | m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); | 92 | m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); |
| 90 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) | 93 | for( CtBehavior behavior : c.getDeclaredBehaviors() ) |
| 91 | { | 94 | { |
| @@ -96,8 +99,10 @@ public class JarIndex | |||
| 96 | // pass 2: index inner classes and anonymous classes | 99 | // pass 2: index inner classes and anonymous classes |
| 97 | for( CtClass c : JarClassIterator.classes( jar ) ) | 100 | for( CtClass c : JarClassIterator.classes( jar ) ) |
| 98 | { | 101 | { |
| 102 | fixClass( c ); | ||
| 103 | |||
| 99 | String outerClassName = findOuterClass( c ); | 104 | String outerClassName = findOuterClass( c ); |
| 100 | if( outerClassName != null )// /* TEMP */ && false ) | 105 | if( outerClassName != null ) |
| 101 | { | 106 | { |
| 102 | String innerClassName = Descriptor.toJvmName( c.getName() ); | 107 | String innerClassName = Descriptor.toJvmName( c.getName() ); |
| 103 | m_innerClasses.put( outerClassName, innerClassName ); | 108 | m_innerClasses.put( outerClassName, innerClassName ); |
| @@ -127,6 +132,17 @@ public class JarIndex | |||
| 127 | renameClasses( renames ); | 132 | renameClasses( renames ); |
| 128 | } | 133 | } |
| 129 | 134 | ||
| 135 | private void fixClass( CtClass c ) | ||
| 136 | { | ||
| 137 | ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); | ||
| 138 | if( classEntry.isInDefaultPackage() ) | ||
| 139 | { | ||
| 140 | // move class out of default package | ||
| 141 | classEntry = new ClassEntry( "default/" + classEntry.getName() ); | ||
| 142 | ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 130 | private void indexBehavior( CtBehavior behavior ) | 146 | private void indexBehavior( CtBehavior behavior ) |
| 131 | { | 147 | { |
| 132 | // get the method entry | 148 | // get the method entry |
| @@ -270,7 +286,7 @@ public class JarIndex | |||
| 270 | else if( callerClasses.size() > 1 ) | 286 | else if( callerClasses.size() > 1 ) |
| 271 | { | 287 | { |
| 272 | // TEMP | 288 | // TEMP |
| 273 | System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); | 289 | System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); |
| 274 | } | 290 | } |
| 275 | } | 291 | } |
| 276 | 292 | ||
| @@ -423,9 +439,9 @@ public class JarIndex | |||
| 423 | return true; | 439 | return true; |
| 424 | } | 440 | } |
| 425 | 441 | ||
| 426 | public Set<String> getObfClassNames( ) | 442 | public Set<ClassEntry> getObfClassEntries( ) |
| 427 | { | 443 | { |
| 428 | return m_obfClassNames; | 444 | return m_obfClassEntries; |
| 429 | } | 445 | } |
| 430 | 446 | ||
| 431 | public Ancestries getAncestries( ) | 447 | public Ancestries getAncestries( ) |
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java new file mode 100644 index 0000000..cba5861 --- /dev/null +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java | |||
| @@ -0,0 +1,115 @@ | |||
| 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.bytecode; | ||
| 12 | |||
| 13 | import java.util.Map; | ||
| 14 | import java.util.Set; | ||
| 15 | |||
| 16 | import javassist.ClassMap; | ||
| 17 | import javassist.CtClass; | ||
| 18 | import javassist.bytecode.ConstPool; | ||
| 19 | import javassist.bytecode.Descriptor; | ||
| 20 | import javassist.bytecode.InnerClassesAttribute; | ||
| 21 | |||
| 22 | import com.beust.jcommander.internal.Sets; | ||
| 23 | import com.google.common.collect.Maps; | ||
| 24 | |||
| 25 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 26 | |||
| 27 | public class ClassRenamer | ||
| 28 | { | ||
| 29 | public static void renameClasses( CtClass c, Map<ClassEntry,ClassEntry> map ) | ||
| 30 | { | ||
| 31 | // build the map used by javassist | ||
| 32 | ClassMap nameMap = new ClassMap(); | ||
| 33 | for( Map.Entry<ClassEntry,ClassEntry> entry : map.entrySet() ) | ||
| 34 | { | ||
| 35 | nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); | ||
| 36 | } | ||
| 37 | c.replaceClassName( nameMap ); | ||
| 38 | |||
| 39 | // translate the names in the InnerClasses attribute | ||
| 40 | ConstPool constants = c.getClassFile().getConstPool(); | ||
| 41 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); | ||
| 42 | if( attr != null ) | ||
| 43 | { | ||
| 44 | for( int i=0; i<attr.tableLength(); i++ ) | ||
| 45 | { | ||
| 46 | ClassEntry inClassEntry = new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) ); | ||
| 47 | ClassEntry outClassEntry = map.get( inClassEntry ); | ||
| 48 | attr.setInnerClassIndex( i, constants.addClassInfo( outClassEntry.getName() ) ); | ||
| 49 | if( attr.outerClassIndex( i ) != 0 ) | ||
| 50 | { | ||
| 51 | attr.setOuterClassIndex( i, constants.addClassInfo( outClassEntry.getOuterClassName() ) ); | ||
| 52 | } | ||
| 53 | if( attr.innerNameIndex( i ) != 0 ) | ||
| 54 | { | ||
| 55 | attr.setInnerNameIndex( i, constants.addUtf8Info( outClassEntry.getInnerClassName() ) ); | ||
| 56 | } | ||
| 57 | |||
| 58 | /* DEBUG | ||
| 59 | System.out.println( String.format( "\tOBF: %s DEOBF: %s-> ATTR: %s,%s,%s", | ||
| 60 | obfClassEntry, deobfClassEntry, | ||
| 61 | attr.outerClass( i ), | ||
| 62 | attr.innerClass( i ), | ||
| 63 | attr.innerName( i ) | ||
| 64 | ) ); | ||
| 65 | */ | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | public static Set<ClassEntry> getAllClassEntries( CtClass c ) | ||
| 71 | { | ||
| 72 | // get the classes that javassist knows about | ||
| 73 | final Set<ClassEntry> entries = Sets.newHashSet(); | ||
| 74 | ClassMap map = new ClassMap( ) | ||
| 75 | { | ||
| 76 | @Override | ||
| 77 | public Object get( Object obj ) | ||
| 78 | { | ||
| 79 | if( obj instanceof String ) | ||
| 80 | { | ||
| 81 | entries.add( new ClassEntry( (String)obj ) ); | ||
| 82 | } | ||
| 83 | return null; | ||
| 84 | } | ||
| 85 | private static final long serialVersionUID = -202160293602070641L; | ||
| 86 | }; | ||
| 87 | c.replaceClassName( map ); | ||
| 88 | |||
| 89 | // also check InnerClassesAttribute | ||
| 90 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); | ||
| 91 | if( attr != null ) | ||
| 92 | { | ||
| 93 | for( int i=0; i<attr.tableLength(); i++ ) | ||
| 94 | { | ||
| 95 | entries.add( new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) ) ); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | return entries; | ||
| 100 | } | ||
| 101 | |||
| 102 | public static void moveAllClassesOutOfDefaultPackage( CtClass c, String newPackageName ) | ||
| 103 | { | ||
| 104 | // rename all classes | ||
| 105 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | ||
| 106 | for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) | ||
| 107 | { | ||
| 108 | if( classEntry.isInDefaultPackage() ) | ||
| 109 | { | ||
| 110 | map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) ); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | ClassRenamer.renameClasses( c, map ); | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 9ce06a5..885b45f 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java | |||
| @@ -10,18 +10,16 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode; | 11 | package cuchaz.enigma.bytecode; |
| 12 | 12 | ||
| 13 | import java.util.Set; | 13 | import java.util.Map; |
| 14 | 14 | ||
| 15 | import javassist.ClassMap; | ||
| 16 | import javassist.CtBehavior; | 15 | import javassist.CtBehavior; |
| 17 | import javassist.CtClass; | 16 | import javassist.CtClass; |
| 18 | import javassist.CtField; | 17 | import javassist.CtField; |
| 19 | import javassist.CtMethod; | 18 | import javassist.CtMethod; |
| 20 | import javassist.bytecode.ConstPool; | 19 | import javassist.bytecode.ConstPool; |
| 21 | import javassist.bytecode.Descriptor; | 20 | import javassist.bytecode.Descriptor; |
| 22 | import javassist.bytecode.InnerClassesAttribute; | ||
| 23 | 21 | ||
| 24 | import com.beust.jcommander.internal.Sets; | 22 | import com.beust.jcommander.internal.Maps; |
| 25 | 23 | ||
| 26 | import cuchaz.enigma.mapping.ClassEntry; | 24 | import cuchaz.enigma.mapping.ClassEntry; |
| 27 | import cuchaz.enigma.mapping.FieldEntry; | 25 | import cuchaz.enigma.mapping.FieldEntry; |
| @@ -136,61 +134,11 @@ public class ClassTranslator | |||
| 136 | 134 | ||
| 137 | // translate all the class names referenced in the code | 135 | // translate all the class names referenced in the code |
| 138 | // the above code only changed method/field/reference names and types, but not the class names themselves | 136 | // the above code only changed method/field/reference names and types, but not the class names themselves |
| 139 | Set<ClassEntry> classEntries = getAllClassEntries( c ); | 137 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); |
| 140 | ClassMap map = new ClassMap(); | 138 | for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) ) |
| 141 | for( ClassEntry obfClassEntry : classEntries ) | ||
| 142 | { | 139 | { |
| 143 | map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); | 140 | map.put( obfClassEntry, m_translator.translateEntry( obfClassEntry ) ); |
| 144 | } | 141 | } |
| 145 | c.replaceClassName( map ); | 142 | ClassRenamer.renameClasses( c, map ); |
| 146 | |||
| 147 | // translate the names in the InnerClasses attribute | ||
| 148 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); | ||
| 149 | if( attr != null ) | ||
| 150 | { | ||
| 151 | for( int i=0; i<attr.tableLength(); i++ ) | ||
| 152 | { | ||
| 153 | ClassEntry obfClassEntry = new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) ); | ||
| 154 | ClassEntry deobfClassEntry = m_translator.translateEntry( obfClassEntry ); | ||
| 155 | attr.setInnerClassIndex( i, constants.addClassInfo( deobfClassEntry.getName() ) ); | ||
| 156 | if( attr.outerClassIndex( i ) != 0 ) | ||
| 157 | { | ||
| 158 | attr.setOuterClassIndex( i, constants.addClassInfo( deobfClassEntry.getOuterClassName() ) ); | ||
| 159 | } | ||
| 160 | if( attr.innerNameIndex( i ) != 0 ) | ||
| 161 | { | ||
| 162 | attr.setInnerNameIndex( i, constants.addUtf8Info( deobfClassEntry.getInnerClassName() ) ); | ||
| 163 | } | ||
| 164 | |||
| 165 | /* DEBUG | ||
| 166 | System.out.println( String.format( "\tOBF: %s DEOBF: %s-> ATTR: %s,%s,%s", | ||
| 167 | obfClassEntry, deobfClassEntry, | ||
| 168 | attr.outerClass( i ), | ||
| 169 | attr.innerClass( i ), | ||
| 170 | attr.innerName( i ) | ||
| 171 | ) ); | ||
| 172 | */ | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | private Set<ClassEntry> getAllClassEntries( CtClass c ) | ||
| 178 | { | ||
| 179 | final Set<ClassEntry> entries = Sets.newHashSet(); | ||
| 180 | ClassMap map = new ClassMap( ) | ||
| 181 | { | ||
| 182 | @Override | ||
| 183 | public Object get( Object obj ) | ||
| 184 | { | ||
| 185 | if( obj instanceof String ) | ||
| 186 | { | ||
| 187 | entries.add( new ClassEntry( (String)obj ) ); | ||
| 188 | } | ||
| 189 | return null; | ||
| 190 | } | ||
| 191 | private static final long serialVersionUID = -202160293602070641L; | ||
| 192 | }; | ||
| 193 | c.replaceClassName( map ); | ||
| 194 | return entries; | ||
| 195 | } | 143 | } |
| 196 | } | 144 | } |
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index c412b1a..2fb5fe0 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java | |||
| @@ -42,7 +42,7 @@ public class InnerClassWriter | |||
| 42 | else | 42 | else |
| 43 | { | 43 | { |
| 44 | // this is an inner class, rename it to outer$inner | 44 | // this is an inner class, rename it to outer$inner |
| 45 | ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); | 45 | ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() ); |
| 46 | c.setName( obfClassEntry.getName() ); | 46 | c.setName( obfClassEntry.getName() ); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -60,8 +60,8 @@ public class InnerClassWriter | |||
| 60 | c.getClassFile().addAttribute( attr ); | 60 | c.getClassFile().addAttribute( attr ); |
| 61 | for( String obfInnerClassName : obfInnerClassNames ) | 61 | for( String obfInnerClassName : obfInnerClassNames ) |
| 62 | { | 62 | { |
| 63 | // deobfuscate the class names | 63 | // get the new inner class name |
| 64 | ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); | 64 | ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfInnerClassName ).getSimpleName() ); |
| 65 | 65 | ||
| 66 | // here's what the JVM spec says about the InnerClasses attribute | 66 | // here's what the JVM spec says about the InnerClasses attribute |
| 67 | // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); | 67 | // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); |
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java index febdfd4..4e63606 100644 --- a/src/cuchaz/enigma/gui/Gui.java +++ b/src/cuchaz/enigma/gui/Gui.java | |||
| @@ -1073,8 +1073,9 @@ public class Gui | |||
| 1073 | } | 1073 | } |
| 1074 | catch( IllegalNameException ex ) | 1074 | catch( IllegalNameException ex ) |
| 1075 | { | 1075 | { |
| 1076 | ex.printStackTrace( System.err ); | ||
| 1077 | text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); | 1076 | text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); |
| 1077 | text.setToolTipText( ex.getReason() ); | ||
| 1078 | GuiTricks.showToolTipNow( text ); | ||
| 1078 | } | 1079 | } |
| 1079 | return; | 1080 | return; |
| 1080 | } | 1081 | } |
diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java index cf4f002..90bce52 100644 --- a/src/cuchaz/enigma/gui/GuiController.java +++ b/src/cuchaz/enigma/gui/GuiController.java | |||
| @@ -114,16 +114,19 @@ public class GuiController | |||
| 114 | @Override | 114 | @Override |
| 115 | public void run( ) | 115 | public void run( ) |
| 116 | { | 116 | { |
| 117 | ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); | ||
| 117 | try | 118 | try |
| 118 | { | 119 | { |
| 119 | ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); | ||
| 120 | m_deobfuscator.writeSources( dirOut, progress ); | 120 | m_deobfuscator.writeSources( dirOut, progress ); |
| 121 | progress.close(); | ||
| 122 | } | 121 | } |
| 123 | catch( IOException ex ) | 122 | catch( Exception ex ) |
| 124 | { | 123 | { |
| 125 | throw new Error( ex ); | 124 | throw new Error( ex ); |
| 126 | } | 125 | } |
| 126 | finally | ||
| 127 | { | ||
| 128 | progress.close(); | ||
| 129 | } | ||
| 127 | } | 130 | } |
| 128 | }.start(); | 131 | }.start(); |
| 129 | } | 132 | } |
diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java index c79f432..9b889ef 100644 --- a/src/cuchaz/enigma/gui/GuiTricks.java +++ b/src/cuchaz/enigma/gui/GuiTricks.java | |||
| @@ -11,8 +11,11 @@ | |||
| 11 | package cuchaz.enigma.gui; | 11 | package cuchaz.enigma.gui; |
| 12 | 12 | ||
| 13 | import java.awt.Font; | 13 | import java.awt.Font; |
| 14 | import java.awt.event.MouseEvent; | ||
| 14 | 15 | ||
| 16 | import javax.swing.JComponent; | ||
| 15 | import javax.swing.JLabel; | 17 | import javax.swing.JLabel; |
| 18 | import javax.swing.ToolTipManager; | ||
| 16 | 19 | ||
| 17 | public class GuiTricks | 20 | public class GuiTricks |
| 18 | { | 21 | { |
| @@ -22,4 +25,19 @@ public class GuiTricks | |||
| 22 | label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); | 25 | label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); |
| 23 | return label; | 26 | return label; |
| 24 | } | 27 | } |
| 28 | |||
| 29 | public static void showToolTipNow( JComponent component ) | ||
| 30 | { | ||
| 31 | // HACKHACK: trick the tooltip manager into showing the tooltip right now | ||
| 32 | ToolTipManager manager = ToolTipManager.sharedInstance(); | ||
| 33 | int oldDelay = manager.getInitialDelay(); | ||
| 34 | manager.setInitialDelay( 0 ); | ||
| 35 | manager.mouseMoved( new MouseEvent( | ||
| 36 | component, | ||
| 37 | MouseEvent.MOUSE_MOVED, | ||
| 38 | System.currentTimeMillis(), | ||
| 39 | 0, 0, 0, 0, false | ||
| 40 | ) ); | ||
| 41 | manager.setInitialDelay( oldDelay ); | ||
| 42 | } | ||
| 25 | } | 43 | } |
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index dad6da9..fdb7c2c 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java | |||
| @@ -110,4 +110,29 @@ public class ClassEntry implements Entry, Serializable | |||
| 110 | { | 110 | { |
| 111 | return new ClassEntry( getOuterClassName() ); | 111 | return new ClassEntry( getOuterClassName() ); |
| 112 | } | 112 | } |
| 113 | |||
| 114 | public boolean isInDefaultPackage( ) | ||
| 115 | { | ||
| 116 | return m_name.indexOf( '/' ) < 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | public String getPackageName( ) | ||
| 120 | { | ||
| 121 | int pos = m_name.lastIndexOf( '/' ); | ||
| 122 | if( pos > 0 ) | ||
| 123 | { | ||
| 124 | return m_name.substring( 0, pos ); | ||
| 125 | } | ||
| 126 | return null; | ||
| 127 | } | ||
| 128 | |||
| 129 | public String getSimpleName( ) | ||
| 130 | { | ||
| 131 | int pos = m_name.lastIndexOf( '/' ); | ||
| 132 | if( pos > 0 ) | ||
| 133 | { | ||
| 134 | return m_name.substring( pos + 1 ); | ||
| 135 | } | ||
| 136 | return m_name; | ||
| 137 | } | ||
| 113 | } | 138 | } |
diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java index 560e5d9..830f05c 100644 --- a/src/cuchaz/enigma/mapping/IllegalNameException.java +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java | |||
| @@ -15,15 +15,35 @@ public class IllegalNameException extends RuntimeException | |||
| 15 | private static final long serialVersionUID = -2279910052561114323L; | 15 | private static final long serialVersionUID = -2279910052561114323L; |
| 16 | 16 | ||
| 17 | private String m_name; | 17 | private String m_name; |
| 18 | private String m_reason; | ||
| 18 | 19 | ||
| 19 | public IllegalNameException( String name ) | 20 | public IllegalNameException( String name ) |
| 20 | { | 21 | { |
| 22 | this( name, null ); | ||
| 23 | } | ||
| 24 | |||
| 25 | public IllegalNameException( String name, String reason ) | ||
| 26 | { | ||
| 21 | m_name = name; | 27 | m_name = name; |
| 28 | m_reason = reason; | ||
| 29 | } | ||
| 30 | |||
| 31 | public String getReason( ) | ||
| 32 | { | ||
| 33 | return m_reason; | ||
| 22 | } | 34 | } |
| 23 | 35 | ||
| 24 | @Override | 36 | @Override |
| 25 | public String getMessage( ) | 37 | public String getMessage( ) |
| 26 | { | 38 | { |
| 27 | return "Illegal name: " + m_name; | 39 | StringBuilder buf = new StringBuilder(); |
| 40 | buf.append( "Illegal name: " ); | ||
| 41 | buf.append( m_name ); | ||
| 42 | if( m_reason != null ) | ||
| 43 | { | ||
| 44 | buf.append( " because " ); | ||
| 45 | buf.append( m_reason ); | ||
| 46 | } | ||
| 47 | return buf.toString(); | ||
| 28 | } | 48 | } |
| 29 | } | 49 | } |
diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java index a8421fa..6df893f 100644 --- a/src/cuchaz/enigma/mapping/NameValidator.java +++ b/src/cuchaz/enigma/mapping/NameValidator.java | |||
| @@ -59,7 +59,11 @@ public class NameValidator | |||
| 59 | { | 59 | { |
| 60 | if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) | 60 | if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) |
| 61 | { | 61 | { |
| 62 | throw new IllegalNameException( name ); | 62 | throw new IllegalNameException( name, "This doesn't look like a legal class name" ); |
| 63 | } | ||
| 64 | if( new ClassEntry( name ).getPackageName() == null ) | ||
| 65 | { | ||
| 66 | throw new IllegalNameException( name, "Classes must be in a package" ); | ||
| 63 | } | 67 | } |
| 64 | return Descriptor.toJvmName( name ); | 68 | return Descriptor.toJvmName( name ); |
| 65 | } | 69 | } |
| @@ -68,7 +72,7 @@ public class NameValidator | |||
| 68 | { | 72 | { |
| 69 | if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) | 73 | if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) |
| 70 | { | 74 | { |
| 71 | throw new IllegalNameException( name ); | 75 | throw new IllegalNameException( name, "This doesn't look like a legal identifier" ); |
| 72 | } | 76 | } |
| 73 | return name; | 77 | return name; |
| 74 | } | 78 | } |