summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/mapping/Mappings.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/mapping/Mappings.java')
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java453
1 files changed, 222 insertions, 231 deletions
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index d493dcf..33dd3c5 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -8,246 +8,237 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11
11package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
12 13
13import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.throwables.MappingConflict;
15 19
16import java.io.File; 20import java.io.File;
17import java.io.IOException; 21import java.io.IOException;
18import java.util.*; 22import java.util.*;
19 23
20import com.google.common.collect.Sets;
21import cuchaz.enigma.analysis.TranslationIndex;
22import cuchaz.enigma.throwables.MappingConflict;
23
24public class Mappings { 24public class Mappings {
25 25
26 protected Map<String, ClassMapping> classesByObf; 26 private final FormatType originMapping;
27 protected Map<String, ClassMapping> classesByDeobf; 27 protected Map<String, ClassMapping> classesByObf;
28 private final FormatType originMapping; 28 protected Map<String, ClassMapping> classesByDeobf;
29 private Mappings previousState; 29 private Mappings previousState;
30 30
31 public Mappings() 31 public Mappings() {
32 { 32 this(FormatType.ENIGMA_DIRECTORY);
33 this(FormatType.ENIGMA_DIRECTORY); 33 }
34 } 34
35 35 public Mappings(FormatType originMapping) {
36 public Mappings(FormatType originMapping) { 36 this.originMapping = originMapping;
37 this.originMapping = originMapping; 37 this.classesByObf = Maps.newHashMap();
38 this.classesByObf = Maps.newHashMap(); 38 this.classesByDeobf = Maps.newHashMap();
39 this.classesByDeobf = Maps.newHashMap(); 39 this.previousState = null;
40 this.previousState = null; 40 }
41 } 41
42 42 public Collection<ClassMapping> classes() {
43 public Collection<ClassMapping> classes() { 43 assert (this.classesByObf.size() >= this.classesByDeobf.size());
44 assert (this.classesByObf.size() >= this.classesByDeobf.size()); 44 return this.classesByObf.values();
45 return this.classesByObf.values(); 45 }
46 } 46
47 47 public void addClassMapping(ClassMapping classMapping) throws MappingConflict {
48 public void addClassMapping(ClassMapping classMapping) throws MappingConflict { 48 if (this.classesByObf.containsKey(classMapping.getObfFullName())) {
49 if (this.classesByObf.containsKey(classMapping.getObfFullName())) { 49 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName());
50 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); 50 }
51 } 51 this.classesByObf.put(classMapping.getObfFullName(), classMapping);
52 this.classesByObf.put(classMapping.getObfFullName(), classMapping); 52
53 53 if (classMapping.getDeobfName() != null) {
54 if (classMapping.getDeobfName() != null) { 54 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) {
55 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { 55 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
56 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); 56 }
57 } 57 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping);
58 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); 58 }
59 } 59 }
60 } 60
61 61 public void removeClassMapping(ClassMapping classMapping) {
62 public void removeClassMapping(ClassMapping classMapping) { 62 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null;
63 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; 63 assert (obfWasRemoved);
64 assert (obfWasRemoved); 64 if (classMapping.getDeobfName() != null) {
65 if (classMapping.getDeobfName() != null) { 65 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
66 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; 66 assert (deobfWasRemoved);
67 assert (deobfWasRemoved); 67 }
68 } 68 }
69 } 69
70 70 public ClassMapping getClassByObf(ClassEntry entry) {
71 71 return getClassByObf(entry.getName());
72 public ClassMapping getClassByObf(ClassEntry entry) { 72 }
73 return getClassByObf(entry.getName()); 73
74 } 74 public ClassMapping getClassByObf(String obfName) {
75 75 return this.classesByObf.get(obfName);
76 public ClassMapping getClassByObf(String obfName) { 76 }
77 return this.classesByObf.get(obfName); 77
78 } 78 public ClassMapping getClassByDeobf(ClassEntry entry) {
79 79 return getClassByDeobf(entry.getName());
80 public ClassMapping getClassByDeobf(ClassEntry entry) { 80 }
81 return getClassByDeobf(entry.getName()); 81
82 } 82 public ClassMapping getClassByDeobf(String deobfName) {
83 83 return this.classesByDeobf.get(deobfName);
84 public ClassMapping getClassByDeobf(String deobfName) { 84 }
85 return this.classesByDeobf.get(deobfName); 85
86 } 86 public void setClassDeobfName(ClassMapping classMapping, String deobfName) {
87 87 if (classMapping.getDeobfName() != null) {
88 public void setClassDeobfName(ClassMapping classMapping, String deobfName) { 88 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
89 if (classMapping.getDeobfName() != null) { 89 assert (wasRemoved);
90 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; 90 }
91 assert (wasRemoved); 91 classMapping.setDeobfName(deobfName);
92 } 92 if (deobfName != null) {
93 classMapping.setDeobfName(deobfName); 93 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null;
94 if (deobfName != null) { 94 assert (wasAdded);
95 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; 95 }
96 assert (wasAdded); 96 }
97 } 97
98 } 98 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
99 99 switch (direction) {
100 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 100 case Deobfuscating:
101 switch (direction) { 101
102 case Deobfuscating: 102 return new Translator(direction, this.classesByObf, index);
103 103
104 return new Translator(direction, this.classesByObf, index); 104 case Obfuscating:
105 105
106 case Obfuscating: 106 // fill in the missing deobf class entries with obf entries
107 107 Map<String, ClassMapping> classes = Maps.newHashMap();
108 // fill in the missing deobf class entries with obf entries 108 for (ClassMapping classMapping : classes()) {
109 Map<String, ClassMapping> classes = Maps.newHashMap(); 109 if (classMapping.getDeobfName() != null) {
110 for (ClassMapping classMapping : classes()) { 110 classes.put(classMapping.getDeobfName(), classMapping);
111 if (classMapping.getDeobfName() != null) { 111 } else {
112 classes.put(classMapping.getDeobfName(), classMapping); 112 classes.put(classMapping.getObfFullName(), classMapping);
113 } else { 113 }
114 classes.put(classMapping.getObfFullName(), classMapping); 114 }
115 } 115
116 } 116 // translate the translation index
117 117 // NOTE: this isn't actually recursive
118 // translate the translation index 118 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index));
119 // NOTE: this isn't actually recursive 119
120 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 120 return new Translator(direction, classes, deobfIndex);
121 121
122 return new Translator(direction, classes, deobfIndex); 122 default:
123 123 throw new Error("Invalid translation direction!");
124 default: 124 }
125 throw new Error("Invalid translation direction!"); 125 }
126 } 126
127 } 127 @Override
128 128 public String toString() {
129 @Override 129 StringBuilder buf = new StringBuilder();
130 public String toString() { 130 for (ClassMapping classMapping : this.classesByObf.values()) {
131 StringBuilder buf = new StringBuilder(); 131 buf.append(classMapping);
132 for (ClassMapping classMapping : this.classesByObf.values()) { 132 buf.append("\n");
133 buf.append(classMapping.toString()); 133 }
134 buf.append("\n"); 134 return buf.toString();
135 } 135 }
136 return buf.toString(); 136
137 } 137 public void renameObfClass(String oldObfName, String newObfName) {
138 138 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> {
139 public void renameObfClass(String oldObfName, String newObfName) { 139 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null;
140 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { 140 assert (wasRemoved);
141 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; 141 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null;
142 assert (wasRemoved); 142 assert (wasAdded);
143 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; 143 });
144 assert (wasAdded); 144 }
145 }); 145
146 } 146 public Set<String> getAllObfClassNames() {
147 147 final Set<String> classNames = Sets.newHashSet();
148 public Set<String> getAllObfClassNames() { 148 for (ClassMapping classMapping : classes()) {
149 final Set<String> classNames = Sets.newHashSet(); 149
150 for (ClassMapping classMapping : classes()) { 150 // add the class name
151 151 classNames.add(classMapping.getObfFullName());
152 // add the class name 152
153 classNames.add(classMapping.getObfFullName()); 153 // add classes from method signatures
154 154 for (MethodMapping methodMapping : classMapping.methods()) {
155 // add classes from method signatures 155 for (Type type : methodMapping.getObfSignature().types()) {
156 for (MethodMapping methodMapping : classMapping.methods()) { 156 if (type.hasClass()) {
157 for (Type type : methodMapping.getObfSignature().types()) { 157 classNames.add(type.getClassEntry().getClassName());
158 if (type.hasClass()) { 158 }
159 classNames.add(type.getClassEntry().getClassName()); 159 }
160 } 160 }
161 } 161 }
162 } 162 return classNames;
163 } 163 }
164 return classNames; 164
165 } 165 public boolean containsDeobfClass(String deobfName) {
166 166 return this.classesByDeobf.containsKey(deobfName);
167 public boolean containsDeobfClass(String deobfName) { 167 }
168 return this.classesByDeobf.containsKey(deobfName); 168
169 } 169 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) {
170 170 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
171 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 171 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType);
172 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 172 }
173 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 173
174 } 174 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
175 175 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
176 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 176 if (classMapping != null)
177 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 177 for (FieldMapping fieldMapping : classMapping.fields())
178 if (classMapping != null) 178 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName()))
179 for (FieldMapping fieldMapping : classMapping.fields()) 179 return true;
180 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) 180
181 return true; 181 return false;
182 182 }
183 return false; 183
184 } 184 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) {
185 185 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
186 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 186 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature);
187 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 187 }
188 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 188
189 } 189 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
190 190 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName());
191 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 191 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name);
192 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 192 }
193 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 193
194 } 194 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
195 195 List<ClassMapping> mappingChain = Lists.newArrayList();
196 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 196 ClassMapping classMapping = null;
197 List<ClassMapping> mappingChain = Lists.newArrayList(); 197 for (ClassEntry obfClassEntry : obfClass.getClassChain()) {
198 ClassMapping classMapping = null; 198 if (mappingChain.isEmpty()) {
199 for (ClassEntry obfClassEntry : obfClass.getClassChain()) { 199 classMapping = this.classesByObf.get(obfClassEntry.getName());
200 if (mappingChain.isEmpty()) { 200 } else if (classMapping != null) {
201 classMapping = this.classesByObf.get(obfClassEntry.getName()); 201 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName());
202 } else if (classMapping != null) { 202 }
203 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); 203 mappingChain.add(classMapping);
204 } 204 }
205 mappingChain.add(classMapping); 205 return mappingChain;
206 } 206 }
207 return mappingChain; 207
208 } 208 public FormatType getOriginMappingFormat() {
209 209 return originMapping;
210 public FormatType getOriginMappingFormat() 210 }
211 { 211
212 return originMapping; 212 public void savePreviousState() {
213 } 213 this.previousState = new Mappings(this.originMapping);
214 214 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf);
215 public void savePreviousState() 215 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf);
216 { 216 classesByDeobf.values().forEach(ClassMapping::resetDirty);
217 this.previousState = new Mappings(this.originMapping); 217 classesByObf.values().forEach(ClassMapping::resetDirty);
218 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); 218 }
219 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); 219
220 classesByDeobf.values().forEach(ClassMapping::resetDirty); 220 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException {
221 classesByObf.values().forEach(ClassMapping::resetDirty); 221 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat);
222 } 222 this.savePreviousState();
223 223 }
224 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException 224
225 { 225 public void saveSRGMappings(File file) throws IOException {
226 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); 226 new MappingsSRGWriter().write(file, this);
227 this.savePreviousState(); 227 }
228 } 228
229 229 public Mappings getPreviousState() {
230 public void saveSRGMappings(File file) throws IOException 230 return previousState;
231 { 231 }
232 new MappingsSRGWriter().write(file, this); 232
233 } 233 public enum FormatType {
234 234 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE
235 public Mappings getPreviousState() { 235 }
236 return previousState; 236
237 } 237 public enum EntryModifier {
238 238 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
239 public enum FormatType 239
240 { 240 public String getFormattedName() {
241 ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE 241 return " ACC:" + super.toString();
242 } 242 }
243 243 }
244 public enum EntryModifier
245 {
246 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
247
248 public String getFormattedName()
249 {
250 return " ACC:" + super.toString();
251 }
252 }
253} 244}