From 8fa1741b621644ef84a9395a4c395d4ff3a89207 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 23 Aug 2014 23:43:31 -0400 Subject: moved all classes from the default package into a package called "default" so they can be properly imported by other classes --- src/cuchaz/enigma/Deobfuscator.java | 49 ++++----- src/cuchaz/enigma/TranslatingTypeLoader.java | 19 +++- src/cuchaz/enigma/analysis/JarClassIterator.java | 45 ++++---- src/cuchaz/enigma/analysis/JarIndex.java | 40 ++++--- src/cuchaz/enigma/bytecode/ClassRenamer.java | 115 +++++++++++++++++++++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 64 ++---------- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 6 +- src/cuchaz/enigma/gui/Gui.java | 3 +- src/cuchaz/enigma/gui/GuiController.java | 9 +- src/cuchaz/enigma/gui/GuiTricks.java | 18 ++++ src/cuchaz/enigma/mapping/ClassEntry.java | 25 +++++ .../enigma/mapping/IllegalNameException.java | 22 +++- src/cuchaz/enigma/mapping/NameValidator.java | 8 +- 13 files changed, 298 insertions(+), 125 deletions(-) create mode 100644 src/cuchaz/enigma/bytecode/ClassRenamer.java (limited to 'src') 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; import java.util.List; import java.util.jar.JarFile; +import javassist.bytecode.Descriptor; + import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.decompiler.DecompilerContext; @@ -118,28 +120,30 @@ public class Deobfuscator public void getSeparatedClasses( List obfClasses, List deobfClasses ) { - for( String obfClassName : m_jarIndex.getObfClassNames() ) + for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassName ) != null ) + if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) { continue; } // separate the classes - ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); - if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) ) + ClassEntry deobfClassEntry = deobfuscateEntry( obfClassEntry ); + if( !deobfClassEntry.equals( obfClassEntry ) ) { - deobfClasses.add( classMapping.getDeobfName() ); + // if the class has a mapping, clearly it's deobfuscated + deobfClasses.add( deobfClassEntry.getName() ); } - else if( obfClassName.indexOf( '/' ) >= 0 ) + else if( !obfClassEntry.getPackageName().equals( "default" ) ) { - // this class is in a package and therefore is not obfuscated - deobfClasses.add( obfClassName ); + // also call it deobufscated if it's not in the "default" package + deobfClasses.add( obfClassEntry.getName() ); } else { - obfClasses.add( obfClassName ); + // otherwise, assume it's still obfuscated + obfClasses.add( obfClassEntry.getName() ); } } } @@ -198,7 +202,7 @@ public class Deobfuscator public void writeSources( File dirOut, ProgressListener progress ) throws IOException { - int numClasses = m_jarIndex.getObfClassNames().size(); + int numClasses = m_jarIndex.getObfClassEntries().size(); if( progress != null ) { progress.init( numClasses ); @@ -206,22 +210,22 @@ public class Deobfuscator int i = 0; // DEOBFUSCATE ALL THE THINGS!! @_@ - for( String obfClassName : m_jarIndex.getObfClassNames() ) + for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() ) { // skip inner classes - if( m_jarIndex.getOuterClass( obfClassName ) != null ) + if( obfClassEntry.isInnerClass() ) { continue; } - ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassName ) ); + ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) ); if( progress != null ) { progress.onProgress( i++, deobfClassEntry.toString() ); } // get the source - String source = getSource( getSourceTree( obfClassName ) ); + String source = getSource( getSourceTree( obfClassEntry.getName() ) ); // write the file File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); @@ -284,7 +288,7 @@ public class Deobfuscator { if( obfEntry instanceof ClassEntry ) { - m_renamer.setClassName( (ClassEntry)obfEntry, newName ); + m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) ); } else if( obfEntry instanceof FieldEntry ) { @@ -348,21 +352,18 @@ public class Deobfuscator { if( obfEntry instanceof ClassEntry ) { - if( obfEntry.getName().indexOf( '$' ) >= 0 ) + ClassEntry obfClassEntry = (ClassEntry)obfEntry; + if( obfClassEntry.isInnerClass() ) { - String[] parts = obfEntry.getName().split( "\\$" ); - assert( parts.length == 2 ); // not supporting recursively-nested classes - String outerClassName = parts[0]; - String innerClassName = parts[1]; - // both classes must be in the list - return m_jarIndex.getObfClassNames().contains( outerClassName ) - && m_jarIndex.getObfClassNames().contains( innerClassName ); + return m_jarIndex.getObfClassEntries().contains( obfClassEntry.getOuterClassEntry() ) + && m_jarIndex.getObfClassEntries().contains( obfClassEntry.getInnerClassName() ); + // TODO: make sure this works for the inner class!! } else { // class must be in the list - return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); + return m_jarIndex.getObfClassEntries().contains( obfEntry ); } } 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; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -30,6 +31,7 @@ import com.strobel.assembler.metadata.ITypeLoader; import cuchaz.enigma.analysis.BridgeFixer; import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; import cuchaz.enigma.bytecode.MethodParameterWriter; @@ -99,22 +101,29 @@ public class TranslatingTypeLoader implements ITypeLoader return null; } - /* DEBUG + // DEBUG if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } - */ + // // get the jar entry String classFileName; if( obfClassEntry.isInnerClass() ) { + // use just the inner class simple name for inner classes classFileName = obfClassEntry.getInnerClassName(); } + else if( obfClassEntry.getPackageName().equals( "default" ) ) + { + // use the outer class simple name for classes in the "default" package + classFileName = obfClassEntry.getSimpleName(); + } else { - classFileName = obfClassEntry.getOuterClassName(); + // otherwise, just use the class name (ie for classes in packages) + classFileName = obfClassEntry.getName(); } JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) @@ -147,6 +156,10 @@ public class TranslatingTypeLoader implements ITypeLoader classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); + // we moved a lot of classes out of the default package into the "default" package + // make sure all the class references are consistent + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + // reconstruct inner classes new InnerClassWriter( m_jarIndex ).write( c ); 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; import com.beust.jcommander.internal.Lists; import cuchaz.enigma.Constants; +import cuchaz.enigma.mapping.ClassEntry; public class JarClassIterator implements Iterator { @@ -35,14 +36,23 @@ public class JarClassIterator implements Iterator private Iterator m_iter; public JarClassIterator( JarFile jar ) - { - this( jar, getClassEntries( jar ) ); - } - - public JarClassIterator( JarFile jar, List entries ) { m_jar = jar; - m_iter = entries.iterator(); + + // get the jar entries that correspond to classes + List classEntries = Lists.newArrayList(); + Enumeration entries = m_jar.entries(); + while( entries.hasMoreElements() ) + { + JarEntry entry = entries.nextElement(); + + // is this a class file? + if( entry.getName().endsWith( ".class" ) ) + { + classEntries.add( entry ); + } + } + m_iter = classEntries.iterator(); } @Override @@ -79,19 +89,13 @@ public class JarClassIterator implements Iterator } } - // determine the class name (ie chop off the ".class") - String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); - // get a javassist handle for the class + String className = Descriptor.toJavaName( getClassEntry( entry ).getName() ); ClassPool classPool = new ClassPool(); classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); return classPool.get( className ); } - catch( IOException ex ) - { - throw new Error( "Unable to read class: " + entry.getName() ); - } - catch( NotFoundException ex ) + catch( IOException | NotFoundException ex ) { throw new Error( "Unable to load class: " + entry.getName() ); } @@ -103,9 +107,9 @@ public class JarClassIterator implements Iterator throw new UnsupportedOperationException(); } - public static List getClassEntries( JarFile jar ) + public static List getClassEntries( JarFile jar ) { - List classes = Lists.newArrayList(); + List classEntries = Lists.newArrayList(); Enumeration entries = jar.entries(); while( entries.hasMoreElements() ) { @@ -114,10 +118,10 @@ public class JarClassIterator implements Iterator // is this a class file? if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) { - classes.add( entry ); + classEntries.add( getClassEntry( entry ) ); } } - return classes; + return classEntries; } public static Iterable classes( final JarFile jar ) @@ -131,4 +135,9 @@ public class JarClassIterator implements Iterator } }; } + + private static ClassEntry getClassEntry( JarEntry entry ) + { + return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); + } } 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; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import javassist.CannotCompileException; @@ -35,13 +34,12 @@ import javassist.expr.MethodCall; import javassist.expr.NewExpr; import com.google.common.collect.HashMultimap; -import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -import com.google.common.collect.Multiset; import com.google.common.collect.Sets; +import cuchaz.enigma.bytecode.ClassRenamer; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; @@ -53,7 +51,7 @@ import cuchaz.enigma.mapping.Translator; public class JarIndex { - private Set m_obfClassNames; + private Set m_obfClassEntries; private Ancestries m_ancestries; private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; @@ -64,7 +62,7 @@ public class JarIndex public JarIndex( ) { - m_obfClassNames = Sets.newHashSet(); + m_obfClassEntries = Sets.newHashSet(); m_ancestries = new Ancestries(); m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); @@ -77,15 +75,20 @@ public class JarIndex public void indexJar( JarFile jar ) { // pass 1: read the class names - for( JarEntry entry : JarClassIterator.getClassEntries( jar ) ) + for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) ) { - String className = entry.getName().substring( 0, entry.getName().length() - 6 ); - m_obfClassNames.add( Descriptor.toJvmName( className ) ); + if( classEntry.isInDefaultPackage() ) + { + // move out of default package + classEntry = new ClassEntry( "default/" + classEntry.getName() ); + } + m_obfClassEntries.add( classEntry ); } // pass 2: index the types, methods for( CtClass c : JarClassIterator.classes( jar ) ) { + fixClass( c ); m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); for( CtBehavior behavior : c.getDeclaredBehaviors() ) { @@ -96,8 +99,10 @@ public class JarIndex // pass 2: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { + fixClass( c ); + String outerClassName = findOuterClass( c ); - if( outerClassName != null )// /* TEMP */ && false ) + if( outerClassName != null ) { String innerClassName = Descriptor.toJvmName( c.getName() ); m_innerClasses.put( outerClassName, innerClassName ); @@ -127,6 +132,17 @@ public class JarIndex renameClasses( renames ); } + private void fixClass( CtClass c ) + { + ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); + if( classEntry.isInDefaultPackage() ) + { + // move class out of default package + classEntry = new ClassEntry( "default/" + classEntry.getName() ); + ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" ); + } + } + private void indexBehavior( CtBehavior behavior ) { // get the method entry @@ -270,7 +286,7 @@ public class JarIndex else if( callerClasses.size() > 1 ) { // TEMP - System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); + System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses ); } } @@ -423,9 +439,9 @@ public class JarIndex return true; } - public Set getObfClassNames( ) + public Set getObfClassEntries( ) { - return m_obfClassNames; + return m_obfClassEntries; } 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 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.bytecode; + +import java.util.Map; +import java.util.Set; + +import javassist.ClassMap; +import javassist.CtClass; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Maps; + +import cuchaz.enigma.mapping.ClassEntry; + +public class ClassRenamer +{ + public static void renameClasses( CtClass c, Map map ) + { + // build the map used by javassist + ClassMap nameMap = new ClassMap(); + for( Map.Entry entry : map.entrySet() ) + { + nameMap.put( entry.getKey().getName(), entry.getValue().getName() ); + } + c.replaceClassName( nameMap ); + + // translate the names in the InnerClasses attribute + ConstPool constants = c.getClassFile().getConstPool(); + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) + { + for( int i=0; i ATTR: %s,%s,%s", + obfClassEntry, deobfClassEntry, + attr.outerClass( i ), + attr.innerClass( i ), + attr.innerName( i ) + ) ); + */ + } + } + } + + public static Set getAllClassEntries( CtClass c ) + { + // get the classes that javassist knows about + final Set entries = Sets.newHashSet(); + ClassMap map = new ClassMap( ) + { + @Override + public Object get( Object obj ) + { + if( obj instanceof String ) + { + entries.add( new ClassEntry( (String)obj ) ); + } + return null; + } + private static final long serialVersionUID = -202160293602070641L; + }; + c.replaceClassName( map ); + + // also check InnerClassesAttribute + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) + { + for( int i=0; i map = Maps.newHashMap(); + for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) ) + { + if( classEntry.isInDefaultPackage() ) + { + map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) ); + } + } + ClassRenamer.renameClasses( c, map ); + } +} 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 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; -import java.util.Set; +import java.util.Map; -import javassist.ClassMap; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; -import javassist.bytecode.InnerClassesAttribute; -import com.beust.jcommander.internal.Sets; +import com.beust.jcommander.internal.Maps; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; @@ -136,61 +134,11 @@ public class ClassTranslator // translate all the class names referenced in the code // the above code only changed method/field/reference names and types, but not the class names themselves - Set classEntries = getAllClassEntries( c ); - ClassMap map = new ClassMap(); - for( ClassEntry obfClassEntry : classEntries ) + Map map = Maps.newHashMap(); + for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) ) { - map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); + map.put( obfClassEntry, m_translator.translateEntry( obfClassEntry ) ); } - c.replaceClassName( map ); - - // translate the names in the InnerClasses attribute - InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); - if( attr != null ) - { - for( int i=0; i ATTR: %s,%s,%s", - obfClassEntry, deobfClassEntry, - attr.outerClass( i ), - attr.innerClass( i ), - attr.innerName( i ) - ) ); - */ - } - } - } - - private Set getAllClassEntries( CtClass c ) - { - final Set entries = Sets.newHashSet(); - ClassMap map = new ClassMap( ) - { - @Override - public Object get( Object obj ) - { - if( obj instanceof String ) - { - entries.add( new ClassEntry( (String)obj ) ); - } - return null; - } - private static final long serialVersionUID = -202160293602070641L; - }; - c.replaceClassName( map ); - return entries; + ClassRenamer.renameClasses( c, map ); } } 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 else { // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() ); c.setName( obfClassEntry.getName() ); } @@ -60,8 +60,8 @@ public class InnerClassWriter c.getClassFile().addAttribute( attr ); for( String obfInnerClassName : obfInnerClassNames ) { - // deobfuscate the class names - ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); + // get the new inner class name + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfInnerClassName ).getSimpleName() ); // here's what the JVM spec says about the InnerClasses attribute // 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 } catch( IllegalNameException ex ) { - ex.printStackTrace( System.err ); text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); + text.setToolTipText( ex.getReason() ); + GuiTricks.showToolTipNow( text ); } return; } 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 @Override public void run( ) { + ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); try { - ProgressDialog progress = new ProgressDialog( m_gui.getFrame() ); m_deobfuscator.writeSources( dirOut, progress ); - progress.close(); } - catch( IOException ex ) + catch( Exception ex ) { throw new Error( ex ); } + finally + { + progress.close(); + } } }.start(); } 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 @@ package cuchaz.enigma.gui; import java.awt.Font; +import java.awt.event.MouseEvent; +import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.ToolTipManager; public class GuiTricks { @@ -22,4 +25,19 @@ public class GuiTricks label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); return label; } + + public static void showToolTipNow( JComponent component ) + { + // HACKHACK: trick the tooltip manager into showing the tooltip right now + ToolTipManager manager = ToolTipManager.sharedInstance(); + int oldDelay = manager.getInitialDelay(); + manager.setInitialDelay( 0 ); + manager.mouseMoved( new MouseEvent( + component, + MouseEvent.MOUSE_MOVED, + System.currentTimeMillis(), + 0, 0, 0, 0, false + ) ); + manager.setInitialDelay( oldDelay ); + } } 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 { return new ClassEntry( getOuterClassName() ); } + + public boolean isInDefaultPackage( ) + { + return m_name.indexOf( '/' ) < 0; + } + + public String getPackageName( ) + { + int pos = m_name.lastIndexOf( '/' ); + if( pos > 0 ) + { + return m_name.substring( 0, pos ); + } + return null; + } + + public String getSimpleName( ) + { + int pos = m_name.lastIndexOf( '/' ); + if( pos > 0 ) + { + return m_name.substring( pos + 1 ); + } + return m_name; + } } 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 private static final long serialVersionUID = -2279910052561114323L; private String m_name; + private String m_reason; public IllegalNameException( String name ) + { + this( name, null ); + } + + public IllegalNameException( String name, String reason ) { m_name = name; + m_reason = reason; + } + + public String getReason( ) + { + return m_reason; } @Override public String getMessage( ) { - return "Illegal name: " + m_name; + StringBuilder buf = new StringBuilder(); + buf.append( "Illegal name: " ); + buf.append( m_name ); + if( m_reason != null ) + { + buf.append( " because " ); + buf.append( m_reason ); + } + return buf.toString(); } } 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 { if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalNameException( name ); + throw new IllegalNameException( name, "This doesn't look like a legal class name" ); + } + if( new ClassEntry( name ).getPackageName() == null ) + { + throw new IllegalNameException( name, "Classes must be in a package" ); } return Descriptor.toJvmName( name ); } @@ -68,7 +72,7 @@ public class NameValidator { if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) { - throw new IllegalNameException( name ); + throw new IllegalNameException( name, "This doesn't look like a legal identifier" ); } return name; } -- cgit v1.2.3