summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/mapping
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/mapping')
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java627
-rw-r--r--src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java371
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java100
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java58
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java268
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java101
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java186
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java160
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java365
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java80
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java130
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java21
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java114
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java210
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java73
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java82
-rw-r--r--src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java92
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java109
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java258
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java38
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java175
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/Entry.java22
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java49
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java44
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java77
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java61
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java93
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java61
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java80
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java48
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java59
33 files changed, 0 insertions, 4255 deletions
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
deleted file mode 100644
index 9c193ef..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ /dev/null
@@ -1,627 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Maps;
15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.entry.FieldEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.MappingConflict;
19
20import java.util.ArrayList;
21import java.util.Map;
22
23// FIXME: Enigma doesn't support inner classes of inner class????!
24public class ClassMapping implements Comparable<ClassMapping> {
25
26 private String obfFullName;
27 private String obfSimpleName;
28 private String deobfName;
29 private String deobfFullName;
30 private String previousDeobfName;
31 private Map<String, ClassMapping> innerClassesByObfSimple;
32 private Map<String, ClassMapping> innerClassesByObfFull;
33 private Map<String, ClassMapping> innerClassesByDeobf;
34 private Map<String, FieldMapping> fieldsByObf;
35 private Map<String, FieldMapping> fieldsByDeobf;
36 private Map<String, MethodMapping> methodsByObf;
37 private Map<String, MethodMapping> methodsByDeobf;
38 private boolean isDirty;
39 private Mappings.EntryModifier modifier;
40
41 public ClassMapping(String obfFullName) {
42 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
43 }
44
45 public ClassMapping(String obfFullName, String deobfName) {
46 this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED);
47 }
48
49 public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) {
50 this.obfFullName = obfFullName;
51 ClassEntry classEntry = new ClassEntry(obfFullName);
52 obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName();
53 previousDeobfName = null;
54 this.deobfName = NameValidator.validateClassName(deobfName, false);
55 innerClassesByObfSimple = Maps.newHashMap();
56 innerClassesByObfFull = Maps.newHashMap();
57 innerClassesByDeobf = Maps.newHashMap();
58 fieldsByObf = Maps.newHashMap();
59 fieldsByDeobf = Maps.newHashMap();
60 methodsByObf = Maps.newHashMap();
61 methodsByDeobf = Maps.newHashMap();
62 isDirty = true;
63 this.modifier = modifier;
64 }
65
66 public static boolean isSimpleClassName(String name) {
67 return name.indexOf('/') < 0 && name.indexOf('$') < 0;
68 }
69
70 public String getObfFullName() {
71 return obfFullName;
72 }
73
74 public String getObfSimpleName() {
75 return obfSimpleName;
76 }
77
78 public String getPreviousDeobfName() {
79 return previousDeobfName;
80 }
81
82 public String getDeobfName() {
83 return deobfName;
84 }
85
86 public String getTranslatedName(TranslationDirection direction) {
87 return direction.choose(deobfName, obfFullName);
88 }
89
90 //// INNER CLASSES ////////
91
92 public void setDeobfName(String val) {
93 previousDeobfName = deobfName;
94 deobfName = NameValidator.validateClassName(val, false);
95 this.isDirty = true;
96 }
97
98 public Iterable<ClassMapping> innerClasses() {
99 assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size());
100 return innerClassesByObfSimple.values();
101 }
102
103 public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict {
104 // FIXME: dirty hack, that can get into issues, but it's a temp fix!
105 if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) {
106 throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName());
107 }
108 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
109 innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping);
110
111 if (classMapping.getDeobfName() != null) {
112 if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) {
113 throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
114 }
115 innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping);
116 }
117 this.isDirty = true;
118 }
119
120 public void removeInnerClassMapping(ClassMapping classMapping) {
121 innerClassesByObfFull.remove(classMapping.getObfFullName());
122 boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null;
123 assert (obfWasRemoved);
124 if (classMapping.getDeobfName() != null) {
125 boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
126 assert (deobfWasRemoved);
127 }
128 this.isDirty = true;
129 }
130
131 public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) {
132 ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName());
133 if (classMapping == null) {
134 classMapping = new ClassMapping(obfInnerClass.getName());
135 innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping);
136 boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null;
137 assert (wasAdded);
138 this.isDirty = true;
139 }
140 return classMapping;
141 }
142
143 public ClassMapping getInnerClassByObfSimple(String obfSimpleName) {
144 assert (isSimpleClassName(obfSimpleName));
145 return innerClassesByObfSimple.get(obfSimpleName);
146 }
147
148 public ClassMapping getInnerClassByDeobf(String deobfName) {
149 assert (isSimpleClassName(deobfName));
150 return innerClassesByDeobf.get(deobfName);
151 }
152
153 public ClassMapping getInnerClassByDeobfThenObfSimple(String name) {
154 ClassMapping classMapping = getInnerClassByDeobf(name);
155 if (classMapping == null) {
156 classMapping = getInnerClassByObfSimple(name);
157 }
158 return classMapping;
159 }
160
161 public String getDeobfInnerClassName(String obfSimpleName) {
162 assert (isSimpleClassName(obfSimpleName));
163 ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName);
164 if (classMapping != null) {
165 return classMapping.getDeobfName();
166 }
167 return null;
168 }
169
170 public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) {
171 ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass);
172 if (classMapping.getDeobfName() != null) {
173 boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
174 assert (wasRemoved);
175 }
176 classMapping.setDeobfName(deobfName);
177 if (deobfName != null) {
178 assert (isSimpleClassName(deobfName));
179 boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null;
180 assert (wasAdded);
181 }
182 this.isDirty = true;
183 }
184
185 public boolean hasInnerClassByObfSimple(String obfSimpleName) {
186 return innerClassesByObfSimple.containsKey(obfSimpleName);
187 }
188
189 //// FIELDS ////////
190
191 public boolean hasInnerClassByDeobf(String deobfName) {
192 return innerClassesByDeobf.containsKey(deobfName);
193 }
194
195 public Iterable<FieldMapping> fields() {
196 assert (fieldsByObf.size() == fieldsByDeobf.size());
197 return fieldsByObf.values();
198 }
199
200 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
201 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
202 }
203
204 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
205 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
206 }
207
208 public void addFieldMapping(FieldMapping fieldMapping) {
209 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
210 if (fieldsByObf.containsKey(obfKey)) {
211 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
212 }
213 if (fieldMapping.getDeobfName() != null) {
214 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
215 if (fieldsByDeobf.containsKey(deobfKey)) {
216 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
217 }
218 boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null;
219 assert (deobfWasAdded);
220 }
221 boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null;
222 assert (obfWasAdded);
223 this.isDirty = true;
224 }
225
226 public void removeFieldMapping(FieldMapping fieldMapping) {
227 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
228 assert (obfWasRemoved);
229 if (fieldMapping.getDeobfName() != null) {
230 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
231 assert (deobfWasRemoved);
232 }
233 this.isDirty = true;
234 }
235
236 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
237 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
238 }
239
240 public FieldMapping getFieldByObf(FieldEntry field) {
241 return getFieldByObf(field.getName(), field.getDesc());
242 }
243
244 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
245 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
246 }
247
248 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
249 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
250 if (fieldMapping != null) {
251 return fieldMapping.getObfName();
252 }
253 return null;
254 }
255
256 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
257 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
258 if (fieldMapping != null) {
259 return fieldMapping.getDeobfName();
260 }
261 return null;
262 }
263
264 private String getFieldKey(String name, TypeDescriptor desc) {
265 if (name == null) {
266 throw new IllegalArgumentException("name cannot be null!");
267 }
268 if (desc == null) {
269 throw new IllegalArgumentException("desc cannot be null!");
270 }
271 return name + ":" + desc;
272 }
273
274 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
275 assert (deobfName != null);
276 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
277 if (fieldMapping == null) {
278 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
279 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
280 assert (obfWasAdded);
281 } else {
282 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
283 assert (wasRemoved);
284 }
285 fieldMapping.setDeobfName(deobfName);
286 if (deobfName != null) {
287 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
288 assert (wasAdded);
289 }
290 this.isDirty = true;
291 }
292
293 //// METHODS ////////
294
295 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
296 assert (newObfName != null);
297 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
298 assert (fieldMapping != null);
299 fieldMapping.setObfName(newObfName);
300 fieldMapping.setObfDesc(newObfDesc);
301 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
302 assert (obfWasAdded);
303 this.isDirty = true;
304 }
305
306 public Iterable<MethodMapping> methods() {
307 assert (methodsByObf.size() >= methodsByDeobf.size());
308 return methodsByObf.values();
309 }
310
311 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
312 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
313 }
314
315 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
316 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
317 }
318
319 public void addMethodMapping(MethodMapping methodMapping) {
320 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
321 if (methodsByObf.containsKey(obfKey)) {
322 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
323 }
324 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
325 assert (wasAdded);
326 if (!methodMapping.isObfuscated()) {
327 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
328 if (methodsByDeobf.containsKey(deobfKey)) {
329 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
330 }
331 boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null;
332 assert (deobfWasAdded);
333 }
334 this.isDirty = true;
335 assert (methodsByObf.size() >= methodsByDeobf.size());
336 }
337
338 public void removeMethodMapping(MethodMapping methodMapping) {
339 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
340 assert (obfWasRemoved);
341 if (!methodMapping.isObfuscated()) {
342 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
343 assert (deobfWasRemoved);
344 }
345 this.isDirty = true;
346 }
347
348 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
349 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
350 }
351
352 public MethodMapping getMethodByObf(MethodEntry method) {
353 return getMethodByObf(method.getName(), method.getDesc());
354 }
355
356 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
357 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
358 }
359
360 private String getMethodKey(String name, MethodDescriptor descriptor) {
361 if (name == null) {
362 throw new IllegalArgumentException("name cannot be null!");
363 }
364 if (descriptor == null) {
365 throw new IllegalArgumentException("descriptor cannot be null!");
366 }
367 return name + descriptor;
368 }
369
370 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
371 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
372 if (methodMapping == null) {
373 methodMapping = createMethodMapping(obfName, obfDescriptor);
374 } else if (!methodMapping.isObfuscated()) {
375 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
376 assert (wasRemoved);
377 }
378 methodMapping.setDeobfName(deobfName);
379 if (deobfName != null) {
380 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
381 assert (wasAdded);
382 }
383 this.isDirty = true;
384 }
385
386 //// ARGUMENTS ////////
387
388 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
389 assert (newObfName != null);
390 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
391 assert (methodMapping != null);
392 methodMapping.setObfName(newObfName);
393 methodMapping.setObfDescriptor(newObfDescriptor);
394 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
395 assert (obfWasAdded);
396 this.isDirty = true;
397 }
398
399 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
400 assert (argumentName != null);
401 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
402 if (methodMapping == null) {
403 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
404 }
405 methodMapping.setLocalVariableName(argumentIndex, argumentName);
406 this.isDirty = true;
407 }
408
409 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
410 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
411 this.isDirty = true;
412 }
413
414 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
415 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
416 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
417 assert (wasAdded);
418 this.isDirty = true;
419 return methodMapping;
420 }
421
422 @Override
423 public String toString() {
424 StringBuilder buf = new StringBuilder();
425 buf.append(obfFullName);
426 buf.append(" <-> ");
427 buf.append(deobfName);
428 buf.append("\n");
429 buf.append("Fields:\n");
430 for (FieldMapping fieldMapping : fields()) {
431 buf.append("\t");
432 buf.append(fieldMapping.getObfName());
433 buf.append(" <-> ");
434 buf.append(fieldMapping.getDeobfName());
435 buf.append("\n");
436 }
437 buf.append("Methods:\n");
438 for (MethodMapping methodMapping : methodsByObf.values()) {
439 buf.append(methodMapping);
440 buf.append("\n");
441 }
442 buf.append("Inner Classes:\n");
443 for (ClassMapping classMapping : innerClassesByObfSimple.values()) {
444 buf.append("\t");
445 buf.append(classMapping.getObfSimpleName());
446 buf.append(" <-> ");
447 buf.append(classMapping.getDeobfName());
448 buf.append("\n");
449 }
450 return buf.toString();
451 }
452
453 @Override
454 public int compareTo(ClassMapping other) {
455 // sort by a, b, c, ... aa, ab, etc
456 if (obfFullName.length() != other.obfFullName.length()) {
457 return obfFullName.length() - other.obfFullName.length();
458 }
459 return obfFullName.compareTo(other.obfFullName);
460 }
461
462 public boolean renameObfClass(String oldObfClassName, String newObfClassName) {
463
464 // rename inner classes
465 for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) {
466 if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) {
467 boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null;
468 assert (wasRemoved);
469 boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null;
470 assert (wasAdded);
471 }
472 }
473
474 // rename field types
475 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
476 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
477 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
478 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
479 assert (wasRemoved);
480 boolean wasAdded = fieldsByObf
481 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
482 assert (wasAdded);
483 }
484 }
485
486 // rename method signatures
487 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
488 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
489 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
490 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
491 assert (wasRemoved);
492 boolean wasAdded = methodsByObf
493 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
494 assert (wasAdded);
495 }
496 }
497
498 if (obfFullName.equals(oldObfClassName)) {
499 // rename this class
500 obfFullName = newObfClassName;
501 return true;
502 }
503 this.isDirty = true;
504 return false;
505 }
506
507 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
508 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
509 return methodMapping != null && methodMapping.containsLocalVariable(name);
510 }
511
512 public ClassEntry getObfEntry() {
513 return new ClassEntry(obfFullName);
514 }
515
516 public ClassEntry getDeObfEntry() {
517 return deobfFullName != null ? new ClassEntry(deobfFullName) : null;
518 }
519
520 public boolean isObfuscated() {
521 return this.deobfName == null || this.deobfName.equals(this.obfFullName);
522 }
523
524 public String getSaveName() {
525 return this.isObfuscated() ? this.obfFullName : this.deobfName;
526 }
527
528 public boolean isDirty() {
529 return isDirty || areInnersDirty();
530 }
531
532 private boolean areInnersDirty(){
533 for (ClassMapping c : this.innerClasses()){
534 if (c.isDirty()){
535 return true;
536 }
537 }
538 return false;
539 }
540
541 public void resetDirty() {
542 this.isDirty = false;
543 }
544
545 public void markDirty() {
546 this.isDirty = true;
547 }
548
549 public Mappings.EntryModifier getModifier() {
550 return modifier;
551 }
552
553 public void setModifier(Mappings.EntryModifier modifier) {
554 if (this.modifier != modifier)
555 this.isDirty = true;
556 this.modifier = modifier;
557 }
558
559 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
560 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
561 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
562
563 if (fieldMapping.getModifier() != modifier) {
564 fieldMapping.setModifier(modifier);
565 this.isDirty = true;
566 }
567 }
568
569 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
570 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
571 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
572
573 if (methodMapping.getModifier() != modifier) {
574 methodMapping.setModifier(modifier);
575 this.isDirty = true;
576 }
577 }
578
579 // Used for tiny parsing to keep track of deobfuscate inner classes
580 public ClassMapping setDeobfInner(String deobName) {
581 this.deobfFullName = deobName;
582 return this;
583 }
584
585 public ClassMapping copy() {
586 ClassMapping copied = new ClassMapping(this.obfFullName);
587 copied.obfSimpleName= this.obfSimpleName;
588 copied.modifier = this.modifier;
589 copied.deobfFullName = this.deobfFullName;
590 copied.deobfName = this.deobfName;
591 copied.innerClassesByDeobf = this.innerClassesByDeobf;
592 copied.innerClassesByObfFull = this.innerClassesByObfFull;
593 copied.innerClassesByObfSimple = this.innerClassesByObfSimple;
594 copied.fieldsByObf = this.fieldsByObf;
595 copied.fieldsByDeobf = this.fieldsByDeobf;
596 copied.methodsByObf = this.methodsByObf;
597 copied.methodsByDeobf = this.methodsByDeobf;
598 return copied;
599 }
600
601 @Override
602 public int hashCode() {
603 return this.obfFullName.hashCode();
604 }
605
606 @Override
607 public boolean equals(Object obj) {
608 return obj instanceof ClassMapping && ((ClassMapping) obj).obfFullName.equals(this.obfFullName);
609 }
610
611 public boolean isEmpty() {
612 if (fieldsByDeobf.isEmpty() && methodsByDeobf.isEmpty() && deobfFullName == null && deobfName == null
613 && innerClassesByObfSimple.values().stream().allMatch(ClassMapping::isEmpty)) {
614
615 // check args
616 for (MethodMapping mapping : methodsByObf.values()) {
617 if (mapping.arguments().iterator().hasNext()) {
618 return false;
619 }
620 }
621
622 return true;
623 }
624
625 return false;
626 }
627}
diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
deleted file mode 100644
index 388e7ac..0000000
--- a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
+++ /dev/null
@@ -1,371 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Map;
23import java.util.ServiceLoader;
24
25public class DirectionalTranslator implements Translator {
26 private final TranslationDirection direction;
27 private final Map<String, ClassMapping> classes;
28 private final TranslationIndex index;
29
30 public DirectionalTranslator(ReferencedEntryPool entryPool) {
31 this.direction = null;
32 this.classes = Maps.newHashMap();
33 this.index = new TranslationIndex(entryPool);
34 }
35
36 public DirectionalTranslator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
37 this.direction = direction;
38 this.classes = classes;
39 this.index = index;
40 }
41
42 public TranslationDirection getDirection() {
43 return direction;
44 }
45
46 public TranslationIndex getTranslationIndex() {
47 return index;
48 }
49
50 @Override
51 public ClassEntry getTranslatedClass(ClassEntry entry) {
52 String className;
53 if (entry.isArray()) {
54 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
55 } else {
56 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
57 }
58 return new ClassEntry(className);
59 }
60
61 @Override
62 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
63 String className;
64 if (entry.isArray()) {
65 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
66 } else {
67 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
68 }
69 Signature translatedSignature = this.getTranslatedSignature(entry.getSignature());
70 return new ClassDefEntry(className, translatedSignature, getClassModifier(entry).transform(entry.getAccess()));
71 }
72
73 private String translateClassName(ClassEntry entry) {
74 // normal classes are easy
75 ClassMapping classMapping = this.classes.get(entry.getName());
76 if (classMapping == null) {
77 return entry.getName();
78 }
79 return classMapping.getTranslatedName(direction);
80 }
81
82 private String translateInnerClassName(ClassEntry entry) {
83 // translate as much of the class chain as we can
84 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
85 String[] obfClassNames = entry.getName().split("\\$");
86 StringBuilder buf = new StringBuilder();
87 for (int i = 0; i < obfClassNames.length; i++) {
88 boolean isFirstClass = buf.length() == 0;
89 String className = null;
90 ClassMapping classMapping = mappingsChain.get(i);
91 if (classMapping != null) {
92 className = this.direction.choose(
93 classMapping.getDeobfName(),
94 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
95 );
96 }
97 if (className == null) {
98 className = obfClassNames[i];
99 }
100 if (!isFirstClass) {
101 buf.append("$");
102 }
103 buf.append(className);
104 }
105 return buf.toString();
106 }
107
108 @Override
109 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
110 String translatedName = translateFieldName(entry);
111 if (translatedName == null) {
112 translatedName = entry.getName();
113 }
114 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
115 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
116 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
117 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
118 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, translatedAccess);
119 }
120
121 @Override
122 public FieldEntry getTranslatedField(FieldEntry entry) {
123 String translatedName = translateFieldName(entry);
124 if (translatedName == null) {
125 translatedName = entry.getName();
126 }
127 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
128 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
129 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
130 }
131
132 private String translateFieldName(FieldEntry entry) {
133 // resolve the class entry
134 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry);
135 if (resolvedClassEntry != null) {
136 // look for the class
137 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
138 if (classMapping != null) {
139 // look for the field
140 FieldMapping mapping = this.direction.choose(
141 classMapping.getFieldByObf(entry.getName(), entry.getDesc()),
142 classMapping.getFieldByDeobf(entry.getName(), getTranslatedTypeDesc(entry.getDesc()))
143 );
144 if (mapping != null) {
145 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
146 }
147 }
148 }
149 return null;
150 }
151
152 @Override
153 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
154 String translatedName = translateMethodName(entry);
155 if (translatedName == null) {
156 translatedName = entry.getName();
157 }
158 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
159 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
160 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
161 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
162 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, access);
163 }
164
165 @Override
166 public MethodEntry getTranslatedMethod(MethodEntry entry) {
167 String translatedName = translateMethodName(entry);
168 if (translatedName == null) {
169 translatedName = entry.getName();
170 }
171 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
172 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
173 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
174 }
175
176 private String translateMethodName(MethodEntry entry) {
177 // resolve the class entry
178 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry);
179 if (resolvedOwner != null) {
180 // look for class
181 ClassMapping classMapping = findClassMapping(resolvedOwner);
182 if (classMapping != null) {
183 // look for the method
184 MethodMapping mapping = this.direction.choose(
185 classMapping.getMethodByObf(entry.getName(), entry.getDesc()),
186 classMapping.getMethodByDeobf(entry.getName(), getTranslatedMethodDesc(entry.getDesc()))
187 );
188 if (mapping != null) {
189 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
190 }
191 }
192 }
193 return null;
194 }
195
196 @Override
197 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
198 String translatedArgumentName = translateLocalVariableName(entry);
199 if (translatedArgumentName == null) {
200 translatedArgumentName = inheritLocalVariableName(entry);
201 }
202 if (translatedArgumentName == null) {
203 translatedArgumentName = entry.getName();
204 }
205 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
206 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
207 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName, entry.isParameter());
208 }
209
210 @Override
211 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
212 String translatedArgumentName = translateLocalVariableName(entry);
213 if (translatedArgumentName == null) {
214 translatedArgumentName = inheritLocalVariableName(entry);
215 }
216 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
217 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
218 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
219 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName != null ? translatedArgumentName : entry.getName(), entry.isParameter(), translatedTypeDesc);
220 }
221
222 @Override
223 public boolean hasClassMapping(ClassEntry entry) {
224 return classes.containsKey(entry.getName());
225 }
226
227 @Override
228 public boolean hasFieldMapping(FieldEntry entry) {
229 return translateFieldName(entry) != null;
230 }
231
232 @Override
233 public boolean hasMethodMapping(MethodEntry entry) {
234 return translateMethodName(entry) != null;
235 }
236
237 @Override
238 public boolean hasLocalVariableMapping(LocalVariableEntry entry) {
239 return translateLocalVariableName(entry) != null || inheritLocalVariableName(entry) != null;
240 }
241
242 // TODO: support not identical behavior (specific to constructor)
243 private String translateLocalVariableName(LocalVariableEntry entry) {
244 // look for identical behavior in superclasses
245 ClassEntry ownerEntry = entry.getOwnerClassEntry();
246 if (ownerEntry != null) {
247 // look for the class
248 ClassMapping classMapping = findClassMapping(ownerEntry);
249 if (classMapping != null) {
250 // look for the method
251 MethodMapping methodMapping = this.direction.choose(
252 classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()),
253 classMapping.getMethodByDeobf(entry.getMethodName(), getTranslatedMethodDesc(entry.getMethodDesc()))
254 );
255 if (methodMapping != null) {
256 int index = entry.getIndex();
257 return this.direction.choose(
258 methodMapping.getDeobfLocalVariableName(index),
259 methodMapping.getObfLocalVariableName(index)
260 );
261 }
262 }
263 }
264 return null;
265 }
266
267 private String inheritLocalVariableName(LocalVariableEntry entry) {
268 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
269 // Check in mother class for the arg
270 for (ClassEntry ancestorEntry : ancestry) {
271 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
272 if (this.index.entryExists(motherArg)) {
273 String result = translateLocalVariableName(motherArg);
274 if (result != null) {
275 return result;
276 }
277 }
278 }
279 return null;
280 }
281
282 @Override
283 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
284 return desc.remap(this::remapClass);
285 }
286
287 @Override
288 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
289 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
290 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
291 for (TypeDescriptor argument : arguments) {
292 translatedArguments.add(getTranslatedTypeDesc(argument));
293 }
294 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
295 }
296
297 @Override
298 public Signature getTranslatedSignature(Signature signature) {
299 if (signature == null) {
300 return null;
301 }
302 return signature.remap(this::remapClass);
303 }
304
305 private ClassMapping findClassMapping(ClassEntry entry) {
306 List<ClassMapping> mappingChain = getClassMappingChain(entry);
307 return mappingChain.get(mappingChain.size() - 1);
308 }
309
310 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
311
312 // get a list of all the classes in the hierarchy
313 String[] parts = entry.getName().split("\\$");
314 List<ClassMapping> mappingsChain = Lists.newArrayList();
315
316 // get mappings for the outer class
317 ClassMapping outerClassMapping = this.classes.get(parts[0]);
318 mappingsChain.add(outerClassMapping);
319
320 for (int i = 1; i < parts.length; i++) {
321
322 // get mappings for the inner class
323 ClassMapping innerClassMapping = null;
324 if (outerClassMapping != null) {
325 innerClassMapping = this.direction.choose(
326 outerClassMapping.getInnerClassByObfSimple(parts[i]),
327 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
328 );
329 }
330 mappingsChain.add(innerClassMapping);
331 outerClassMapping = innerClassMapping;
332 }
333
334 assert (mappingsChain.size() == parts.length);
335 return mappingsChain;
336 }
337
338 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
339 ClassMapping classMapping = findClassMapping(entry);
340 if (classMapping != null) {
341 return classMapping.getModifier();
342 }
343 return Mappings.EntryModifier.UNCHANGED;
344 }
345
346 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
347 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
348 if (classMapping != null) {
349 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
350 if (fieldMapping != null) {
351 return fieldMapping.getModifier();
352 }
353 }
354 return Mappings.EntryModifier.UNCHANGED;
355 }
356
357 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
358 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
359 if (classMapping != null) {
360 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
361 if (methodMapping != null) {
362 return methodMapping.getModifier();
363 }
364 }
365 return Mappings.EntryModifier.UNCHANGED;
366 }
367
368 private String remapClass(String name) {
369 return getTranslatedClass(new ClassEntry(name)).getName();
370 }
371}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
deleted file mode 100644
index 8fbe095..0000000
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ /dev/null
@@ -1,100 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.FieldEntry;
16import cuchaz.enigma.throwables.IllegalNameException;
17
18public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
19
20 private String obfName;
21 private String deobfName;
22 private TypeDescriptor obfDesc;
23 private Mappings.EntryModifier modifier;
24
25 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
26 this.obfName = obfName;
27 this.deobfName = NameValidator.validateFieldName(deobfName);
28 this.obfDesc = obfDesc;
29 this.modifier = modifier;
30 }
31
32 @Override
33 public FieldEntry getObfEntry(ClassEntry classEntry) {
34 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
35 }
36
37 @Override
38 public String getObfName() {
39 return this.obfName;
40 }
41
42 public void setObfName(String name) {
43 try {
44 NameValidator.validateMethodName(name);
45 } catch (IllegalNameException ex) {
46 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
47 if (this.deobfName == null) {
48 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
49 setDeobfName(name + "_auto_deob");
50 }
51 }
52 this.obfName = name;
53 }
54
55 public String getDeobfName() {
56 return this.deobfName;
57 }
58
59 public void setDeobfName(String val) {
60 this.deobfName = NameValidator.validateFieldName(val);
61 }
62
63 public TypeDescriptor getObfDesc() {
64 return this.obfDesc;
65 }
66
67 public void setObfDesc(TypeDescriptor val) {
68 this.obfDesc = val;
69 }
70
71 public Mappings.EntryModifier getModifier() {
72 return modifier;
73 }
74
75 public void setModifier(Mappings.EntryModifier modifier) {
76 this.modifier = modifier;
77 }
78
79 @Override
80 public int compareTo(FieldMapping other) {
81 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
82 }
83
84 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
85 // rename obf classes in the desc
86 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
87 if (className.equals(oldObfClassName)) {
88 return newObfClassName;
89 }
90 return className;
91 });
92
93 if (!newDesc.equals(this.obfDesc)) {
94 this.obfDesc = newDesc;
95 return true;
96 }
97 return false;
98 }
99
100}
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
deleted file mode 100644
index bfe66b2..0000000
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
+++ /dev/null
@@ -1,58 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.LocalVariableEntry;
15import cuchaz.enigma.mapping.entry.MethodEntry;
16
17public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
18
19 private int index;
20 private String name;
21
22 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public LocalVariableMapping(int index, String name) {
24 this.index = index;
25 this.name = NameValidator.validateArgumentName(name);
26 }
27
28 public LocalVariableMapping(LocalVariableMapping other) {
29 this.index = other.index;
30 this.name = other.name;
31 }
32
33 public int getIndex() {
34 return this.index;
35 }
36
37 public String getName() {
38 return this.name;
39 }
40
41 public void setName(String val) {
42 this.name = NameValidator.validateArgumentName(val);
43 }
44
45 @Deprecated
46 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
47 return new LocalVariableEntry(methodEntry, index, name);
48 }
49
50 public LocalVariableEntry getObfEntry(MethodEntry methodEntry, boolean parameter) {
51 return new LocalVariableEntry(methodEntry, index, name, parameter);
52 }
53
54 @Override
55 public int compareTo(LocalVariableMapping other) {
56 return Integer.compare(this.index, other.index);
57 }
58}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
deleted file mode 100644
index c865079..0000000
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ /dev/null
@@ -1,268 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.api.EnigmaPlugin;
19import cuchaz.enigma.bytecode.AccessFlags;
20import cuchaz.enigma.mapping.entry.ClassEntry;
21import cuchaz.enigma.mapping.entry.MethodEntry;
22import cuchaz.enigma.throwables.MappingConflict;
23
24import java.io.File;
25import java.io.IOException;
26import java.util.*;
27import java.util.stream.Collectors;
28
29public class Mappings {
30
31 private final FormatType originMapping;
32 protected Map<String, ClassMapping> classesByObf;
33 protected Map<String, ClassMapping> classesByDeobf;
34 private Mappings previousState;
35
36 public Mappings() {
37 this(FormatType.ENIGMA_DIRECTORY);
38 }
39
40 public Mappings(FormatType originMapping) {
41 this.originMapping = originMapping;
42 this.classesByObf = Maps.newHashMap();
43 this.classesByDeobf = Maps.newHashMap();
44 }
45
46 public Collection<ClassMapping> classes() {
47 assert (this.classesByObf.size() >= this.classesByDeobf.size());
48 return this.classesByObf.values();
49 }
50
51 public void addClassMapping(ClassMapping classMapping) throws MappingConflict {
52 if (this.classesByObf.containsKey(classMapping.getObfFullName())) {
53 throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName());
54 }
55 this.classesByObf.put(classMapping.getObfFullName(), classMapping);
56
57 if (classMapping.getDeobfName() != null) {
58 if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) {
59 throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName());
60 }
61 this.classesByDeobf.put(classMapping.getDeobfName(), classMapping);
62 }
63 }
64
65 public void removeClassMapping(ClassMapping classMapping) {
66 boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null;
67 assert (obfWasRemoved);
68 if (classMapping.getDeobfName() != null) {
69 boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
70 assert (deobfWasRemoved);
71 }
72 }
73
74 public ClassMapping getClassByObf(ClassEntry entry) {
75 return getClassByObf(entry.getName());
76 }
77
78 public ClassMapping getClassByObf(String obfName) {
79 return this.classesByObf.get(obfName);
80 }
81
82 public ClassMapping getClassByDeobf(ClassEntry entry) {
83 return getClassByDeobf(entry.getName());
84 }
85
86 public ClassMapping getClassByDeobf(String deobfName) {
87 return this.classesByDeobf.get(deobfName);
88 }
89
90 public void setClassDeobfName(ClassMapping classMapping, String deobfName) {
91 if (classMapping.getDeobfName() != null) {
92 boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null;
93 assert (wasRemoved);
94 }
95 classMapping.setDeobfName(deobfName);
96 if (deobfName != null) {
97 boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null;
98 assert (wasAdded);
99 }
100 }
101
102 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
103 switch (direction) {
104 case DEOBFUSCATING:
105
106 return new DirectionalTranslator(direction, this.classesByObf, index);
107
108 case OBFUSCATING:
109
110 // fill in the missing deobf class entries with obf entries
111 Map<String, ClassMapping> classes = Maps.newHashMap();
112 for (ClassMapping classMapping : classes()) {
113 if (classMapping.getDeobfName() != null) {
114 classes.put(classMapping.getDeobfName(), classMapping);
115 } else {
116 classes.put(classMapping.getObfFullName(), classMapping);
117 }
118 }
119
120 // translate the translation index
121 // NOTE: this isn't actually recursive
122 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
123
124 return new DirectionalTranslator(direction, classes, deobfIndex);
125
126 default:
127 throw new Error("Invalid translation direction!");
128 }
129 }
130
131 @Override
132 public String toString() {
133 StringBuilder buf = new StringBuilder();
134 for (ClassMapping classMapping : this.classesByObf.values()) {
135 buf.append(classMapping);
136 buf.append("\n");
137 }
138 return buf.toString();
139 }
140
141 public void renameObfClass(String oldObfName, String newObfName) {
142 new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> {
143 boolean wasRemoved = this.classesByObf.remove(oldObfName) != null;
144 assert (wasRemoved);
145 boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null;
146 assert (wasAdded);
147 });
148 }
149
150 public Set<String> getAllObfClassNames() {
151 final Set<String> classNames = Sets.newHashSet();
152 for (ClassMapping classMapping : classes()) {
153
154 // add the class name
155 classNames.add(classMapping.getObfFullName());
156
157 // add classes from method signatures
158 for (MethodMapping methodMapping : classMapping.methods()) {
159 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
160 if (desc.containsType()) {
161 classNames.add(desc.getTypeEntry().getClassName());
162 }
163 }
164 }
165 }
166 return classNames;
167 }
168
169 public boolean containsDeobfClass(String deobfName) {
170 return this.classesByDeobf.containsKey(deobfName);
171 }
172
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
174 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
175 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
176 }
177
178 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
179 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
180 if (classMapping != null)
181 for (FieldMapping fieldMapping : classMapping.fields())
182 if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName()))
183 return true;
184
185 return false;
186 }
187
188 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
189 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
190 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
191 }
192
193 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
194 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
195 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
196 }
197
198 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
199 List<ClassMapping> mappingChain = Lists.newArrayList();
200 ClassMapping classMapping = null;
201 for (ClassEntry obfClassEntry : obfClass.getClassChain()) {
202 if (mappingChain.isEmpty()) {
203 classMapping = this.classesByObf.get(obfClassEntry.getName());
204 } else if (classMapping != null) {
205 classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName());
206 }
207 mappingChain.add(classMapping);
208 }
209 return mappingChain;
210 }
211
212 public FormatType getOriginMappingFormat() {
213 return originMapping;
214 }
215
216 public void savePreviousState() {
217 this.previousState = new Mappings(this.originMapping);
218 this.previousState.classesByDeobf = new HashMap<>();
219 for (Map.Entry<String, ClassMapping> entry : this.classesByDeobf.entrySet()) {
220 this.previousState.classesByDeobf.put(entry.getKey(), entry.getValue().copy());
221 }
222 this.previousState.classesByObf = new HashMap<>();
223 for (Map.Entry<String, ClassMapping> entry : this.classesByObf.entrySet()) {
224 this.previousState.classesByObf.put(entry.getKey(), entry.getValue().copy());
225 }
226 classesByDeobf.values().forEach(ClassMapping::resetDirty);
227 classesByObf.values().forEach(ClassMapping::resetDirty);
228 }
229
230 public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException {
231 new MappingsEnigmaWriter().write(file, this, isDirectoryFormat);
232 this.savePreviousState();
233 }
234
235 public void saveSRGMappings(File file) throws IOException {
236 new MappingsSRGWriter().write(file, this);
237 }
238
239 public Mappings getPreviousState() {
240 return previousState;
241 }
242
243 public enum FormatType {
244 ENIGMA_FILE, ENIGMA_DIRECTORY, TINY_FILE, SRG_FILE
245 }
246
247 public enum EntryModifier {
248 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
249
250 public String getFormattedName() {
251 return " ACC:" + super.toString();
252 }
253
254 public AccessFlags transform(AccessFlags access) {
255 switch (this) {
256 case PUBLIC:
257 return access.setPublic();
258 case PROTECTED:
259 return access.setProtected();
260 case PRIVATE:
261 return access.setPrivate();
262 case UNCHANGED:
263 default:
264 return access;
265 }
266 }
267 }
268}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
deleted file mode 100644
index a42f255..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ /dev/null
@@ -1,101 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry;
18import cuchaz.enigma.mapping.entry.EntryFactory;
19import cuchaz.enigma.mapping.entry.FieldEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
21
22import java.util.Map;
23
24public class MappingsChecker {
25
26 private JarIndex index;
27 private Map<ClassEntry, ClassMapping> droppedClassMappings;
28 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
29 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
30 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
31
32 public MappingsChecker(JarIndex index) {
33 this.index = index;
34 this.droppedClassMappings = Maps.newHashMap();
35 this.droppedInnerClassMappings = Maps.newHashMap();
36 this.droppedFieldMappings = Maps.newHashMap();
37 this.droppedMethodMappings = Maps.newHashMap();
38 }
39
40 public Map<ClassEntry, ClassMapping> getDroppedClassMappings() {
41 return this.droppedClassMappings;
42 }
43
44 public Map<ClassEntry, ClassMapping> getDroppedInnerClassMappings() {
45 return this.droppedInnerClassMappings;
46 }
47
48 public Map<FieldEntry, FieldMapping> getDroppedFieldMappings() {
49 return this.droppedFieldMappings;
50 }
51
52 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
53 return this.droppedMethodMappings;
54 }
55
56 public void dropBrokenMappings(Mappings mappings) {
57 for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) {
58 if (!checkClassMapping(classMapping)) {
59 mappings.removeClassMapping(classMapping);
60 this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping);
61 }
62 }
63 }
64
65 private boolean checkClassMapping(ClassMapping classMapping) {
66
67 // check the class
68 ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping);
69 if (!this.index.getObfClassEntries().contains(classEntry)) {
70 return false;
71 }
72
73 // check the fields
74 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
75 FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping);
76 if (!this.index.containsObfField(obfFieldEntry)) {
77 classMapping.removeFieldMapping(fieldMapping);
78 this.droppedFieldMappings.put(obfFieldEntry, fieldMapping);
79 }
80 }
81
82 // check methods
83 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
84 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
85 if (!this.index.containsObfMethod(obfMethodEntry)) {
86 classMapping.removeMethodMapping(methodMapping);
87 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
88 }
89 }
90
91 // check inner classes
92 for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) {
93 if (!checkClassMapping(innerClassMapping)) {
94 classMapping.removeInnerClassMapping(innerClassMapping);
95 this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping);
96 }
97 }
98
99 return true;
100 }
101}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
deleted file mode 100644
index ddbee76..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ /dev/null
@@ -1,186 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import com.google.common.collect.Queues;
5import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException;
7
8import java.io.BufferedReader;
9import java.io.File;
10import java.io.FileInputStream;
11import java.io.IOException;
12import java.io.InputStream;
13import java.io.InputStreamReader;
14import java.util.Deque;
15import java.util.function.Supplier;
16
17public class MappingsEnigmaReader {
18
19 public Mappings read(File file) throws IOException, MappingParseException {
20 Mappings mappings;
21
22 // Multiple file
23 if (file.isDirectory()) {
24 mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY);
25 readDirectory(mappings, file);
26 } else {
27 mappings = new Mappings();
28 readFile(mappings, file);
29 }
30 return mappings;
31 }
32
33 public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException {
34 File[] files = directory.listFiles();
35 if (files != null) {
36 for (File file : files) {
37 if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping"))
38 readFile(mappings, file);
39 else if (file.isDirectory())
40 readDirectory(mappings, file.getAbsoluteFile());
41 }
42 mappings.savePreviousState();
43 } else
44 throw new IOException("Cannot access directory" + directory.getAbsolutePath());
45 }
46
47 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException {
48 return readFileStream(mappings, new FileInputStream(file), file::getAbsolutePath);
49 }
50
51 public Mappings readFileStream(Mappings mappings, InputStream stream, Supplier<String> filenameSupplier) throws IOException, MappingParseException {
52 try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, Charsets.UTF_8))) {
53 Deque<Object> mappingStack = Queues.newArrayDeque();
54
55 int lineNumber = 0;
56 String line;
57 while ((line = in.readLine()) != null) {
58 lineNumber++;
59
60 // strip comments
61 int commentPos = line.indexOf('#');
62 if (commentPos >= 0) {
63 line = line.substring(0, commentPos);
64 }
65
66 // skip blank lines
67 if (line.trim().length() <= 0) {
68 continue;
69 }
70
71 // get the indent of this line
72 int indent = 0;
73 for (int i = 0; i < line.length(); i++) {
74 if (line.charAt(i) != '\t') {
75 break;
76 }
77 indent++;
78 }
79
80 // handle stack pops
81 while (indent < mappingStack.size()) {
82 mappingStack.pop();
83 }
84
85 String[] parts = line.trim().split("\\s");
86 try {
87 // read the first token
88 String token = parts[0];
89
90 if (token.equalsIgnoreCase("CLASS")) {
91 ClassMapping classMapping;
92 if (indent <= 0) {
93 // outer class
94 classMapping = readClass(parts, false);
95 mappings.addClassMapping(classMapping);
96 } else {
97
98 // inner class
99 if (!(mappingStack.peek() instanceof ClassMapping)) {
100 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected CLASS entry here!");
101 }
102
103 classMapping = readClass(parts, true);
104 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping);
105 }
106 mappingStack.push(classMapping);
107 } else if (token.equalsIgnoreCase("FIELD")) {
108 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
109 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected FIELD entry here!");
110 }
111 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts));
112 } else if (token.equalsIgnoreCase("METHOD")) {
113 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
114 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected METHOD entry here!");
115 }
116 MethodMapping methodMapping = readMethod(parts);
117 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping);
118 mappingStack.push(methodMapping);
119 } else if (token.equalsIgnoreCase("ARG")) {
120 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
121 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected ARG entry here!");
122 }
123 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts));
124 }
125 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
126 throw new MappingParseException(filenameSupplier, lineNumber, "Malformed line:\n" + line);
127 } catch (MappingConflict e) {
128 throw new MappingParseException(filenameSupplier, lineNumber, e.getMessage());
129 }
130 }
131 return mappings;
132 }
133 }
134
135 private LocalVariableMapping readArgument(String[] parts) {
136 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
137 }
138
139 private ClassMapping readClass(String[] parts, boolean makeSimple) {
140 if (parts.length == 2) {
141 return new ClassMapping(parts[1]);
142 } else if (parts.length == 3) {
143 boolean access = parts[2].startsWith("ACC:");
144 ClassMapping mapping;
145 if (access)
146 mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4)));
147 else
148 mapping = new ClassMapping(parts[1], parts[2]);
149
150 return mapping;
151 } else if (parts.length == 4)
152 return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4)));
153 return null;
154 }
155
156 /* TEMP */
157 protected FieldMapping readField(String[] parts) {
158 FieldMapping mapping = null;
159 if (parts.length == 4) {
160 boolean access = parts[3].startsWith("ACC:");
161 if (access)
162 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
163 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
164 else
165 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
166 } else if (parts.length == 5)
167 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
168 return mapping;
169 }
170
171 private MethodMapping readMethod(String[] parts) {
172 MethodMapping mapping = null;
173 if (parts.length == 3)
174 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
175 else if (parts.length == 4) {
176 boolean access = parts[3].startsWith("ACC:");
177 if (access)
178 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
179 else
180 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
181 } else if (parts.length == 5)
182 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
183 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
184 return mapping;
185 }
186}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
deleted file mode 100644
index e3302b1..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ /dev/null
@@ -1,160 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Charsets;
15
16import java.io.*;
17import java.util.*;
18
19public class MappingsEnigmaWriter {
20
21 public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException {
22 if (!isDirectoryFormat) {
23 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8));
24 write(outputWriter, mappings);
25 outputWriter.close();
26 } else
27 writeAsDirectory(out, mappings);
28 }
29
30 public void writeAsDirectory(File target, Mappings mappings) throws IOException {
31 if (!target.exists() && !target.mkdirs())
32 throw new IOException("Cannot create mapping directory!");
33
34 Mappings previousState = mappings.getPreviousState();
35 for (ClassMapping classMapping : sorted(mappings.classes())) {
36 File result = new File(target, classMapping.getSaveName() + ".mapping");
37
38 if (!classMapping.isDirty()) {
39 continue;
40 }
41
42 if (classMapping.isEmpty()) {
43 if (result.exists()) {
44 result.delete();
45 }
46 continue;
47 }
48
49 if (previousState != null) {
50 ClassMapping previousClass = previousState.classesByObf.get(classMapping.getObfFullName());
51 File previousFile;
52 if (previousClass != null) {
53 previousFile = new File(target, previousClass.getSaveName() + ".mapping");
54 } else {
55 previousFile = new File(target, classMapping.getObfFullName() + ".mapping");
56 }
57 if (previousFile.exists() && !previousFile.delete()) {
58 System.err.println("Failed to delete old class mapping " + previousFile.getName());
59 }
60 }
61
62 File packageFile = result.getParentFile();
63 if (!packageFile.exists()) {
64 packageFile.mkdirs();
65 }
66 result.createNewFile();
67
68 try (PrintWriter outputWriter = new PrintWriter(new BufferedWriter(new FileWriter(result)))) {
69 write(outputWriter, classMapping, 0);
70 }
71 }
72
73 // Remove dropped mappings
74 if (previousState != null) {
75 Set<ClassMapping> droppedClassMappings = new HashSet<>(previousState.classes());
76 droppedClassMappings.removeAll(mappings.classes());
77 for (ClassMapping droppedMapping : droppedClassMappings) {
78 File result = new File(target, droppedMapping.getSaveName() + ".mapping");
79 if (!result.exists()) {
80 continue;
81 }
82 if (!result.delete()) {
83 System.err.println("Failed to delete dropped class mapping " + result.getName());
84 }
85 }
86 }
87 }
88
89 public void write(PrintWriter out, Mappings mappings) throws IOException {
90 for (ClassMapping classMapping : sorted(mappings.classes())) {
91 write(out, classMapping, 0);
92 }
93 }
94
95 protected void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
96 if (classMapping.getDeobfName() == null) {
97 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(),
98 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
99 } else {
100 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(),
101 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
102 }
103
104 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
105 write(out, innerClassMapping, depth + 1);
106 }
107
108 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
109 write(out, fieldMapping, depth + 1);
110 }
111
112 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
113 write(out, methodMapping, depth + 1);
114 }
115 }
116
117 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
118 if (fieldMapping.getDeobfName() == null)
119 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
120 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
121 else
122 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(),
123 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
124 }
125
126 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
127 if (methodMapping.isObfuscated()) {
128 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
129 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
130 } else {
131 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(),
132 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
133 }
134
135 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
136 write(out, localVariableMapping, depth + 1);
137 }
138 }
139
140 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
141 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
142 }
143
144 protected <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
145 List<T> out = new ArrayList<>();
146 for (T t : classes) {
147 out.add(t);
148 }
149 Collections.sort(out);
150 return out;
151 }
152
153 private String getIndent(int depth) {
154 StringBuilder buf = new StringBuilder();
155 for (int i = 0; i < depth; i++) {
156 buf.append("\t");
157 }
158 return buf.toString();
159 }
160}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
deleted file mode 100644
index 8ef4f12..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ /dev/null
@@ -1,365 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.mapping.entry.*;
17import cuchaz.enigma.throwables.IllegalNameException;
18import cuchaz.enigma.throwables.MappingConflict;
19
20import java.io.IOException;
21import java.io.ObjectOutputStream;
22import java.io.OutputStream;
23import java.util.List;
24import java.util.Set;
25import java.util.zip.GZIPOutputStream;
26
27public class MappingsRenamer {
28
29 private final JarIndex index;
30 private final ReferencedEntryPool entryPool;
31 private Mappings mappings;
32
33 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
34 this.index = index;
35 this.mappings = mappings;
36 this.entryPool = entryPool;
37 }
38
39 public void setMappings(Mappings mappings) {
40 this.mappings = mappings;
41 }
42
43 public void setClassName(ClassEntry obf, String deobfName) {
44
45 deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass());
46
47 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
48 if (mappingChain.size() == 1) {
49
50 if (deobfName != null) {
51 // make sure we don't rename to an existing obf or deobf class
52 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
53 throw new IllegalNameException(deobfName, "There is already a class with that name");
54 }
55 }
56
57 ClassMapping classMapping = mappingChain.get(0);
58 mappings.setClassDeobfName(classMapping, deobfName);
59
60 } else {
61
62 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
63
64 if (deobfName != null) {
65 // make sure we don't rename to an existing obf or deobf inner class
66 if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) {
67 throw new IllegalNameException(deobfName, "There is already a class with that name");
68 }
69 }
70
71 outerClassMapping.setInnerClassName(obf, deobfName);
72 }
73 }
74
75 public void removeClassMapping(ClassEntry obf) {
76 setClassName(obf, null);
77 }
78
79 public void markClassAsDeobfuscated(ClassEntry obf) {
80 String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName();
81 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf);
82 if (mappingChain.size() == 1) {
83 ClassMapping classMapping = mappingChain.get(0);
84 mappings.setClassDeobfName(classMapping, deobfName);
85 } else {
86 ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2);
87 outerClassMapping.setInnerClassName(obf, deobfName);
88 }
89 }
90
91 public void setFieldName(FieldEntry obf, String deobfName) {
92 deobfName = NameValidator.validateFieldName(deobfName);
93 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
94 ClassEntry definedClass = null;
95 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) {
96 definedClass = obf.getOwnerClassEntry();
97 } else {
98 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
99 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
100 definedClass = ancestorEntry;
101 break;
102 }
103 }
104 }
105
106 if (definedClass != null) {
107 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
108 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
109 if (className == null)
110 className = definedClass.getClassName();
111 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
112 }
113
114 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
115 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
116 }
117
118 public void removeFieldMapping(FieldEntry obf) {
119 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
120 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
121 }
122
123 public void markFieldAsDeobfuscated(FieldEntry obf) {
124 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
125 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
126 }
127
128 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
129 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
130
131 // TODO: Verify if I don't break things
132 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
133 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
134 || index.containsObfMethod(targetEntry)) {
135 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
136 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
137 if (deobfClassName == null) {
138 deobfClassName = entry.getClassName();
139 }
140 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
141 }
142
143 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
144 validateMethodTreeName(entry.updateOwnership(child), deobfName);
145 }
146 }
147
148 public void setMethodTreeName(MethodEntry obf, String deobfName) {
149 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obf);
150
151 deobfName = NameValidator.validateMethodName(deobfName);
152 for (MethodEntry entry : implementations) {
153 validateMethodTreeName(entry, deobfName);
154 }
155
156 for (MethodEntry entry : implementations) {
157 setMethodName(entry, deobfName);
158 }
159 }
160
161 public void setMethodName(MethodEntry obf, String deobfName) {
162 deobfName = NameValidator.validateMethodName(deobfName);
163 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
164 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
165
166 // TODO: Verify if I don't break things
167 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
168 || index.containsObfMethod(targetEntry)) {
169 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
170 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
171 if (deobfClassName == null) {
172 deobfClassName = obf.getClassName();
173 }
174 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
175 }
176
177 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
178 }
179
180 public void removeMethodTreeMapping(MethodEntry obf) {
181 index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping);
182 }
183
184 public void removeMethodMapping(MethodEntry obf) {
185 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
186 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
187 }
188
189 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
190 index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated);
191 }
192
193 public void markMethodAsDeobfuscated(MethodEntry obf) {
194 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
195 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
196 }
197
198 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
199 MethodEntry obfMethod = obf.getOwnerEntry();
200 if (!obf.isParameter()) {
201 setLocalVariableName(obf, deobfName);
202 return;
203 }
204
205 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
206 for (MethodEntry entry : implementations) {
207 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
208 if (classMapping != null) {
209 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
210 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
211 // TODO: Verify if I don't break things
212 if (mapping != null) {
213 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
214 if (localVariableMapping.getIndex() != obf.getIndex()) {
215 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
216 || localVariableMapping.getName().equals(deobfName)) {
217 throw new IllegalNameException(deobfName, "There is already an argument with that name");
218 }
219 }
220 }
221 }
222 }
223 }
224
225 for (MethodEntry entry : implementations) {
226 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName(), obf.isParameter()), deobfName);
227 }
228 }
229
230 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
231 deobfName = NameValidator.validateArgumentName(deobfName);
232 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
233 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
234 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
235 // TODO: Verify if I don't break things
236 if (mapping != null) {
237 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
238 if (localVariableMapping.getIndex() != obf.getIndex()) {
239 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
240 || localVariableMapping.getName().equals(deobfName)) {
241 throw new IllegalNameException(deobfName, "There is already an argument with that name");
242 }
243 }
244 }
245 }
246
247 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
248 }
249
250 public void removeLocalVariableMapping(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
253 }
254
255 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
256 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
257 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
258 }
259
260 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
261 classMapping.removeFieldMapping(fieldMapping);
262 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
263 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
264 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
265 targetClassMapping.addFieldMapping(fieldMapping);
266 return true;
267 } else {
268 System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName());
269 }
270 }
271 return false;
272 }
273
274 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
275 classMapping.removeMethodMapping(methodMapping);
276 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
277 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
278 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
279 targetClassMapping.addMethodMapping(methodMapping);
280 return true;
281 } else {
282 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
283 }
284 }
285 return false;
286 }
287
288 public void write(OutputStream out) throws IOException {
289 // TEMP: just use the object output for now. We can find a more efficient storage format later
290 GZIPOutputStream gzipout = new GZIPOutputStream(out);
291 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
292 oout.writeObject(this);
293 gzipout.finish();
294 }
295
296 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) {
297 List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry);
298 return mappingChain.get(mappingChain.size() - 1);
299 }
300
301 private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) {
302 List<ClassEntry> classChain = obfClassEntry.getClassChain();
303 List<ClassMapping> mappingChain = mappings.getClassMappingChain(obfClassEntry);
304 for (int i = 0; i < classChain.size(); i++) {
305 ClassEntry classEntry = classChain.get(i);
306 ClassMapping classMapping = mappingChain.get(i);
307 if (classMapping == null) {
308
309 // create it
310 classMapping = new ClassMapping(classEntry.getName());
311 mappingChain.set(i, classMapping);
312
313 // add it to the right parent
314 try {
315 if (i == 0) {
316 mappings.addClassMapping(classMapping);
317 } else {
318 mappingChain.get(i - 1).addInnerClassMapping(classMapping);
319 }
320 } catch (MappingConflict mappingConflict) {
321 mappingConflict.printStackTrace();
322 }
323 }
324 }
325 return mappingChain;
326 }
327
328 public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry);
330 classMapping.setModifier(modifier);
331 }
332
333 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
335 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
336 }
337
338 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
339 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
340 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
341 }
342
343 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
344 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
345 return classMapping.getModifier();
346 }
347
348 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
349 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
350 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
351 if (fieldMapping == null) {
352 return Mappings.EntryModifier.UNCHANGED;
353 }
354 return fieldMapping.getModifier();
355 }
356
357 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
358 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
359 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
360 if (methodMapping == null) {
361 return Mappings.EntryModifier.UNCHANGED;
362 }
363 return methodMapping.getModifier();
364 }
365}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
deleted file mode 100644
index 32f0ee9..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ /dev/null
@@ -1,80 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.analysis.TranslationIndex;
5import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
6
7import java.io.*;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.List;
11
12/**
13 * Created by Mark on 11/08/2016.
14 */
15public class MappingsSRGWriter {
16
17 public void write(File file, Mappings mappings) throws IOException {
18 if (file.exists()) {
19 file.delete();
20 }
21 file.createNewFile();
22
23 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
24
25 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
26 List<String> fieldMappings = new ArrayList<>();
27 List<String> methodMappings = new ArrayList<>();
28 for (ClassMapping classMapping : sorted(mappings.classes())) {
29 if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) {
30 continue;
31 }
32 writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName());
33 writer.write(System.lineSeparator());
34 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
35 if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) {
36 continue;
37 }
38 String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName();
39 String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName();
40 writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName());
41 writer.write(System.lineSeparator());
42 for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) {
43 fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName());
44 }
45
46 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
47 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
48 }
49 }
50
51 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
52 fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName());
53 }
54
55 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
56 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
57 }
58 }
59 for (String fd : fieldMappings) {
60 writer.write(fd);
61 writer.write(System.lineSeparator());
62 }
63
64 for (String md : methodMappings) {
65 writer.write(md);
66 writer.write(System.lineSeparator());
67 }
68
69 writer.close();
70 }
71
72 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
73 List<T> out = new ArrayList<>();
74 for (T t : classes) {
75 out.add(t);
76 }
77 Collections.sort(out);
78 return out;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
deleted file mode 100644
index 756ac43..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ /dev/null
@@ -1,130 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Charsets;
4import com.google.common.collect.Maps;
5import cuchaz.enigma.mapping.entry.ClassEntry;
6import cuchaz.enigma.throwables.MappingConflict;
7import cuchaz.enigma.throwables.MappingParseException;
8
9import java.io.File;
10import java.io.IOException;
11import java.nio.file.Files;
12import java.util.ArrayList;
13import java.util.List;
14import java.util.Map;
15
16public class MappingsTinyReader {
17 public ClassMapping readClass(String[] parts) {
18 // Extract the inner naming of the deob form if it have one
19 String deobName = parts[2].contains("$") ? parts[2].substring(parts[2].lastIndexOf('$') + 1) : parts[2];
20 return new ClassMapping(parts[1], deobName).setDeobfInner(parts[2]);
21 }
22
23 public FieldMapping readField(String[] parts) {
24 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
25 }
26
27 public MethodMapping readMethod(String[] parts) {
28 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
29 }
30
31 public Mappings read(File file) throws IOException, MappingParseException {
32 Mappings mappings = new Mappings(Mappings.FormatType.TINY_FILE);
33 List<String> lines = Files.readAllLines(file.toPath(), Charsets.UTF_8);
34 Map<String, ClassMapping> classMappingMap = Maps.newHashMap();
35 lines.remove(0); // TODO: use the header
36 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
37 String line = lines.get(lineNumber);
38 String[] parts = line.split("\t");
39 try {
40 String token = parts[0];
41 ClassMapping classMapping;
42 switch (token) {
43 case "CLASS":
44
45 // Check for orphan created by field or method entries. It shouldn't be possible but I prefer to handle this case
46 if (classMappingMap.containsKey(parts[1])) {
47 classMapping = classMappingMap.get(parts[1]);
48
49 // We have the full deob name, Enigma only support simple class name so we extract it.
50 String deobName = parts[2].contains("$") ?
51 parts[2].substring(parts[2].lastIndexOf('$') + 1) :
52 parts[2];
53
54 // Add full deob name to the class mapping to handle inner class after this loop
55 classMappingMap.put(parts[2], classMapping.setDeobfInner(parts[2]));
56 classMapping.setDeobfName(deobName);
57
58 // Avoid to make the mapping dirty directly at the startup
59 classMapping.resetDirty();
60 } else
61 classMapping = readClass(parts);
62 classMappingMap.put(parts[1], classMapping);
63 break;
64 case "FIELD":
65 // We can have missing classes mappings because they don't have a ob name, so we create it and use it
66 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
67 classMapping.addFieldMapping(readField(parts));
68 break;
69 case "METHOD":
70 // We can have missing classes mappings because they don't have a ob name, so we create it and use it
71 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
72 classMapping.addMethodMapping(readMethod(parts));
73 break;
74 case "MTH-ARG":
75 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
76 classMapping.setArgumentName(parts[3], new MethodDescriptor(parts[2]), Integer.parseInt(parts[4]), parts[5]);
77 break;
78 default:
79 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !");
80 }
81 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
82 ex.printStackTrace();
83 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
84 }
85 }
86
87 List<ClassMapping> toRegister = new ArrayList<>(classMappingMap.values());
88
89 // After having completely parsed the file, we need to register it to the real mapping
90 for (ClassMapping classMapping : toRegister) {
91 ClassEntry obEntry = classMapping.getObfEntry();
92 ClassEntry deobEntry = classMapping.getDeObfEntry();
93 try {
94 if (obEntry.isInnerClass()) {
95 ClassMapping parent = classMappingMap.get(obEntry.getOuterClassName());
96 // Inner class can miss their parent... So we create it and add it to the mappings
97 if (parent == null) {
98 parent = new ClassMapping(obEntry.getOuterClassName()); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
99 classMappingMap.put(obEntry.getOuterClassName(), parent);
100 mappings.addClassMapping(parent);
101 }
102 // Add the inner class to the parent
103 parent.addInnerClassMapping(classMapping);
104 }
105 // obf class can become deobf inner classs, manage this case.
106 else if (deobEntry != null && deobEntry.isInnerClass()) {
107 String outerClassName = deobEntry.getOuterClassName();
108 ClassMapping parent = classMappingMap.get(outerClassName);
109
110 // Only the inner is deob??? Okay
111 if (parent == null) {
112 parent = classMappingMap.get(outerClassName);
113 if (parent == null) {
114 parent = new ClassMapping(outerClassName); // FIXME: WE ACTUALLY DON'T MANAGE INNER CLASS OF INNER CLASS
115 classMappingMap.put(outerClassName, parent);
116 mappings.addClassMapping(parent);
117 }
118 }
119 parent.addInnerClassMapping(classMapping);
120 } else
121 mappings.addClassMapping(classMapping);
122 } catch (MappingConflict e) {
123 throw new MappingParseException(file, -1, e.getMessage());
124 }
125 }
126 lines.clear();
127 classMappingMap.clear();
128 return mappings;
129 }
130}
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
deleted file mode 100644
index 6effb91..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ /dev/null
@@ -1,21 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.Entry;
16
17public interface MemberMapping<T extends Entry> {
18 T getObfEntry(ClassEntry classEntry);
19
20 String getObfName();
21}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
deleted file mode 100644
index 0fc0351..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
+++ /dev/null
@@ -1,114 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.utils.Utils;
17
18import java.util.ArrayList;
19import java.util.List;
20import java.util.function.Function;
21
22public class MethodDescriptor {
23
24 private List<TypeDescriptor> argumentDescs;
25 private TypeDescriptor returnDesc;
26
27 public MethodDescriptor(String desc) {
28 try {
29 this.argumentDescs = Lists.newArrayList();
30 int i = 0;
31 while (i < desc.length()) {
32 char c = desc.charAt(i);
33 if (c == '(') {
34 assert (this.argumentDescs.isEmpty());
35 assert (this.returnDesc == null);
36 i++;
37 } else if (c == ')') {
38 i++;
39 break;
40 } else {
41 String type = TypeDescriptor.parseFirst(desc.substring(i));
42 this.argumentDescs.add(new TypeDescriptor(type));
43 i += type.length();
44 }
45 }
46 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
47 } catch (Exception ex) {
48 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
49 }
50 }
51
52 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
53 this.argumentDescs = argumentDescs;
54 this.returnDesc = returnDesc;
55 }
56
57 public List<TypeDescriptor> getArgumentDescs() {
58 return this.argumentDescs;
59 }
60
61 public TypeDescriptor getReturnDesc() {
62 return this.returnDesc;
63 }
64
65 @Override
66 public String toString() {
67 StringBuilder buf = new StringBuilder();
68 buf.append("(");
69 for (TypeDescriptor desc : this.argumentDescs) {
70 buf.append(desc);
71 }
72 buf.append(")");
73 buf.append(this.returnDesc);
74 return buf.toString();
75 }
76
77 public Iterable<TypeDescriptor> types() {
78 List<TypeDescriptor> descs = Lists.newArrayList();
79 descs.addAll(this.argumentDescs);
80 descs.add(this.returnDesc);
81 return descs;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
87 }
88
89 public boolean equals(MethodDescriptor other) {
90 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
91 }
92
93 @Override
94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
96 }
97
98 public boolean hasClass(ClassEntry classEntry) {
99 for (TypeDescriptor desc : types()) {
100 if (desc.containsType() && desc.getTypeEntry().equals(classEntry)) {
101 return true;
102 }
103 }
104 return false;
105 }
106
107 public MethodDescriptor remap(Function<String, String> remapper) {
108 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
109 for (TypeDescriptor desc : this.argumentDescs) {
110 argumentDescs.add(desc.remap(remapper));
111 }
112 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
113 }
114}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
deleted file mode 100644
index 2f10144..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ /dev/null
@@ -1,210 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.IllegalNameException;
19import cuchaz.enigma.throwables.MappingConflict;
20
21import java.util.Map;
22
23public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
24
25 private String obfName;
26 private String deobfName;
27 private MethodDescriptor obfDescriptor;
28 private Map<Integer, LocalVariableMapping> localVariables;
29 private Mappings.EntryModifier modifier;
30
31 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
32 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
33 }
34
35 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
36 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
37 }
38
39 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
40 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
41 Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null");
42 this.obfName = obfName;
43 this.deobfName = NameValidator.validateMethodName(deobfName);
44 this.obfDescriptor = obfDescriptor;
45 this.localVariables = Maps.newTreeMap();
46 this.modifier = modifier;
47 }
48
49 public MethodMapping(MethodMapping other, Translator translator) {
50 this.obfName = other.obfName;
51 this.deobfName = other.deobfName;
52 this.modifier = other.modifier;
53 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
54 this.localVariables = Maps.newTreeMap();
55 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
56 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
57 }
58 }
59
60 @Override
61 public String getObfName() {
62 return this.obfName;
63 }
64
65 public void setObfName(String name) {
66 try {
67 NameValidator.validateMethodName(name);
68 } catch (IllegalNameException ex) {
69 // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
70 if (this.deobfName == null) {
71 System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
72 setDeobfName(name + "_auto_deob");
73 }
74 }
75 this.obfName = name;
76 }
77
78 public String getDeobfName() {
79 if (deobfName == null) {
80 return obfName;
81 }
82 return this.deobfName;
83 }
84
85 public void setDeobfName(String val) {
86 this.deobfName = NameValidator.validateMethodName(val);
87 }
88
89 public MethodDescriptor getObfDesc() {
90 return this.obfDescriptor;
91 }
92
93 public void setObfDescriptor(MethodDescriptor val) {
94 this.obfDescriptor = val;
95 }
96
97 public Iterable<LocalVariableMapping> arguments() {
98 return this.localVariables.values();
99 }
100
101 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
102 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
103 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
104 }
105 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
106 }
107
108 public String getObfLocalVariableName(int index) {
109 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
110 if (localVariableMapping != null) {
111 return localVariableMapping.getName();
112 }
113
114 return null;
115 }
116
117 public String getDeobfLocalVariableName(int index) {
118 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
119 if (localVariableMapping != null) {
120 return localVariableMapping.getName();
121 }
122
123 return null;
124 }
125
126 public void setLocalVariableName(int index, String name) {
127 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
128 if (localVariableMapping == null) {
129 localVariableMapping = new LocalVariableMapping(index, name);
130 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
131 assert (wasAdded);
132 } else {
133 localVariableMapping.setName(name);
134 }
135 }
136
137 public void removeLocalVariableName(int index) {
138 boolean wasRemoved = this.localVariables.remove(index) != null;
139 assert (wasRemoved);
140 }
141
142 @Override
143 public String toString() {
144 StringBuilder buf = new StringBuilder();
145 buf.append("\t");
146 buf.append(this.obfName);
147 buf.append(" <-> ");
148 buf.append(this.deobfName);
149 buf.append("\n");
150 buf.append("\t");
151 buf.append(this.obfDescriptor);
152 buf.append("\n");
153 buf.append("\tLocal Variables:\n");
154 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
155 buf.append("\t\t");
156 buf.append(localVariableMapping.getIndex());
157 buf.append(" -> ");
158 buf.append(localVariableMapping.getName());
159 buf.append("\n");
160 }
161 return buf.toString();
162 }
163
164 @Override
165 public int compareTo(MethodMapping other) {
166 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
167 }
168
169 public boolean containsLocalVariable(String name) {
170 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
171 if (localVariableMapping.getName().equals(name)) {
172 return true;
173 }
174 }
175 return false;
176 }
177
178 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
179 // rename obf classes in the signature
180 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
181 if (className.equals(oldObfClassName)) {
182 return newObfClassName;
183 }
184 return className;
185 });
186
187 if (!newDescriptor.equals(this.obfDescriptor)) {
188 this.obfDescriptor = newDescriptor;
189 return true;
190 }
191 return false;
192 }
193
194 @Override
195 public MethodEntry getObfEntry(ClassEntry classEntry) {
196 return new MethodEntry(classEntry, this.obfName, this.obfDescriptor);
197 }
198
199 public Mappings.EntryModifier getModifier() {
200 return modifier;
201 }
202
203 public void setModifier(Mappings.EntryModifier modifier) {
204 this.modifier = modifier;
205 }
206
207 public boolean isObfuscated() {
208 return deobfName == null || deobfName.equals(obfName);
209 }
210}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
deleted file mode 100644
index fca8cfc..0000000
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ /dev/null
@@ -1,73 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.throwables.IllegalNameException;
16
17import java.util.Arrays;
18import java.util.List;
19import java.util.regex.Pattern;
20
21public class NameValidator {
22
23 private static final Pattern IdentifierPattern;
24 private static final Pattern ClassPattern;
25 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
31 );
32
33 static {
34 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
35 IdentifierPattern = Pattern.compile(identifierRegex);
36 ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex));
37 }
38
39 public static String validateClassName(String name, boolean packageRequired) {
40 if (name == null) {
41 return null;
42 }
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 }
46 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package");
48 }
49 return name;
50 }
51
52 public static String validateFieldName(String name) {
53 if (name == null) {
54 return null;
55 }
56 if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) {
57 throw new IllegalNameException(name, "This doesn't look like a legal identifier");
58 }
59 return name;
60 }
61
62 public static String validateMethodName(String name) {
63 return validateFieldName(name);
64 }
65
66 public static String validateArgumentName(String name) {
67 return validateFieldName(name);
68 }
69
70 public static boolean isReserved(String name) {
71 return ReservedWords.contains(name);
72 }
73}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
deleted file mode 100644
index 071e4af..0000000
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ /dev/null
@@ -1,82 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
4import org.objectweb.asm.signature.SignatureReader;
5import org.objectweb.asm.signature.SignatureVisitor;
6import org.objectweb.asm.signature.SignatureWriter;
7
8import java.util.function.Function;
9import java.util.regex.Pattern;
10
11public class Signature {
12 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
13
14 private final String signature;
15 private final boolean isType;
16
17 private Signature(String signature, boolean isType) {
18 if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) {
19 signature = signature.replaceAll(":Ljava/lang/Object;:", "::");
20 }
21
22 this.signature = signature;
23 this.isType = isType;
24 }
25
26 public static Signature createTypedSignature(String signature) {
27 if (signature != null && !signature.isEmpty()) {
28 return new Signature(signature, true);
29 }
30 return new Signature(null, true);
31 }
32
33 public static Signature createSignature(String signature) {
34 if (signature != null && !signature.isEmpty()) {
35 return new Signature(signature, false);
36 }
37 return new Signature(null, false);
38 }
39
40 public String getSignature() {
41 return signature;
42 }
43
44 public boolean isType() {
45 return isType;
46 }
47
48 public Signature remap(Function<String, String> remapper) {
49 if (signature == null) {
50 return this;
51 }
52 SignatureWriter writer = new SignatureWriter();
53 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer);
54 if (isType) {
55 new SignatureReader(signature).acceptType(visitor);
56 } else {
57 new SignatureReader(signature).accept(visitor);
58 }
59 return new Signature(writer.toString(), isType);
60 }
61
62 @Override
63 public boolean equals(Object obj) {
64 if (obj instanceof Signature) {
65 Signature other = (Signature) obj;
66 return (other.signature == null && signature == null || other.signature != null
67 && signature != null && other.signature.equals(signature))
68 && other.isType == this.isType;
69 }
70 return false;
71 }
72
73 @Override
74 public int hashCode() {
75 return signature.hashCode() | (isType ? 1 : 0) << 16;
76 }
77
78 @Override
79 public String toString() {
80 return signature;
81 }
82}
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
deleted file mode 100644
index ddc5af4..0000000
--- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java
+++ /dev/null
@@ -1,92 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15
16import java.io.IOException;
17import java.io.StringReader;
18import java.util.List;
19
20public class SignatureUpdater {
21
22 public static String update(String signature, ClassNameUpdater updater) {
23 try {
24 StringBuilder buf = new StringBuilder();
25
26 // read the signature character-by-character
27 StringReader reader = new StringReader(signature);
28 int i;
29 while ((i = reader.read()) != -1) {
30 char c = (char) i;
31
32 // does this character start a class name?
33 if (c == 'L') {
34 // update the class name and add it to the buffer
35 buf.append('L');
36 String className = readClass(reader);
37 if (className == null) {
38 throw new IllegalArgumentException("Malformed signature: " + signature);
39 }
40 buf.append(updater.update(className));
41 buf.append(';');
42 } else {
43 // copy the character into the buffer
44 buf.append(c);
45 }
46 }
47
48 return buf.toString();
49 } catch (IOException ex) {
50 // I'm pretty sure a StringReader will never throw one of these
51 throw new Error(ex);
52 }
53 }
54
55 private static String readClass(StringReader reader) throws IOException {
56 // read all the characters in the buffer until we hit a ';'
57 // remember to treat generics correctly
58 StringBuilder buf = new StringBuilder();
59 int depth = 0;
60 int i;
61 while ((i = reader.read()) != -1) {
62 char c = (char) i;
63
64 if (c == '<') {
65 depth++;
66 } else if (c == '>') {
67 depth--;
68 } else if (depth == 0) {
69 if (c == ';') {
70 return buf.toString();
71 } else {
72 buf.append(c);
73 }
74 }
75 }
76
77 return null;
78 }
79
80 public static List<String> getClasses(String signature) {
81 final List<String> classNames = Lists.newArrayList();
82 update(signature, className -> {
83 classNames.add(className);
84 return className;
85 });
86 return classNames;
87 }
88
89 public interface ClassNameUpdater {
90 String update(String className);
91 }
92}
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
deleted file mode 100644
index 4bbde54..0000000
--- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
+++ /dev/null
@@ -1,36 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14public enum TranslationDirection {
15
16 DEOBFUSCATING {
17 @Override
18 public <T> T choose(T deobfChoice, T obfChoice) {
19 if (deobfChoice == null) {
20 return obfChoice;
21 }
22 return deobfChoice;
23 }
24 },
25 OBFUSCATING {
26 @Override
27 public <T> T choose(T deobfChoice, T obfChoice) {
28 if (obfChoice == null) {
29 return deobfChoice;
30 }
31 return obfChoice;
32 }
33 };
34
35 public abstract <T> T choose(T deobfChoice, T obfChoice);
36}
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
deleted file mode 100644
index a9ff1cb..0000000
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ /dev/null
@@ -1,109 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.mapping.entry.*;
15import org.objectweb.asm.Handle;
16import org.objectweb.asm.Type;
17
18public interface Translator {
19 ClassEntry getTranslatedClass(ClassEntry entry);
20
21 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
22
23 FieldEntry getTranslatedField(FieldEntry entry);
24
25 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
26
27 MethodEntry getTranslatedMethod(MethodEntry entry);
28
29 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
30
31 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
32
33 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
34
35 boolean hasClassMapping(ClassEntry entry);
36
37 boolean hasFieldMapping(FieldEntry entry);
38
39 boolean hasMethodMapping(MethodEntry entry);
40
41 boolean hasLocalVariableMapping(LocalVariableEntry entry);
42
43 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
44
45 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
46
47 Signature getTranslatedSignature(Signature signature);
48
49 default Type getTranslatedType(Type type) {
50 String descString = type.getDescriptor();
51 switch (type.getSort()) {
52 case Type.OBJECT: {
53 ClassEntry classEntry = new ClassEntry(type.getInternalName());
54 return Type.getObjectType(getTranslatedClass(classEntry).getName());
55 }
56 case Type.ARRAY: {
57 TypeDescriptor descriptor = new TypeDescriptor(descString);
58 return Type.getType(getTranslatedTypeDesc(descriptor).toString());
59 }
60 case Type.METHOD: {
61 MethodDescriptor descriptor = new MethodDescriptor(descString);
62 return Type.getMethodType(getTranslatedMethodDesc(descriptor).toString());
63 }
64 }
65 return type;
66 }
67
68 default Handle getTranslatedHandle(Handle handle) {
69 MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc()));
70 MethodEntry translatedMethod = getTranslatedMethod(entry);
71 ClassEntry ownerClass = translatedMethod.getOwnerClassEntry();
72 return new Handle(handle.getTag(), ownerClass.getName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface());
73 }
74
75 default Object getTranslatedValue(Object value) {
76 if (value instanceof Type) {
77 return this.getTranslatedType((Type) value);
78 } else if (value instanceof Handle) {
79 return getTranslatedHandle((Handle) value);
80 }
81 return value;
82 }
83
84 @SuppressWarnings("unchecked")
85 default <T extends Entry> T getTranslatedEntry(T entry) {
86 if (entry instanceof ClassDefEntry) {
87 return (T) getTranslatedClassDef((ClassDefEntry) entry);
88 } else if (entry instanceof ClassEntry) {
89 return (T) getTranslatedClass((ClassEntry) entry);
90 } else if (entry instanceof FieldDefEntry) {
91 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
92 } else if (entry instanceof MethodDefEntry) {
93 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
94 } else if (entry instanceof FieldEntry) {
95 return (T) getTranslatedField((FieldEntry) entry);
96 } else if (entry instanceof MethodEntry) {
97 return (T) getTranslatedMethod((MethodEntry) entry);
98 } else if (entry instanceof LocalVariableDefEntry) {
99 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
100 } else if (entry instanceof LocalVariableEntry) {
101 return (T) getTranslatedVariable((LocalVariableEntry) entry);
102 } else if (entry instanceof TypeDescriptor) {
103 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
104 } else if (entry instanceof MethodDescriptor) {
105 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
106 }
107 throw new IllegalArgumentException("Cannot translate unknown entry type");
108 }
109}
diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
deleted file mode 100644
index 6e58aa0..0000000
--- a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
+++ /dev/null
@@ -1,258 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17
18import java.util.Map;
19import java.util.function.Function;
20
21public class TypeDescriptor {
22
23 protected final String desc;
24
25 public TypeDescriptor(String desc) {
26 Preconditions.checkNotNull(desc, "Desc cannot be null");
27
28 // don't deal with generics
29 // this is just for raw jvm types
30 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
31 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
32 }
33
34 this.desc = desc;
35 }
36
37 public static String parseFirst(String in) {
38
39 if (in == null || in.length() <= 0) {
40 throw new IllegalArgumentException("No desc to parse, input is empty!");
41 }
42
43 // read one desc from the input
44
45 char c = in.charAt(0);
46
47 // first check for void
48 if (c == 'V') {
49 return "V";
50 }
51
52 // then check for primitives
53 Primitive primitive = Primitive.get(c);
54 if (primitive != null) {
55 return in.substring(0, 1);
56 }
57
58 // then check for classes
59 if (c == 'L') {
60 return readClass(in);
61 }
62
63 // then check for templates
64 if (c == 'T') {
65 return readClass(in);
66 }
67
68 // then check for arrays
69 int dim = countArrayDimension(in);
70 if (dim > 0) {
71 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
72 return in.substring(0, dim + arrayType.length());
73 }
74
75 throw new IllegalArgumentException("don't know how to parse: " + in);
76 }
77
78 private static int countArrayDimension(String in) {
79 int i = 0;
80 while (i < in.length() && in.charAt(i) == '[')
81 i++;
82 return i;
83 }
84
85 private static String readClass(String in) {
86 // read all the characters in the buffer until we hit a ';'
87 // include the parameters too
88 StringBuilder buf = new StringBuilder();
89 int depth = 0;
90 for (int i = 0; i < in.length(); i++) {
91 char c = in.charAt(i);
92 buf.append(c);
93
94 if (c == '<') {
95 depth++;
96 } else if (c == '>') {
97 depth--;
98 } else if (depth == 0 && c == ';') {
99 return buf.toString();
100 }
101 }
102 return null;
103 }
104
105 public static TypeDescriptor of(String name) {
106 return new TypeDescriptor("L" + name + ";");
107 }
108
109 @Override
110 public String toString() {
111 return this.desc;
112 }
113
114 public boolean isVoid() {
115 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
116 }
117
118 public boolean isPrimitive() {
119 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
120 }
121
122 public Primitive getPrimitive() {
123 if (!isPrimitive()) {
124 throw new IllegalStateException("not a primitive");
125 }
126 return Primitive.get(this.desc.charAt(0));
127 }
128
129 public boolean isType() {
130 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
131 }
132
133 public ClassEntry getTypeEntry() {
134 if (isType()) {
135 String name = this.desc.substring(1, this.desc.length() - 1);
136
137 int pos = name.indexOf('<');
138 if (pos >= 0) {
139 // remove the parameters from the class name
140 name = name.substring(0, pos);
141 }
142
143 return new ClassEntry(name);
144
145 } else if (isArray() && getArrayType().isType()) {
146 return getArrayType().getTypeEntry();
147 } else {
148 throw new IllegalStateException("desc doesn't have a class");
149 }
150 }
151
152 public boolean isArray() {
153 return this.desc.charAt(0) == '[';
154 }
155
156 public int getArrayDimension() {
157 if (!isArray()) {
158 throw new IllegalStateException("not an array");
159 }
160 return countArrayDimension(this.desc);
161 }
162
163 public TypeDescriptor getArrayType() {
164 if (!isArray()) {
165 throw new IllegalStateException("not an array");
166 }
167 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length()));
168 }
169
170 public boolean containsType() {
171 return isType() || (isArray() && getArrayType().containsType());
172 }
173
174 @Override
175 public boolean equals(Object other) {
176 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
177 }
178
179 public boolean equals(TypeDescriptor other) {
180 return this.desc.equals(other.desc);
181 }
182
183 @Override
184 public int hashCode() {
185 return this.desc.hashCode();
186 }
187
188 public TypeDescriptor remap(Function<String, String> remapper) {
189 String desc = this.desc;
190 if (isType() || (isArray() && containsType())) {
191 String replacedName = remapper.apply(this.getTypeEntry().getName());
192 if (replacedName != null) {
193 if (this.isType()) {
194 desc = "L" + replacedName + ";";
195 } else {
196 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
197 }
198 }
199 }
200 return new TypeDescriptor(desc);
201 }
202
203 private static String getArrayPrefix(int dimension) {
204 StringBuilder buf = new StringBuilder();
205 for (int i = 0; i < dimension; i++) {
206 buf.append("[");
207 }
208 return buf.toString();
209 }
210
211 public int getSize() {
212 switch (desc.charAt(0)) {
213 case 'J':
214 case 'D':
215 if (desc.length() == 1) {
216 return 2;
217 } else {
218 return 1;
219 }
220 default:
221 return 1;
222 }
223 }
224
225 public enum Primitive {
226 BYTE('B'),
227 CHARACTER('C'),
228 SHORT('S'),
229 INTEGER('I'),
230 LONG('J'),
231 FLOAT('F'),
232 DOUBLE('D'),
233 BOOLEAN('Z');
234
235 private static final Map<Character, Primitive> lookup;
236
237 static {
238 lookup = Maps.newTreeMap();
239 for (Primitive val : values()) {
240 lookup.put(val.getCode(), val);
241 }
242 }
243
244 private char code;
245
246 Primitive(char code) {
247 this.code = code;
248 }
249
250 public static Primitive get(char code) {
251 return lookup.get(code);
252 }
253
254 public char getCode() {
255 return this.code;
256 }
257 }
258}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
deleted file mode 100644
index df72e7e..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
+++ /dev/null
@@ -1,38 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17
18public class ClassDefEntry extends ClassEntry implements DefEntry {
19 private final AccessFlags access;
20 private final Signature signature;
21
22 public ClassDefEntry(String className, Signature signature, AccessFlags access) {
23 super(className);
24 Preconditions.checkNotNull(signature, "Class signature cannot be null");
25 Preconditions.checkNotNull(access, "Class access cannot be null");
26 this.signature = signature;
27 this.access = access;
28 }
29
30 public Signature getSignature() {
31 return signature;
32 }
33
34 @Override
35 public AccessFlags getAccess() {
36 return access;
37 }
38}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
deleted file mode 100644
index c795825..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
+++ /dev/null
@@ -1,175 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Lists;
16
17import java.util.List;
18
19public class ClassEntry implements Entry {
20
21 private final String name;
22
23 public ClassEntry(String className) {
24 Preconditions.checkNotNull(className, "Class name cannot be null");
25
26 if (className.indexOf('.') >= 0) {
27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
28 }
29
30 this.name = className;
31
32 if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) {
33 throw new IllegalArgumentException("Inner class must not have a package: " + className);
34 }
35 }
36
37 public ClassEntry(ClassEntry other) {
38 this.name = other.name;
39 }
40
41 @Override
42 public String getName() {
43 return this.name;
44 }
45
46 @Override
47 public String getClassName() {
48 return this.name;
49 }
50
51 @Override
52 public ClassEntry getOwnerClassEntry() {
53 return this;
54 }
55
56 @Override
57 public ClassEntry updateOwnership(ClassEntry classEntry) {
58 return classEntry;
59 }
60
61 @Override
62 public int hashCode() {
63 return this.name.hashCode();
64 }
65
66 @Override
67 public boolean equals(Object other) {
68 return other instanceof ClassEntry && equals((ClassEntry) other);
69 }
70
71 public boolean equals(ClassEntry other) {
72 return other != null && this.name.equals(other.name);
73 }
74
75 @Override
76 public String toString() {
77 return this.name;
78 }
79
80 public boolean isArray() {
81 return this.name.lastIndexOf('[') >= 0;
82 }
83
84 public boolean isInnerClass() {
85 return this.name.lastIndexOf('$') >= 0;
86 }
87
88 public List<String> getClassChainNames() {
89 return Lists.newArrayList(this.name.split("\\$"));
90 }
91
92 public List<ClassEntry> getClassChain() {
93 List<ClassEntry> entries = Lists.newArrayList();
94 StringBuilder buf = new StringBuilder();
95 for (String name : getClassChainNames()) {
96 if (buf.length() > 0) {
97 buf.append("$");
98 }
99 buf.append(name);
100 entries.add(new ClassEntry(buf.toString()));
101 }
102 return entries;
103 }
104
105 public String getOutermostClassName() {
106 if (isInnerClass()) {
107 return this.name.substring(0, this.name.indexOf('$'));
108 }
109 return this.name;
110 }
111
112 public ClassEntry getOutermostClassEntry() {
113 return new ClassEntry(getOutermostClassName());
114 }
115
116 public String getOuterClassName() {
117 if (!isInnerClass()) {
118 throw new Error("This is not an inner class!");
119 }
120 return this.name.substring(0, this.name.lastIndexOf('$'));
121 }
122
123 public ClassEntry getOuterClassEntry() {
124 return new ClassEntry(getOuterClassName());
125 }
126
127 public String getInnermostClassName() {
128 if (!isInnerClass()) {
129 throw new Error("This is not an inner class!");
130 }
131 return this.name.substring(this.name.lastIndexOf('$') + 1);
132 }
133
134 public boolean isInDefaultPackage() {
135 return this.name.indexOf('/') < 0;
136 }
137
138 public String getPackageName() {
139 return getPackageName(this.name);
140 }
141
142 public String getSimpleName() {
143 int pos = this.name.lastIndexOf('/');
144 if (pos > 0) {
145 return this.name.substring(pos + 1);
146 }
147 return this.name;
148 }
149
150 public static String getPackageName(String name) {
151 int pos = name.lastIndexOf('/');
152 if (pos > 0) {
153 return name.substring(0, pos);
154 }
155 return null;
156 }
157
158 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
159 assert (classChain.contains(this));
160 StringBuilder buf = new StringBuilder();
161 for (ClassEntry chainEntry : classChain) {
162 if (buf.length() == 0) {
163 buf.append(chainEntry.getName());
164 } else {
165 buf.append("$");
166 buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName());
167 }
168
169 if (chainEntry == this) {
170 break;
171 }
172 }
173 return new ClassEntry(buf.toString());
174 }
175}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java
deleted file mode 100644
index 43ad027..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/DefEntry.java
+++ /dev/null
@@ -1,7 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import cuchaz.enigma.bytecode.AccessFlags;
4
5public interface DefEntry extends Entry {
6 AccessFlags getAccess();
7}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
deleted file mode 100644
index b612140..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
+++ /dev/null
@@ -1,22 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14public interface Entry {
15 String getName();
16
17 String getClassName();
18
19 ClassEntry getOwnerClassEntry();
20
21 Entry updateOwnership(ClassEntry classEntry);
22}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
deleted file mode 100644
index 5bd159f..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.mapping.ClassMapping;
16import cuchaz.enigma.mapping.FieldMapping;
17import cuchaz.enigma.mapping.MethodDescriptor;
18import cuchaz.enigma.mapping.MethodMapping;
19
20public class EntryFactory {
21 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
22 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
23 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
24 }
25
26 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
27 return new ClassEntry(classMapping.getObfFullName());
28 }
29
30 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
31 return new ClassEntry(classMapping.getDeobfName());
32 }
33
34 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
35 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
36 }
37
38 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
39 return new MethodEntry(classEntry, name, desc);
40 }
41
42 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
43 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
44 }
45
46 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
47 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
deleted file mode 100644
index 223410f..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
+++ /dev/null
@@ -1,44 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17import cuchaz.enigma.mapping.TypeDescriptor;
18
19public class FieldDefEntry extends FieldEntry implements DefEntry {
20 private final AccessFlags access;
21 private final Signature signature;
22
23 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
24 super(ownerEntry, name, desc);
25 Preconditions.checkNotNull(access, "Field access cannot be null");
26 Preconditions.checkNotNull(signature, "Field signature cannot be null");
27 this.access = access;
28 this.signature = signature;
29 }
30
31 @Override
32 public AccessFlags getAccess() {
33 return access;
34 }
35
36 public Signature getSignature() {
37 return signature;
38 }
39
40 @Override
41 public FieldDefEntry updateOwnership(ClassEntry owner) {
42 return new FieldDefEntry(owner, this.name, this.desc, signature, access);
43 }
44}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
deleted file mode 100644
index b6e1554..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
+++ /dev/null
@@ -1,77 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.TypeDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class FieldEntry implements Entry {
19
20 protected final ClassEntry ownerEntry;
21 protected final String name;
22 protected final TypeDescriptor desc;
23
24 // NOTE: this argument order is important for the MethodReader/MethodWriter
25 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
26 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
27 Preconditions.checkNotNull(name, "Field name cannot be null");
28 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
29
30 this.ownerEntry = ownerEntry;
31 this.name = name;
32 this.desc = desc;
33 }
34
35 @Override
36 public ClassEntry getOwnerClassEntry() {
37 return this.ownerEntry;
38 }
39
40 @Override
41 public String getName() {
42 return this.name;
43 }
44
45 @Override
46 public String getClassName() {
47 return this.ownerEntry.getName();
48 }
49
50 public TypeDescriptor getDesc() {
51 return this.desc;
52 }
53
54 @Override
55 public FieldEntry updateOwnership(ClassEntry owner) {
56 return new FieldEntry(owner, this.name, this.desc);
57 }
58
59 @Override
60 public int hashCode() {
61 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
62 }
63
64 @Override
65 public boolean equals(Object other) {
66 return other instanceof FieldEntry && equals((FieldEntry) other);
67 }
68
69 public boolean equals(FieldEntry other) {
70 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
71 }
72
73 @Override
74 public String toString() {
75 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
deleted file mode 100644
index d186664..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
+++ /dev/null
@@ -1,61 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableDefEntry extends LocalVariableEntry {
13
14 protected final MethodDefEntry ownerEntry;
15 protected final TypeDescriptor desc;
16
17 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
18 this(ownerEntry, index, name, true, desc);
19 }
20
21 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, boolean parameter, TypeDescriptor desc) {
22 super(ownerEntry, index, name, parameter);
23 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
24
25 this.ownerEntry = ownerEntry;
26 this.desc = desc;
27 }
28
29 @Override
30 public MethodDefEntry getOwnerEntry() {
31 return this.ownerEntry;
32 }
33
34 public TypeDescriptor getDesc() {
35 return desc;
36 }
37
38 @Override
39 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
40 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter, desc);
41 }
42
43 @Override
44 public int hashCode() {
45 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
46 }
47
48 @Override
49 public boolean equals(Object other) {
50 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
51 }
52
53 public boolean equals(LocalVariableDefEntry other) {
54 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
55 }
56
57 @Override
58 public String toString() {
59 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
60 }
61}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
deleted file mode 100644
index 3507b25..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
+++ /dev/null
@@ -1,93 +0,0 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableEntry implements Entry {
13
14 protected final MethodEntry ownerEntry;
15 protected final String name;
16 protected final int index;
17 protected final boolean parameter;
18
19 @Deprecated
20 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
21 this(ownerEntry, index, name, true);
22 }
23
24 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name, boolean parameter) {
25 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
26 Preconditions.checkNotNull(name, "Variable name cannot be null");
27 Preconditions.checkArgument(index >= 0, "Index must be positive");
28
29 this.ownerEntry = ownerEntry;
30 this.name = name;
31 this.index = index;
32 this.parameter = parameter;
33 }
34
35 public boolean isParameter() {
36 return this.parameter;
37 }
38
39 public MethodEntry getOwnerEntry() {
40 return this.ownerEntry;
41 }
42
43 public int getIndex() {
44 return index;
45 }
46
47 @Override
48 public String getName() {
49 return this.name;
50 }
51
52 @Override
53 public ClassEntry getOwnerClassEntry() {
54 return this.ownerEntry.getOwnerClassEntry();
55 }
56
57 @Override
58 public String getClassName() {
59 return this.ownerEntry.getClassName();
60 }
61
62 @Override
63 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
64 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name, parameter);
65 }
66
67 public String getMethodName() {
68 return this.ownerEntry.getName();
69 }
70
71 public MethodDescriptor getMethodDesc() {
72 return this.ownerEntry.getDesc();
73 }
74
75 @Override
76 public int hashCode() {
77 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
78 }
79
80 @Override
81 public boolean equals(Object other) {
82 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
83 }
84
85 public boolean equals(LocalVariableEntry other) {
86 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
87 }
88
89 @Override
90 public String toString() {
91 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
92 }
93}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
deleted file mode 100644
index fa9e668..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
+++ /dev/null
@@ -1,61 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.MethodDescriptor;
17import cuchaz.enigma.mapping.Signature;
18
19public class MethodDefEntry extends MethodEntry implements DefEntry {
20
21 private final AccessFlags access;
22 private final Signature signature;
23
24 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) {
25 super(classEntry, name, descriptor);
26 Preconditions.checkNotNull(access, "Method access cannot be null");
27 Preconditions.checkNotNull(signature, "Method signature cannot be null");
28 this.access = access;
29 this.signature = signature;
30 }
31
32 @Override
33 public AccessFlags getAccess() {
34 return access;
35 }
36
37 public Signature getSignature() {
38 return signature;
39 }
40
41 @Override
42 public MethodDefEntry updateOwnership(ClassEntry classEntry) {
43 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, signature, access);
44 }
45
46 public int getArgumentIndex(ClassDefEntry ownerEntry, int localVariableIndex) {
47 int argumentIndex = localVariableIndex;
48
49 // Enum constructors have an implicit "name" and "ordinal" parameter as well as "this"
50 if (ownerEntry.getAccess().isEnum() && getName().startsWith("<")) {
51 argumentIndex -= 2;
52 }
53
54 // If we're not static, "this" is bound to index 0
55 if (!getAccess().isStatic()) {
56 argumentIndex -= 1;
57 }
58
59 return argumentIndex;
60 }
61}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
deleted file mode 100644
index 1abc5b1..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
+++ /dev/null
@@ -1,80 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.MethodDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class MethodEntry implements Entry {
19
20 protected final ClassEntry classEntry;
21 protected final String name;
22 protected final MethodDescriptor descriptor;
23
24 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
25 Preconditions.checkNotNull(classEntry, "Class cannot be null");
26 Preconditions.checkNotNull(name, "Method name cannot be null");
27 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
28
29 this.classEntry = classEntry;
30 this.name = name;
31 this.descriptor = descriptor;
32 }
33
34 @Override
35 public ClassEntry getOwnerClassEntry() {
36 return this.classEntry;
37 }
38
39 @Override
40 public String getName() {
41 return this.name;
42 }
43
44 public MethodDescriptor getDesc() {
45 return this.descriptor;
46 }
47
48 public boolean isConstructor() {
49 return name.equals("<init>") || name.equals("<clinit>");
50 }
51
52 @Override
53 public String getClassName() {
54 return this.classEntry.getName();
55 }
56
57 @Override
58 public MethodEntry updateOwnership(ClassEntry classEntry) {
59 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
60 }
61
62 @Override
63 public int hashCode() {
64 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
65 }
66
67 @Override
68 public boolean equals(Object other) {
69 return other instanceof MethodEntry && equals((MethodEntry) other);
70 }
71
72 public boolean equals(MethodEntry other) {
73 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
74 }
75
76 @Override
77 public String toString() {
78 return this.classEntry.getName() + "." + this.name + this.descriptor;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
deleted file mode 100644
index 73770c5..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
+++ /dev/null
@@ -1,48 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MemberReference;
16import com.strobel.assembler.metadata.MethodDefinition;
17import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.MethodDescriptor;
19import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.TypeDescriptor;
21
22public class ProcyonEntryFactory {
23 private final ReferencedEntryPool entryPool;
24
25 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
26 this.entryPool = entryPool;
27 }
28
29 public FieldEntry getFieldEntry(MemberReference def) {
30 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
31 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature());
32 }
33
34 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
35 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
36 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
37 }
38
39 public MethodEntry getMethodEntry(MemberReference def) {
40 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
41 return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature());
42 }
43
44 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
45 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
46 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
47 }
48}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
deleted file mode 100644
index 12b3955..0000000
--- a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
+++ /dev/null
@@ -1,59 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping.entry;
13
14import cuchaz.enigma.mapping.MethodDescriptor;
15import cuchaz.enigma.mapping.TypeDescriptor;
16
17import java.util.HashMap;
18import java.util.Map;
19
20public class ReferencedEntryPool {
21 private final Map<String, ClassEntry> classEntries = new HashMap<>();
22 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
23 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
24
25 public ClassEntry getClass(String name) {
26 // TODO: FIXME - I'm a hack!
27 if ("[T".equals(name) || "[[T".equals(name) || "[[[T".equals(name)) {
28 name = name.replaceAll("T", "Ljava/lang/Object;");
29 }
30
31 final String computeName = name;
32 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(computeName));
33 }
34
35 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
36 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
37 }
38
39 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
40 String key = name + desc.toString();
41 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
42 }
43
44 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
45 return getField(ownerEntry, name, new TypeDescriptor(desc));
46 }
47
48 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
49 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
50 }
51
52 private Map<String, MethodEntry> getClassMethods(String name) {
53 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
54 }
55
56 private Map<String, FieldEntry> getClassFields(String name) {
57 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
58 }
59}