summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/cuchaz/enigma/Constants.java1
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java38
-rw-r--r--src/main/java/cuchaz/enigma/TranslatingTypeLoader.java10
-rw-r--r--src/main/java/cuchaz/enigma/analysis/JarIndex.java23
-rw-r--r--src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java78
-rw-r--r--src/main/java/cuchaz/enigma/analysis/TranslationIndex.java2
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java42
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java25
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java39
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java18
-rw-r--r--src/main/java/cuchaz/enigma/convert/ClassIdentity.java2
-rw-r--r--src/main/java/cuchaz/enigma/convert/MappingsConverter.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java55
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java59
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java26
-rw-r--r--src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java5
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java11
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java5
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java2
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java116
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java104
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java42
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java47
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java78
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java37
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java12
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java19
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java42
32 files changed, 831 insertions, 132 deletions
diff --git a/src/main/java/cuchaz/enigma/Constants.java b/src/main/java/cuchaz/enigma/Constants.java
index 5bdb3af7..04730480 100644
--- a/src/main/java/cuchaz/enigma/Constants.java
+++ b/src/main/java/cuchaz/enigma/Constants.java
@@ -16,5 +16,4 @@ public class Constants {
16 public static final String URL = "http://www.cuchazinteractive.com/enigma"; 16 public static final String URL = "http://www.cuchazinteractive.com/enigma";
17 public static final int MiB = 1024 * 1024; // 1 mebibyte 17 public static final int MiB = 1024 * 1024; // 1 mebibyte
18 public static final int KiB = 1024; // 1 kebibyte 18 public static final int KiB = 1024; // 1 kebibyte
19 public static final String NONE_PACKAGE = "none";
20} 19}
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 20bb8b98..2766380b 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -146,7 +146,7 @@ public class Deobfuscator {
146 if (!deobfClassEntry.equals(obfClassEntry)) { 146 if (!deobfClassEntry.equals(obfClassEntry)) {
147 // if the class has a mapping, clearly it's deobfuscated 147 // if the class has a mapping, clearly it's deobfuscated
148 deobfClasses.add(deobfClassEntry); 148 deobfClasses.add(deobfClassEntry);
149 } else if (!obfClassEntry.getPackageName().equals(Constants.NONE_PACKAGE)) { 149 } else if (obfClassEntry.getPackageName() != null) {
150 // also call it deobufscated if it's not in the none package 150 // also call it deobufscated if it's not in the none package
151 deobfClasses.add(obfClassEntry); 151 deobfClasses.add(obfClassEntry);
152 } else { 152 } else {
@@ -532,12 +532,20 @@ public class Deobfuscator {
532 return false; 532 return false;
533 } else if (obfEntry instanceof ArgumentEntry) { 533 } else if (obfEntry instanceof ArgumentEntry) {
534 return translator.translate((ArgumentEntry) obfEntry) != null; 534 return translator.translate((ArgumentEntry) obfEntry) != null;
535 } else if (obfEntry instanceof LocalVariableEntry) {
536 // TODO: Implement it
537 //return translator.translate((LocalVariableEntry)obfEntry) != null;
538 return false;
535 } else { 539 } else {
536 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 540 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
537 } 541 }
538 } 542 }
539 543
540 public void rename(Entry obfEntry, String newName) { 544 public void rename(Entry obfEntry, String newName) {
545 rename(obfEntry, newName, true);
546 }
547
548 public void rename(Entry obfEntry, String newName, boolean clearCache) {
541 if (obfEntry instanceof ClassEntry) { 549 if (obfEntry instanceof ClassEntry) {
542 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); 550 this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName));
543 } else if (obfEntry instanceof FieldEntry) { 551 } else if (obfEntry instanceof FieldEntry) {
@@ -548,12 +556,15 @@ public class Deobfuscator {
548 throw new IllegalArgumentException("Cannot rename constructors"); 556 throw new IllegalArgumentException("Cannot rename constructors");
549 } else if (obfEntry instanceof ArgumentEntry) { 557 } else if (obfEntry instanceof ArgumentEntry) {
550 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); 558 this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName);
559 } else if (obfEntry instanceof LocalVariableEntry) {
560 // TODO: Implement it
551 } else { 561 } else {
552 throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); 562 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
553 } 563 }
554 564
555 // clear caches 565 // clear caches
556 this.translatorCache.clear(); 566 if (clearCache)
567 this.translatorCache.clear();
557 } 568 }
558 569
559 public void removeMapping(Entry obfEntry) { 570 public void removeMapping(Entry obfEntry) {
@@ -586,6 +597,8 @@ public class Deobfuscator {
586 throw new IllegalArgumentException("Cannot rename constructors"); 597 throw new IllegalArgumentException("Cannot rename constructors");
587 } else if (obfEntry instanceof ArgumentEntry) { 598 } else if (obfEntry instanceof ArgumentEntry) {
588 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); 599 this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry);
600 } else if (obfEntry instanceof LocalVariableEntry) {
601 // TODO: Implement it
589 } else { 602 } else {
590 throw new Error("Unknown entry type: " + obfEntry); 603 throw new Error("Unknown entry type: " + obfEntry);
591 } 604 }
@@ -593,4 +606,25 @@ public class Deobfuscator {
593 // clear caches 606 // clear caches
594 this.translatorCache.clear(); 607 this.translatorCache.clear();
595 } 608 }
609
610 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry)
611 {
612 Entry obfEntry = obfuscateEntry(entry);
613 if (obfEntry instanceof ClassEntry)
614 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
615 else if (obfEntry instanceof FieldEntry)
616 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
617 else if (obfEntry instanceof BehaviorEntry)
618 this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry);
619 else
620 throw new Error("Unknown entry type: " + obfEntry);
621 }
622
623 public Mappings.EntryModifier getModifier(Entry obEntry)
624 {
625 Entry entry = obfuscateEntry(obEntry);
626 if (entry != null)
627 obEntry = entry;
628 return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry);
629 }
596} 630}
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
index 13efbd51..73405662 100644
--- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java
@@ -187,10 +187,6 @@ public class TranslatingTypeLoader implements ITypeLoader {
187 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { 187 public List<String> getClassNamesToTry(ClassEntry obfClassEntry) {
188 List<String> classNamesToTry = Lists.newArrayList(); 188 List<String> classNamesToTry = Lists.newArrayList();
189 classNamesToTry.add(obfClassEntry.getName()); 189 classNamesToTry.add(obfClassEntry.getName());
190 if (obfClassEntry.getPackageName().equals(Constants.NONE_PACKAGE)) {
191 // taking off the none package, if any
192 classNamesToTry.add(obfClassEntry.getSimpleName());
193 }
194 if (obfClassEntry.isInnerClass()) { 190 if (obfClassEntry.isInnerClass()) {
195 // try just the inner class name 191 // try just the inner class name
196 classNamesToTry.add(obfClassEntry.getInnermostClassName()); 192 classNamesToTry.add(obfClassEntry.getInnermostClassName());
@@ -201,12 +197,8 @@ public class TranslatingTypeLoader implements ITypeLoader {
201 public CtClass transformClass(CtClass c) 197 public CtClass transformClass(CtClass c)
202 throws IOException, NotFoundException, CannotCompileException { 198 throws IOException, NotFoundException, CannotCompileException {
203 199
204 // we moved a lot of classes out of the default package into the none package
205 // make sure all the class references are consistent
206 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
207
208 // reconstruct inner classes 200 // reconstruct inner classes
209 new InnerClassWriter(this.jarIndex).write(c); 201 new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c);
210 202
211 // re-get the javassist handle since we changed class names 203 // re-get the javassist handle since we changed class names
212 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 204 ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
diff --git a/src/main/java/cuchaz/enigma/analysis/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
index e501540d..ee1dd544 100644
--- a/src/main/java/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/JarIndex.java
@@ -59,17 +59,10 @@ public class JarIndex {
59 public void indexJar(JarFile jar, boolean buildInnerClasses) { 59 public void indexJar(JarFile jar, boolean buildInnerClasses) {
60 60
61 // step 1: read the class names 61 // step 1: read the class names
62 for (ClassEntry classEntry : JarClassIterator.getClassEntries(jar)) { 62 this.obfClassEntries.addAll(JarClassIterator.getClassEntries(jar));
63 if (classEntry.isInDefaultPackage()) {
64 // move out of default package
65 classEntry = new ClassEntry(Constants.NONE_PACKAGE + "/" + classEntry.getName());
66 }
67 this.obfClassEntries.add(classEntry);
68 }
69 63
70 // step 2: index field/method/constructor access 64 // step 2: index field/method/constructor access
71 for (CtClass c : JarClassIterator.classes(jar)) { 65 for (CtClass c : JarClassIterator.classes(jar)) {
72 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
73 for (CtField field : c.getDeclaredFields()) { 66 for (CtField field : c.getDeclaredFields()) {
74 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); 67 FieldEntry fieldEntry = EntryFactory.getFieldEntry(field);
75 this.access.put(fieldEntry, Access.get(field)); 68 this.access.put(fieldEntry, Access.get(field));
@@ -84,7 +77,6 @@ public class JarIndex {
84 77
85 // step 3: index extends, implements, fields, and methods 78 // step 3: index extends, implements, fields, and methods
86 for (CtClass c : JarClassIterator.classes(jar)) { 79 for (CtClass c : JarClassIterator.classes(jar)) {
87 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
88 this.translationIndex.indexClass(c); 80 this.translationIndex.indexClass(c);
89 String className = Descriptor.toJvmName(c.getName()); 81 String className = Descriptor.toJvmName(c.getName());
90 for (String interfaceName : c.getClassFile().getInterfaces()) { 82 for (String interfaceName : c.getClassFile().getInterfaces()) {
@@ -101,7 +93,6 @@ public class JarIndex {
101 93
102 // step 4: index field, method, constructor references 94 // step 4: index field, method, constructor references
103 for (CtClass c : JarClassIterator.classes(jar)) { 95 for (CtClass c : JarClassIterator.classes(jar)) {
104 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
105 for (CtBehavior behavior : c.getDeclaredBehaviors()) { 96 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
106 indexBehaviorReferences(behavior); 97 indexBehaviorReferences(behavior);
107 } 98 }
@@ -111,7 +102,6 @@ public class JarIndex {
111 102
112 // step 5: index inner classes and anonymous classes 103 // step 5: index inner classes and anonymous classes
113 for (CtClass c : JarClassIterator.classes(jar)) { 104 for (CtClass c : JarClassIterator.classes(jar)) {
114 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NONE_PACKAGE);
115 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); 105 ClassEntry innerClassEntry = EntryFactory.getClassEntry(c);
116 ClassEntry outerClassEntry = findOuterClass(c); 106 ClassEntry outerClassEntry = findOuterClass(c);
117 if (outerClassEntry != null) { 107 if (outerClassEntry != null) {
@@ -781,6 +771,14 @@ public class JarIndex {
781 return this.access.containsKey(obfBehaviorEntry); 771 return this.access.containsKey(obfBehaviorEntry);
782 } 772 }
783 773
774 public boolean containsEntryWithSameName(Entry entry)
775 {
776 for (Entry target : this.access.keySet())
777 if (target.getName().equals(entry.getName()) && entry.getClass().isInstance(target.getClass()))
778 return true;
779 return false;
780 }
781
784 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) { 782 public boolean containsObfArgument(ArgumentEntry obfArgumentEntry) {
785 // check the behavior 783 // check the behavior
786 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) { 784 if (!containsObfBehavior(obfArgumentEntry.getBehaviorEntry())) {
@@ -801,6 +799,9 @@ public class JarIndex {
801 return containsObfBehavior((BehaviorEntry) obfEntry); 799 return containsObfBehavior((BehaviorEntry) obfEntry);
802 } else if (obfEntry instanceof ArgumentEntry) { 800 } else if (obfEntry instanceof ArgumentEntry) {
803 return containsObfArgument((ArgumentEntry) obfEntry); 801 return containsObfArgument((ArgumentEntry) obfEntry);
802 } else if (obfEntry instanceof LocalVariableEntry) {
803 // TODO: Implement it
804 return false;
804 } else { 805 } else {
805 throw new Error("Entry type not supported: " + obfEntry.getClass().getName()); 806 throw new Error("Entry type not supported: " + obfEntry.getClass().getName());
806 } 807 }
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
index c328f5f1..e690abdf 100644
--- a/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -10,6 +10,9 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import com.google.common.collect.HashMultimap;
14import com.google.common.collect.Multimap;
15import com.google.common.collect.Multimaps;
13import com.strobel.assembler.metadata.MemberReference; 16import com.strobel.assembler.metadata.MemberReference;
14import com.strobel.assembler.metadata.MethodReference; 17import com.strobel.assembler.metadata.MethodReference;
15import com.strobel.assembler.metadata.ParameterDefinition; 18import com.strobel.assembler.metadata.ParameterDefinition;
@@ -17,6 +20,7 @@ import com.strobel.assembler.metadata.TypeReference;
17import com.strobel.decompiler.languages.TextLocation; 20import com.strobel.decompiler.languages.TextLocation;
18import com.strobel.decompiler.languages.java.ast.*; 21import com.strobel.decompiler.languages.java.ast.*;
19import cuchaz.enigma.mapping.*; 22import cuchaz.enigma.mapping.*;
23import javassist.bytecode.Descriptor;
20 24
21import java.util.HashMap; 25import java.util.HashMap;
22import java.util.Map; 26import java.util.Map;
@@ -27,11 +31,14 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
27 31
28 // TODO: Really fix Procyon index problem with inner classes 32 // TODO: Really fix Procyon index problem with inner classes
29 private int argumentPosition; 33 private int argumentPosition;
30 private Map<String, Entry> argumentCache = new HashMap<>(); 34 private int localsPosition;
35 private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create();
36 private Map<String, Entry> identifierEntryCache = new HashMap<>();
31 37
32 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) { 38 public SourceIndexBehaviorVisitor(BehaviorEntry behaviorEntry) {
33 this.behaviorEntry = behaviorEntry; 39 this.behaviorEntry = behaviorEntry;
34 this.argumentPosition = 0; 40 this.argumentPosition = 0;
41 this.localsPosition = 0;
35 } 42 }
36 43
37 @Override 44 @Override
@@ -66,6 +73,9 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
66 } 73 }
67 } 74 }
68 75
76 // Check for identifier
77 node.getArguments().stream().filter(expression -> expression instanceof IdentifierExpression)
78 .forEach(expression -> this.checkIdentifier((IdentifierExpression) expression, index));
69 return recurse(node, index); 79 return recurse(node, index);
70 } 80 }
71 81
@@ -106,7 +116,7 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
106 argumentPosition++, node.getName()); 116 argumentPosition++, node.getName());
107 Identifier identifier = node.getNameToken(); 117 Identifier identifier = node.getNameToken();
108 // cache the argument entry and the identifier 118 // cache the argument entry and the identifier
109 argumentCache.put(identifier.getName(), argumentEntry); 119 identifierEntryCache.put(identifier.getName(), argumentEntry);
110 index.addDeclaration(identifier, argumentEntry); 120 index.addDeclaration(identifier, argumentEntry);
111 } 121 }
112 122
@@ -121,12 +131,31 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
121 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature())); 131 FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new Type(ref.getErasedSignature()));
122 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry); 132 index.addReference(node.getIdentifierToken(), fieldEntry, this.behaviorEntry);
123 } 133 }
124 else if (argumentCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token! 134 else
125 index.addDeclaration(node.getIdentifierToken(), argumentCache.get(node.getIdentifier())); 135 this.checkIdentifier(node, index);
126
127 return recurse(node, index); 136 return recurse(node, index);
128 } 137 }
129 138
139 private void checkIdentifier(IdentifierExpression node, SourceIndex index)
140 {
141 if (identifierEntryCache.containsKey(node.getIdentifier())) // If it's in the argument cache, create a token!
142 index.addDeclaration(node.getIdentifierToken(), identifierEntryCache.get(node.getIdentifier()));
143 else
144 unmatchedIdentifier.put(node.getIdentifier(), node.getIdentifierToken()); // Not matched actually, put it!
145 }
146
147 private void addDeclarationToUnmatched(String key, SourceIndex index)
148 {
149 Entry entry = identifierEntryCache.get(key);
150
151 // This cannot happened in theory
152 if (entry == null)
153 return;
154 for (Identifier identifier : unmatchedIdentifier.get(key))
155 index.addDeclaration(identifier, entry);
156 unmatchedIdentifier.removeAll(key);
157 }
158
130 @Override 159 @Override
131 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { 160 public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) {
132 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); 161 MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE);
@@ -141,4 +170,43 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
141 170
142 return recurse(node, index); 171 return recurse(node, index);
143 } 172 }
173
174 @Override
175 public Void visitForEachStatement(ForEachStatement node, SourceIndex index) {
176 if (node.getVariableType() instanceof SimpleType)
177 {
178 SimpleType type = (SimpleType) node.getVariableType();
179 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
180 Identifier identifier = node.getVariableNameToken();
181 String signature = Descriptor.of(typeReference.getErasedDescription());
182 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, identifier.getName(), new Type(signature));
183 identifierEntryCache.put(identifier.getName(), localVariableEntry);
184 addDeclarationToUnmatched(identifier.getName(), index);
185 index.addDeclaration(identifier, localVariableEntry);
186 }
187 return recurse(node, index);
188 }
189
190 @Override
191 public Void visitVariableDeclaration(VariableDeclarationStatement node, SourceIndex index) {
192 AstNodeCollection<VariableInitializer> variables = node.getVariables();
193
194 // Single assignation
195 if (variables.size() == 1)
196 {
197 VariableInitializer initializer = variables.firstOrNullObject();
198 if (initializer != null && node.getType() instanceof SimpleType)
199 {
200 SimpleType type = (SimpleType) node.getType();
201 TypeReference typeReference = type.getUserData(Keys.TYPE_REFERENCE);
202 String signature = Descriptor.of(typeReference.getErasedDescription());
203 Identifier identifier = initializer.getNameToken();
204 LocalVariableEntry localVariableEntry = new LocalVariableEntry(behaviorEntry, localsPosition++, initializer.getName(), new Type(signature));
205 identifierEntryCache.put(identifier.getName(), localVariableEntry);
206 addDeclarationToUnmatched(identifier.getName(), index);
207 index.addDeclaration(identifier, localVariableEntry);
208 }
209 }
210 return recurse(node, index);
211 }
144} 212}
diff --git a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
index 25edaef4..d51131f6 100644
--- a/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/TranslationIndex.java
@@ -181,6 +181,8 @@ public class TranslationIndex {
181 return behaviorExists((BehaviorEntry) entry); 181 return behaviorExists((BehaviorEntry) entry);
182 } else if (entry instanceof ArgumentEntry) { 182 } else if (entry instanceof ArgumentEntry) {
183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry()); 183 return behaviorExists(((ArgumentEntry) entry).getBehaviorEntry());
184 } else if (entry instanceof LocalVariableEntry) {
185 return behaviorExists(((LocalVariableEntry) entry).getBehaviorEntry());
184 } 186 }
185 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass()); 187 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
186 } 188 }
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
index eb7e9a17..d49f13ab 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -19,8 +19,9 @@ import java.util.Map;
19 19
20import cuchaz.enigma.mapping.ClassEntry; 20import cuchaz.enigma.mapping.ClassEntry;
21import cuchaz.enigma.mapping.ClassNameReplacer; 21import cuchaz.enigma.mapping.ClassNameReplacer;
22import cuchaz.enigma.mapping.Mappings;
22import cuchaz.enigma.mapping.Translator; 23import cuchaz.enigma.mapping.Translator;
23import javassist.CtClass; 24import javassist.*;
24import javassist.bytecode.*; 25import javassist.bytecode.*;
25import javassist.bytecode.SignatureAttribute.*; 26import javassist.bytecode.SignatureAttribute.*;
26 27
@@ -70,6 +71,41 @@ public class ClassRenamer {
70 } 71 }
71 } 72 }
72 73
74 public static void applyModifier(Object obj, Mappings.EntryModifier modifier)
75 {
76 int mod = -1;
77 if (obj instanceof CtField)
78 mod = ((CtField) obj).getModifiers();
79 else if (obj instanceof CtBehavior)
80 mod = ((CtBehavior) obj).getModifiers();
81 else if (obj instanceof CtClass)
82 mod = ((CtClass) obj).getModifiers();
83
84 if (mod != -1)
85 {
86 switch (modifier)
87 {
88 case PRIVATE:
89 mod = Modifier.setPrivate(mod);
90 break;
91 case PROTECTED:
92 mod = Modifier.setProtected(mod);
93 break;
94 case PUBLIC:
95 mod = Modifier.setPublic(mod);
96 break;
97 default:
98 break;
99 }
100 if (obj instanceof CtField)
101 ((CtField) obj).setModifiers(mod);
102 else if (obj instanceof CtBehavior)
103 ((CtBehavior) obj).setModifiers(mod);
104 else
105 ((CtClass) obj).setModifiers(mod);
106 }
107 }
108
73 public static void renameClasses(CtClass c, final Translator translator) { 109 public static void renameClasses(CtClass c, final Translator translator) {
74 renameClasses(c, className -> { 110 renameClasses(c, className -> {
75 ClassEntry entry = translator.translateEntry(new ClassEntry(className)); 111 ClassEntry entry = translator.translateEntry(new ClassEntry(className));
@@ -110,6 +146,7 @@ public class ClassRenamer {
110 146
111 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) 147 // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo)
112 ConstPool constPool = c.getClassFile().getConstPool(); 148 ConstPool constPool = c.getClassFile().getConstPool();
149 String className = constPool.getClassName();
113 constPool.renameClass(map); 150 constPool.renameClass(map);
114 151
115 // rename class attributes 152 // rename class attributes
@@ -140,8 +177,9 @@ public class ClassRenamer {
140 if (attr != null) { 177 if (attr != null) {
141 for (int i = 0; i < attr.tableLength(); i++) { 178 for (int i = 0; i < attr.tableLength(); i++) {
142 179
180 String innerName = attr.innerClass(i);
143 // get the inner class full name (which has already been translated) 181 // get the inner class full name (which has already been translated)
144 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); 182 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
145 183
146 if (attr.innerNameIndex(i) != 0) { 184 if (attr.innerNameIndex(i) != 0) {
147 // update the inner name 185 // update the inner name
diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
index 6c05b838..62ebfafb 100644
--- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -11,14 +11,9 @@
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import cuchaz.enigma.mapping.*; 13import cuchaz.enigma.mapping.*;
14import javassist.CtBehavior; 14import cuchaz.enigma.mapping.Translator;
15import javassist.CtClass; 15import javassist.*;
16import javassist.CtField; 16import javassist.bytecode.*;
17import javassist.CtMethod;
18import javassist.bytecode.ConstPool;
19import javassist.bytecode.Descriptor;
20import javassist.bytecode.EnclosingMethodAttribute;
21import javassist.bytecode.SourceFileAttribute;
22 17
23public class ClassTranslator { 18public class ClassTranslator {
24 19
@@ -74,6 +69,9 @@ public class ClassTranslator {
74 } 69 }
75 70
76 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); 71 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
72 Mappings.EntryModifier modifier = this.translator.getModifier(classEntry);
73 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
74 ClassRenamer.applyModifier(c, modifier);
77 75
78 // translate all the fields 76 // translate all the fields
79 for (CtField field : c.getDeclaredFields()) { 77 for (CtField field : c.getDeclaredFields()) {
@@ -81,6 +79,10 @@ public class ClassTranslator {
81 // translate the name 79 // translate the name
82 FieldEntry entry = EntryFactory.getFieldEntry(field); 80 FieldEntry entry = EntryFactory.getFieldEntry(field);
83 String translatedName = this.translator.translate(entry); 81 String translatedName = this.translator.translate(entry);
82 modifier = this.translator.getModifier(entry);
83 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
84 ClassRenamer.applyModifier(field, modifier);
85
84 if (translatedName != null) { 86 if (translatedName != null) {
85 field.setName(translatedName); 87 field.setName(translatedName);
86 } 88 }
@@ -95,6 +97,10 @@ public class ClassTranslator {
95 97
96 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); 98 BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior);
97 99
100 modifier = this.translator.getModifier(entry);
101 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
102 ClassRenamer.applyModifier(behavior, modifier);
103
98 if (behavior instanceof CtMethod) { 104 if (behavior instanceof CtMethod) {
99 CtMethod method = (CtMethod) behavior; 105 CtMethod method = (CtMethod) behavior;
100 106
@@ -149,5 +155,8 @@ public class ClassTranslator {
149 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; 155 String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java";
150 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); 156 c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile));
151 } 157 }
158 InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag);
159 if (attr != null)
160 InnerClassWriter.changeModifier(c, attr, translator);
152 } 161 }
153} 162}
diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
index 6d926106..6e2a29d5 100644
--- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
+++ b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -15,22 +15,22 @@ import com.google.common.collect.Lists;
15import java.util.Collection; 15import java.util.Collection;
16import java.util.List; 16import java.util.List;
17 17
18import cuchaz.enigma.Deobfuscator;
18import cuchaz.enigma.analysis.JarIndex; 19import cuchaz.enigma.analysis.JarIndex;
19import cuchaz.enigma.mapping.BehaviorEntry; 20import cuchaz.enigma.mapping.*;
20import cuchaz.enigma.mapping.ClassEntry; 21import javassist.ClassPool;
21import cuchaz.enigma.mapping.EntryFactory;
22import javassist.CtClass; 22import javassist.CtClass;
23import javassist.bytecode.AccessFlag; 23import javassist.NotFoundException;
24import javassist.bytecode.ConstPool; 24import javassist.bytecode.*;
25import javassist.bytecode.EnclosingMethodAttribute;
26import javassist.bytecode.InnerClassesAttribute;
27 25
28public class InnerClassWriter { 26public class InnerClassWriter {
29 27
30 private JarIndex index; 28 private JarIndex index;
29 private Translator deobfuscatorTranslator;
31 30
32 public InnerClassWriter(JarIndex index) { 31 public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) {
33 this.index = index; 32 this.index = index;
33 this.deobfuscatorTranslator = deobfuscatorTranslator;
34 } 34 }
35 35
36 public void write(CtClass c) { 36 public void write(CtClass c) {
@@ -96,6 +96,29 @@ public class InnerClassWriter {
96 } 96 }
97 } 97 }
98 98
99 // FIXME: modiffier is not applied to inner class
100 public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator)
101 {
102 ClassPool pool = c.getClassPool();
103 for (int i = 0; i < attr.tableLength(); i++) {
104
105 String innerName = attr.innerClass(i);
106 // get the inner class full name (which has already been translated)
107 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName));
108 try
109 {
110 CtClass innerClass = pool.get(innerName);
111 Mappings.EntryModifier modifier = translator.getModifier(classEntry);
112 if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED)
113 ClassRenamer.applyModifier(innerClass, modifier);
114 } catch (NotFoundException e)
115 {
116 // This shouldn't be possible in theory
117 //e.printStackTrace();
118 }
119 }
120 }
121
99 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { 122 private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) {
100 123
101 // get the new inner class name 124 // get the new inner class name
diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
index 3f4b96f9..24b5f363 100644
--- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
+++ b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java
@@ -76,11 +76,27 @@ public class LocalVariableRenamer {
76 int numArgs = 0; 76 int numArgs = 0;
77 if (behaviorEntry.getSignature() != null) { 77 if (behaviorEntry.getSignature() != null) {
78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); 78 numArgs = behaviorEntry.getSignature().getArgumentTypes().size();
79
80 boolean isNestedClassConstructor = false;
81
82 // If the behavior is a constructor and if it have more than one arg, it's probably from a nested!
83 if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1)
84 {
85 // Get the first arg type
86 Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0);
87
88 // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class
89 if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) {
90 isNestedClassConstructor = true;
91 numArgs--;
92 }
93 }
94
79 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { 95 for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) {
80 int argi = i - starti; 96 int argi = i - starti;
81 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); 97 String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, ""));
82 if (argName == null) { 98 if (argName == null) {
83 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argi); 99 Type argType = behaviorEntry.getSignature().getArgumentTypes().get(isNestedClassConstructor ? argi + 1 : argi);
84 // Unfortunately each of these have different name getters, so they have different code paths 100 // Unfortunately each of these have different name getters, so they have different code paths
85 if (argType.isPrimitive()) { 101 if (argType.isPrimitive()) {
86 Type.Primitive argCls = argType.getPrimitive(); 102 Type.Primitive argCls = argType.getPrimitive();
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
index 0960c86b..73600114 100644
--- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
+++ b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java
@@ -58,7 +58,7 @@ public class ClassIdentity {
58 58
59 // classes not in the none package can be passed through 59 // classes not in the none package can be passed through
60 ClassEntry classEntry = new ClassEntry(className); 60 ClassEntry classEntry = new ClassEntry(className);
61 if (!classEntry.getPackageName().equals(Constants.NONE_PACKAGE)) { 61 if (classEntry.getPackageName() != null) {
62 return className; 62 return className;
63 } 63 }
64 64
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
index 3d2cb86a..929c89f2 100644
--- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
@@ -243,7 +243,7 @@ public class MappingsConverter {
243 243
244 // non obfuscated classes can be migrated 244 // non obfuscated classes can be migrated
245 ClassEntry classEntry = oldObfType.getClassEntry(); 245 ClassEntry classEntry = oldObfType.getClassEntry();
246 if (!classEntry.getPackageName().equals(Constants.NONE_PACKAGE)) { 246 if (classEntry.getPackageName() != null) {
247 return true; 247 return true;
248 } 248 }
249 249
diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
index 3cd1705a..edf1e30b 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
@@ -131,7 +131,7 @@ public class ClassMatchingGui {
131 sourceTypePanel.add(button); 131 sourceTypePanel.add(button);
132 } 132 }
133 133
134 m_sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR); 134 m_sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
135 m_sourceClasses.setSelectionListener(this::setSourceClass); 135 m_sourceClasses.setSelectionListener(this::setSourceClass);
136 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); 136 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
137 sourcePanel.add(sourceScroller); 137 sourcePanel.add(sourceScroller);
@@ -147,7 +147,7 @@ public class ClassMatchingGui {
147 destPanel.add(m_top10Matches); 147 destPanel.add(m_top10Matches);
148 m_top10Matches.addActionListener(event -> toggleTop10Matches()); 148 m_top10Matches.addActionListener(event -> toggleTop10Matches());
149 149
150 m_destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR); 150 m_destClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
151 m_destClasses.setSelectionListener(this::setDestClass); 151 m_destClasses.setSelectionListener(this::setDestClass);
152 JScrollPane destScroller = new JScrollPane(m_destClasses); 152 JScrollPane destScroller = new JScrollPane(m_destClasses);
153 destPanel.add(destScroller); 153 destPanel.add(destScroller);
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index 92fcaba0..435509e5 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -44,7 +44,7 @@ public class ClassSelector extends JTree {
44 private RenameSelectionListener renameSelectionListener; 44 private RenameSelectionListener renameSelectionListener;
45 private Comparator<ClassEntry> comparator; 45 private Comparator<ClassEntry> comparator;
46 46
47 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator) { 47 public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) {
48 this.comparator = comparator; 48 this.comparator = comparator;
49 49
50 // configure the tree control 50 // configure the tree control
@@ -77,7 +77,7 @@ public class ClassSelector extends JTree {
77 { 77 {
78 @Override public boolean isCellEditable(EventObject event) 78 @Override public boolean isCellEditable(EventObject event)
79 { 79 {
80 return !(event instanceof MouseEvent) && super.isCellEditable(event); 80 return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event);
81 } 81 }
82 }; 82 };
83 this.setCellEditor(editor); 83 this.setCellEditor(editor);
@@ -112,7 +112,7 @@ public class ClassSelector extends JTree {
112 try 112 try
113 { 113 {
114 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); 114 renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node);
115 node.setUserObject(objectData); 115 node.setUserObject(objectData); // Make sure that it's modified
116 } catch (IllegalNameException ex) 116 } catch (IllegalNameException ex)
117 { 117 {
118 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, 118 JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION,
@@ -181,6 +181,10 @@ public class ClassSelector extends JTree {
181 // I can never keep this rule straight when writing these damn things... 181 // I can never keep this rule straight when writing these damn things...
182 // a < b => -1, a == b => 0, a > b => +1 182 // a < b => -1, a == b => 0, a > b => +1
183 183
184 if(b == null || a == null){
185 return 0;
186 }
187
184 String[] aparts = a.split("/"); 188 String[] aparts = a.split("/");
185 String[] bparts = b.split("/"); 189 String[] bparts = b.split("/");
186 for (int i = 0; true; i++) { 190 for (int i = 0; true; i++) {
@@ -447,6 +451,51 @@ public class ClassSelector extends JTree {
447 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); 451 model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode));
448 } 452 }
449 453
454 public void reload()
455 {
456 DefaultTreeModel model = (DefaultTreeModel) getModel();
457 model.reload(sort(rootNodes));
458 }
459
460 private DefaultMutableTreeNode sort(DefaultMutableTreeNode node) {
461
462 for(int i = 0; i < node.getChildCount() - 1; i++) {
463 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
464 if (child == null)
465 continue;
466 String nt = child.toString();
467
468 for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
469 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
470 if (prevNode == null || prevNode.getUserObject() == null)
471 continue;
472 String np = prevNode.getUserObject().toString();
473
474 if(nt.compareToIgnoreCase(np) > 0) {
475 node.insert(child, j);
476 node.insert(prevNode, i);
477 }
478 }
479 if(child.getChildCount() > 0) {
480 sort(child);
481 }
482 }
483
484 for(int i = 0; i < node.getChildCount() - 1; i++) {
485 DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
486 for(int j = i + 1; j <= node.getChildCount() - 1; j++) {
487 DefaultMutableTreeNode prevNode = (DefaultMutableTreeNode) node.getChildAt(j);
488
489 if(!prevNode.isLeaf() && child.isLeaf()) {
490 node.insert(child, j);
491 node.insert(prevNode, i);
492 }
493 }
494 }
495
496 return node;
497 }
498
450 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode) 499 private int getPlacementIndex(ClassSelectorPackageNode newPackageNode, ClassSelectorClassNode classNode)
451 { 500 {
452 List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode); 501 List<ClassSelectorClassNode> classNodes = classNodes(newPackageNode);
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 0ccb3f79..ed18777a 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -136,6 +136,9 @@ public class Gui {
136 m_selectionHighlightPainter = new SelectionHighlightPainter(); 136 m_selectionHighlightPainter = new SelectionHighlightPainter();
137 this.editor = new PanelEditor(this); 137 this.editor = new PanelEditor(this);
138 JScrollPane sourceScroller = new JScrollPane(this.editor); 138 JScrollPane sourceScroller = new JScrollPane(this.editor);
139 this.editor.setContentType("text/java");
140 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit();
141 kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker");
139 142
140 // init editor popup menu 143 // init editor popup menu
141 this.popupMenu = new PopupMenuBar(this); 144 this.popupMenu = new PopupMenuBar(this);
@@ -439,6 +442,8 @@ public class Gui {
439 showConstructorEntry((ConstructorEntry) m_reference.entry); 442 showConstructorEntry((ConstructorEntry) m_reference.entry);
440 } else if (m_reference.entry instanceof ArgumentEntry) { 443 } else if (m_reference.entry instanceof ArgumentEntry) {
441 showArgumentEntry((ArgumentEntry) m_reference.entry); 444 showArgumentEntry((ArgumentEntry) m_reference.entry);
445 } else if (m_reference.entry instanceof LocalVariableEntry) {
446 showLocalVariableEntry((LocalVariableEntry) m_reference.entry);
442 } else { 447 } else {
443 throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName()); 448 throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName());
444 } 449 }
@@ -446,26 +451,39 @@ public class Gui {
446 redraw(); 451 redraw();
447 } 452 }
448 453
454 private void showLocalVariableEntry(LocalVariableEntry entry) {
455 addNameValue(m_infoPanel, "Variable", entry.getName());
456 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
457 addNameValue(m_infoPanel, "Method", entry.getBehaviorEntry().getName());
458 addNameValue(m_infoPanel, "Index", Integer.toString(entry.getIndex()));
459 addNameValue(m_infoPanel, "Type", entry.getType().toString());
460 }
461
449 private void showClassEntry(ClassEntry entry) { 462 private void showClassEntry(ClassEntry entry) {
450 addNameValue(m_infoPanel, "Class", entry.getName()); 463 addNameValue(m_infoPanel, "Class", entry.getName());
464 addModifierComboBox(m_infoPanel, "Modifier", entry);
451 } 465 }
452 466
453 private void showFieldEntry(FieldEntry entry) { 467 private void showFieldEntry(FieldEntry entry) {
454 addNameValue(m_infoPanel, "Field", entry.getName()); 468 addNameValue(m_infoPanel, "Field", entry.getName());
455 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); 469 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
456 addNameValue(m_infoPanel, "Type", entry.getType().toString()); 470 addNameValue(m_infoPanel, "Type", entry.getType().toString());
471 addModifierComboBox(m_infoPanel, "Modifier", entry);
457 } 472 }
458 473
459 private void showMethodEntry(MethodEntry entry) { 474 private void showMethodEntry(MethodEntry entry) {
460 addNameValue(m_infoPanel, "Method", entry.getName()); 475 addNameValue(m_infoPanel, "Method", entry.getName());
461 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); 476 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
462 addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); 477 addNameValue(m_infoPanel, "Signature", entry.getSignature().toString());
478 addModifierComboBox(m_infoPanel, "Modifier", entry);
479
463 } 480 }
464 481
465 private void showConstructorEntry(ConstructorEntry entry) { 482 private void showConstructorEntry(ConstructorEntry entry) {
466 addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); 483 addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName());
467 if (!entry.isStatic()) { 484 if (!entry.isStatic()) {
468 addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); 485 addNameValue(m_infoPanel, "Signature", entry.getSignature().toString());
486 addModifierComboBox(m_infoPanel, "Modifier", entry);
469 } 487 }
470 } 488 }
471 489
@@ -488,6 +506,25 @@ public class Gui {
488 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); 506 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT)));
489 } 507 }
490 508
509 private JComboBox<Mappings.EntryModifier> addModifierComboBox(JPanel container, String name, Entry entry)
510 {
511 if (!getController().entryIsInJar(entry))
512 return null;
513 JPanel panel = new JPanel();
514 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0));
515 container.add(panel);
516 JLabel label = new JLabel(name + ":", JLabel.RIGHT);
517 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
518 panel.add(label);
519 JComboBox<Mappings.EntryModifier> combo = new JComboBox<>(Mappings.EntryModifier.values());
520 ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT);
521 combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
522 combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal());
523 combo.addItemListener(getController()::modifierChange);
524 panel.add(combo);
525 return combo;
526 }
527
491 public void onCaretMove(int pos) { 528 public void onCaretMove(int pos) {
492 529
493 Token token = this.controller.getToken(pos); 530 Token token = this.controller.getToken(pos);
@@ -574,8 +611,7 @@ public class Gui {
574 611
575 int offset = text.getText().lastIndexOf('/') + 1; 612 int offset = text.getText().lastIndexOf('/') + 1;
576 // If it's a class and isn't in the default package, assume that it's deobfuscated. 613 // If it's a class and isn't in the default package, assume that it's deobfuscated.
577 if (m_reference.getNameableEntry() instanceof ClassEntry && !text.getText().startsWith(Constants.NONE_PACKAGE) 614 if (m_reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0)
578 && offset != 0)
579 text.select(offset, text.getText().length()); 615 text.select(offset, text.getText().length());
580 else 616 else
581 text.selectAll(); 617 text.selectAll();
@@ -766,21 +802,24 @@ public class Gui {
766 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); 802 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
767 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 803 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
768 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 804 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
769 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false); 805 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount());
806 childNode.setUserObject(dataChild);
770 } 807 }
808 node.setUserObject(data);
809 // Ob package will never be modified, just reload deob view
810 this.deobfPanel.deobfClasses.reload();
771 } 811 }
772 // class rename 812 // class rename
773 else if (data instanceof ClassEntry) 813 else if (data instanceof ClassEntry)
774 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false); 814 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true);
775 } 815 }
776 816
777 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName) 817 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName)
778 { 818 {
779 String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); 819 String oldEntry = deobfReference.entry.getClassEntry().getPackageName();
780 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); 820 String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName();
781 if (oldEntry != null && newEntry != null) 821 moveClassTree(deobfReference, newName, oldEntry == null,
782 moveClassTree(deobfReference, newName, oldEntry.equals(Constants.NONE_PACKAGE), 822 newEntry == null);
783 newEntry.equals(Constants.NONE_PACKAGE));
784 } 823 }
785 824
786 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb) 825 public void moveClassTree(EntryReference<Entry, Entry> deobfReference, String newName, boolean isOldOb, boolean isNewOb)
@@ -795,6 +834,8 @@ public class Gui {
795 ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); 834 ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry);
796 this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); 835 this.obfPanel.obfClasses.removeNode(packageNode, oldEntry);
797 this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); 836 this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode);
837 this.deobfPanel.deobfClasses.reload();
838 this.obfPanel.obfClasses.reload();
798 } 839 }
799 // Deob -> ob 840 // Deob -> ob
800 else if (isNewOb && !isOldOb) 841 else if (isNewOb && !isOldOb)
@@ -803,17 +844,21 @@ public class Gui {
803 ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); 844 ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry);
804 this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); 845 this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry);
805 this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); 846 this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode);
847 this.deobfPanel.deobfClasses.reload();
848 this.obfPanel.obfClasses.reload();
806 } 849 }
807 // Local move 850 // Local move
808 else if (isOldOb) 851 else if (isOldOb)
809 { 852 {
810 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); 853 this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null);
811 this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); 854 this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry));
855 this.obfPanel.obfClasses.reload();
812 } 856 }
813 else 857 else
814 { 858 {
815 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); 859 this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null);
816 this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); 860 this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry));
861 this.deobfPanel.deobfClasses.reload();
817 } 862 }
818 } 863 }
819} 864}
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 3188ff02..c2e202e2 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -20,6 +20,7 @@ import cuchaz.enigma.mapping.*;
20import cuchaz.enigma.throwables.MappingParseException; 20import cuchaz.enigma.throwables.MappingParseException;
21import cuchaz.enigma.utils.ReadableToken; 21import cuchaz.enigma.utils.ReadableToken;
22 22
23import java.awt.event.ItemEvent;
23import java.io.File; 24import java.io.File;
24import java.io.IOException; 25import java.io.IOException;
25import java.util.Collection; 26import java.util.Collection;
@@ -193,16 +194,16 @@ public class GuiController {
193 } 194 }
194 195
195 public void rename(EntryReference<Entry, Entry> deobfReference, String newName) { 196 public void rename(EntryReference<Entry, Entry> deobfReference, String newName) {
196 rename(deobfReference, newName, true); 197 rename(deobfReference, newName, true, true);
197 } 198 }
198 199
199 public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree) 200 public void rename(EntryReference<Entry, Entry> deobfReference, String newName, boolean refreshClassTree, boolean clearTranslationCache)
200 { 201 {
201 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 202 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
202 this.deobfuscator.rename(obfReference.getNameableEntry(), newName); 203 this.deobfuscator.rename(obfReference.getNameableEntry(), newName, clearTranslationCache);
203 this.isDirty = true; 204 this.isDirty = true;
204 205
205 if (refreshClassTree && deobfReference.entry instanceof ClassEntry) 206 if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
206 this.gui.moveClassTree(deobfReference, newName); 207 this.gui.moveClassTree(deobfReference, newName);
207 refreshCurrentClass(obfReference); 208 refreshCurrentClass(obfReference);
208 209
@@ -221,7 +222,7 @@ public class GuiController {
221 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference); 222 EntryReference<Entry, Entry> obfReference = this.deobfuscator.obfuscateReference(deobfReference);
222 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); 223 this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry());
223 this.isDirty = true; 224 this.isDirty = true;
224 if (deobfReference.entry instanceof ClassEntry) 225 if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass())
225 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); 226 this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false);
226 refreshCurrentClass(obfReference); 227 refreshCurrentClass(obfReference);
227 } 228 }
@@ -338,4 +339,19 @@ public class GuiController {
338 } 339 }
339 }.start(); 340 }.start();
340 } 341 }
342
343 public Deobfuscator getDeobfuscator()
344 {
345 return deobfuscator;
346 }
347
348 public void modifierChange(ItemEvent event)
349 {
350 if (event.getStateChange() == ItemEvent.SELECTED)
351 {
352 deobfuscator.changeModifier(gui.m_reference.entry, (Mappings.EntryModifier) event.getItem());
353 this.isDirty = true;
354 refreshCurrentClass();
355 }
356 }
341} 357}
diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
index 50550910..ecc280d5 100644
--- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
+++ b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
@@ -138,7 +138,7 @@ public class MemberMatchingGui<T extends Entry> {
138 sourceTypePanel.add(button); 138 sourceTypePanel.add(button);
139 } 139 }
140 140
141 m_sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR); 141 m_sourceClasses = new ClassSelector(null, ClassSelector.DEOBF_CLASS_COMPARATOR, false);
142 m_sourceClasses.setSelectionListener(this::setSourceClass); 142 m_sourceClasses.setSelectionListener(this::setSourceClass);
143 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses); 143 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
144 classesPanel.add(sourceScroller); 144 classesPanel.add(sourceScroller);
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index 1c9dad4b..83418267 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -49,6 +49,11 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode {
49 super.setUserObject(classEntry); 49 super.setUserObject(classEntry);
50 } 50 }
51 51
52 @Override public Object getUserObject()
53 {
54 return classEntry;
55 }
56
52 public boolean equals(ClassSelectorClassNode other) { 57 public boolean equals(ClassSelectorClassNode other) {
53 return this.classEntry.equals(other.classEntry); 58 return this.classEntry.equals(other.classEntry);
54 } 59 }
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index d2e421e7..31b4ebfa 100644
--- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -19,7 +19,7 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
19 private String packageName; 19 private String packageName;
20 20
21 public ClassSelectorPackageNode(String packageName) { 21 public ClassSelectorPackageNode(String packageName) {
22 this.packageName = packageName; 22 this.packageName = packageName != null ? packageName : "(none)";
23 } 23 }
24 24
25 public String getPackageName() { 25 public String getPackageName() {
@@ -33,9 +33,14 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
33 super.setUserObject(userObject); 33 super.setUserObject(userObject);
34 } 34 }
35 35
36 @Override public Object getUserObject()
37 {
38 return packageName;
39 }
40
36 @Override 41 @Override
37 public String toString() { 42 public String toString() {
38 return Descriptor.toJavaName(this.packageName); 43 return !packageName.equals("(none)") ? Descriptor.toJavaName(this.packageName) : "(none)";
39 } 44 }
40 45
41 @Override 46 @Override
@@ -44,6 +49,6 @@ public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
44 } 49 }
45 50
46 public boolean equals(ClassSelectorPackageNode other) { 51 public boolean equals(ClassSelectorPackageNode other) {
47 return this.packageName.equals(other.packageName); 52 return other != null && this.packageName.equals(other.packageName);
48 } 53 }
49} 54}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
index 447c51af..4f551756 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
@@ -17,7 +17,7 @@ public class PanelDeobf extends JPanel {
17 public PanelDeobf(Gui gui) { 17 public PanelDeobf(Gui gui) {
18 this.gui = gui; 18 this.gui = gui;
19 19
20 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR); 20 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true);
21 this.deobfClasses.setSelectionListener(gui::navigateTo); 21 this.deobfClasses.setSelectionListener(gui::navigateTo);
22 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); 22 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename);
23 23
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
index cf3c4e93..914952b9 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
@@ -10,7 +10,6 @@ import javax.swing.JEditorPane;
10 10
11import cuchaz.enigma.gui.BrowserCaret; 11import cuchaz.enigma.gui.BrowserCaret;
12import cuchaz.enigma.gui.Gui; 12import cuchaz.enigma.gui.Gui;
13import de.sciss.syntaxpane.DefaultSyntaxKit;
14 13
15public class PanelEditor extends JEditorPane { 14public class PanelEditor extends JEditorPane {
16 private final Gui gui; 15 private final Gui gui;
@@ -21,7 +20,6 @@ public class PanelEditor extends JEditorPane {
21 this.setEditable(false); 20 this.setEditable(false);
22 this.setSelectionColor(new Color(31, 46, 90)); 21 this.setSelectionColor(new Color(31, 46, 90));
23 this.setCaret(new BrowserCaret()); 22 this.setCaret(new BrowserCaret());
24 this.setContentType("text/java");
25 this.addCaretListener(event -> gui.onCaretMove(event.getDot())); 23 this.addCaretListener(event -> gui.onCaretMove(event.getDot()));
26 final PanelEditor self = this; 24 final PanelEditor self = this;
27 this.addMouseListener(new MouseAdapter() 25 this.addMouseListener(new MouseAdapter()
@@ -71,8 +69,5 @@ public class PanelEditor extends JEditorPane {
71 } 69 }
72 } 70 }
73 }); 71 });
74
75 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.getEditorKit();
76 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker");
77 } 72 }
78} 73}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 74772a5f..27bb70b4 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -28,7 +28,7 @@ public class PanelObf extends JPanel {
28 return aname.compareTo(bname); 28 return aname.compareTo(bname);
29 }; 29 };
30 30
31 this.obfClasses = new ClassSelector(gui, obfClassComparator); 31 this.obfClasses = new ClassSelector(gui, obfClassComparator, false);
32 this.obfClasses.setSelectionListener(gui::navigateTo); 32 this.obfClasses.setSelectionListener(gui::navigateTo);
33 this.obfClasses.setRenameSelectionListener(gui::onPanelRename); 33 this.obfClasses.setRenameSelectionListener(gui::onPanelRename);
34 34
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index 36b35f73..8f893889 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -17,33 +17,49 @@ import java.util.Map;
17 17
18import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
19 19
20// FIXME: Enigma doesn't support inner classes of inner class????!
20public class ClassMapping implements Comparable<ClassMapping> { 21public class ClassMapping implements Comparable<ClassMapping> {
21 22
22 private String m_obfFullName; 23 private String m_obfFullName;
23 private String m_obfSimpleName; 24 private String m_obfSimpleName;
24 private String m_deobfName; 25 private String m_deobfName;
26 private String m_previousDeobfName;
25 private Map<String, ClassMapping> m_innerClassesByObfSimple; 27 private Map<String, ClassMapping> m_innerClassesByObfSimple;
28 private Map<String, ClassMapping> m_innerClassesByObfFull;
26 private Map<String, ClassMapping> m_innerClassesByDeobf; 29 private Map<String, ClassMapping> m_innerClassesByDeobf;
27 private Map<String, FieldMapping> m_fieldsByObf; 30 private Map<String, FieldMapping> m_fieldsByObf;
28 private Map<String, FieldMapping> m_fieldsByDeobf; 31 private Map<String, FieldMapping> m_fieldsByDeobf;
29 private Map<String, MethodMapping> m_methodsByObf; 32 private Map<String, MethodMapping> m_methodsByObf;
30 private Map<String, MethodMapping> m_methodsByDeobf; 33 private Map<String, MethodMapping> m_methodsByDeobf;
34 private boolean isDirty;
35 private Mappings.EntryModifier modifier;
31 36
32 public ClassMapping(String obfFullName) { 37 public ClassMapping(String obfFullName)
33 this(obfFullName, null); 38 {
39 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
34 } 40 }
35 41
36 public ClassMapping(String obfFullName, String deobfName) { 42 public ClassMapping(String obfFullName, String deobfName)
43 {
44 this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED);
45 }
46
47 public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier)
48 {
37 m_obfFullName = obfFullName; 49 m_obfFullName = obfFullName;
38 ClassEntry classEntry = new ClassEntry(obfFullName); 50 ClassEntry classEntry = new ClassEntry(obfFullName);
39 m_obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); 51 m_obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName();
52 m_previousDeobfName = null;
40 m_deobfName = NameValidator.validateClassName(deobfName, false); 53 m_deobfName = NameValidator.validateClassName(deobfName, false);
41 m_innerClassesByObfSimple = Maps.newHashMap(); 54 m_innerClassesByObfSimple = Maps.newHashMap();
55 m_innerClassesByObfFull = Maps.newHashMap();
42 m_innerClassesByDeobf = Maps.newHashMap(); 56 m_innerClassesByDeobf = Maps.newHashMap();
43 m_fieldsByObf = Maps.newHashMap(); 57 m_fieldsByObf = Maps.newHashMap();
44 m_fieldsByDeobf = Maps.newHashMap(); 58 m_fieldsByDeobf = Maps.newHashMap();
45 m_methodsByObf = Maps.newHashMap(); 59 m_methodsByObf = Maps.newHashMap();
46 m_methodsByDeobf = Maps.newHashMap(); 60 m_methodsByDeobf = Maps.newHashMap();
61 isDirty = true;
62 this.modifier = modifier;
47 } 63 }
48 64
49 public String getObfFullName() { 65 public String getObfFullName() {
@@ -54,12 +70,18 @@ public class ClassMapping implements Comparable<ClassMapping> {
54 return m_obfSimpleName; 70 return m_obfSimpleName;
55 } 71 }
56 72
73 public String getPreviousDeobfName() {
74 return m_previousDeobfName;
75 }
76
57 public String getDeobfName() { 77 public String getDeobfName() {
58 return m_deobfName; 78 return m_deobfName;
59 } 79 }
60 80
61 public void setDeobfName(String val) { 81 public void setDeobfName(String val) {
82 m_previousDeobfName = m_deobfName;
62 m_deobfName = NameValidator.validateClassName(val, false); 83 m_deobfName = NameValidator.validateClassName(val, false);
84 this.isDirty = true;
63 } 85 }
64 86
65 //// INNER CLASSES //////// 87 //// INNER CLASSES ////////
@@ -70,9 +92,11 @@ public class ClassMapping implements Comparable<ClassMapping> {
70 } 92 }
71 93
72 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { 94 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict {
73 if (this.m_innerClassesByObfSimple.containsKey(classMapping.getObfSimpleName())) { 95 // FIXME: dirty hack, that can get into issues, but it's a temp fix!
96 if (this.m_innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) {
74 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.m_innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); 97 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.m_innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName());
75 } 98 }
99 m_innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
76 m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); 100 m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping);
77 101
78 if (classMapping.getDeobfName() != null) { 102 if (classMapping.getDeobfName() != null) {
@@ -81,23 +105,28 @@ public class ClassMapping implements Comparable<ClassMapping> {
81 } 105 }
82 m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); 106 m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping);
83 } 107 }
108 this.isDirty = true;
84 } 109 }
85 110
86 public void removeInnerClassMapping(ClassMapping classMapping) { 111 public void removeInnerClassMapping(ClassMapping classMapping) {
112 m_innerClassesByObfFull.remove(classMapping.getObfFullName());
87 boolean obfWasRemoved = m_innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; 113 boolean obfWasRemoved = m_innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null;
88 assert (obfWasRemoved); 114 assert (obfWasRemoved);
89 if (classMapping.getDeobfName() != null) { 115 if (classMapping.getDeobfName() != null) {
90 boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; 116 boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
91 assert (deobfWasRemoved); 117 assert (deobfWasRemoved);
92 } 118 }
119 this.isDirty = true;
93 } 120 }
94 121
95 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { 122 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) {
96 ClassMapping classMapping = m_innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); 123 ClassMapping classMapping = m_innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName());
97 if (classMapping == null) { 124 if (classMapping == null) {
98 classMapping = new ClassMapping(obfInnerClass.getName()); 125 classMapping = new ClassMapping(obfInnerClass.getName());
126 m_innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
99 boolean wasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; 127 boolean wasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null;
100 assert (wasAdded); 128 assert (wasAdded);
129 this.isDirty = true;
101 } 130 }
102 return classMapping; 131 return classMapping;
103 } 132 }
@@ -141,6 +170,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
141 boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null; 170 boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null;
142 assert (wasAdded); 171 assert (wasAdded);
143 } 172 }
173 this.isDirty = true;
144 } 174 }
145 175
146 public boolean hasInnerClassByObfSimple(String obfSimpleName) { 176 public boolean hasInnerClassByObfSimple(String obfSimpleName) {
@@ -172,15 +202,17 @@ public class ClassMapping implements Comparable<ClassMapping> {
172 if (m_fieldsByObf.containsKey(obfKey)) { 202 if (m_fieldsByObf.containsKey(obfKey)) {
173 throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); 203 throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey);
174 } 204 }
175 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 205 if (fieldMapping.getDeobfName() != null) {
176 if (m_fieldsByDeobf.containsKey(deobfKey)) { 206 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType());
177 throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); 207 if (m_fieldsByDeobf.containsKey(deobfKey)) {
208 throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey);
209 }
210 boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null;
211 assert (deobfWasAdded);
178 } 212 }
179 boolean obfWasAdded = m_fieldsByObf.put(obfKey, fieldMapping) == null; 213 boolean obfWasAdded = m_fieldsByObf.put(obfKey, fieldMapping) == null;
180 assert (obfWasAdded); 214 assert (obfWasAdded);
181 boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null; 215 this.isDirty = true;
182 assert (deobfWasAdded);
183 assert (m_fieldsByObf.size() == m_fieldsByDeobf.size());
184 } 216 }
185 217
186 public void removeFieldMapping(FieldMapping fieldMapping) { 218 public void removeFieldMapping(FieldMapping fieldMapping) {
@@ -190,6 +222,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
190 boolean deobfWasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 222 boolean deobfWasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null;
191 assert (deobfWasRemoved); 223 assert (deobfWasRemoved);
192 } 224 }
225 this.isDirty = true;
193 } 226 }
194 227
195 public FieldMapping getFieldByObf(String obfName, Type obfType) { 228 public FieldMapping getFieldByObf(String obfName, Type obfType) {
@@ -226,12 +259,11 @@ public class ClassMapping implements Comparable<ClassMapping> {
226 return name + ":" + type; 259 return name + ":" + type;
227 } 260 }
228 261
229
230 public void setFieldName(String obfName, Type obfType, String deobfName) { 262 public void setFieldName(String obfName, Type obfType, String deobfName) {
231 assert (deobfName != null); 263 assert (deobfName != null);
232 FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); 264 FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType));
233 if (fieldMapping == null) { 265 if (fieldMapping == null) {
234 fieldMapping = new FieldMapping(obfName, obfType, deobfName); 266 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED);
235 boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 267 boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null;
236 assert (obfWasAdded); 268 assert (obfWasAdded);
237 } else { 269 } else {
@@ -243,6 +275,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
243 boolean wasAdded = m_fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 275 boolean wasAdded = m_fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null;
244 assert (wasAdded); 276 assert (wasAdded);
245 } 277 }
278 this.isDirty = true;
246 } 279 }
247 280
248 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 281 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) {
@@ -253,6 +286,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
253 fieldMapping.setObfType(newObfType); 286 fieldMapping.setObfType(newObfType);
254 boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 287 boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null;
255 assert(obfWasAdded); 288 assert(obfWasAdded);
289 this.isDirty = true;
256 } 290 }
257 291
258 //// METHODS //////// 292 //// METHODS ////////
@@ -285,6 +319,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
285 boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null; 319 boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null;
286 assert (deobfWasAdded); 320 assert (deobfWasAdded);
287 } 321 }
322 this.isDirty = true;
288 assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); 323 assert (m_methodsByObf.size() >= m_methodsByDeobf.size());
289 } 324 }
290 325
@@ -295,6 +330,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
295 boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 330 boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null;
296 assert (deobfWasRemoved); 331 assert (deobfWasRemoved);
297 } 332 }
333 this.isDirty = true;
298 } 334 }
299 335
300 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 336 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) {
@@ -328,6 +364,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
328 boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 364 boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null;
329 assert (wasAdded); 365 assert (wasAdded);
330 } 366 }
367 this.isDirty = true;
331 } 368 }
332 369
333 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 370 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) {
@@ -338,6 +375,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
338 methodMapping.setObfSignature(newObfSignature); 375 methodMapping.setObfSignature(newObfSignature);
339 boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 376 boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null;
340 assert(obfWasAdded); 377 assert(obfWasAdded);
378 this.isDirty = true;
341 } 379 }
342 380
343 //// ARGUMENTS //////// 381 //// ARGUMENTS ////////
@@ -349,16 +387,19 @@ public class ClassMapping implements Comparable<ClassMapping> {
349 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 387 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature);
350 } 388 }
351 methodMapping.setArgumentName(argumentIndex, argumentName); 389 methodMapping.setArgumentName(argumentIndex, argumentName);
390 this.isDirty = true;
352 } 391 }
353 392
354 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 393 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) {
355 m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 394 m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex);
395 this.isDirty = true;
356 } 396 }
357 397
358 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 398 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) {
359 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 399 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature);
360 boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 400 boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null;
361 assert (wasAdded); 401 assert (wasAdded);
402 this.isDirty = true;
362 return methodMapping; 403 return methodMapping;
363 } 404 }
364 405
@@ -441,6 +482,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
441 m_obfFullName = newObfClassName; 482 m_obfFullName = newObfClassName;
442 return true; 483 return true;
443 } 484 }
485 this.isDirty = true;
444 return false; 486 return false;
445 } 487 }
446 488
@@ -456,4 +498,54 @@ public class ClassMapping implements Comparable<ClassMapping> {
456 public ClassEntry getObfEntry() { 498 public ClassEntry getObfEntry() {
457 return new ClassEntry(m_obfFullName); 499 return new ClassEntry(m_obfFullName);
458 } 500 }
501
502 public boolean isDirty()
503 {
504 return isDirty;
505 }
506
507 public void resetDirty()
508 {
509 this.isDirty = false;
510 }
511
512 public void setModifier(Mappings.EntryModifier modifier)
513 {
514 if (this.modifier != modifier)
515 this.isDirty = true;
516 this.modifier = modifier;
517 }
518
519 public Mappings.EntryModifier getModifier()
520 {
521 return modifier;
522 }
523
524 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) {
525 FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType));
526 if (fieldMapping == null) {
527 fieldMapping = new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED);
528 m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping);
529 }
530
531 if (fieldMapping.getModifier() != modifier)
532 {
533 fieldMapping.setModifier(modifier);
534 this.isDirty = true;
535 }
536 }
537
538 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) {
539 MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, sig));
540 if (methodMapping == null) {
541 methodMapping = new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED);
542 m_methodsByObf.put(getMethodKey(obfName, sig), methodMapping);
543 }
544
545 if (methodMapping.getModifier() != modifier)
546 {
547 methodMapping.setModifier(modifier);
548 this.isDirty = true;
549 }
550 }
459} 551}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index 1b596606..e75485cd 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -15,16 +15,19 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
15 private String obfName; 15 private String obfName;
16 private String deobfName; 16 private String deobfName;
17 private Type obfType; 17 private Type obfType;
18 private Mappings.EntryModifier modifier;
18 19
19 public FieldMapping(String obfName, Type obfType, String deobfName) { 20 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) {
20 this.obfName = obfName; 21 this.obfName = obfName;
21 this.deobfName = NameValidator.validateFieldName(deobfName); 22 this.deobfName = NameValidator.validateFieldName(deobfName);
22 this.obfType = obfType; 23 this.obfType = obfType;
24 this.modifier = modifier;
23 } 25 }
24 26
25 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { 27 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) {
26 this.obfName = other.obfName; 28 this.obfName = other.obfName;
27 this.deobfName = other.deobfName; 29 this.deobfName = other.deobfName;
30 this.modifier = other.modifier;
28 this.obfType = new Type(other.obfType, obfClassNameReplacer); 31 this.obfType = new Type(other.obfType, obfClassNameReplacer);
29 } 32 }
30 33
@@ -58,6 +61,16 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
58 this.obfType = val; 61 this.obfType = val;
59 } 62 }
60 63
64 public void setModifier(Mappings.EntryModifier modifier)
65 {
66 this.modifier = modifier;
67 }
68
69 public Mappings.EntryModifier getModifier()
70 {
71 return modifier;
72 }
73
61 @Override 74 @Override
62 public int compareTo(FieldMapping other) { 75 public int compareTo(FieldMapping other) {
63 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 76 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType);
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
new file mode 100644
index 00000000..8bbaaaf6
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
@@ -0,0 +1,104 @@
1package cuchaz.enigma.mapping;
2
3import cuchaz.enigma.utils.Utils;
4
5/**
6 * Desc...
7 * Created by Thog
8 * 19/10/2016
9 */
10public class LocalVariableEntry implements Entry
11{
12
13 protected final BehaviorEntry behaviorEntry;
14 protected final String name;
15 protected final Type type;
16 protected final int index;
17
18 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) {
19 if (behaviorEntry == null) {
20 throw new IllegalArgumentException("Behavior cannot be null!");
21 }
22 if (index < 0) {
23 throw new IllegalArgumentException("Index must be non-negative!");
24 }
25 if (name == null) {
26 throw new IllegalArgumentException("Variable name cannot be null!");
27 }
28 if (type == null) {
29 throw new IllegalArgumentException("Variable type cannot be null!");
30 }
31
32 this.behaviorEntry = behaviorEntry;
33 this.name = name;
34 this.type = type;
35 this.index = index;
36 }
37
38
39 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) {
40 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry);
41 this.name = other.name;
42 this.type = other.type;
43 this.index = other.index;
44 }
45
46 public BehaviorEntry getBehaviorEntry() {
47 return this.behaviorEntry;
48 }
49
50 public Type getType() {
51 return type;
52 }
53
54 public int getIndex() {
55 return index;
56 }
57
58 @Override
59 public String getName() {
60 return this.name;
61 }
62
63 @Override
64 public ClassEntry getClassEntry() {
65 return this.behaviorEntry.getClassEntry();
66 }
67
68 @Override
69 public String getClassName() {
70 return this.behaviorEntry.getClassName();
71 }
72
73 @Override
74 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) {
75 return new LocalVariableEntry(this, classEntry);
76 }
77
78 public String getMethodName() {
79 return this.behaviorEntry.getName();
80 }
81
82 public Signature getMethodSignature() {
83 return this.behaviorEntry.getSignature();
84 }
85
86 @Override
87 public int hashCode() {
88 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
89 }
90
91 @Override
92 public boolean equals(Object other) {
93 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
94 }
95
96 public boolean equals(LocalVariableEntry other) {
97 return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index;
98 }
99
100 @Override
101 public String toString() {
102 return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ":" + this.type + ")";
103 }
104}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index 7061be79..d493dcfa 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -15,11 +15,7 @@ import com.google.common.collect.Maps;
15 15
16import java.io.File; 16import java.io.File;
17import java.io.IOException; 17import java.io.IOException;
18import java.util.ArrayList; 18import java.util.*;
19import java.util.Collection;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
23 19
24import com.google.common.collect.Sets; 20import com.google.common.collect.Sets;
25import cuchaz.enigma.analysis.TranslationIndex; 21import cuchaz.enigma.analysis.TranslationIndex;
@@ -30,6 +26,7 @@ public class Mappings {
30 protected Map<String, ClassMapping> classesByObf; 26 protected Map<String, ClassMapping> classesByObf;
31 protected Map<String, ClassMapping> classesByDeobf; 27 protected Map<String, ClassMapping> classesByDeobf;
32 private final FormatType originMapping; 28 private final FormatType originMapping;
29 private Mappings previousState;
33 30
34 public Mappings() 31 public Mappings()
35 { 32 {
@@ -40,6 +37,7 @@ public class Mappings {
40 this.originMapping = originMapping; 37 this.originMapping = originMapping;
41 this.classesByObf = Maps.newHashMap(); 38 this.classesByObf = Maps.newHashMap();
42 this.classesByDeobf = Maps.newHashMap(); 39 this.classesByDeobf = Maps.newHashMap();
40 this.previousState = null;
43 } 41 }
44 42
45 public Collection<ClassMapping> classes() { 43 public Collection<ClassMapping> classes() {
@@ -175,6 +173,16 @@ public class Mappings {
175 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 173 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType);
176 } 174 }
177 175
176 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
177 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
178 if (classMapping != null)
179 for (FieldMapping fieldMapping : classMapping.fields())
180 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName()))
181 return true;
182
183 return false;
184 }
185
178 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 186 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) {
179 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 187 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
180 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 188 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature);
@@ -204,9 +212,19 @@ public class Mappings {
204 return originMapping; 212 return originMapping;
205 } 213 }
206 214
215 public void savePreviousState()
216 {
217 this.previousState = new Mappings(this.originMapping);
218 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf);
219 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf);
220 classesByDeobf.values().forEach(ClassMapping::resetDirty);
221 classesByObf.values().forEach(ClassMapping::resetDirty);
222 }
223
207 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException 224 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException
208 { 225 {
209 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); 226 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat);
227 this.savePreviousState();
210 } 228 }
211 229
212 public void saveSRGMappings(File file) throws IOException 230 public void saveSRGMappings(File file) throws IOException
@@ -214,8 +232,22 @@ public class Mappings {
214 new MappingsSRGWriter().write(file, this); 232 new MappingsSRGWriter().write(file, this);
215 } 233 }
216 234
235 public Mappings getPreviousState() {
236 return previousState;
237 }
238
217 public enum FormatType 239 public enum FormatType
218 { 240 {
219 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE 241 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE
220 } 242 }
243
244 public enum EntryModifier
245 {
246 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
247
248 public String getFormattedName()
249 {
250 return " ACC:" + super.toString();
251 }
252 }
221} 253}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index 74c5340c..cdfed726 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -37,6 +37,7 @@ public class MappingsEnigmaReader
37 else if (file.isDirectory()) 37 else if (file.isDirectory())
38 readDirectory(mappings, file.getAbsoluteFile()); 38 readDirectory(mappings, file.getAbsoluteFile());
39 } 39 }
40 mappings.savePreviousState();
40 } 41 }
41 else 42 else
42 throw new IOException("Cannot access directory" + directory.getAbsolutePath()); 43 throw new IOException("Cannot access directory" + directory.getAbsolutePath());
@@ -134,21 +135,51 @@ public class MappingsEnigmaReader
134 private ClassMapping readClass(String[] parts, boolean makeSimple) { 135 private ClassMapping readClass(String[] parts, boolean makeSimple) {
135 if (parts.length == 2) { 136 if (parts.length == 2) {
136 return new ClassMapping(parts[1]); 137 return new ClassMapping(parts[1]);
137 } else { 138 } else if (parts.length == 3) {
138 return new ClassMapping(parts[1], parts[2]); 139 boolean access = parts[2].startsWith("ACC:");
139 } 140 ClassMapping mapping;
141 if (access)
142 mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4)));
143 else
144 mapping = new ClassMapping(parts[1], parts[2]);
145
146 return mapping;
147 } else if (parts.length == 4)
148 return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4)));
149 return null;
140 } 150 }
141 151
142 /* TEMP */ 152 /* TEMP */
143 protected FieldMapping readField(String[] parts) { 153 protected FieldMapping readField(String[] parts) {
144 return new FieldMapping(parts[1], new Type(parts[3]), parts[2]); 154 FieldMapping mapping = null;
155 if (parts.length == 4)
156 {
157 boolean access = parts[3].startsWith("ACC:");
158 if (access)
159 mapping = new FieldMapping(parts[1], new Type(parts[2]), null,
160 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
161 else
162 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
163 }
164 else if (parts.length == 5)
165 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
166 return mapping;
145 } 167 }
146 168
147 private MethodMapping readMethod(String[] parts) { 169 private MethodMapping readMethod(String[] parts) {
148 if (parts.length == 3) { 170 MethodMapping mapping = null;
149 return new MethodMapping(parts[1], new Signature(parts[2])); 171 if (parts.length == 3)
150 } else { 172 mapping = new MethodMapping(parts[1], new Signature(parts[2]));
151 return new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); 173 else if (parts.length == 4){
174 boolean access = parts[3].startsWith("ACC:");
175 if (access)
176 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
177 else
178 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]);
152 } 179 }
180 else if (parts.length == 5)
181 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2],
182 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
183 return mapping;
153 } 184 }
154} 185}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index ba0720a0..c09f4a67 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -30,24 +30,14 @@ public class MappingsEnigmaWriter {
30 writeAsDirectory(out, mappings); 30 writeAsDirectory(out, mappings);
31 } 31 }
32 32
33 private void deleteDir(File file) {
34 File[] contents = file.listFiles();
35 if (contents != null) {
36 for (File f : contents) {
37 deleteDir(f);
38 }
39 }
40 file.delete();
41 }
42
43 public void writeAsDirectory(File target, Mappings mappings) throws IOException { 33 public void writeAsDirectory(File target, Mappings mappings) throws IOException {
44 //TODO: Know what have changes during write to not rewrite all the things
45 deleteDir(target);
46 if (!target.exists() && !target.mkdirs()) 34 if (!target.exists() && !target.mkdirs())
47 throw new IOException("Cannot create mapping directory!"); 35 throw new IOException("Cannot create mapping directory!");
48
49 36
50 for (ClassMapping classMapping : sorted(mappings.classes())) { 37 for (ClassMapping classMapping : sorted(mappings.classes())) {
38 if (!classMapping.isDirty())
39 continue;
40 this.deletePreviousClassMapping(target, classMapping);
51 File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); 41 File obFile = new File(target, classMapping.getObfFullName() + ".mapping");
52 File result; 42 File result;
53 if (classMapping.getDeobfName() == null) 43 if (classMapping.getDeobfName() == null)
@@ -68,8 +58,53 @@ public class MappingsEnigmaWriter {
68 outputWriter.close(); 58 outputWriter.close();
69 } 59 }
70 60
61 // Remove dropped mappings
62 if (mappings.getPreviousState() != null)
63 {
64 List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes());
65 List<ClassMapping> classMappings = new ArrayList<>(mappings.classes());
66 droppedClassMappings.removeAll(classMappings);
67 for (ClassMapping classMapping : droppedClassMappings)
68 {
69 File obFile = new File(target, classMapping.getObfFullName() + ".mapping");
70 File result;
71 if (classMapping.getDeobfName() == null)
72 result = obFile;
73 else
74 {
75 // Make sure that old version of the file doesn't exist
76 if (obFile.exists())
77 obFile.delete();
78 result = new File(target, classMapping.getDeobfName() + ".mapping");
79 }
80 if (result.exists())
81 result.delete();
82 }
83 }
71 } 84 }
72 85
86 private void deletePreviousClassMapping(File target, ClassMapping classMapping) {
87 File prevFile = null;
88 // Deob rename
89 if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName()))
90 {
91 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
92 }
93 // Deob to ob rename
94 else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null)
95 {
96 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
97 }
98 // Ob to Deob rename
99 else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null)
100 {
101 prevFile = new File(target, classMapping.getObfFullName() + ".mapping");
102 }
103
104 if (prevFile != null && prevFile.exists())
105 prevFile.delete();
106 }
107
73 public void write(PrintWriter out, Mappings mappings) throws IOException { 108 public void write(PrintWriter out, Mappings mappings) throws IOException {
74 for (ClassMapping classMapping : sorted(mappings.classes())) { 109 for (ClassMapping classMapping : sorted(mappings.classes())) {
75 write(out, classMapping, 0); 110 write(out, classMapping, 0);
@@ -78,9 +113,9 @@ public class MappingsEnigmaWriter {
78 113
79 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { 114 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
80 if (classMapping.getDeobfName() == null) { 115 if (classMapping.getDeobfName() == null) {
81 out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfFullName()); 116 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
82 } else { 117 } else {
83 out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName()); 118 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
84 } 119 }
85 120
86 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 121 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
@@ -97,14 +132,17 @@ public class MappingsEnigmaWriter {
97 } 132 }
98 133
99 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException { 134 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException {
100 out.format("%sFIELD %s %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString()); 135 if (fieldMapping.getDeobfName() == null)
136 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
137 else
138 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
101 } 139 }
102 140
103 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 141 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
104 if (methodMapping.getDeobfName() == null) { 142 if (methodMapping.getDeobfName() == null) {
105 out.format("%sMETHOD %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature()); 143 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" :methodMapping.getModifier().getFormattedName());
106 } else { 144 } else {
107 out.format("%sMETHOD %s %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature()); 145 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
108 } 146 }
109 147
110 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 148 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 5c299e3e..bac62503 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -90,8 +90,23 @@ public class MappingsRenamer {
90 public void setFieldName(FieldEntry obf, String deobfName) { 90 public void setFieldName(FieldEntry obf, String deobfName) {
91 deobfName = NameValidator.validateFieldName(deobfName); 91 deobfName = NameValidator.validateFieldName(deobfName);
92 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 92 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType());
93 if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName, obf.getType()) || m_index.containsObfField(targetEntry)) { 93 ClassEntry definedClass = null;
94 throw new IllegalNameException(deobfName, "There is already a field with that name"); 94 if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName) || m_index.containsEntryWithSameName(targetEntry))
95 definedClass = obf.getClassEntry();
96 else {
97 for (ClassEntry ancestorEntry : this.m_index.getTranslationIndex().getAncestry(obf.getClassEntry())) {
98 if (m_mappings.containsDeobfField(ancestorEntry, deobfName) || m_index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) {
99 definedClass = ancestorEntry;
100 break;
101 }
102 }
103 }
104
105 if (definedClass != null) {
106 String className = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(definedClass.getClassName());
107 if (className == null)
108 className = definedClass.getClassName();
109 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
95 } 110 }
96 111
97 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 112 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry());
@@ -306,4 +321,22 @@ public class MappingsRenamer {
306 } 321 }
307 return mappingChain; 322 return mappingChain;
308 } 323 }
324
325 public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier)
326 {
327 ClassMapping classMapping = getOrCreateClassMapping(obEntry);
328 classMapping.setModifier(modifier);
329 }
330
331 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier)
332 {
333 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry());
334 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier);
335 }
336
337 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier)
338 {
339 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry());
340 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier);
341 }
309} 342}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index 229bf464..4d0c2610 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -34,25 +34,25 @@ public class MappingsSRGWriter {
34 if(innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null){ 34 if(innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null){
35 continue; 35 continue;
36 } 36 }
37 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName().replace("none/", ""); 37 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName();
38 String innerDebofClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName().replace("none/", ""); 38 String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName();
39 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); 39 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName());
40 writer.write(System.lineSeparator()); 40 writer.write(System.lineSeparator());
41 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { 41 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) {
42 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDebofClassName + "/" + fieldMapping.getDeobfName()); 42 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName());
43 } 43 }
44 44
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString().replace("none/", "") + " " + innerDebofClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature()));
47 } 47 }
48 } 48 }
49 49
50 for (FieldMapping fieldMapping : sorted(classMapping.fields())) { 50 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
51 fieldMappings.add("FD: " + classMapping.getObfFullName().replace("none/", "") + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); 51 fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName());
52 } 52 }
53 53
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 54 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
55 methodMappings.add("MD: " + classMapping.getObfFullName().replace("none/", "") + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString().replace("none/", "") + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature()));
56 } 56 }
57 } 57 }
58 for(String fd : fieldMappings){ 58 for(String fd : fieldMappings){
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index 99b9c887..455ff6b7 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -22,12 +22,17 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
22 private String deobfName; 22 private String deobfName;
23 private Signature obfSignature; 23 private Signature obfSignature;
24 private Map<Integer, ArgumentMapping> arguments; 24 private Map<Integer, ArgumentMapping> arguments;
25 private Mappings.EntryModifier modifier;
25 26
26 public MethodMapping(String obfName, Signature obfSignature) { 27 public MethodMapping(String obfName, Signature obfSignature) {
27 this(obfName, obfSignature, null); 28 this(obfName, obfSignature, null,Mappings.EntryModifier.UNCHANGED);
28 } 29 }
29 30
30 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 31 public MethodMapping(String obfName, Signature obfSignature, String deobfName) {
32 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED);
33 }
34
35 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) {
31 if (obfName == null) { 36 if (obfName == null) {
32 throw new IllegalArgumentException("obf name cannot be null!"); 37 throw new IllegalArgumentException("obf name cannot be null!");
33 } 38 }
@@ -38,11 +43,13 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
38 this.deobfName = NameValidator.validateMethodName(deobfName); 43 this.deobfName = NameValidator.validateMethodName(deobfName);
39 this.obfSignature = obfSignature; 44 this.obfSignature = obfSignature;
40 this.arguments = Maps.newTreeMap(); 45 this.arguments = Maps.newTreeMap();
46 this.modifier = modifier;
41 } 47 }
42 48
43 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 49 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) {
44 this.obfName = other.obfName; 50 this.obfName = other.obfName;
45 this.deobfName = other.deobfName; 51 this.deobfName = other.deobfName;
52 this.modifier = other.modifier;
46 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 53 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer);
47 this.arguments = Maps.newTreeMap(); 54 this.arguments = Maps.newTreeMap();
48 for (Map.Entry<Integer,ArgumentMapping> entry : other.arguments.entrySet()) { 55 for (Map.Entry<Integer,ArgumentMapping> entry : other.arguments.entrySet()) {
@@ -187,4 +194,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
187 return new MethodEntry(classEntry, this.obfName, this.obfSignature); 194 return new MethodEntry(classEntry, this.obfName, this.obfSignature);
188 } 195 }
189 } 196 }
197
198 public Mappings.EntryModifier getModifier()
199 {
200 return modifier;
201 }
202
203 public void setModifier(Mappings.EntryModifier modifier)
204 {
205 this.modifier = modifier;
206 }
190} 207}
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
index b05714cc..e94009ee 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -58,6 +58,8 @@ public class Translator {
58 return (T) translateEntry((ConstructorEntry) entry); 58 return (T) translateEntry((ConstructorEntry) entry);
59 } else if (entry instanceof ArgumentEntry) { 59 } else if (entry instanceof ArgumentEntry) {
60 return (T) translateEntry((ArgumentEntry) entry); 60 return (T) translateEntry((ArgumentEntry) entry);
61 } else if (entry instanceof LocalVariableEntry) {
62 return (T) translateEntry((LocalVariableEntry) entry);
61 } else { 63 } else {
62 throw new Error("Unknown entry type: " + entry.getClass().getName()); 64 throw new Error("Unknown entry type: " + entry.getClass().getName());
63 } 65 }
@@ -74,11 +76,28 @@ public class Translator {
74 return translate(entry); 76 return translate(entry);
75 } else if (entry instanceof ArgumentEntry) { 77 } else if (entry instanceof ArgumentEntry) {
76 return translate((ArgumentEntry) entry); 78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry);
77 } else { 81 } else {
78 throw new Error("Unknown entry type: " + entry.getClass().getName()); 82 throw new Error("Unknown entry type: " + entry.getClass().getName());
79 } 83 }
80 } 84 }
81 85
86 public String translate(LocalVariableEntry in)
87 {
88 LocalVariableEntry translated = translateEntry(in);
89 if (translated.equals(in)) {
90 return null;
91 }
92 return translated.getName();
93 }
94
95 public LocalVariableEntry translateEntry(LocalVariableEntry in)
96 {
97 // TODO: Implement it
98 return in;
99 }
100
82 public String translate(ClassEntry in) { 101 public String translate(ClassEntry in) {
83 ClassEntry translated = translateEntry(in); 102 ClassEntry translated = translateEntry(in);
84 if (translated.equals(in)) { 103 if (translated.equals(in)) {
@@ -310,4 +329,27 @@ public class Translator {
310 assert (mappingsChain.size() == parts.length); 329 assert (mappingsChain.size() == parts.length);
311 return mappingsChain; 330 return mappingsChain;
312 } 331 }
332
333 public Mappings.EntryModifier getModifier(Entry entry)
334 {
335 ClassMapping classMapping = findClassMapping(entry.getClassEntry());
336 if (classMapping != null && !entry.getName().equals("<clinit>"))
337 {
338 if (entry instanceof ClassEntry)
339 return classMapping.getModifier();
340 else if (entry instanceof FieldEntry)
341 {
342 FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType());
343 return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
344 }
345 else if (entry instanceof BehaviorEntry)
346 {
347 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature());
348 return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
349 }
350 else
351 throw new Error("Unknown entry type: " + entry.getClass().getName());
352 }
353 return Mappings.EntryModifier.UNCHANGED;
354 }
313} 355}