diff options
| author | 2015-02-08 21:29:25 -0500 | |
|---|---|---|
| committer | 2015-02-08 21:29:25 -0500 | |
| commit | ed9b5cdfc648e86fd463bfa8d86b94c41671e14c (patch) | |
| tree | 2619bbc7e04dfa3b82f8dfd3b1d31f529766cd4b /src/cuchaz/enigma/mapping | |
| download | enigma-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')
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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | import cuchaz.enigma.Util; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import javassist.CtBehavior; | ||
| 14 | import javassist.CtConstructor; | ||
| 15 | import javassist.CtMethod; | ||
| 16 | import javassist.bytecode.Descriptor; | ||
| 17 | |||
| 18 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.ArrayList; | ||
| 15 | import java.util.Map; | ||
| 16 | |||
| 17 | import com.google.common.collect.Maps; | ||
| 18 | |||
| 19 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | import cuchaz.enigma.Util; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | import cuchaz.enigma.Util; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import javassist.CtBehavior; | ||
| 4 | import javassist.CtClass; | ||
| 5 | import javassist.CtConstructor; | ||
| 6 | import javassist.CtField; | ||
| 7 | import javassist.CtMethod; | ||
| 8 | import javassist.bytecode.Descriptor; | ||
| 9 | import javassist.expr.ConstructorCall; | ||
| 10 | import javassist.expr.FieldAccess; | ||
| 11 | import javassist.expr.MethodCall; | ||
| 12 | import javassist.expr.NewExpr; | ||
| 13 | |||
| 14 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.ArrayList; | ||
| 15 | import java.util.Collection; | ||
| 16 | import java.util.Map; | ||
| 17 | import java.util.Set; | ||
| 18 | |||
| 19 | import com.google.common.collect.Maps; | ||
| 20 | import com.google.common.collect.Sets; | ||
| 21 | |||
| 22 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 23 | |||
| 24 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.BufferedReader; | ||
| 14 | import java.io.IOException; | ||
| 15 | import java.io.Reader; | ||
| 16 | import java.util.Deque; | ||
| 17 | |||
| 18 | import com.google.common.collect.Queues; | ||
| 19 | |||
| 20 | import cuchaz.enigma.Constants; | ||
| 21 | |||
| 22 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.ObjectOutputStream; | ||
| 15 | import java.io.OutputStream; | ||
| 16 | import java.util.Set; | ||
| 17 | import java.util.zip.GZIPOutputStream; | ||
| 18 | |||
| 19 | import cuchaz.enigma.Constants; | ||
| 20 | import cuchaz.enigma.analysis.JarIndex; | ||
| 21 | |||
| 22 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.PrintWriter; | ||
| 15 | import java.io.Writer; | ||
| 16 | import java.util.ArrayList; | ||
| 17 | import java.util.Collections; | ||
| 18 | import java.util.List; | ||
| 19 | |||
| 20 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | import cuchaz.enigma.Util; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.Map; | ||
| 15 | import java.util.TreeMap; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.Arrays; | ||
| 14 | import java.util.List; | ||
| 15 | import java.util.regex.Pattern; | ||
| 16 | |||
| 17 | import javassist.bytecode.Descriptor; | ||
| 18 | |||
| 19 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import java.util.List; | ||
| 4 | |||
| 5 | import com.beust.jcommander.internal.Lists; | ||
| 6 | |||
| 7 | import cuchaz.enigma.Util; | ||
| 8 | |||
| 9 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.StringReader; | ||
| 15 | import java.util.List; | ||
| 16 | |||
| 17 | import com.google.common.collect.Lists; | ||
| 18 | |||
| 19 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | |||
| 17 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 18 | |||
| 19 | public 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 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import java.util.Map; | ||
| 4 | |||
| 5 | import com.google.common.collect.Maps; | ||
| 6 | |||
| 7 | public 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 | } | ||