summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2015-02-23 23:29:22 -0500
committerGravatar jeff2015-02-23 23:29:22 -0500
commit2dc7428e37bdd7a119f53d02ce157675509b0d63 (patch)
tree68f409ac726166e427eea3a199eb462130c53ccd /src
parentmake types serializable (diff)
downloadenigma-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.java43
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java4
-rw-r--r--src/cuchaz/enigma/MainFormatConverter.java2
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java6
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java67
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java43
-rw-r--r--src/cuchaz/enigma/convert/ClassMatcher.java6
-rw-r--r--src/cuchaz/enigma/mapping/ClassMapping.java41
-rw-r--r--src/cuchaz/enigma/mapping/EntryFactory.java35
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java14
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java2
-rw-r--r--src/cuchaz/enigma/mapping/MappingsWriter.java4
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java154
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
15import javassist.CtClass; 15import javassist.CtClass;
16import javassist.bytecode.ConstPool; 16import javassist.bytecode.ConstPool;
17import javassist.bytecode.Descriptor;
18import javassist.bytecode.EnclosingMethodAttribute; 17import javassist.bytecode.EnclosingMethodAttribute;
19import javassist.bytecode.InnerClassesAttribute; 18import javassist.bytecode.InnerClassesAttribute;
20import cuchaz.enigma.Constants;
21import cuchaz.enigma.analysis.JarIndex; 19import cuchaz.enigma.analysis.JarIndex;
22import cuchaz.enigma.mapping.BehaviorEntry; 20import cuchaz.enigma.mapping.BehaviorEntry;
23import cuchaz.enigma.mapping.ClassEntry; 21import cuchaz.enigma.mapping.ClassEntry;
22import cuchaz.enigma.mapping.EntryFactory;
24 23
25public class InnerClassWriter { 24public 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 ******************************************************************************/
11package cuchaz.enigma.mapping; 11package cuchaz.enigma.mapping;
12 12
13import java.util.List;
13import java.util.Map; 14import java.util.Map;
14 15
16import com.beust.jcommander.internal.Lists;
15import com.google.common.collect.Maps; 17import com.google.common.collect.Maps;
16 18
17import cuchaz.enigma.analysis.TranslationIndex; 19import 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}