summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/mapping
diff options
context:
space:
mode:
authorGravatar jeff2015-02-08 21:29:25 -0500
committerGravatar jeff2015-02-08 21:29:25 -0500
commited9b5cdfc648e86fd463bfa8d86b94c41671e14c (patch)
tree2619bbc7e04dfa3b82f8dfd3b1d31f529766cd4b /src/cuchaz/enigma/mapping
downloadenigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.tar.gz
enigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.tar.xz
enigma-fork-ed9b5cdfc648e86fd463bfa8d86b94c41671e14c.zip
switch all classes to new signature/type system
Diffstat (limited to 'src/cuchaz/enigma/mapping')
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentEntry.java116
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentMapping.java44
-rw-r--r--src/cuchaz/enigma/mapping/BehaviorEntry.java15
-rw-r--r--src/cuchaz/enigma/mapping/BehaviorEntryFactory.java57
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java123
-rw-r--r--src/cuchaz/enigma/mapping/ClassMapping.java405
-rw-r--r--src/cuchaz/enigma/mapping/ClassNameReplacer.java5
-rw-r--r--src/cuchaz/enigma/mapping/ConstructorEntry.java116
-rw-r--r--src/cuchaz/enigma/mapping/Entry.java18
-rw-r--r--src/cuchaz/enigma/mapping/EntryPair.java22
-rw-r--r--src/cuchaz/enigma/mapping/FieldEntry.java88
-rw-r--r--src/cuchaz/enigma/mapping/FieldMapping.java43
-rw-r--r--src/cuchaz/enigma/mapping/IllegalNameException.java44
-rw-r--r--src/cuchaz/enigma/mapping/JavassistUtil.java83
-rw-r--r--src/cuchaz/enigma/mapping/MappingParseException.java29
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java188
-rw-r--r--src/cuchaz/enigma/mapping/MappingsReader.java175
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java237
-rw-r--r--src/cuchaz/enigma/mapping/MappingsWriter.java88
-rw-r--r--src/cuchaz/enigma/mapping/MethodEntry.java104
-rw-r--r--src/cuchaz/enigma/mapping/MethodMapping.java161
-rw-r--r--src/cuchaz/enigma/mapping/NameValidator.java80
-rw-r--r--src/cuchaz/enigma/mapping/Signature.java109
-rw-r--r--src/cuchaz/enigma/mapping/SignatureUpdater.java94
-rw-r--r--src/cuchaz/enigma/mapping/TranslationDirection.java29
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java239
-rw-r--r--src/cuchaz/enigma/mapping/Type.java218
27 files changed, 2930 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java
new file mode 100644
index 0000000..aa22265
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java
@@ -0,0 +1,116 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15import cuchaz.enigma.Util;
16
17public class ArgumentEntry implements Entry, Serializable {
18
19 private static final long serialVersionUID = 4472172468162696006L;
20
21 private BehaviorEntry m_behaviorEntry;
22 private int m_index;
23 private String m_name;
24
25 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) {
26 if (behaviorEntry == null) {
27 throw new IllegalArgumentException("Behavior cannot be null!");
28 }
29 if (index < 0) {
30 throw new IllegalArgumentException("Index must be non-negative!");
31 }
32 if (name == null) {
33 throw new IllegalArgumentException("Argument name cannot be null!");
34 }
35
36 m_behaviorEntry = behaviorEntry;
37 m_index = index;
38 m_name = name;
39 }
40
41 public ArgumentEntry(ArgumentEntry other) {
42 m_behaviorEntry = (BehaviorEntry)m_behaviorEntry.cloneToNewClass(getClassEntry());
43 m_index = other.m_index;
44 m_name = other.m_name;
45 }
46
47 public ArgumentEntry(ArgumentEntry other, String newClassName) {
48 m_behaviorEntry = (BehaviorEntry)other.m_behaviorEntry.cloneToNewClass(new ClassEntry(newClassName));
49 m_index = other.m_index;
50 m_name = other.m_name;
51 }
52
53 public BehaviorEntry getBehaviorEntry() {
54 return m_behaviorEntry;
55 }
56
57 public int getIndex() {
58 return m_index;
59 }
60
61 @Override
62 public String getName() {
63 return m_name;
64 }
65
66 @Override
67 public ClassEntry getClassEntry() {
68 return m_behaviorEntry.getClassEntry();
69 }
70
71 @Override
72 public String getClassName() {
73 return m_behaviorEntry.getClassName();
74 }
75
76 @Override
77 public ArgumentEntry cloneToNewClass(ClassEntry classEntry) {
78 return new ArgumentEntry(this, classEntry.getName());
79 }
80
81 public String getMethodName() {
82 return m_behaviorEntry.getName();
83 }
84
85 public Signature getMethodSignature() {
86 return m_behaviorEntry.getSignature();
87 }
88
89 @Override
90 public int hashCode() {
91 return Util.combineHashesOrdered(
92 m_behaviorEntry,
93 Integer.valueOf(m_index).hashCode(),
94 m_name.hashCode()
95 );
96 }
97
98 @Override
99 public boolean equals(Object other) {
100 if (other instanceof ArgumentEntry) {
101 return equals((ArgumentEntry)other);
102 }
103 return false;
104 }
105
106 public boolean equals(ArgumentEntry other) {
107 return m_behaviorEntry.equals(other.m_behaviorEntry)
108 && m_index == other.m_index
109 && m_name.equals(other.m_name);
110 }
111
112 @Override
113 public String toString() {
114 return m_behaviorEntry.toString() + "(" + m_index + ":" + m_name + ")";
115 }
116}
diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java
new file mode 100644
index 0000000..f4d8e77
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15public class ArgumentMapping implements Serializable, Comparable<ArgumentMapping> {
16
17 private static final long serialVersionUID = 8610742471440861315L;
18
19 private int m_index;
20 private String m_name;
21
22 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public ArgumentMapping(int index, String name) {
24 m_index = index;
25 m_name = NameValidator.validateArgumentName(name);
26 }
27
28 public int getIndex() {
29 return m_index;
30 }
31
32 public String getName() {
33 return m_name;
34 }
35
36 public void setName(String val) {
37 m_name = NameValidator.validateArgumentName(val);
38 }
39
40 @Override
41 public int compareTo(ArgumentMapping other) {
42 return Integer.compare(m_index, other.m_index);
43 }
44}
diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java
new file mode 100644
index 0000000..535788f
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java
@@ -0,0 +1,15 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public interface BehaviorEntry extends Entry {
14 Signature getSignature();
15}
diff --git a/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java
new file mode 100644
index 0000000..61e501b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/BehaviorEntryFactory.java
@@ -0,0 +1,57 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import javassist.CtBehavior;
14import javassist.CtConstructor;
15import javassist.CtMethod;
16import javassist.bytecode.Descriptor;
17
18public class BehaviorEntryFactory {
19
20 public static BehaviorEntry create(String className, String name, String signature) {
21 return create(new ClassEntry(className), name, signature);
22 }
23
24 public static BehaviorEntry create(ClassEntry classEntry, String name, String signature) {
25 if (name.equals("<init>")) {
26 return new ConstructorEntry(classEntry, new Signature(signature));
27 } else if (name.equals("<clinit>")) {
28 return new ConstructorEntry(classEntry);
29 } else {
30 return new MethodEntry(classEntry, name, new Signature(signature));
31 }
32 }
33
34 public static BehaviorEntry create(CtBehavior behavior) {
35 String className = Descriptor.toJvmName(behavior.getDeclaringClass().getName());
36 if (behavior instanceof CtMethod) {
37 return create(className, behavior.getName(), behavior.getSignature());
38 } else if (behavior instanceof CtConstructor) {
39 CtConstructor constructor = (CtConstructor)behavior;
40 if (constructor.isClassInitializer()) {
41 return create(className, "<clinit>", null);
42 } else {
43 return create(className, "<init>", constructor.getSignature());
44 }
45 } else {
46 throw new IllegalArgumentException("Unable to create BehaviorEntry from " + behavior);
47 }
48 }
49
50 public static BehaviorEntry createObf(ClassEntry classEntry, MethodMapping methodMapping) {
51 return create(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature().toString());
52 }
53
54 public static BehaviorEntry createDeobf(ClassEntry classEntry, MethodMapping methodMapping) {
55 return create(classEntry, methodMapping.getDeobfName(), methodMapping.getObfSignature().toString());
56 }
57}
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java
new file mode 100644
index 0000000..cf41001
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ClassEntry.java
@@ -0,0 +1,123 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15public class ClassEntry implements Entry, Serializable {
16
17 private static final long serialVersionUID = 4235460580973955811L;
18
19 private String m_name;
20
21 public ClassEntry(String className) {
22 if (className == null) {
23 throw new IllegalArgumentException("Class name cannot be null!");
24 }
25 if (className.indexOf('.') >= 0) {
26 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
27 }
28
29 m_name = className;
30
31 if (isInnerClass() && getInnerClassName().indexOf('/') >= 0) {
32 throw new IllegalArgumentException("Inner class must not have a package: " + className);
33 }
34 }
35
36 public ClassEntry(ClassEntry other) {
37 m_name = other.m_name;
38 }
39
40 @Override
41 public String getName() {
42 return m_name;
43 }
44
45 @Override
46 public String getClassName() {
47 return m_name;
48 }
49
50 @Override
51 public ClassEntry getClassEntry() {
52 return this;
53 }
54
55 @Override
56 public ClassEntry cloneToNewClass(ClassEntry classEntry) {
57 return classEntry;
58 }
59
60 @Override
61 public int hashCode() {
62 return m_name.hashCode();
63 }
64
65 @Override
66 public boolean equals(Object other) {
67 if (other instanceof ClassEntry) {
68 return equals((ClassEntry)other);
69 }
70 return false;
71 }
72
73 public boolean equals(ClassEntry other) {
74 return m_name.equals(other.m_name);
75 }
76
77 @Override
78 public String toString() {
79 return m_name;
80 }
81
82 public boolean isInnerClass() {
83 return m_name.lastIndexOf('$') >= 0;
84 }
85
86 public String getOuterClassName() {
87 if (isInnerClass()) {
88 return m_name.substring(0, m_name.lastIndexOf('$'));
89 }
90 return m_name;
91 }
92
93 public String getInnerClassName() {
94 if (!isInnerClass()) {
95 throw new Error("This is not an inner class!");
96 }
97 return m_name.substring(m_name.lastIndexOf('$') + 1);
98 }
99
100 public ClassEntry getOuterClassEntry() {
101 return new ClassEntry(getOuterClassName());
102 }
103
104 public boolean isInDefaultPackage() {
105 return m_name.indexOf('/') < 0;
106 }
107
108 public String getPackageName() {
109 int pos = m_name.lastIndexOf('/');
110 if (pos > 0) {
111 return m_name.substring(0, pos);
112 }
113 return null;
114 }
115
116 public String getSimpleName() {
117 int pos = m_name.lastIndexOf('/');
118 if (pos > 0) {
119 return m_name.substring(pos + 1);
120 }
121 return m_name;
122 }
123}
diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java
new file mode 100644
index 0000000..e2c3d56
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ClassMapping.java
@@ -0,0 +1,405 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.Map;
16
17import com.google.common.collect.Maps;
18
19public class ClassMapping implements Serializable, Comparable<ClassMapping> {
20
21 private static final long serialVersionUID = -5148491146902340107L;
22
23 private String m_obfName;
24 private String m_deobfName;
25 private Map<String,ClassMapping> m_innerClassesByObf;
26 private Map<String,ClassMapping> m_innerClassesByDeobf;
27 private Map<String,FieldMapping> m_fieldsByObf;
28 private Map<String,FieldMapping> m_fieldsByDeobf;
29 private Map<String,MethodMapping> m_methodsByObf;
30 private Map<String,MethodMapping> m_methodsByDeobf;
31
32 public ClassMapping(String obfName) {
33 this(obfName, null);
34 }
35
36 public ClassMapping(String obfName, String deobfName) {
37 m_obfName = obfName;
38 m_deobfName = NameValidator.validateClassName(deobfName, false);
39 m_innerClassesByObf = Maps.newHashMap();
40 m_innerClassesByDeobf = Maps.newHashMap();
41 m_fieldsByObf = Maps.newHashMap();
42 m_fieldsByDeobf = Maps.newHashMap();
43 m_methodsByObf = Maps.newHashMap();
44 m_methodsByDeobf = Maps.newHashMap();
45 }
46
47 public String getObfName() {
48 return m_obfName;
49 }
50
51 public String getDeobfName() {
52 return m_deobfName;
53 }
54
55 public void setDeobfName(String val) {
56 m_deobfName = NameValidator.validateClassName(val, false);
57 }
58
59 //// INNER CLASSES ////////
60
61 public Iterable<ClassMapping> innerClasses() {
62 assert (m_innerClassesByObf.size() >= m_innerClassesByDeobf.size());
63 return m_innerClassesByObf.values();
64 }
65
66 public void addInnerClassMapping(ClassMapping classMapping) {
67 assert (isSimpleClassName(classMapping.getObfName()));
68 boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null;
69 assert (obfWasAdded);
70 if (classMapping.getDeobfName() != null) {
71 assert (isSimpleClassName(classMapping.getDeobfName()));
72 boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null;
73 assert (deobfWasAdded);
74 }
75 }
76
77 public void removeInnerClassMapping(ClassMapping classMapping) {
78 boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null;
79 assert (obfWasRemoved);
80 if (classMapping.getDeobfName() != null) {
81 boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
82 assert (deobfWasRemoved);
83 }
84 }
85
86 public ClassMapping getOrCreateInnerClass(String obfName) {
87 assert (isSimpleClassName(obfName));
88 ClassMapping classMapping = m_innerClassesByObf.get(obfName);
89 if (classMapping == null) {
90 classMapping = new ClassMapping(obfName);
91 boolean wasAdded = m_innerClassesByObf.put(obfName, classMapping) == null;
92 assert (wasAdded);
93 }
94 return classMapping;
95 }
96
97 public ClassMapping getInnerClassByObf(String obfName) {
98 assert (isSimpleClassName(obfName));
99 return m_innerClassesByObf.get(obfName);
100 }
101
102 public ClassMapping getInnerClassByDeobf(String deobfName) {
103 assert (isSimpleClassName(deobfName));
104 return m_innerClassesByDeobf.get(deobfName);
105 }
106
107 public ClassMapping getInnerClassByDeobfThenObf(String name) {
108 ClassMapping classMapping = getInnerClassByDeobf(name);
109 if (classMapping == null) {
110 classMapping = getInnerClassByObf(name);
111 }
112 return classMapping;
113 }
114
115 public String getObfInnerClassName(String deobfName) {
116 assert (isSimpleClassName(deobfName));
117 ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName);
118 if (classMapping != null) {
119 return classMapping.getObfName();
120 }
121 return null;
122 }
123
124 public String getDeobfInnerClassName(String obfName) {
125 assert (isSimpleClassName(obfName));
126 ClassMapping classMapping = m_innerClassesByObf.get(obfName);
127 if (classMapping != null) {
128 return classMapping.getDeobfName();
129 }
130 return null;
131 }
132
133 public void setInnerClassName(String obfName, String deobfName) {
134 assert (isSimpleClassName(obfName));
135 ClassMapping classMapping = getOrCreateInnerClass(obfName);
136 if (classMapping.getDeobfName() != null) {
137 boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null;
138 assert (wasRemoved);
139 }
140 classMapping.setDeobfName(deobfName);
141 if (deobfName != null) {
142 assert (isSimpleClassName(deobfName));
143 boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null;
144 assert (wasAdded);
145 }
146 }
147
148 //// FIELDS ////////
149
150 public Iterable<FieldMapping> fields() {
151 assert (m_fieldsByObf.size() == m_fieldsByDeobf.size());
152 return m_fieldsByObf.values();
153 }
154
155 public boolean containsObfField(String obfName) {
156 return m_fieldsByObf.containsKey(obfName);
157 }
158
159 public boolean containsDeobfField(String deobfName) {
160 return m_fieldsByDeobf.containsKey(deobfName);
161 }
162
163 public void addFieldMapping(FieldMapping fieldMapping) {
164 if (m_fieldsByObf.containsKey(fieldMapping.getObfName())) {
165 throw new Error("Already have mapping for " + m_obfName + "." + fieldMapping.getObfName());
166 }
167 if (m_fieldsByDeobf.containsKey(fieldMapping.getDeobfName())) {
168 throw new Error("Already have mapping for " + m_deobfName + "." + fieldMapping.getDeobfName());
169 }
170 boolean obfWasAdded = m_fieldsByObf.put(fieldMapping.getObfName(), fieldMapping) == null;
171 assert (obfWasAdded);
172 boolean deobfWasAdded = m_fieldsByDeobf.put(fieldMapping.getDeobfName(), fieldMapping) == null;
173 assert (deobfWasAdded);
174 assert (m_fieldsByObf.size() == m_fieldsByDeobf.size());
175 }
176
177 public void removeFieldMapping(FieldMapping fieldMapping) {
178 boolean obfWasRemoved = m_fieldsByObf.remove(fieldMapping.getObfName()) != null;
179 assert (obfWasRemoved);
180 if (fieldMapping.getDeobfName() != null) {
181 boolean deobfWasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null;
182 assert (deobfWasRemoved);
183 }
184 }
185
186 public FieldMapping getFieldByObf(String obfName) {
187 return m_fieldsByObf.get(obfName);
188 }
189
190 public FieldMapping getFieldByDeobf(String deobfName) {
191 return m_fieldsByDeobf.get(deobfName);
192 }
193
194 public String getObfFieldName(String deobfName) {
195 FieldMapping fieldMapping = m_fieldsByDeobf.get(deobfName);
196 if (fieldMapping != null) {
197 return fieldMapping.getObfName();
198 }
199 return null;
200 }
201
202 public String getDeobfFieldName(String obfName) {
203 FieldMapping fieldMapping = m_fieldsByObf.get(obfName);
204 if (fieldMapping != null) {
205 return fieldMapping.getDeobfName();
206 }
207 return null;
208 }
209
210 public void setFieldName(String obfName, String deobfName) {
211 FieldMapping fieldMapping = m_fieldsByObf.get(obfName);
212 if (fieldMapping == null) {
213 fieldMapping = new FieldMapping(obfName, deobfName);
214 boolean obfWasAdded = m_fieldsByObf.put(obfName, fieldMapping) == null;
215 assert (obfWasAdded);
216 } else {
217 boolean wasRemoved = m_fieldsByDeobf.remove(fieldMapping.getDeobfName()) != null;
218 assert (wasRemoved);
219 }
220 fieldMapping.setDeobfName(deobfName);
221 if (deobfName != null) {
222 boolean wasAdded = m_fieldsByDeobf.put(deobfName, fieldMapping) == null;
223 assert (wasAdded);
224 }
225 }
226
227 //// METHODS ////////
228
229 public Iterable<MethodMapping> methods() {
230 assert (m_methodsByObf.size() >= m_methodsByDeobf.size());
231 return m_methodsByObf.values();
232 }
233
234 public boolean containsObfMethod(String obfName, Signature obfSignature) {
235 return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature));
236 }
237
238 public boolean containsDeobfMethod(String deobfName, Signature deobfSignature) {
239 return m_methodsByDeobf.containsKey(getMethodKey(deobfName, deobfSignature));
240 }
241
242 public void addMethodMapping(MethodMapping methodMapping) {
243 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature());
244 if (m_methodsByObf.containsKey(obfKey)) {
245 throw new Error("Already have mapping for " + m_obfName + "." + obfKey);
246 }
247 boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null;
248 assert (wasAdded);
249 if (methodMapping.getDeobfName() != null) {
250 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature());
251 if (m_methodsByDeobf.containsKey(deobfKey)) {
252 throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey);
253 }
254 boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null;
255 assert (deobfWasAdded);
256 }
257 assert (m_methodsByObf.size() >= m_methodsByDeobf.size());
258 }
259
260 public void removeMethodMapping(MethodMapping methodMapping) {
261 boolean obfWasRemoved = m_methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null;
262 assert (obfWasRemoved);
263 if (methodMapping.getDeobfName() != null) {
264 boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null;
265 assert (deobfWasRemoved);
266 }
267 }
268
269 public MethodMapping getMethodByObf(String obfName, Signature signature) {
270 return m_methodsByObf.get(getMethodKey(obfName, signature));
271 }
272
273 public MethodMapping getMethodByDeobf(String deobfName, Signature signature) {
274 return m_methodsByDeobf.get(getMethodKey(deobfName, signature));
275 }
276
277 private String getMethodKey(String name, Signature signature) {
278 if (name == null) {
279 throw new IllegalArgumentException("name cannot be null!");
280 }
281 if (signature == null) {
282 throw new IllegalArgumentException("signature cannot be null!");
283 }
284 return name + signature;
285 }
286
287 public void setMethodName(String obfName, Signature obfSignature, String deobfName) {
288 MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, obfSignature));
289 if (methodMapping == null) {
290 methodMapping = createMethodMapping(obfName, obfSignature);
291 } else if (methodMapping.getDeobfName() != null) {
292 boolean wasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null;
293 assert (wasRemoved);
294 }
295 methodMapping.setDeobfName(deobfName);
296 if (deobfName != null) {
297 boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null;
298 assert (wasAdded);
299 }
300 }
301
302 //// ARGUMENTS ////////
303
304 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) {
305 MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature));
306 if (methodMapping == null) {
307 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature);
308 }
309 methodMapping.setArgumentName(argumentIndex, argumentName);
310 }
311
312 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) {
313 m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex);
314 }
315
316 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) {
317 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature);
318 boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null;
319 assert (wasAdded);
320 return methodMapping;
321 }
322
323 @Override
324 public String toString() {
325 StringBuilder buf = new StringBuilder();
326 buf.append(m_obfName);
327 buf.append(" <-> ");
328 buf.append(m_deobfName);
329 buf.append("\n");
330 buf.append("Fields:\n");
331 for (FieldMapping fieldMapping : fields()) {
332 buf.append("\t");
333 buf.append(fieldMapping.getObfName());
334 buf.append(" <-> ");
335 buf.append(fieldMapping.getDeobfName());
336 buf.append("\n");
337 }
338 buf.append("Methods:\n");
339 for (MethodMapping methodMapping : m_methodsByObf.values()) {
340 buf.append(methodMapping.toString());
341 buf.append("\n");
342 }
343 buf.append("Inner Classes:\n");
344 for (ClassMapping classMapping : m_innerClassesByObf.values()) {
345 buf.append("\t");
346 buf.append(classMapping.getObfName());
347 buf.append(" <-> ");
348 buf.append(classMapping.getDeobfName());
349 buf.append("\n");
350 }
351 return buf.toString();
352 }
353
354 @Override
355 public int compareTo(ClassMapping other) {
356 // sort by a, b, c, ... aa, ab, etc
357 if (m_obfName.length() != other.m_obfName.length()) {
358 return m_obfName.length() - other.m_obfName.length();
359 }
360 return m_obfName.compareTo(other.m_obfName);
361 }
362
363 public boolean renameObfClass(String oldObfClassName, String newObfClassName) {
364
365 // rename inner classes
366 for (ClassMapping innerClassMapping : new ArrayList<ClassMapping>(m_innerClassesByObf.values())) {
367 if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) {
368 boolean wasRemoved = m_innerClassesByObf.remove(oldObfClassName) != null;
369 assert (wasRemoved);
370 boolean wasAdded = m_innerClassesByObf.put(newObfClassName, innerClassMapping) == null;
371 assert (wasAdded);
372 }
373 }
374
375 // rename method signatures
376 for (MethodMapping methodMapping : new ArrayList<MethodMapping>(m_methodsByObf.values())) {
377 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature());
378 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
379 boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null;
380 assert (wasRemoved);
381 boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null;
382 assert (wasAdded);
383 }
384 }
385
386 if (m_obfName.equals(oldObfClassName)) {
387 // rename this class
388 m_obfName = newObfClassName;
389 return true;
390 }
391 return false;
392 }
393
394 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
395 MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature()));
396 if (methodMapping != null) {
397 return methodMapping.containsArgument(name);
398 }
399 return false;
400 }
401
402 public static boolean isSimpleClassName(String name) {
403 return name.indexOf('/') < 0 && name.indexOf('$') < 0;
404 }
405}
diff --git a/src/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/cuchaz/enigma/mapping/ClassNameReplacer.java
new file mode 100644
index 0000000..bf984fd
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ClassNameReplacer.java
@@ -0,0 +1,5 @@
1package cuchaz.enigma.mapping;
2
3public interface ClassNameReplacer {
4 String replace(String className);
5}
diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java
new file mode 100644
index 0000000..5f3760f
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java
@@ -0,0 +1,116 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15import cuchaz.enigma.Util;
16
17public class ConstructorEntry implements BehaviorEntry, Serializable {
18
19 private static final long serialVersionUID = -868346075317366758L;
20
21 private ClassEntry m_classEntry;
22 private Signature m_signature;
23
24 public ConstructorEntry(ClassEntry classEntry) {
25 this(classEntry, null);
26 }
27
28 public ConstructorEntry(ClassEntry classEntry, Signature signature) {
29 if (classEntry == null) {
30 throw new IllegalArgumentException("Class cannot be null!");
31 }
32
33 m_classEntry = classEntry;
34 m_signature = signature;
35 }
36
37 public ConstructorEntry(ConstructorEntry other) {
38 m_classEntry = new ClassEntry(other.m_classEntry);
39 m_signature = other.m_signature;
40 }
41
42 public ConstructorEntry(ConstructorEntry other, String newClassName) {
43 m_classEntry = new ClassEntry(newClassName);
44 m_signature = other.m_signature;
45 }
46
47 @Override
48 public ClassEntry getClassEntry() {
49 return m_classEntry;
50 }
51
52 @Override
53 public String getName() {
54 if (isStatic()) {
55 return "<clinit>";
56 }
57 return "<init>";
58 }
59
60 public boolean isStatic() {
61 return m_signature == null;
62 }
63
64 @Override
65 public Signature getSignature() {
66 return m_signature;
67 }
68
69 @Override
70 public String getClassName() {
71 return m_classEntry.getName();
72 }
73
74 @Override
75 public ConstructorEntry cloneToNewClass(ClassEntry classEntry) {
76 return new ConstructorEntry(this, classEntry.getName());
77 }
78
79 @Override
80 public int hashCode() {
81 if (isStatic()) {
82 return Util.combineHashesOrdered(m_classEntry);
83 } else {
84 return Util.combineHashesOrdered(m_classEntry, m_signature);
85 }
86 }
87
88 @Override
89 public boolean equals(Object other) {
90 if (other instanceof ConstructorEntry) {
91 return equals((ConstructorEntry)other);
92 }
93 return false;
94 }
95
96 public boolean equals(ConstructorEntry other) {
97 if (isStatic() != other.isStatic()) {
98 return false;
99 }
100
101 if (isStatic()) {
102 return m_classEntry.equals(other.m_classEntry);
103 } else {
104 return m_classEntry.equals(other.m_classEntry) && m_signature.equals(other.m_signature);
105 }
106 }
107
108 @Override
109 public String toString() {
110 if (isStatic()) {
111 return m_classEntry.getName() + "." + getName();
112 } else {
113 return m_classEntry.getName() + "." + getName() + m_signature;
114 }
115 }
116}
diff --git a/src/cuchaz/enigma/mapping/Entry.java b/src/cuchaz/enigma/mapping/Entry.java
new file mode 100644
index 0000000..39e1507
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Entry.java
@@ -0,0 +1,18 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public interface Entry {
14 String getName();
15 String getClassName();
16 ClassEntry getClassEntry();
17 Entry cloneToNewClass(ClassEntry classEntry);
18}
diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java
new file mode 100644
index 0000000..60411c4
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/EntryPair.java
@@ -0,0 +1,22 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public class EntryPair<T extends Entry> {
14
15 public T obf;
16 public T deobf;
17
18 public EntryPair(T obf, T deobf) {
19 this.obf = obf;
20 this.deobf = deobf;
21 }
22}
diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java
new file mode 100644
index 0000000..6cc9eb7
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/FieldEntry.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15import cuchaz.enigma.Util;
16
17public class FieldEntry implements Entry, Serializable {
18
19 private static final long serialVersionUID = 3004663582802885451L;
20
21 private ClassEntry m_classEntry;
22 private String m_name;
23
24 // NOTE: this argument order is important for the MethodReader/MethodWriter
25 public FieldEntry(ClassEntry classEntry, String name) {
26 if (classEntry == null) {
27 throw new IllegalArgumentException("Class cannot be null!");
28 }
29 if (name == null) {
30 throw new IllegalArgumentException("Field name cannot be null!");
31 }
32
33 m_classEntry = classEntry;
34 m_name = name;
35 }
36
37 public FieldEntry(FieldEntry other) {
38 m_classEntry = new ClassEntry(other.m_classEntry);
39 m_name = other.m_name;
40 }
41
42 public FieldEntry(FieldEntry other, String newClassName) {
43 m_classEntry = new ClassEntry(newClassName);
44 m_name = other.m_name;
45 }
46
47 @Override
48 public ClassEntry getClassEntry() {
49 return m_classEntry;
50 }
51
52 @Override
53 public String getName() {
54 return m_name;
55 }
56
57 @Override
58 public String getClassName() {
59 return m_classEntry.getName();
60 }
61
62 @Override
63 public FieldEntry cloneToNewClass(ClassEntry classEntry) {
64 return new FieldEntry(this, classEntry.getName());
65 }
66
67 @Override
68 public int hashCode() {
69 return Util.combineHashesOrdered(m_classEntry, m_name);
70 }
71
72 @Override
73 public boolean equals(Object other) {
74 if (other instanceof FieldEntry) {
75 return equals((FieldEntry)other);
76 }
77 return false;
78 }
79
80 public boolean equals(FieldEntry other) {
81 return m_classEntry.equals(other.m_classEntry) && m_name.equals(other.m_name);
82 }
83
84 @Override
85 public String toString() {
86 return m_classEntry.getName() + "." + m_name;
87 }
88}
diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java
new file mode 100644
index 0000000..5f5c270
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/FieldMapping.java
@@ -0,0 +1,43 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15public class FieldMapping implements Serializable, Comparable<FieldMapping> {
16
17 private static final long serialVersionUID = 8610742471440861315L;
18
19 private String m_obfName;
20 private String m_deobfName;
21
22 public FieldMapping(String obfName, String deobfName) {
23 m_obfName = obfName;
24 m_deobfName = NameValidator.validateFieldName(deobfName);
25 }
26
27 public String getObfName() {
28 return m_obfName;
29 }
30
31 public String getDeobfName() {
32 return m_deobfName;
33 }
34
35 public void setDeobfName(String val) {
36 m_deobfName = NameValidator.validateFieldName(val);
37 }
38
39 @Override
40 public int compareTo(FieldMapping other) {
41 return m_obfName.compareTo(other.m_obfName);
42 }
43}
diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java
new file mode 100644
index 0000000..aacaf3b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/IllegalNameException.java
@@ -0,0 +1,44 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public class IllegalNameException extends RuntimeException {
14
15 private static final long serialVersionUID = -2279910052561114323L;
16
17 private String m_name;
18 private String m_reason;
19
20 public IllegalNameException(String name) {
21 this(name, null);
22 }
23
24 public IllegalNameException(String name, String reason) {
25 m_name = name;
26 m_reason = reason;
27 }
28
29 public String getReason() {
30 return m_reason;
31 }
32
33 @Override
34 public String getMessage() {
35 StringBuilder buf = new StringBuilder();
36 buf.append("Illegal name: ");
37 buf.append(m_name);
38 if (m_reason != null) {
39 buf.append(" because ");
40 buf.append(m_reason);
41 }
42 return buf.toString();
43 }
44}
diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java
new file mode 100644
index 0000000..0c446c4
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/JavassistUtil.java
@@ -0,0 +1,83 @@
1package cuchaz.enigma.mapping;
2
3import javassist.CtBehavior;
4import javassist.CtClass;
5import javassist.CtConstructor;
6import javassist.CtField;
7import javassist.CtMethod;
8import javassist.bytecode.Descriptor;
9import javassist.expr.ConstructorCall;
10import javassist.expr.FieldAccess;
11import javassist.expr.MethodCall;
12import javassist.expr.NewExpr;
13
14public class JavassistUtil {
15
16 public static ClassEntry getClassEntry(CtClass c) {
17 return new ClassEntry(Descriptor.toJvmName(c.getName()));
18 }
19
20 public static ClassEntry getSuperclassEntry(CtClass c) {
21 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
22 }
23
24 public static MethodEntry getMethodEntry(CtMethod method) {
25 return new MethodEntry(
26 getClassEntry(method.getDeclaringClass()),
27 method.getName(),
28 new Signature(method.getMethodInfo().getDescriptor())
29 );
30 }
31
32 public static MethodEntry getMethodEntry(MethodCall call) {
33 return new MethodEntry(
34 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
35 call.getMethodName(),
36 new Signature(call.getSignature())
37 );
38 }
39
40 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
41 return new ConstructorEntry(
42 getClassEntry(constructor.getDeclaringClass()),
43 new Signature(constructor.getMethodInfo().getDescriptor())
44 );
45 }
46
47 public static ConstructorEntry getConstructorEntry(ConstructorCall call) {
48 return new ConstructorEntry(
49 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
50 new Signature(call.getSignature())
51 );
52 }
53
54 public static ConstructorEntry getConstructorEntry(NewExpr call) {
55 return new ConstructorEntry(
56 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
57 new Signature(call.getSignature())
58 );
59 }
60
61 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
62 if (behavior instanceof CtMethod) {
63 return getMethodEntry((CtMethod)behavior);
64 } else if (behavior instanceof CtConstructor) {
65 return getConstructorEntry((CtConstructor)behavior);
66 }
67 throw new Error("behavior is neither Method nor Constructor!");
68 }
69
70 public static FieldEntry getFieldEntry(CtField field) {
71 return new FieldEntry(
72 getClassEntry(field.getDeclaringClass()),
73 field.getName()
74 );
75 }
76
77 public static FieldEntry getFieldEntry(FieldAccess call) {
78 return new FieldEntry(
79 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
80 call.getFieldName()
81 );
82 }
83}
diff --git a/src/cuchaz/enigma/mapping/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java
new file mode 100644
index 0000000..1974c22
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MappingParseException.java
@@ -0,0 +1,29 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public class MappingParseException extends Exception {
14
15 private static final long serialVersionUID = -5487280332892507236L;
16
17 private int m_line;
18 private String m_message;
19
20 public MappingParseException(int line, String message) {
21 m_line = line;
22 m_message = message;
23 }
24
25 @Override
26 public String getMessage() {
27 return "Line " + m_line + ": " + m_message;
28 }
29}
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java
new file mode 100644
index 0000000..57d8001
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Mappings.java
@@ -0,0 +1,188 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Map;
17import java.util.Set;
18
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
21
22import cuchaz.enigma.analysis.TranslationIndex;
23
24public class Mappings implements Serializable {
25
26 private static final long serialVersionUID = 4649790259460259026L;
27
28 protected Map<String,ClassMapping> m_classesByObf;
29 protected Map<String,ClassMapping> m_classesByDeobf;
30
31 public Mappings() {
32 m_classesByObf = Maps.newHashMap();
33 m_classesByDeobf = Maps.newHashMap();
34 }
35
36 public Mappings(Iterable<ClassMapping> classes) {
37 this();
38
39 for (ClassMapping classMapping : classes) {
40 m_classesByObf.put(classMapping.getObfName(), classMapping);
41 if (classMapping.getDeobfName() != null) {
42 m_classesByDeobf.put(classMapping.getDeobfName(), classMapping);
43 }
44 }
45 }
46
47 public Collection<ClassMapping> classes() {
48 assert (m_classesByObf.size() >= m_classesByDeobf.size());
49 return m_classesByObf.values();
50 }
51
52 public void addClassMapping(ClassMapping classMapping) {
53 if (m_classesByObf.containsKey(classMapping.getObfName())) {
54 throw new Error("Already have mapping for " + classMapping.getObfName());
55 }
56 boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null;
57 assert (obfWasAdded);
58 if (classMapping.getDeobfName() != null) {
59 if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) {
60 throw new Error("Already have mapping for " + classMapping.getDeobfName());
61 }
62 boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null;
63 assert (deobfWasAdded);
64 }
65 }
66
67 public void removeClassMapping(ClassMapping classMapping) {
68 boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null;
69 assert (obfWasRemoved);
70 if (classMapping.getDeobfName() != null) {
71 boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null;
72 assert (deobfWasRemoved);
73 }
74 }
75
76 public ClassMapping getClassByObf(ClassEntry entry) {
77 return getClassByObf(entry.getName());
78 }
79
80 public ClassMapping getClassByObf(String obfName) {
81 return m_classesByObf.get(obfName);
82 }
83
84 public ClassMapping getClassByDeobf(ClassEntry entry) {
85 return getClassByDeobf(entry.getName());
86 }
87
88 public ClassMapping getClassByDeobf(String deobfName) {
89 return m_classesByDeobf.get(deobfName);
90 }
91
92 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
93 switch (direction) {
94 case Deobfuscating:
95
96 return new Translator(direction, m_classesByObf, index);
97
98 case Obfuscating:
99
100 // fill in the missing deobf class entries with obf entries
101 Map<String,ClassMapping> classes = Maps.newHashMap();
102 for (ClassMapping classMapping : classes()) {
103 if (classMapping.getDeobfName() != null) {
104 classes.put(classMapping.getDeobfName(), classMapping);
105 } else {
106 classes.put(classMapping.getObfName(), classMapping);
107 }
108 }
109
110 // translate the translation index
111 // NOTE: this isn't actually recursive
112 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index));
113
114 return new Translator(direction, classes, deobfIndex);
115
116 default:
117 throw new Error("Invalid translation direction!");
118 }
119 }
120
121 @Override
122 public String toString() {
123 StringBuilder buf = new StringBuilder();
124 for (ClassMapping classMapping : m_classesByObf.values()) {
125 buf.append(classMapping.toString());
126 buf.append("\n");
127 }
128 return buf.toString();
129 }
130
131 public void renameObfClass(String oldObfName, String newObfName) {
132 for (ClassMapping classMapping : new ArrayList<ClassMapping>(classes())) {
133 if (classMapping.renameObfClass(oldObfName, newObfName)) {
134 boolean wasRemoved = m_classesByObf.remove(oldObfName) != null;
135 assert (wasRemoved);
136 boolean wasAdded = m_classesByObf.put(newObfName, classMapping) == null;
137 assert (wasAdded);
138 }
139 }
140 }
141
142 public Set<String> getAllObfClassNames() {
143 final Set<String> classNames = Sets.newHashSet();
144 for (ClassMapping classMapping : classes()) {
145
146 // add the class name
147 classNames.add(classMapping.getObfName());
148
149 // add classes from method signatures
150 for (MethodMapping methodMapping : classMapping.methods()) {
151 for (Type type : methodMapping.getObfSignature().types()) {
152 if (type.hasClass()) {
153 classNames.add(type.getClassEntry().getClassName());
154 }
155 }
156 }
157 }
158 return classNames;
159 }
160
161 public boolean containsDeobfClass(String deobfName) {
162 return m_classesByDeobf.containsKey(deobfName);
163 }
164
165 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
166 ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName());
167 if (classMapping != null) {
168 return classMapping.containsDeobfField(deobfName);
169 }
170 return false;
171 }
172
173 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature deobfSignature) {
174 ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName());
175 if (classMapping != null) {
176 return classMapping.containsDeobfMethod(deobfName, deobfSignature);
177 }
178 return false;
179 }
180
181 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) {
182 ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName());
183 if (classMapping != null) {
184 return classMapping.containsArgument(obfBehaviorEntry, name);
185 }
186 return false;
187 }
188}
diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java
new file mode 100644
index 0000000..adf460e
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MappingsReader.java
@@ -0,0 +1,175 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.BufferedReader;
14import java.io.IOException;
15import java.io.Reader;
16import java.util.Deque;
17
18import com.google.common.collect.Queues;
19
20import cuchaz.enigma.Constants;
21
22public class MappingsReader {
23
24 public Mappings read(Reader in) throws IOException, MappingParseException {
25 return read(new BufferedReader(in));
26 }
27
28 public Mappings read(BufferedReader in) throws IOException, MappingParseException {
29 Mappings mappings = new Mappings();
30 Deque<Object> mappingStack = Queues.newArrayDeque();
31
32 int lineNumber = 0;
33 String line = null;
34 while ( (line = in.readLine()) != null) {
35 lineNumber++;
36
37 // strip comments
38 int commentPos = line.indexOf('#');
39 if (commentPos >= 0) {
40 line = line.substring(0, commentPos);
41 }
42
43 // skip blank lines
44 if (line.trim().length() <= 0) {
45 continue;
46 }
47
48 // get the indent of this line
49 int indent = 0;
50 for (int i = 0; i < line.length(); i++) {
51 if (line.charAt(i) != '\t') {
52 break;
53 }
54 indent++;
55 }
56
57 // handle stack pops
58 while (indent < mappingStack.size()) {
59 mappingStack.pop();
60 }
61
62 String[] parts = line.trim().split("\\s");
63 try {
64 // read the first token
65 String token = parts[0];
66
67 if (token.equalsIgnoreCase("CLASS")) {
68 ClassMapping classMapping;
69 if (indent == 0) {
70 // outer class
71 classMapping = readClass(parts, false);
72 mappings.addClassMapping(classMapping);
73 } else if (indent == 1) {
74 // inner class
75 if (! (mappingStack.getFirst() instanceof ClassMapping)) {
76 throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!");
77 }
78
79 classMapping = readClass(parts, true);
80 ((ClassMapping)mappingStack.getFirst()).addInnerClassMapping(classMapping);
81 } else {
82 throw new MappingParseException(lineNumber, "Unexpected CLASS entry nesting!");
83 }
84 mappingStack.push(classMapping);
85 } else if (token.equalsIgnoreCase("FIELD")) {
86 if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) {
87 throw new MappingParseException(lineNumber, "Unexpected FIELD entry here!");
88 }
89 ((ClassMapping)mappingStack.getFirst()).addFieldMapping(readField(parts));
90 } else if (token.equalsIgnoreCase("METHOD")) {
91 if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof ClassMapping)) {
92 throw new MappingParseException(lineNumber, "Unexpected METHOD entry here!");
93 }
94 MethodMapping methodMapping = readMethod(parts);
95 ((ClassMapping)mappingStack.getFirst()).addMethodMapping(methodMapping);
96 mappingStack.push(methodMapping);
97 } else if (token.equalsIgnoreCase("ARG")) {
98 if (mappingStack.isEmpty() || ! (mappingStack.getFirst() instanceof MethodMapping)) {
99 throw new MappingParseException(lineNumber, "Unexpected ARG entry here!");
100 }
101 ((MethodMapping)mappingStack.getFirst()).addArgumentMapping(readArgument(parts));
102 }
103 } catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) {
104 throw new MappingParseException(lineNumber, "Malformed line!");
105 }
106 }
107
108 return mappings;
109 }
110
111 private ArgumentMapping readArgument(String[] parts) {
112 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]);
113 }
114
115 private ClassMapping readClass(String[] parts, boolean makeSimple) {
116 if (parts.length == 2) {
117 String obfName = processName(parts[1], makeSimple);
118 return new ClassMapping(obfName);
119 } else {
120 String obfName = processName(parts[1], makeSimple);
121 String deobfName = processName(parts[2], makeSimple);
122 return new ClassMapping(obfName, deobfName);
123 }
124 }
125
126 private String processName(String name, boolean makeSimple) {
127 if (makeSimple) {
128 return new ClassEntry(name).getSimpleName();
129 } else {
130 return moveClassOutOfDefaultPackage(name, Constants.NonePackage);
131 }
132 }
133
134 private String moveClassOutOfDefaultPackage(String className, String newPackageName) {
135 ClassEntry classEntry = new ClassEntry(className);
136 if (classEntry.isInDefaultPackage()) {
137 return newPackageName + "/" + classEntry.getName();
138 }
139 return className;
140 }
141
142 private FieldMapping readField(String[] parts) {
143 return new FieldMapping(parts[1], parts[2]);
144 }
145
146 private MethodMapping readMethod(String[] parts) {
147 if (parts.length == 3) {
148 String obfName = parts[1];
149 Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[2]), Constants.NonePackage);
150 return new MethodMapping(obfName, obfSignature);
151 } else {
152 String obfName = parts[1];
153 String deobfName = parts[2];
154 Signature obfSignature = moveSignatureOutOfDefaultPackage(new Signature(parts[3]), Constants.NonePackage);
155 if (obfName.equals(deobfName)) {
156 return new MethodMapping(obfName, obfSignature);
157 } else {
158 return new MethodMapping(obfName, obfSignature, deobfName);
159 }
160 }
161 }
162
163 private Signature moveSignatureOutOfDefaultPackage(Signature signature, final String newPackageName) {
164 return new Signature(signature, new ClassNameReplacer() {
165 @Override
166 public String replace(String className) {
167 ClassEntry classEntry = new ClassEntry(className);
168 if (classEntry.isInDefaultPackage()) {
169 return newPackageName + "/" + className;
170 }
171 return null;
172 }
173 });
174 }
175}
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java
new file mode 100644
index 0000000..0a41c2b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -0,0 +1,237 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.IOException;
14import java.io.ObjectOutputStream;
15import java.io.OutputStream;
16import java.util.Set;
17import java.util.zip.GZIPOutputStream;
18
19import cuchaz.enigma.Constants;
20import cuchaz.enigma.analysis.JarIndex;
21
22public class MappingsRenamer {
23
24 private JarIndex m_index;
25 private Mappings m_mappings;
26
27 public MappingsRenamer(JarIndex index, Mappings mappings) {
28 m_index = index;
29 m_mappings = mappings;
30 }
31
32 public void setClassName(ClassEntry obf, String deobfName) {
33 deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass());
34 ClassEntry targetEntry = new ClassEntry(deobfName);
35 if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(targetEntry)) {
36 throw new IllegalNameException(deobfName, "There is already a class with that name");
37 }
38
39 ClassMapping classMapping = getOrCreateClassMapping(obf);
40
41 if (obf.isInnerClass()) {
42 classMapping.setInnerClassName(obf.getInnerClassName(), deobfName);
43 } else {
44 if (classMapping.getDeobfName() != null) {
45 boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null;
46 assert (wasRemoved);
47 }
48 classMapping.setDeobfName(deobfName);
49 boolean wasAdded = m_mappings.m_classesByDeobf.put(deobfName, classMapping) == null;
50 assert (wasAdded);
51 }
52 }
53
54 public void removeClassMapping(ClassEntry obf) {
55 ClassMapping classMapping = getClassMapping(obf);
56 if (obf.isInnerClass()) {
57 classMapping.setInnerClassName(obf.getName(), null);
58 } else {
59 boolean wasRemoved = m_mappings.m_classesByDeobf.remove(classMapping.getDeobfName()) != null;
60 assert (wasRemoved);
61 classMapping.setDeobfName(null);
62 }
63 }
64
65 public void markClassAsDeobfuscated(ClassEntry obf) {
66 ClassMapping classMapping = getOrCreateClassMapping(obf);
67 if (obf.isInnerClass()) {
68 String innerClassName = Constants.NonePackage + "/" + obf.getInnerClassName();
69 classMapping.setInnerClassName(innerClassName, innerClassName);
70 } else {
71 classMapping.setDeobfName(obf.getName());
72 boolean wasAdded = m_mappings.m_classesByDeobf.put(obf.getName(), classMapping) == null;
73 assert (wasAdded);
74 }
75 }
76
77 public void setFieldName(FieldEntry obf, String deobfName) {
78 deobfName = NameValidator.validateFieldName(deobfName);
79 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName);
80 if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName) || m_index.containsObfField(targetEntry)) {
81 throw new IllegalNameException(deobfName, "There is already a field with that name");
82 }
83
84 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
85 classMapping.setFieldName(obf.getName(), deobfName);
86 }
87
88 public void removeFieldMapping(FieldEntry obf) {
89 ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry());
90 classMapping.setFieldName(obf.getName(), null);
91 }
92
93 public void markFieldAsDeobfuscated(FieldEntry obf) {
94 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
95 classMapping.setFieldName(obf.getName(), obf.getName());
96 }
97
98 public void setMethodTreeName(MethodEntry obf, String deobfName) {
99 Set<MethodEntry> implementations = m_index.getRelatedMethodImplementations(obf);
100
101 deobfName = NameValidator.validateMethodName(deobfName);
102 for (MethodEntry entry : implementations) {
103 Signature deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature());
104 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature);
105 if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
106 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName());
107 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
108 }
109 }
110
111 for (MethodEntry entry : implementations) {
112 setMethodName(entry, deobfName);
113 }
114 }
115
116 public void setMethodName(MethodEntry obf, String deobfName) {
117 deobfName = NameValidator.validateMethodName(deobfName);
118 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature());
119 if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
120 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName());
121 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
122 }
123
124 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
125 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName);
126 }
127
128 public void removeMethodTreeMapping(MethodEntry obf) {
129 for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) {
130 removeMethodMapping(implementation);
131 }
132 }
133
134 public void removeMethodMapping(MethodEntry obf) {
135 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
136 classMapping.setMethodName(obf.getName(), obf.getSignature(), null);
137 }
138
139 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
140 for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) {
141 markMethodAsDeobfuscated(implementation);
142 }
143 }
144
145 public void markMethodAsDeobfuscated(MethodEntry obf) {
146 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
147 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName());
148 }
149
150 public void setArgumentName(ArgumentEntry obf, String deobfName) {
151 deobfName = NameValidator.validateArgumentName(deobfName);
152 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
153 if (m_mappings.containsArgument(obf.getBehaviorEntry(), deobfName)) {
154 throw new IllegalNameException(deobfName, "There is already an argument with that name");
155 }
156
157 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
158 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName);
159 }
160
161 public void removeArgumentMapping(ArgumentEntry obf) {
162 ClassMapping classMapping = getClassMappingOrInnerClassMapping(obf.getClassEntry());
163 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex());
164 }
165
166 public void markArgumentAsDeobfuscated(ArgumentEntry obf) {
167 ClassMapping classMapping = getOrCreateClassMappingOrInnerClassMapping(obf.getClassEntry());
168 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName());
169 }
170
171 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
172 classMapping.removeFieldMapping(fieldMapping);
173 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
174 if (!targetClassMapping.containsObfField(fieldMapping.getObfName())) {
175 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName())) {
176 targetClassMapping.addFieldMapping(fieldMapping);
177 return true;
178 } else {
179 System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName());
180 }
181 }
182 return false;
183 }
184
185 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
186 classMapping.removeMethodMapping(methodMapping);
187 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
188 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) {
189 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) {
190 targetClassMapping.addMethodMapping(methodMapping);
191 return true;
192 } else {
193 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature());
194 }
195 }
196 return false;
197 }
198
199 public void write(OutputStream out) throws IOException {
200 // TEMP: just use the object output for now. We can find a more efficient storage format later
201 GZIPOutputStream gzipout = new GZIPOutputStream(out);
202 ObjectOutputStream oout = new ObjectOutputStream(gzipout);
203 oout.writeObject(this);
204 gzipout.finish();
205 }
206
207 private ClassMapping getClassMapping(ClassEntry obfClassEntry) {
208 return m_mappings.m_classesByObf.get(obfClassEntry.getOuterClassName());
209 }
210
211 private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) {
212 String obfClassName = obfClassEntry.getOuterClassName();
213 ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName);
214 if (classMapping == null) {
215 classMapping = new ClassMapping(obfClassName);
216 boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null;
217 assert (obfWasAdded);
218 }
219 return classMapping;
220 }
221
222 private ClassMapping getClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) {
223 ClassMapping classMapping = getClassMapping(obfClassEntry);
224 if (obfClassEntry.isInDefaultPackage()) {
225 classMapping = classMapping.getInnerClassByObf(obfClassEntry.getInnerClassName());
226 }
227 return classMapping;
228 }
229
230 private ClassMapping getOrCreateClassMappingOrInnerClassMapping(ClassEntry obfClassEntry) {
231 ClassMapping classMapping = getOrCreateClassMapping(obfClassEntry);
232 if (obfClassEntry.isInnerClass()) {
233 classMapping = classMapping.getOrCreateInnerClass(obfClassEntry.getInnerClassName());
234 }
235 return classMapping;
236 }
237}
diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java
new file mode 100644
index 0000000..5ac409f
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MappingsWriter.java
@@ -0,0 +1,88 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.IOException;
14import java.io.PrintWriter;
15import java.io.Writer;
16import java.util.ArrayList;
17import java.util.Collections;
18import java.util.List;
19
20public class MappingsWriter {
21
22 public void write(Writer out, Mappings mappings) throws IOException {
23 write(new PrintWriter(out), mappings);
24 }
25
26 public void write(PrintWriter out, Mappings mappings) throws IOException {
27 for (ClassMapping classMapping : sorted(mappings.classes())) {
28 write(out, classMapping, 0);
29 }
30 }
31
32 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
33 if (classMapping.getDeobfName() == null) {
34 out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName());
35 } else {
36 out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName());
37 }
38
39 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
40 write(out, innerClassMapping, depth + 1);
41 }
42
43 for (FieldMapping fieldMapping : sorted(classMapping.fields())) {
44 write(out, fieldMapping, depth + 1);
45 }
46
47 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
48 write(out, methodMapping, depth + 1);
49 }
50 }
51
52 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) throws IOException {
53 out.format("%sFIELD %s %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName());
54 }
55
56 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
57 if (methodMapping.getDeobfName() == null) {
58 out.format("%sMETHOD %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature());
59 } else {
60 out.format("%sMETHOD %s %s %s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature());
61 }
62
63 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) {
64 write(out, argumentMapping, depth + 1);
65 }
66 }
67
68 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) throws IOException {
69 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName());
70 }
71
72 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
73 List<T> out = new ArrayList<T>();
74 for (T t : classes) {
75 out.add(t);
76 }
77 Collections.sort(out);
78 return out;
79 }
80
81 private String getIndent(int depth) {
82 StringBuilder buf = new StringBuilder();
83 for (int i = 0; i < depth; i++) {
84 buf.append("\t");
85 }
86 return buf.toString();
87 }
88}
diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java
new file mode 100644
index 0000000..057e02b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MethodEntry.java
@@ -0,0 +1,104 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15import cuchaz.enigma.Util;
16
17public class MethodEntry implements BehaviorEntry, Serializable {
18
19 private static final long serialVersionUID = 4770915224467247458L;
20
21 private ClassEntry m_classEntry;
22 private String m_name;
23 private Signature m_signature;
24
25 public MethodEntry(ClassEntry classEntry, String name, Signature signature) {
26 if (classEntry == null) {
27 throw new IllegalArgumentException("Class cannot be null!");
28 }
29 if (name == null) {
30 throw new IllegalArgumentException("Method name cannot be null!");
31 }
32 if (signature == null) {
33 throw new IllegalArgumentException("Method signature cannot be null!");
34 }
35 if (name.startsWith("<")) {
36 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!");
37 }
38
39 m_classEntry = classEntry;
40 m_name = name;
41 m_signature = signature;
42 }
43
44 public MethodEntry(MethodEntry other) {
45 m_classEntry = new ClassEntry(other.m_classEntry);
46 m_name = other.m_name;
47 m_signature = other.m_signature;
48 }
49
50 public MethodEntry(MethodEntry other, String newClassName) {
51 m_classEntry = new ClassEntry(newClassName);
52 m_name = other.m_name;
53 m_signature = other.m_signature;
54 }
55
56 @Override
57 public ClassEntry getClassEntry() {
58 return m_classEntry;
59 }
60
61 @Override
62 public String getName() {
63 return m_name;
64 }
65
66 @Override
67 public Signature getSignature() {
68 return m_signature;
69 }
70
71 @Override
72 public String getClassName() {
73 return m_classEntry.getName();
74 }
75
76 @Override
77 public MethodEntry cloneToNewClass(ClassEntry classEntry) {
78 return new MethodEntry(this, classEntry.getName());
79 }
80
81 @Override
82 public int hashCode() {
83 return Util.combineHashesOrdered(m_classEntry, m_name, m_signature);
84 }
85
86 @Override
87 public boolean equals(Object other) {
88 if (other instanceof MethodEntry) {
89 return equals((MethodEntry)other);
90 }
91 return false;
92 }
93
94 public boolean equals(MethodEntry other) {
95 return m_classEntry.equals(other.m_classEntry)
96 && m_name.equals(other.m_name)
97 && m_signature.equals(other.m_signature);
98 }
99
100 @Override
101 public String toString() {
102 return m_classEntry.getName() + "." + m_name + m_signature;
103 }
104}
diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java
new file mode 100644
index 0000000..1704428
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MethodMapping.java
@@ -0,0 +1,161 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14import java.util.Map;
15import java.util.TreeMap;
16
17public class MethodMapping implements Serializable, Comparable<MethodMapping> {
18
19 private static final long serialVersionUID = -4409570216084263978L;
20
21 private String m_obfName;
22 private String m_deobfName;
23 private Signature m_obfSignature;
24 private Map<Integer,ArgumentMapping> m_arguments;
25
26 public MethodMapping(String obfName, Signature obfSignature) {
27 this(obfName, obfSignature, null);
28 }
29
30 public MethodMapping(String obfName, Signature obfSignature, String deobfName) {
31 if (obfName == null) {
32 throw new IllegalArgumentException("obf name cannot be null!");
33 }
34 if (obfSignature == null) {
35 throw new IllegalArgumentException("obf signature cannot be null!");
36 }
37 m_obfName = obfName;
38 m_deobfName = NameValidator.validateMethodName(deobfName);
39 m_obfSignature = obfSignature;
40 m_arguments = new TreeMap<Integer,ArgumentMapping>();
41 }
42
43 public String getObfName() {
44 return m_obfName;
45 }
46
47 public String getDeobfName() {
48 return m_deobfName;
49 }
50
51 public void setDeobfName(String val) {
52 m_deobfName = NameValidator.validateMethodName(val);
53 }
54
55 public Signature getObfSignature() {
56 return m_obfSignature;
57 }
58
59 public Iterable<ArgumentMapping> arguments() {
60 return m_arguments.values();
61 }
62
63 public boolean isConstructor() {
64 return m_obfName.startsWith("<");
65 }
66
67 public void addArgumentMapping(ArgumentMapping argumentMapping) {
68 boolean wasAdded = m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null;
69 assert (wasAdded);
70 }
71
72 public String getObfArgumentName(int index) {
73 ArgumentMapping argumentMapping = m_arguments.get(index);
74 if (argumentMapping != null) {
75 return argumentMapping.getName();
76 }
77
78 return null;
79 }
80
81 public String getDeobfArgumentName(int index) {
82 ArgumentMapping argumentMapping = m_arguments.get(index);
83 if (argumentMapping != null) {
84 return argumentMapping.getName();
85 }
86
87 return null;
88 }
89
90 public void setArgumentName(int index, String name) {
91 ArgumentMapping argumentMapping = m_arguments.get(index);
92 if (argumentMapping == null) {
93 argumentMapping = new ArgumentMapping(index, name);
94 boolean wasAdded = m_arguments.put(index, argumentMapping) == null;
95 assert (wasAdded);
96 } else {
97 argumentMapping.setName(name);
98 }
99 }
100
101 public void removeArgumentName(int index) {
102 boolean wasRemoved = m_arguments.remove(index) != null;
103 assert (wasRemoved);
104 }
105
106 @Override
107 public String toString() {
108 StringBuilder buf = new StringBuilder();
109 buf.append("\t");
110 buf.append(m_obfName);
111 buf.append(" <-> ");
112 buf.append(m_deobfName);
113 buf.append("\n");
114 buf.append("\t");
115 buf.append(m_obfSignature);
116 buf.append("\n");
117 buf.append("\tArguments:\n");
118 for (ArgumentMapping argumentMapping : m_arguments.values()) {
119 buf.append("\t\t");
120 buf.append(argumentMapping.getIndex());
121 buf.append(" -> ");
122 buf.append(argumentMapping.getName());
123 buf.append("\n");
124 }
125 return buf.toString();
126 }
127
128 @Override
129 public int compareTo(MethodMapping other) {
130 return (m_obfName + m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature);
131 }
132
133 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
134
135 // rename obf classes in the signature
136 Signature newSignature = new Signature(m_obfSignature, new ClassNameReplacer() {
137 @Override
138 public String replace(String className) {
139 if (className.equals(oldObfClassName)) {
140 return newObfClassName;
141 }
142 return null;
143 }
144 });
145
146 if (!newSignature.equals(m_obfSignature)) {
147 m_obfSignature = newSignature;
148 return true;
149 }
150 return false;
151 }
152
153 public boolean containsArgument(String name) {
154 for (ArgumentMapping argumentMapping : m_arguments.values()) {
155 if (argumentMapping.getName().equals(name)) {
156 return true;
157 }
158 }
159 return false;
160 }
161}
diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java
new file mode 100644
index 0000000..35a17f9
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/NameValidator.java
@@ -0,0 +1,80 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.util.Arrays;
14import java.util.List;
15import java.util.regex.Pattern;
16
17import javassist.bytecode.Descriptor;
18
19public class NameValidator {
20
21 private static final Pattern IdentifierPattern;
22 private static final Pattern ClassPattern;
23 private static final List<String> ReservedWords = Arrays.asList(
24 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
25 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
26 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
27 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
28 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
29 );
30
31 static {
32
33 // java allows all kinds of weird characters...
34 StringBuilder startChars = new StringBuilder();
35 StringBuilder partChars = new StringBuilder();
36 for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) {
37 if (Character.isJavaIdentifierStart(i)) {
38 startChars.appendCodePoint(i);
39 }
40 if (Character.isJavaIdentifierPart(i)) {
41 partChars.appendCodePoint(i);
42 }
43 }
44
45 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
46 IdentifierPattern = Pattern.compile(identifierRegex);
47 ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex));
48 }
49
50 public static String validateClassName(String name, boolean packageRequired) {
51 if (name == null) {
52 return null;
53 }
54 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
55 throw new IllegalNameException(name, "This doesn't look like a legal class name");
56 }
57 if (packageRequired && new ClassEntry(name).getPackageName() == null) {
58 throw new IllegalNameException(name, "Class must be in a package");
59 }
60 return Descriptor.toJvmName(name);
61 }
62
63 public static String validateFieldName(String name) {
64 if (name == null) {
65 return null;
66 }
67 if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) {
68 throw new IllegalNameException(name, "This doesn't look like a legal identifier");
69 }
70 return name;
71 }
72
73 public static String validateMethodName(String name) {
74 return validateFieldName(name);
75 }
76
77 public static String validateArgumentName(String name) {
78 return validateFieldName(name);
79 }
80}
diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java
new file mode 100644
index 0000000..ff7f807
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Signature.java
@@ -0,0 +1,109 @@
1package cuchaz.enigma.mapping;
2
3import java.util.List;
4
5import com.beust.jcommander.internal.Lists;
6
7import cuchaz.enigma.Util;
8
9public class Signature {
10
11 private List<Type> m_argumentTypes;
12 private Type m_returnType;
13
14 public Signature(String signature) {
15 try {
16 m_argumentTypes = Lists.newArrayList();
17 int i=0;
18 while (i<signature.length()) {
19 char c = signature.charAt(i);
20 if (c == '(') {
21 assert(m_argumentTypes.isEmpty());
22 assert(m_returnType == null);
23 i++;
24 } else if (c == ')') {
25 i++;
26 break;
27 } else {
28 String type = Type.parseFirst(signature.substring(i));
29 m_argumentTypes.add(new Type(type));
30 i += type.length();
31 }
32 }
33 m_returnType = new Type(Type.parseFirst(signature.substring(i)));
34 } catch (Exception ex) {
35 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex);
36 }
37 }
38
39 public Signature(Signature other, ClassNameReplacer replacer) {
40 m_argumentTypes = Lists.newArrayList(other.m_argumentTypes);
41 for (int i=0; i<m_argumentTypes.size(); i++) {
42 m_argumentTypes.set(i, new Type(m_argumentTypes.get(i), replacer));
43 }
44 m_returnType = new Type(other.m_returnType, replacer);
45 }
46
47 public List<Type> getArgumentTypes() {
48 return m_argumentTypes;
49 }
50
51 public Type getReturnType() {
52 return m_returnType;
53 }
54
55 @Override
56 public String toString() {
57 StringBuilder buf = new StringBuilder();
58 buf.append("(");
59 for (Type type : m_argumentTypes) {
60 buf.append(type.toString());
61 }
62 buf.append(")");
63 buf.append(m_returnType.toString());
64 return buf.toString();
65 }
66
67 public Iterable<Type> types() {
68 List<Type> types = Lists.newArrayList();
69 types.addAll(m_argumentTypes);
70 types.add(m_returnType);
71 return types;
72 }
73
74 public Iterable<ClassEntry> classes() {
75 List<ClassEntry> out = Lists.newArrayList();
76 for (Type type : types()) {
77 if (type.isClass()) {
78 out.add(type.getClassEntry());
79 }
80 }
81 return out;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 if (other instanceof Signature) {
87 return equals((Signature)other);
88 }
89 return false;
90 }
91
92 public boolean equals(Signature other) {
93 return m_argumentTypes.equals(other.m_argumentTypes) && m_returnType.equals(other.m_returnType);
94 }
95
96 @Override
97 public int hashCode() {
98 return Util.combineHashesOrdered(m_argumentTypes.hashCode(), m_returnType.hashCode());
99 }
100
101 public boolean hasClass(ClassEntry classEntry) {
102 for (Type type : types()) {
103 if (type.hasClass() && type.getClassEntry().equals(classEntry)) {
104 return true;
105 }
106 }
107 return false;
108 }
109}
diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java
new file mode 100644
index 0000000..3477cd5
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java
@@ -0,0 +1,94 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.IOException;
14import java.io.StringReader;
15import java.util.List;
16
17import com.google.common.collect.Lists;
18
19public class SignatureUpdater {
20
21 public interface ClassNameUpdater {
22 String update(String className);
23 }
24
25 public static String update(String signature, ClassNameUpdater updater) {
26 try {
27 StringBuilder buf = new StringBuilder();
28
29 // read the signature character-by-character
30 StringReader reader = new StringReader(signature);
31 int i = -1;
32 while ( (i = reader.read()) != -1) {
33 char c = (char)i;
34
35 // does this character start a class name?
36 if (c == 'L') {
37 // update the class name and add it to the buffer
38 buf.append('L');
39 String className = readClass(reader);
40 if (className == null) {
41 throw new IllegalArgumentException("Malformed signature: " + signature);
42 }
43 buf.append(updater.update(className));
44 buf.append(';');
45 } else {
46 // copy the character into the buffer
47 buf.append(c);
48 }
49 }
50
51 return buf.toString();
52 } catch (IOException ex) {
53 // I'm pretty sure a StringReader will never throw one of these
54 throw new Error(ex);
55 }
56 }
57
58 private static String readClass(StringReader reader) throws IOException {
59 // read all the characters in the buffer until we hit a ';'
60 // remember to treat generics correctly
61 StringBuilder buf = new StringBuilder();
62 int depth = 0;
63 int i = -1;
64 while ( (i = reader.read()) != -1) {
65 char c = (char)i;
66
67 if (c == '<') {
68 depth++;
69 } else if (c == '>') {
70 depth--;
71 } else if (depth == 0) {
72 if (c == ';') {
73 return buf.toString();
74 } else {
75 buf.append(c);
76 }
77 }
78 }
79
80 return null;
81 }
82
83 public static List<String> getClasses(String signature) {
84 final List<String> classNames = Lists.newArrayList();
85 update(signature, new ClassNameUpdater() {
86 @Override
87 public String update(String className) {
88 classNames.add(className);
89 return className;
90 }
91 });
92 return classNames;
93 }
94}
diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java
new file mode 100644
index 0000000..d1b14cd
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/TranslationDirection.java
@@ -0,0 +1,29 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13public enum TranslationDirection {
14
15 Deobfuscating {
16 @Override
17 public <T> T choose(T deobfChoice, T obfChoice) {
18 return deobfChoice;
19 }
20 },
21 Obfuscating {
22 @Override
23 public <T> T choose(T deobfChoice, T obfChoice) {
24 return obfChoice;
25 }
26 };
27
28 public abstract <T> T choose(T deobfChoice, T obfChoice);
29}
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
new file mode 100644
index 0000000..5eba18c
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -0,0 +1,239 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.util.Map;
14
15import com.google.common.collect.Maps;
16
17import cuchaz.enigma.analysis.TranslationIndex;
18
19public class Translator {
20
21 private TranslationDirection m_direction;
22 private Map<String,ClassMapping> m_classes;
23 private TranslationIndex m_index;
24
25 public Translator() {
26 m_direction = null;
27 m_classes = Maps.newHashMap();
28 }
29
30 public Translator(TranslationDirection direction, Map<String,ClassMapping> classes, TranslationIndex index) {
31 m_direction = direction;
32 m_classes = classes;
33 m_index = index;
34 }
35
36 @SuppressWarnings("unchecked")
37 public <T extends Entry> T translateEntry(T entry) {
38 if (entry instanceof ClassEntry) {
39 return (T)translateEntry((ClassEntry)entry);
40 } else if (entry instanceof FieldEntry) {
41 return (T)translateEntry((FieldEntry)entry);
42 } else if (entry instanceof MethodEntry) {
43 return (T)translateEntry((MethodEntry)entry);
44 } else if (entry instanceof ConstructorEntry) {
45 return (T)translateEntry((ConstructorEntry)entry);
46 } else if (entry instanceof ArgumentEntry) {
47 return (T)translateEntry((ArgumentEntry)entry);
48 } else {
49 throw new Error("Unknown entry type: " + entry.getClass().getName());
50 }
51 }
52
53 public String translateClass(String className) {
54 return translate(new ClassEntry(className));
55 }
56
57 public String translate(ClassEntry in) {
58 ClassMapping classMapping = m_classes.get(in.getOuterClassName());
59 if (classMapping != null) {
60 if (in.isInnerClass()) {
61 // translate the inner class
62 String translatedInnerClassName = m_direction.choose(
63 classMapping.getDeobfInnerClassName(in.getInnerClassName()),
64 classMapping.getObfInnerClassName(in.getInnerClassName())
65 );
66 if (translatedInnerClassName != null) {
67 // try to translate the outer name
68 String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName());
69 if (translatedOuterClassName != null) {
70 return translatedOuterClassName + "$" + translatedInnerClassName;
71 } else {
72 return in.getOuterClassName() + "$" + translatedInnerClassName;
73 }
74 }
75 } else {
76 // just return outer
77 return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName());
78 }
79 }
80 return null;
81 }
82
83 public ClassEntry translateEntry(ClassEntry in) {
84
85 // can we translate the inner class?
86 String name = translate(in);
87 if (name != null) {
88 return new ClassEntry(name);
89 }
90
91 if (in.isInnerClass()) {
92
93 // guess not. just translate the outer class name then
94 String outerClassName = translate(in.getOuterClassEntry());
95 if (outerClassName != null) {
96 return new ClassEntry(outerClassName + "$" + in.getInnerClassName());
97 }
98 }
99
100 return in;
101 }
102
103 public String translate(FieldEntry in) {
104
105 // resolve the class entry
106 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
107 if (resolvedClassEntry != null) {
108
109 // look for the class
110 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
111 if (classMapping != null) {
112
113 // look for the field
114 String translatedName = m_direction.choose(
115 classMapping.getDeobfFieldName(in.getName()),
116 classMapping.getObfFieldName(in.getName())
117 );
118 if (translatedName != null) {
119 return translatedName;
120 }
121 }
122 }
123 return null;
124 }
125
126 public FieldEntry translateEntry(FieldEntry in) {
127 String name = translate(in);
128 if (name == null) {
129 name = in.getName();
130 }
131 return new FieldEntry(translateEntry(in.getClassEntry()), name);
132 }
133
134 public String translate(MethodEntry in) {
135
136 // resolve the class entry
137 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
138 if (resolvedClassEntry != null) {
139
140 // look for class
141 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
142 if (classMapping != null) {
143
144 // look for the method
145 MethodMapping methodMapping = m_direction.choose(
146 classMapping.getMethodByObf(in.getName(), in.getSignature()),
147 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
148 );
149 if (methodMapping != null) {
150 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
151 }
152 }
153 }
154 return null;
155 }
156
157 public MethodEntry translateEntry(MethodEntry in) {
158 String name = translate(in);
159 if (name == null) {
160 name = in.getName();
161 }
162 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature()));
163 }
164
165 public ConstructorEntry translateEntry(ConstructorEntry in) {
166 if (in.isStatic()) {
167 return new ConstructorEntry(translateEntry(in.getClassEntry()));
168 } else {
169 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature()));
170 }
171 }
172
173 public BehaviorEntry translateEntry(BehaviorEntry in) {
174 if (in instanceof MethodEntry) {
175 return translateEntry((MethodEntry)in);
176 } else if (in instanceof ConstructorEntry) {
177 return translateEntry((ConstructorEntry)in);
178 }
179 throw new Error("Wrong entry type!");
180 }
181
182 public String translate(ArgumentEntry in) {
183
184 // look for the class
185 ClassMapping classMapping = findClassMapping(in.getClassEntry());
186 if (classMapping != null) {
187
188 // look for the method
189 MethodMapping methodMapping = m_direction.choose(
190 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()),
191 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature()))
192 );
193 if (methodMapping != null) {
194 return m_direction.choose(
195 methodMapping.getDeobfArgumentName(in.getIndex()),
196 methodMapping.getObfArgumentName(in.getIndex())
197 );
198 }
199 }
200 return null;
201 }
202
203 public ArgumentEntry translateEntry(ArgumentEntry in) {
204 String name = translate(in);
205 if (name == null) {
206 name = in.getName();
207 }
208 return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name);
209 }
210
211 public Type translateType(Type type) {
212 return new Type(type, new ClassNameReplacer() {
213 @Override
214 public String replace(String className) {
215 return translateClass(className);
216 }
217 });
218 }
219
220 public Signature translateSignature(Signature signature) {
221 return new Signature(signature, new ClassNameReplacer() {
222 @Override
223 public String replace(String className) {
224 return translateClass(className);
225 }
226 });
227 }
228
229 private ClassMapping findClassMapping(ClassEntry classEntry) {
230 ClassMapping classMapping = m_classes.get(classEntry.getOuterClassName());
231 if (classMapping != null && classEntry.isInnerClass()) {
232 classMapping = m_direction.choose(
233 classMapping.getInnerClassByObf(classEntry.getInnerClassName()),
234 classMapping.getInnerClassByDeobfThenObf(classEntry.getInnerClassName())
235 );
236 }
237 return classMapping;
238 }
239}
diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java
new file mode 100644
index 0000000..9f5d52f
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Type.java
@@ -0,0 +1,218 @@
1package cuchaz.enigma.mapping;
2
3import java.util.Map;
4
5import com.google.common.collect.Maps;
6
7public class Type {
8
9 public enum Primitive {
10 Byte('B'),
11 Character('C'),
12 Short('S'),
13 Integer('I'),
14 Long('J'),
15 Float('F'),
16 Double('D'),
17 Boolean('Z');
18
19 private static final Map<Character,Primitive> m_lookup;
20
21 static {
22 m_lookup = Maps.newTreeMap();
23 for (Primitive val : values()) {
24 m_lookup.put(val.getCode(), val);
25 }
26 }
27
28 public static Primitive get(char code) {
29 return m_lookup.get(code);
30 }
31
32 private char m_code;
33
34 private Primitive(char code) {
35 m_code = code;
36 }
37
38 public char getCode() {
39 return m_code;
40 }
41 }
42
43 public static String parseFirst(String in) {
44
45 if (in == null || in.length() <= 0) {
46 throw new IllegalArgumentException("No type to parse, input is empty!");
47 }
48
49 // read one type from the input
50
51 char c = in.charAt(0);
52
53 // first check for void
54 if (c == 'V') {
55 return "V";
56 }
57
58 // then check for primitives
59 Primitive primitive = Primitive.get(c);
60 if (primitive != null) {
61 return in.substring(0, 1);
62 }
63
64 // then check for classes
65 if (c == 'L') {
66 return readClass(in);
67 }
68
69 // then check for arrays
70 int dim = countArrayDimension(in);
71 if (dim > 0) {
72 String arrayType = Type.parseFirst(in.substring(dim));
73 return in.substring(0, dim + arrayType.length());
74 }
75
76 throw new IllegalArgumentException("don't know how to parse: " + in);
77 }
78
79 private String m_name;
80
81 public Type(String name) {
82 m_name = name;
83 }
84
85 public Type(ClassEntry classEntry) {
86 m_name = "L" + classEntry.getClassName() + ";";
87 }
88
89 public Type(Type type, ClassNameReplacer replacer) {
90 m_name = type.m_name;
91 if (type.isClass()) {
92 String replacedName = replacer.replace(type.getClassEntry().getClassName());
93 if (replacedName != null) {
94 m_name = "L" + replacedName + ";";
95 }
96 } else if (type.isArray() && type.hasClass()) {
97 String replacedName = replacer.replace(type.getClassEntry().getClassName());
98 if (replacedName != null) {
99 m_name = Type.getArrayPrefix(type.getArrayDimension()) + "L" + replacedName + ";";
100 }
101 }
102 }
103
104 @Override
105 public String toString() {
106 return m_name;
107 }
108
109 public boolean isVoid() {
110 return m_name.length() == 1 && m_name.charAt(0) == 'V';
111 }
112
113 public boolean isPrimitive() {
114 return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null;
115 }
116
117 public Primitive getPrimitive() {
118 if (!isPrimitive()) {
119 throw new IllegalStateException("not a primitive");
120 }
121 return Primitive.get(m_name.charAt(0));
122 }
123
124 public boolean isClass() {
125 return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';';
126 }
127
128 public ClassEntry getClassEntry() {
129 if (isClass()) {
130 String name = m_name.substring(1, m_name.length() - 1);
131
132 int pos = name.indexOf('<');
133 if (pos >= 0) {
134 // remove the parameters from the class name
135 name = name.substring(0, pos);
136 }
137
138 return new ClassEntry(name);
139
140 } else if (isArray() && getArrayType().isClass()) {
141 return getArrayType().getClassEntry();
142 } else {
143 throw new IllegalStateException("type doesn't have a class");
144 }
145 }
146
147 public boolean isArray() {
148 return m_name.charAt(0) == '[';
149 }
150
151 public int getArrayDimension() {
152 if (!isArray()) {
153 throw new IllegalStateException("not an array");
154 }
155 return countArrayDimension(m_name);
156 }
157
158 public Type getArrayType() {
159 if (!isArray()) {
160 throw new IllegalStateException("not an array");
161 }
162 return new Type(m_name.substring(getArrayDimension(), m_name.length()));
163 }
164
165 private static String getArrayPrefix(int dimension) {
166 StringBuilder buf = new StringBuilder();
167 for (int i=0; i<dimension; i++) {
168 buf.append("[");
169 }
170 return buf.toString();
171 }
172
173 public boolean hasClass() {
174 return isClass() || (isArray() && getArrayType().hasClass());
175 }
176
177 @Override
178 public boolean equals(Object other) {
179 if (other instanceof Type) {
180 return equals((Type)other);
181 }
182 return false;
183 }
184
185 public boolean equals(Type other) {
186 return m_name.equals(other.m_name);
187 }
188
189 public int hashCode() {
190 return m_name.hashCode();
191 }
192
193 private static int countArrayDimension(String in) {
194 int i=0;
195 for(; i < in.length() && in.charAt(i) == '['; i++);
196 return i;
197 }
198
199 private static String readClass(String in) {
200 // read all the characters in the buffer until we hit a ';'
201 // remember to treat parameters correctly
202 StringBuilder buf = new StringBuilder();
203 int depth = 0;
204 for (int i=0; i<in.length(); i++) {
205 char c = in.charAt(i);
206 buf.append(c);
207
208 if (c == '<') {
209 depth++;
210 } else if (c == '>') {
211 depth--;
212 } else if (depth == 0 && c == ';') {
213 return buf.toString();
214 }
215 }
216 return null;
217 }
218}