diff options
| author | 2015-02-23 23:29:22 -0500 | |
|---|---|---|
| committer | 2015-02-23 23:29:22 -0500 | |
| commit | 2dc7428e37bdd7a119f53d02ce157675509b0d63 (patch) | |
| tree | 68f409ac726166e427eea3a199eb462130c53ccd /src | |
| parent | make types serializable (diff) | |
| download | enigma-2dc7428e37bdd7a119f53d02ce157675509b0d63.tar.gz enigma-2dc7428e37bdd7a119f53d02ce157675509b0d63.tar.xz enigma-2dc7428e37bdd7a119f53d02ce157675509b0d63.zip | |
lots of work in better handling of inner classes
also working on recognizing unobfuscated and deobfuscated jars
(needed for M3L)
Diffstat (limited to 'src')
| -rw-r--r-- | src/cuchaz/enigma/CommandMain.java | 43 | ||||
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 4 | ||||
| -rw-r--r-- | src/cuchaz/enigma/MainFormatConverter.java | 2 | ||||
| -rw-r--r-- | src/cuchaz/enigma/TranslatingTypeLoader.java | 6 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 67 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/InnerClassWriter.java | 43 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/ClassMatcher.java | 6 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ClassMapping.java | 41 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/EntryFactory.java | 35 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Mappings.java | 14 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsRenamer.java | 2 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsWriter.java | 4 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Translator.java | 154 |
13 files changed, 261 insertions, 160 deletions
diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 1ec2ad23..0253a92f 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java | |||
| @@ -51,7 +51,7 @@ public class CommandMain { | |||
| 51 | try { | 51 | try { |
| 52 | 52 | ||
| 53 | // process the command | 53 | // process the command |
| 54 | String command = getArg(args, 0, "command"); | 54 | String command = getArg(args, 0, "command", true); |
| 55 | if (command.equalsIgnoreCase("deobfuscate")) { | 55 | if (command.equalsIgnoreCase("deobfuscate")) { |
| 56 | deobfuscate(args); | 56 | deobfuscate(args); |
| 57 | } else if(command.equalsIgnoreCase("decompile")) { | 57 | } else if(command.equalsIgnoreCase("decompile")) { |
| @@ -70,46 +70,55 @@ public class CommandMain { | |||
| 70 | System.out.println("Usage:"); | 70 | System.out.println("Usage:"); |
| 71 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>"); | 71 | System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain <command>"); |
| 72 | System.out.println("\twhere <command> is one of:"); | 72 | System.out.println("\twhere <command> is one of:"); |
| 73 | System.out.println("\t\tdeobfuscate <mappings file> <in jar> <out jar>"); | 73 | System.out.println("\t\tdeobfuscate <in jar> <out jar> [<mappings file>]"); |
| 74 | System.out.println("\t\tdecompile <mappings file> <in jar> <out folder>"); | 74 | System.out.println("\t\tdecompile <in jar> <out folder> [<mappings file>]"); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | private static void decompile(String[] args) | 77 | private static void decompile(String[] args) |
| 78 | throws Exception { | 78 | throws Exception { |
| 79 | File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); | 79 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 80 | File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); | 80 | File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); |
| 81 | File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); | 81 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); |
| 82 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 82 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 83 | deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); | 83 | deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | private static void deobfuscate(String[] args) | 86 | private static void deobfuscate(String[] args) |
| 87 | throws Exception { | 87 | throws Exception { |
| 88 | File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); | 88 | File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); |
| 89 | File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); | 89 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); |
| 90 | File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); | 90 | File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); |
| 91 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 91 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 92 | deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); | 92 | deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) | 95 | private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) |
| 96 | throws Exception { | 96 | throws Exception { |
| 97 | System.out.println("Reading mappings..."); | ||
| 98 | Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); | ||
| 99 | System.out.println("Reading jar..."); | 97 | System.out.println("Reading jar..."); |
| 100 | Deobfuscator deobfuscator = new Deobfuscator(jar); | 98 | Deobfuscator deobfuscator = new Deobfuscator(jar); |
| 101 | deobfuscator.setMappings(mappings); | 99 | if (fileMappings != null) { |
| 100 | System.out.println("Reading mappings..."); | ||
| 101 | Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); | ||
| 102 | deobfuscator.setMappings(mappings); | ||
| 103 | } | ||
| 102 | return deobfuscator; | 104 | return deobfuscator; |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 105 | private static String getArg(String[] args, int i, String name) { | 107 | private static String getArg(String[] args, int i, String name, boolean required) { |
| 106 | if (i >= args.length) { | 108 | if (i >= args.length) { |
| 107 | throw new IllegalArgumentException(name + " is required"); | 109 | if (required) { |
| 110 | throw new IllegalArgumentException(name + " is required"); | ||
| 111 | } else { | ||
| 112 | return null; | ||
| 113 | } | ||
| 108 | } | 114 | } |
| 109 | return args[i]; | 115 | return args[i]; |
| 110 | } | 116 | } |
| 111 | 117 | ||
| 112 | private static File getWritableFile(String path) { | 118 | private static File getWritableFile(String path) { |
| 119 | if (path == null) { | ||
| 120 | return null; | ||
| 121 | } | ||
| 113 | File file = new File(path).getAbsoluteFile(); | 122 | File file = new File(path).getAbsoluteFile(); |
| 114 | File dir = file.getParentFile(); | 123 | File dir = file.getParentFile(); |
| 115 | if (dir == null || !dir.exists()) { | 124 | if (dir == null || !dir.exists()) { |
| @@ -119,6 +128,9 @@ public class CommandMain { | |||
| 119 | } | 128 | } |
| 120 | 129 | ||
| 121 | private static File getWritableFolder(String path) { | 130 | private static File getWritableFolder(String path) { |
| 131 | if (path == null) { | ||
| 132 | return null; | ||
| 133 | } | ||
| 122 | File dir = new File(path).getAbsoluteFile(); | 134 | File dir = new File(path).getAbsoluteFile(); |
| 123 | if (!dir.exists()) { | 135 | if (!dir.exists()) { |
| 124 | throw new IllegalArgumentException("Cannot write to folder: " + dir); | 136 | throw new IllegalArgumentException("Cannot write to folder: " + dir); |
| @@ -127,6 +139,9 @@ public class CommandMain { | |||
| 127 | } | 139 | } |
| 128 | 140 | ||
| 129 | private static File getReadableFile(String path) { | 141 | private static File getReadableFile(String path) { |
| 142 | if (path == null) { | ||
| 143 | return null; | ||
| 144 | } | ||
| 130 | File file = new File(path).getAbsoluteFile(); | 145 | File file = new File(path).getAbsoluteFile(); |
| 131 | if (!file.exists()) { | 146 | if (!file.exists()) { |
| 132 | throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); | 147 | throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); |
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index c1954fc7..b7440a72 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -163,9 +163,9 @@ public class Deobfuscator { | |||
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | // check inner classes | 165 | // check inner classes |
| 166 | for (ClassMapping innerClassMapping : classMapping.innerClasses()) { | 166 | for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { |
| 167 | if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { | 167 | if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { |
| 168 | System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); | 168 | System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); |
| 169 | classMapping.removeInnerClassMapping(innerClassMapping); | 169 | classMapping.removeInnerClassMapping(innerClassMapping); |
| 170 | } | 170 | } |
| 171 | } | 171 | } |
diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index d4bb2db3..5db0e539 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java | |||
| @@ -111,7 +111,7 @@ public class MainFormatConverter { | |||
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) { | 113 | private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) { |
| 114 | return new ClassEntry(classMapping.getObfName()).getSimpleName() + "." + fieldMapping.getObfName(); | 114 | return classMapping.getObfSimpleName() + "." + fieldMapping.getObfName(); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | private static String getFieldKey(FieldEntry obfFieldEntry) { | 117 | private static String getFieldKey(FieldEntry obfFieldEntry) { |
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 12cde4b9..26d5e7a7 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java | |||
| @@ -110,10 +110,10 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 110 | ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); | 110 | ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); |
| 111 | 111 | ||
| 112 | // is this an inner class referenced directly? | 112 | // is this an inner class referenced directly? |
| 113 | String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); | 113 | ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfClassEntry); |
| 114 | if (obfOuterClassName != null) { | 114 | if (obfOuterClassEntry != null) { |
| 115 | // this class doesn't really exist. Reference it by outer$inner instead | 115 | // this class doesn't really exist. Reference it by outer$inner instead |
| 116 | System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); | 116 | System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassEntry)); |
| 117 | return null; | 117 | return null; |
| 118 | } | 118 | } |
| 119 | 119 | ||
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1c74f158..6e7c69da 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -61,9 +61,9 @@ public class JarIndex { | |||
| 61 | private Multimap<String,MethodEntry> m_methodImplementations; | 61 | private Multimap<String,MethodEntry> m_methodImplementations; |
| 62 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; | 62 | private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; |
| 63 | private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences; | 63 | private Multimap<FieldEntry,EntryReference<FieldEntry,BehaviorEntry>> m_fieldReferences; |
| 64 | private Multimap<String,String> m_innerClasses; | 64 | private Multimap<ClassEntry,ClassEntry> m_innerClassesByOuter; |
| 65 | private Map<String,String> m_outerClasses; | 65 | private Map<ClassEntry,ClassEntry> m_outerClassesByInner; |
| 66 | private Map<String,BehaviorEntry> m_anonymousClasses; | 66 | private Map<ClassEntry,BehaviorEntry> m_anonymousClasses; |
| 67 | private Map<MethodEntry,MethodEntry> m_bridgedMethods; | 67 | private Map<MethodEntry,MethodEntry> m_bridgedMethods; |
| 68 | 68 | ||
| 69 | public JarIndex() { | 69 | public JarIndex() { |
| @@ -74,8 +74,8 @@ public class JarIndex { | |||
| 74 | m_methodImplementations = HashMultimap.create(); | 74 | m_methodImplementations = HashMultimap.create(); |
| 75 | m_behaviorReferences = HashMultimap.create(); | 75 | m_behaviorReferences = HashMultimap.create(); |
| 76 | m_fieldReferences = HashMultimap.create(); | 76 | m_fieldReferences = HashMultimap.create(); |
| 77 | m_innerClasses = HashMultimap.create(); | 77 | m_innerClassesByOuter = HashMultimap.create(); |
| 78 | m_outerClasses = Maps.newHashMap(); | 78 | m_outerClassesByInner = Maps.newHashMap(); |
| 79 | m_anonymousClasses = Maps.newHashMap(); | 79 | m_anonymousClasses = Maps.newHashMap(); |
| 80 | m_bridgedMethods = Maps.newHashMap(); | 80 | m_bridgedMethods = Maps.newHashMap(); |
| 81 | } | 81 | } |
| @@ -129,33 +129,40 @@ public class JarIndex { | |||
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | if (buildInnerClasses) { | 131 | if (buildInnerClasses) { |
| 132 | |||
| 132 | // step 5: index inner classes and anonymous classes | 133 | // step 5: index inner classes and anonymous classes |
| 133 | for (CtClass c : JarClassIterator.classes(jar)) { | 134 | for (CtClass c : JarClassIterator.classes(jar)) { |
| 134 | ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); | 135 | ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); |
| 135 | String outerClassName = findOuterClass(c); | 136 | ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); |
| 136 | if (outerClassName != null) { | 137 | ClassEntry outerClassEntry = findOuterClass(c); |
| 137 | String innerClassName = c.getSimpleName(); | 138 | if (outerClassEntry != null) { |
| 138 | m_innerClasses.put(outerClassName, innerClassName); | 139 | m_innerClassesByOuter.put(outerClassEntry, innerClassEntry); |
| 139 | boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; | 140 | boolean innerWasAdded = m_outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; |
| 140 | assert (innerWasAdded); | 141 | assert (innerWasAdded); |
| 141 | 142 | ||
| 142 | BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); | 143 | BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); |
| 143 | if (enclosingBehavior != null) { | 144 | if (enclosingBehavior != null) { |
| 144 | m_anonymousClasses.put(innerClassName, enclosingBehavior); | 145 | m_anonymousClasses.put(innerClassEntry, enclosingBehavior); |
| 145 | 146 | ||
| 146 | // DEBUG | 147 | // DEBUG |
| 147 | // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); | 148 | //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); |
| 148 | } else { | 149 | } else { |
| 149 | // DEBUG | 150 | // DEBUG |
| 150 | // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); | 151 | //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); |
| 151 | } | 152 | } |
| 152 | } | 153 | } |
| 153 | } | 154 | } |
| 154 | 155 | ||
| 155 | // step 6: update other indices with inner class info | 156 | // step 6: update other indices with inner class info |
| 156 | Map<String,String> renames = Maps.newHashMap(); | 157 | Map<String,String> renames = Maps.newHashMap(); |
| 157 | for (Map.Entry<String,String> entry : m_outerClasses.entrySet()) { | 158 | for (Map.Entry<ClassEntry,ClassEntry> mapEntry : m_innerClassesByOuter.entries()) { |
| 158 | renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); | 159 | ClassEntry outerClassEntry = mapEntry.getKey(); |
| 160 | ClassEntry innerClassEntry = mapEntry.getValue(); | ||
| 161 | outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); | ||
| 162 | String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); | ||
| 163 | // DEBUG | ||
| 164 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); | ||
| 165 | renames.put(innerClassEntry.getName(), newName); | ||
| 159 | } | 166 | } |
| 160 | EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); | 167 | EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); |
| 161 | m_translationIndex.renameClasses(renames); | 168 | m_translationIndex.renameClasses(renames); |
| @@ -290,7 +297,7 @@ public class JarIndex { | |||
| 290 | } | 297 | } |
| 291 | } | 298 | } |
| 292 | 299 | ||
| 293 | private String findOuterClass(CtClass c) { | 300 | private ClassEntry findOuterClass(CtClass c) { |
| 294 | 301 | ||
| 295 | // inner classes: | 302 | // inner classes: |
| 296 | // have constructors that can (illegally) set synthetic fields | 303 | // have constructors that can (illegally) set synthetic fields |
| @@ -341,19 +348,19 @@ public class JarIndex { | |||
| 341 | // do we have an answer yet? | 348 | // do we have an answer yet? |
| 342 | if (callerClasses.isEmpty()) { | 349 | if (callerClasses.isEmpty()) { |
| 343 | if (illegallySetClasses.size() == 1) { | 350 | if (illegallySetClasses.size() == 1) { |
| 344 | return illegallySetClasses.iterator().next().getName(); | 351 | return illegallySetClasses.iterator().next(); |
| 345 | } else { | 352 | } else { |
| 346 | System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); | 353 | System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); |
| 347 | } | 354 | } |
| 348 | } else { | 355 | } else { |
| 349 | if (callerClasses.size() == 1) { | 356 | if (callerClasses.size() == 1) { |
| 350 | return callerClasses.iterator().next().getName(); | 357 | return callerClasses.iterator().next(); |
| 351 | } else { | 358 | } else { |
| 352 | // multiple callers, do the illegally set classes narrow it down? | 359 | // multiple callers, do the illegally set classes narrow it down? |
| 353 | Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); | 360 | Set<ClassEntry> intersection = Sets.newHashSet(callerClasses); |
| 354 | intersection.retainAll(illegallySetClasses); | 361 | intersection.retainAll(illegallySetClasses); |
| 355 | if (intersection.size() == 1) { | 362 | if (intersection.size() == 1) { |
| 356 | return intersection.iterator().next().getName(); | 363 | return intersection.iterator().next(); |
| 357 | } else { | 364 | } else { |
| 358 | System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); | 365 | System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); |
| 359 | } | 366 | } |
| @@ -448,7 +455,7 @@ public class JarIndex { | |||
| 448 | return true; | 455 | return true; |
| 449 | } | 456 | } |
| 450 | 457 | ||
| 451 | private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { | 458 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { |
| 452 | 459 | ||
| 453 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 460 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 454 | 461 | ||
| @@ -669,23 +676,19 @@ public class JarIndex { | |||
| 669 | return behaviorEntries; | 676 | return behaviorEntries; |
| 670 | } | 677 | } |
| 671 | 678 | ||
| 672 | public Collection<String> getInnerClasses(String obfOuterClassName) { | 679 | public Collection<ClassEntry> getInnerClasses(ClassEntry obfOuterClassEntry) { |
| 673 | return m_innerClasses.get(obfOuterClassName); | 680 | return m_innerClassesByOuter.get(obfOuterClassEntry); |
| 674 | } | 681 | } |
| 675 | 682 | ||
| 676 | public String getOuterClass(String obfInnerClassName) { | 683 | public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { |
| 677 | // make sure we use the right name | 684 | return m_outerClassesByInner.get(obfInnerClassEntry); |
| 678 | if (new ClassEntry(obfInnerClassName).getPackageName() != null) { | ||
| 679 | throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); | ||
| 680 | } | ||
| 681 | return m_outerClasses.get(obfInnerClassName); | ||
| 682 | } | 685 | } |
| 683 | 686 | ||
| 684 | public boolean isAnonymousClass(String obfInnerClassName) { | 687 | public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { |
| 685 | return m_anonymousClasses.containsKey(obfInnerClassName); | 688 | return m_anonymousClasses.containsKey(obfInnerClassEntry); |
| 686 | } | 689 | } |
| 687 | 690 | ||
| 688 | public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { | 691 | public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { |
| 689 | return m_anonymousClasses.get(obfInnerClassName); | 692 | return m_anonymousClasses.get(obfInnerClassName); |
| 690 | } | 693 | } |
| 691 | 694 | ||
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5350b86c..e82f56ca 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java | |||
| @@ -14,13 +14,12 @@ import java.util.Collection; | |||
| 14 | 14 | ||
| 15 | import javassist.CtClass; | 15 | import javassist.CtClass; |
| 16 | import javassist.bytecode.ConstPool; | 16 | import javassist.bytecode.ConstPool; |
| 17 | import javassist.bytecode.Descriptor; | ||
| 18 | import javassist.bytecode.EnclosingMethodAttribute; | 17 | import javassist.bytecode.EnclosingMethodAttribute; |
| 19 | import javassist.bytecode.InnerClassesAttribute; | 18 | import javassist.bytecode.InnerClassesAttribute; |
| 20 | import cuchaz.enigma.Constants; | ||
| 21 | import cuchaz.enigma.analysis.JarIndex; | 19 | import cuchaz.enigma.analysis.JarIndex; |
| 22 | import cuchaz.enigma.mapping.BehaviorEntry; | 20 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 23 | import cuchaz.enigma.mapping.ClassEntry; | 21 | import cuchaz.enigma.mapping.ClassEntry; |
| 22 | import cuchaz.enigma.mapping.EntryFactory; | ||
| 24 | 23 | ||
| 25 | public class InnerClassWriter { | 24 | public class InnerClassWriter { |
| 26 | 25 | ||
| @@ -32,18 +31,23 @@ public class InnerClassWriter { | |||
| 32 | 31 | ||
| 33 | public void write(CtClass c) { | 32 | public void write(CtClass c) { |
| 34 | 33 | ||
| 35 | // is this an inner or outer class? | 34 | // first, assume this is an inner class |
| 36 | String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); | 35 | ClassEntry obfInnerClassEntry = EntryFactory.getClassEntry(c); |
| 37 | String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); | 36 | ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfInnerClassEntry); |
| 38 | if (obfOuterClassName == null) { | 37 | |
| 39 | // this is an outer class | 38 | // see if we're right |
| 40 | obfOuterClassName = Descriptor.toJvmName(c.getName()); | 39 | if (obfOuterClassEntry == null) { |
| 40 | |||
| 41 | // nope, it's an outer class | ||
| 42 | obfInnerClassEntry = null; | ||
| 43 | obfOuterClassEntry = EntryFactory.getClassEntry(c); | ||
| 41 | } else { | 44 | } else { |
| 42 | // this is an inner class, rename it to outer$inner | 45 | |
| 43 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); | 46 | // yeah, it's an inner class, rename it to outer$inner |
| 47 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); | ||
| 44 | c.setName(obfClassEntry.getName()); | 48 | c.setName(obfClassEntry.getName()); |
| 45 | 49 | ||
| 46 | BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); | 50 | BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); |
| 47 | if (caller != null) { | 51 | if (caller != null) { |
| 48 | // write the enclosing method attribute | 52 | // write the enclosing method attribute |
| 49 | if (caller.getName().equals("<clinit>")) { | 53 | if (caller.getName().equals("<clinit>")) { |
| @@ -55,18 +59,19 @@ public class InnerClassWriter { | |||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | // write the inner classes if needed | 61 | // write the inner classes if needed |
| 58 | Collection<String> obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); | 62 | Collection<ClassEntry> obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); |
| 59 | if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { | 63 | if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { |
| 60 | writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); | 64 | writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); |
| 61 | } | 65 | } |
| 62 | } | 66 | } |
| 63 | 67 | ||
| 64 | private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection<String> obfInnerClassNames) { | 68 | private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection<ClassEntry> obfInnerClassEntries) { |
| 65 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); | 69 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); |
| 66 | c.getClassFile().addAttribute(attr); | 70 | c.getClassFile().addAttribute(attr); |
| 67 | for (String obfInnerClassName : obfInnerClassNames) { | 71 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { |
| 72 | |||
| 68 | // get the new inner class name | 73 | // get the new inner class name |
| 69 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); | 74 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); |
| 70 | 75 | ||
| 71 | // here's what the JVM spec says about the InnerClasses attribute | 76 | // here's what the JVM spec says about the InnerClasses attribute |
| 72 | // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); | 77 | // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); |
| @@ -77,7 +82,7 @@ public class InnerClassWriter { | |||
| 77 | int outerClassIndex = 0; | 82 | int outerClassIndex = 0; |
| 78 | int innerClassSimpleNameIndex = 0; | 83 | int innerClassSimpleNameIndex = 0; |
| 79 | int accessFlags = 0; | 84 | int accessFlags = 0; |
| 80 | if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { | 85 | if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { |
| 81 | outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); | 86 | outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); |
| 82 | innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); | 87 | innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); |
| 83 | } | 88 | } |
| @@ -96,7 +101,7 @@ public class InnerClassWriter { | |||
| 96 | */ | 101 | */ |
| 97 | 102 | ||
| 98 | // make sure the outer class references only the new inner class names | 103 | // make sure the outer class references only the new inner class names |
| 99 | c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); | 104 | c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); |
| 100 | } | 105 | } |
| 101 | } | 106 | } |
| 102 | } | 107 | } |
diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index d70b8ebb..224d0048 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java | |||
| @@ -222,7 +222,7 @@ public class ClassMatcher { | |||
| 222 | // check the method matches | 222 | // check the method matches |
| 223 | System.out.println("Checking methods..."); | 223 | System.out.println("Checking methods..."); |
| 224 | for (ClassMapping classMapping : mappings.classes()) { | 224 | for (ClassMapping classMapping : mappings.classes()) { |
| 225 | ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); | 225 | ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); |
| 226 | for (MethodMapping methodMapping : classMapping.methods()) { | 226 | for (MethodMapping methodMapping : classMapping.methods()) { |
| 227 | 227 | ||
| 228 | // skip constructors | 228 | // skip constructors |
| @@ -240,13 +240,13 @@ public class ClassMatcher { | |||
| 240 | 240 | ||
| 241 | // show the available methods | 241 | // show the available methods |
| 242 | System.err.println("\tAvailable dest methods:"); | 242 | System.err.println("\tAvailable dest methods:"); |
| 243 | CtClass c = destLoader.loadClass(classMapping.getObfName()); | 243 | CtClass c = destLoader.loadClass(classMapping.getObfFullName()); |
| 244 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 244 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 245 | System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); | 245 | System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | System.err.println("\tAvailable source methods:"); | 248 | System.err.println("\tAvailable source methods:"); |
| 249 | c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); | 249 | c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); |
| 250 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 250 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 251 | System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); | 251 | System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); |
| 252 | } | 252 | } |
diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 885400b4..3610e33b 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java | |||
| @@ -20,7 +20,8 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 20 | 20 | ||
| 21 | private static final long serialVersionUID = -5148491146902340107L; | 21 | private static final long serialVersionUID = -5148491146902340107L; |
| 22 | 22 | ||
| 23 | private String m_obfName; | 23 | private String m_obfFullName; |
| 24 | private String m_obfSimpleName; | ||
| 24 | private String m_deobfName; | 25 | private String m_deobfName; |
| 25 | private Map<String,ClassMapping> m_innerClassesByObf; | 26 | private Map<String,ClassMapping> m_innerClassesByObf; |
| 26 | private Map<String,ClassMapping> m_innerClassesByDeobf; | 27 | private Map<String,ClassMapping> m_innerClassesByDeobf; |
| @@ -34,7 +35,8 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | public ClassMapping(String obfName, String deobfName) { | 37 | public ClassMapping(String obfName, String deobfName) { |
| 37 | m_obfName = obfName; | 38 | m_obfFullName = obfName; |
| 39 | m_obfSimpleName = new ClassEntry(obfName).getSimpleName(); | ||
| 38 | m_deobfName = NameValidator.validateClassName(deobfName, false); | 40 | m_deobfName = NameValidator.validateClassName(deobfName, false); |
| 39 | m_innerClassesByObf = Maps.newHashMap(); | 41 | m_innerClassesByObf = Maps.newHashMap(); |
| 40 | m_innerClassesByDeobf = Maps.newHashMap(); | 42 | m_innerClassesByDeobf = Maps.newHashMap(); |
| @@ -44,8 +46,12 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 44 | m_methodsByDeobf = Maps.newHashMap(); | 46 | m_methodsByDeobf = Maps.newHashMap(); |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | public String getObfName() { | 49 | public String getObfFullName() { |
| 48 | return m_obfName; | 50 | return m_obfFullName; |
| 51 | } | ||
| 52 | |||
| 53 | public String getObfSimpleName() { | ||
| 54 | return m_obfSimpleName; | ||
| 49 | } | 55 | } |
| 50 | 56 | ||
| 51 | public String getDeobfName() { | 57 | public String getDeobfName() { |
| @@ -64,8 +70,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 64 | } | 70 | } |
| 65 | 71 | ||
| 66 | public void addInnerClassMapping(ClassMapping classMapping) { | 72 | public void addInnerClassMapping(ClassMapping classMapping) { |
| 67 | assert (isSimpleClassName(classMapping.getObfName())); | 73 | boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfSimpleName(), classMapping) == null; |
| 68 | boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; | ||
| 69 | assert (obfWasAdded); | 74 | assert (obfWasAdded); |
| 70 | if (classMapping.getDeobfName() != null) { | 75 | if (classMapping.getDeobfName() != null) { |
| 71 | assert (isSimpleClassName(classMapping.getDeobfName())); | 76 | assert (isSimpleClassName(classMapping.getDeobfName())); |
| @@ -75,7 +80,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 75 | } | 80 | } |
| 76 | 81 | ||
| 77 | public void removeInnerClassMapping(ClassMapping classMapping) { | 82 | public void removeInnerClassMapping(ClassMapping classMapping) { |
| 78 | boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; | 83 | boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfSimpleName()) != null; |
| 79 | assert (obfWasRemoved); | 84 | assert (obfWasRemoved); |
| 80 | if (classMapping.getDeobfName() != null) { | 85 | if (classMapping.getDeobfName() != null) { |
| 81 | boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | 86 | boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; |
| @@ -112,11 +117,11 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 112 | return classMapping; | 117 | return classMapping; |
| 113 | } | 118 | } |
| 114 | 119 | ||
| 115 | public String getObfInnerClassName(String deobfName) { | 120 | public String getObfInnerClassSimpleName(String deobfName) { |
| 116 | assert (isSimpleClassName(deobfName)); | 121 | assert (isSimpleClassName(deobfName)); |
| 117 | ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); | 122 | ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); |
| 118 | if (classMapping != null) { | 123 | if (classMapping != null) { |
| 119 | return classMapping.getObfName(); | 124 | return classMapping.getObfSimpleName(); |
| 120 | } | 125 | } |
| 121 | return null; | 126 | return null; |
| 122 | } | 127 | } |
| @@ -163,7 +168,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 163 | public void addFieldMapping(FieldMapping fieldMapping) { | 168 | public void addFieldMapping(FieldMapping fieldMapping) { |
| 164 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | 169 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); |
| 165 | if (m_fieldsByObf.containsKey(obfKey)) { | 170 | if (m_fieldsByObf.containsKey(obfKey)) { |
| 166 | throw new Error("Already have mapping for " + m_obfName + "." + obfKey); | 171 | throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); |
| 167 | } | 172 | } |
| 168 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); | 173 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); |
| 169 | if (m_fieldsByDeobf.containsKey(deobfKey)) { | 174 | if (m_fieldsByDeobf.containsKey(deobfKey)) { |
| @@ -257,7 +262,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 257 | public void addMethodMapping(MethodMapping methodMapping) { | 262 | public void addMethodMapping(MethodMapping methodMapping) { |
| 258 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | 263 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); |
| 259 | if (m_methodsByObf.containsKey(obfKey)) { | 264 | if (m_methodsByObf.containsKey(obfKey)) { |
| 260 | throw new Error("Already have mapping for " + m_obfName + "." + obfKey); | 265 | throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); |
| 261 | } | 266 | } |
| 262 | boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; | 267 | boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; |
| 263 | assert (wasAdded); | 268 | assert (wasAdded); |
| @@ -339,7 +344,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 339 | @Override | 344 | @Override |
| 340 | public String toString() { | 345 | public String toString() { |
| 341 | StringBuilder buf = new StringBuilder(); | 346 | StringBuilder buf = new StringBuilder(); |
| 342 | buf.append(m_obfName); | 347 | buf.append(m_obfFullName); |
| 343 | buf.append(" <-> "); | 348 | buf.append(" <-> "); |
| 344 | buf.append(m_deobfName); | 349 | buf.append(m_deobfName); |
| 345 | buf.append("\n"); | 350 | buf.append("\n"); |
| @@ -359,7 +364,7 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 359 | buf.append("Inner Classes:\n"); | 364 | buf.append("Inner Classes:\n"); |
| 360 | for (ClassMapping classMapping : m_innerClassesByObf.values()) { | 365 | for (ClassMapping classMapping : m_innerClassesByObf.values()) { |
| 361 | buf.append("\t"); | 366 | buf.append("\t"); |
| 362 | buf.append(classMapping.getObfName()); | 367 | buf.append(classMapping.getObfSimpleName()); |
| 363 | buf.append(" <-> "); | 368 | buf.append(" <-> "); |
| 364 | buf.append(classMapping.getDeobfName()); | 369 | buf.append(classMapping.getDeobfName()); |
| 365 | buf.append("\n"); | 370 | buf.append("\n"); |
| @@ -370,10 +375,10 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 370 | @Override | 375 | @Override |
| 371 | public int compareTo(ClassMapping other) { | 376 | public int compareTo(ClassMapping other) { |
| 372 | // sort by a, b, c, ... aa, ab, etc | 377 | // sort by a, b, c, ... aa, ab, etc |
| 373 | if (m_obfName.length() != other.m_obfName.length()) { | 378 | if (m_obfFullName.length() != other.m_obfFullName.length()) { |
| 374 | return m_obfName.length() - other.m_obfName.length(); | 379 | return m_obfFullName.length() - other.m_obfFullName.length(); |
| 375 | } | 380 | } |
| 376 | return m_obfName.compareTo(other.m_obfName); | 381 | return m_obfFullName.compareTo(other.m_obfFullName); |
| 377 | } | 382 | } |
| 378 | 383 | ||
| 379 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { | 384 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { |
| @@ -399,9 +404,9 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping> { | |||
| 399 | } | 404 | } |
| 400 | } | 405 | } |
| 401 | 406 | ||
| 402 | if (m_obfName.equals(oldObfClassName)) { | 407 | if (m_obfFullName.equals(oldObfClassName)) { |
| 403 | // rename this class | 408 | // rename this class |
| 404 | m_obfName = newObfClassName; | 409 | m_obfFullName = newObfClassName; |
| 405 | return true; | 410 | return true; |
| 406 | } | 411 | } |
| 407 | return false; | 412 | return false; |
diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index dceea29d..bbdfa739 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -25,25 +25,19 @@ public class EntryFactory { | |||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { | 27 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { |
| 28 | return new ClassEntry(getChainedOuterClassName(jarIndex, classMapping.getObfName())); | 28 | return getChainedOuterClassName(jarIndex, new ClassEntry(classMapping.getObfFullName())); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | private static String getChainedOuterClassName(JarIndex jarIndex, String obfClassName) { | 31 | public static ClassEntry getChainedOuterClassName(JarIndex jarIndex, ClassEntry obfClassEntry) { |
| 32 | 32 | ||
| 33 | // lookup the chain of outer classes | 33 | // lookup the chain of outer classes |
| 34 | List<String> obfOuterClassNames = Lists.newArrayList(); | 34 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); |
| 35 | String checkName = obfClassName; | 35 | ClassEntry checkClassEntry = obfClassEntry; |
| 36 | while (true) { | 36 | while (true) { |
| 37 | 37 | ClassEntry obfOuterClassEntry = jarIndex.getOuterClass(checkClassEntry); | |
| 38 | // if this class name has a package, then it can't be an inner class | 38 | if (obfOuterClassEntry != null) { |
| 39 | if (!new ClassEntry(checkName).isInDefaultPackage()) { | 39 | obfClassChain.add(obfOuterClassEntry); |
| 40 | break; | 40 | checkClassEntry = obfOuterClassEntry; |
| 41 | } | ||
| 42 | |||
| 43 | String obfOuterClassName = jarIndex.getOuterClass(checkName); | ||
| 44 | if (obfOuterClassName != null) { | ||
| 45 | obfOuterClassNames.add(obfOuterClassName); | ||
| 46 | checkName = obfOuterClassName; | ||
| 47 | } else { | 41 | } else { |
| 48 | break; | 42 | break; |
| 49 | } | 43 | } |
| @@ -51,12 +45,15 @@ public class EntryFactory { | |||
| 51 | 45 | ||
| 52 | // build the chained class name | 46 | // build the chained class name |
| 53 | StringBuilder buf = new StringBuilder(); | 47 | StringBuilder buf = new StringBuilder(); |
| 54 | for (int i=obfOuterClassNames.size()-1; i>=0; i--) { | 48 | for (int i=obfClassChain.size()-1; i>=0; i--) { |
| 55 | buf.append(obfOuterClassNames.get(i)); | 49 | if (buf.length() == 0) { |
| 56 | buf.append("$"); | 50 | buf.append(obfClassChain.get(i).getName()); |
| 51 | } else { | ||
| 52 | buf.append("$"); | ||
| 53 | buf.append(obfClassChain.get(i).getSimpleName()); | ||
| 54 | } | ||
| 57 | } | 55 | } |
| 58 | buf.append(obfClassName); | 56 | return new ClassEntry(buf.toString()); |
| 59 | return buf.toString(); | ||
| 60 | } | 57 | } |
| 61 | 58 | ||
| 62 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | 59 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { |
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 675fdf1f..a85bcbf6 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java | |||
| @@ -37,7 +37,7 @@ public class Mappings implements Serializable { | |||
| 37 | this(); | 37 | this(); |
| 38 | 38 | ||
| 39 | for (ClassMapping classMapping : classes) { | 39 | for (ClassMapping classMapping : classes) { |
| 40 | m_classesByObf.put(classMapping.getObfName(), classMapping); | 40 | m_classesByObf.put(classMapping.getObfFullName(), classMapping); |
| 41 | if (classMapping.getDeobfName() != null) { | 41 | if (classMapping.getDeobfName() != null) { |
| 42 | m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); | 42 | m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); |
| 43 | } | 43 | } |
| @@ -50,10 +50,10 @@ public class Mappings implements Serializable { | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | public void addClassMapping(ClassMapping classMapping) { | 52 | public void addClassMapping(ClassMapping classMapping) { |
| 53 | if (m_classesByObf.containsKey(classMapping.getObfName())) { | 53 | if (m_classesByObf.containsKey(classMapping.getObfFullName())) { |
| 54 | throw new Error("Already have mapping for " + classMapping.getObfName()); | 54 | throw new Error("Already have mapping for " + classMapping.getObfFullName()); |
| 55 | } | 55 | } |
| 56 | boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; | 56 | boolean obfWasAdded = m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; |
| 57 | assert (obfWasAdded); | 57 | assert (obfWasAdded); |
| 58 | if (classMapping.getDeobfName() != null) { | 58 | if (classMapping.getDeobfName() != null) { |
| 59 | if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { | 59 | if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { |
| @@ -65,7 +65,7 @@ public class Mappings implements Serializable { | |||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | public void removeClassMapping(ClassMapping classMapping) { | 67 | public void removeClassMapping(ClassMapping classMapping) { |
| 68 | boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; | 68 | boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfFullName()) != null; |
| 69 | assert (obfWasRemoved); | 69 | assert (obfWasRemoved); |
| 70 | if (classMapping.getDeobfName() != null) { | 70 | if (classMapping.getDeobfName() != null) { |
| 71 | boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; | 71 | boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; |
| @@ -103,7 +103,7 @@ public class Mappings implements Serializable { | |||
| 103 | if (classMapping.getDeobfName() != null) { | 103 | if (classMapping.getDeobfName() != null) { |
| 104 | classes.put(classMapping.getDeobfName(), classMapping); | 104 | classes.put(classMapping.getDeobfName(), classMapping); |
| 105 | } else { | 105 | } else { |
| 106 | classes.put(classMapping.getObfName(), classMapping); | 106 | classes.put(classMapping.getObfFullName(), classMapping); |
| 107 | } | 107 | } |
| 108 | } | 108 | } |
| 109 | 109 | ||
| @@ -144,7 +144,7 @@ public class Mappings implements Serializable { | |||
| 144 | for (ClassMapping classMapping : classes()) { | 144 | for (ClassMapping classMapping : classes()) { |
| 145 | 145 | ||
| 146 | // add the class name | 146 | // add the class name |
| 147 | classNames.add(classMapping.getObfName()); | 147 | classNames.add(classMapping.getObfFullName()); |
| 148 | 148 | ||
| 149 | // add classes from method signatures | 149 | // add classes from method signatures |
| 150 | for (MethodMapping methodMapping : classMapping.methods()) { | 150 | for (MethodMapping methodMapping : classMapping.methods()) { |
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index ea343c4e..16f700d2 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -213,7 +213,7 @@ public class MappingsRenamer { | |||
| 213 | ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); | 213 | ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); |
| 214 | if (classMapping == null) { | 214 | if (classMapping == null) { |
| 215 | classMapping = new ClassMapping(obfClassName); | 215 | classMapping = new ClassMapping(obfClassName); |
| 216 | boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; | 216 | boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; |
| 217 | assert (obfWasAdded); | 217 | assert (obfWasAdded); |
| 218 | } | 218 | } |
| 219 | return classMapping; | 219 | return classMapping; |
diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index c7c2cc00..8b62db8c 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java | |||
| @@ -31,9 +31,9 @@ public class MappingsWriter { | |||
| 31 | 31 | ||
| 32 | private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { | 32 | private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { |
| 33 | if (classMapping.getDeobfName() == null) { | 33 | if (classMapping.getDeobfName() == null) { |
| 34 | out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); | 34 | out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfFullName()); |
| 35 | } else { | 35 | } else { |
| 36 | out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); | 36 | out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName()); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { | 39 | for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { |
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 759dddf9..d9850324 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java | |||
| @@ -10,8 +10,10 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.mapping; | 11 | package cuchaz.enigma.mapping; |
| 12 | 12 | ||
| 13 | import java.util.List; | ||
| 13 | import java.util.Map; | 14 | import java.util.Map; |
| 14 | 15 | ||
| 16 | import com.beust.jcommander.internal.Lists; | ||
| 15 | import com.google.common.collect.Maps; | 17 | import com.google.common.collect.Maps; |
| 16 | 18 | ||
| 17 | import cuchaz.enigma.analysis.TranslationIndex; | 19 | import cuchaz.enigma.analysis.TranslationIndex; |
| @@ -50,54 +52,106 @@ public class Translator { | |||
| 50 | } | 52 | } |
| 51 | } | 53 | } |
| 52 | 54 | ||
| 55 | public <T extends Entry> String translate(T entry) { | ||
| 56 | if (entry instanceof ClassEntry) { | ||
| 57 | return translate((ClassEntry)entry); | ||
| 58 | } else if (entry instanceof FieldEntry) { | ||
| 59 | return translate((FieldEntry)entry); | ||
| 60 | } else if (entry instanceof MethodEntry) { | ||
| 61 | return translate((MethodEntry)entry); | ||
| 62 | } else if (entry instanceof ConstructorEntry) { | ||
| 63 | return translate((ConstructorEntry)entry); | ||
| 64 | } else if (entry instanceof ArgumentEntry) { | ||
| 65 | return translate((ArgumentEntry)entry); | ||
| 66 | } else { | ||
| 67 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 53 | public String translateClass(String className) { | 71 | public String translateClass(String className) { |
| 54 | return translate(new ClassEntry(className)); | 72 | return translate(new ClassEntry(className)); |
| 55 | } | 73 | } |
| 56 | 74 | ||
| 57 | public String translate(ClassEntry in) { | 75 | public String translate(ClassEntry in) { |
| 58 | ClassMapping classMapping = m_classes.get(in.getOuterClassName()); | 76 | |
| 59 | if (classMapping != null) { | 77 | if (in.isInnerClass()) { |
| 60 | if (in.isInnerClass()) { | 78 | |
| 61 | // translate the inner class | 79 | // translate everything in the class chain, or return null |
| 62 | String translatedInnerClassName = m_direction.choose( | 80 | List<ClassMapping> mappingsChain = getClassMappingChain(in); |
| 63 | classMapping.getDeobfInnerClassName(in.getInnerClassName()), | 81 | StringBuilder buf = new StringBuilder(); |
| 64 | classMapping.getObfInnerClassName(in.getInnerClassName()) | 82 | for (ClassMapping classMapping : mappingsChain) { |
| 83 | if (classMapping == null) { | ||
| 84 | return null; | ||
| 85 | } | ||
| 86 | boolean isFirstClass = buf.length() == 0; | ||
| 87 | String name = m_direction.choose( | ||
| 88 | classMapping.getDeobfName(), | ||
| 89 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() | ||
| 65 | ); | 90 | ); |
| 66 | if (translatedInnerClassName != null) { | 91 | if (name == null) { |
| 67 | // try to translate the outer name | 92 | return null; |
| 68 | String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); | 93 | } |
| 69 | if (translatedOuterClassName != null) { | 94 | if (!isFirstClass) { |
| 70 | return translatedOuterClassName + "$" + translatedInnerClassName; | 95 | buf.append("$"); |
| 71 | } else { | ||
| 72 | return in.getOuterClassName() + "$" + translatedInnerClassName; | ||
| 73 | } | ||
| 74 | } | 96 | } |
| 75 | } else { | 97 | buf.append(name); |
| 76 | // just return outer | ||
| 77 | return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); | ||
| 78 | } | 98 | } |
| 99 | return buf.toString(); | ||
| 100 | |||
| 101 | } else { | ||
| 102 | |||
| 103 | // normal classes are easier | ||
| 104 | ClassMapping classMapping = m_classes.get(in.getName()); | ||
| 105 | if (classMapping == null) { | ||
| 106 | return null; | ||
| 107 | } | ||
| 108 | return m_direction.choose( | ||
| 109 | classMapping.getDeobfName(), | ||
| 110 | classMapping.getObfFullName() | ||
| 111 | ); | ||
| 79 | } | 112 | } |
| 80 | return null; | ||
| 81 | } | 113 | } |
| 82 | 114 | ||
| 83 | public ClassEntry translateEntry(ClassEntry in) { | 115 | public ClassEntry translateEntry(ClassEntry in) { |
| 84 | 116 | ||
| 85 | // can we translate the inner class? | ||
| 86 | String name = translate(in); | ||
| 87 | if (name != null) { | ||
| 88 | return new ClassEntry(name); | ||
| 89 | } | ||
| 90 | |||
| 91 | if (in.isInnerClass()) { | 117 | if (in.isInnerClass()) { |
| 92 | 118 | ||
| 93 | // guess not. just translate the outer class name then | 119 | // translate as much of the class chain as we can |
| 94 | String outerClassName = translate(in.getOuterClassEntry()); | 120 | List<ClassMapping> mappingsChain = getClassMappingChain(in); |
| 95 | if (outerClassName != null) { | 121 | String[] obfClassNames = in.getName().split("\\$"); |
| 96 | return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); | 122 | StringBuilder buf = new StringBuilder(); |
| 123 | for (int i=0; i<obfClassNames.length; i++) { | ||
| 124 | boolean isFirstClass = buf.length() == 0; | ||
| 125 | String className = null; | ||
| 126 | ClassMapping classMapping = mappingsChain.get(i); | ||
| 127 | if (classMapping != null) { | ||
| 128 | className = m_direction.choose( | ||
| 129 | classMapping.getDeobfName(), | ||
| 130 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() | ||
| 131 | ); | ||
| 132 | } | ||
| 133 | if (className == null) { | ||
| 134 | className = obfClassNames[i]; | ||
| 135 | } | ||
| 136 | if (!isFirstClass) { | ||
| 137 | buf.append("$"); | ||
| 138 | } | ||
| 139 | buf.append(className); | ||
| 97 | } | 140 | } |
| 141 | return new ClassEntry(buf.toString()); | ||
| 142 | |||
| 143 | } else { | ||
| 144 | |||
| 145 | // normal classes are easy | ||
| 146 | ClassMapping classMapping = m_classes.get(in.getName()); | ||
| 147 | if (classMapping == null) { | ||
| 148 | return in; | ||
| 149 | } | ||
| 150 | return m_direction.choose( | ||
| 151 | classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, | ||
| 152 | new ClassEntry(classMapping.getObfFullName()) | ||
| 153 | ); | ||
| 98 | } | 154 | } |
| 99 | |||
| 100 | return in; | ||
| 101 | } | 155 | } |
| 102 | 156 | ||
| 103 | public String translate(FieldEntry in) { | 157 | public String translate(FieldEntry in) { |
| @@ -226,14 +280,36 @@ public class Translator { | |||
| 226 | }); | 280 | }); |
| 227 | } | 281 | } |
| 228 | 282 | ||
| 229 | private ClassMapping findClassMapping(ClassEntry classEntry) { | 283 | private ClassMapping findClassMapping(ClassEntry in) { |
| 230 | ClassMapping classMapping = m_classes.get(classEntry.getOuterClassName()); | 284 | List<ClassMapping> mappingChain = getClassMappingChain(in); |
| 231 | if (classMapping != null && classEntry.isInnerClass()) { | 285 | return mappingChain.get(mappingChain.size() - 1); |
| 232 | classMapping = m_direction.choose( | 286 | } |
| 233 | classMapping.getInnerClassByObf(classEntry.getInnerClassName()), | 287 | |
| 234 | classMapping.getInnerClassByDeobfThenObf(classEntry.getInnerClassName()) | 288 | private List<ClassMapping> getClassMappingChain(ClassEntry in) { |
| 235 | ); | 289 | |
| 290 | // get a list of all the classes in the hierarchy | ||
| 291 | String[] parts = in.getName().split("\\$"); | ||
| 292 | List<ClassMapping> mappingsChain = Lists.newArrayList(); | ||
| 293 | |||
| 294 | // get mappings for the outer class | ||
| 295 | ClassMapping outerClassMapping = m_classes.get(parts[0]); | ||
| 296 | mappingsChain.add(outerClassMapping); | ||
| 297 | |||
| 298 | for (int i=1; i<parts.length; i++) { | ||
| 299 | |||
| 300 | // get mappings for the inner class | ||
| 301 | ClassMapping innerClassMapping = null; | ||
| 302 | if (outerClassMapping != null) { | ||
| 303 | innerClassMapping = m_direction.choose( | ||
| 304 | outerClassMapping.getInnerClassByObf(parts[i]), | ||
| 305 | outerClassMapping.getInnerClassByDeobfThenObf(parts[i]) | ||
| 306 | ); | ||
| 307 | } | ||
| 308 | mappingsChain.add(innerClassMapping); | ||
| 309 | outerClassMapping = innerClassMapping; | ||
| 236 | } | 310 | } |
| 237 | return classMapping; | 311 | |
| 312 | assert(mappingsChain.size() == parts.length); | ||
| 313 | return mappingsChain; | ||
| 238 | } | 314 | } |
| 239 | } | 315 | } |