diff options
| author | 2015-05-21 23:30:00 +0100 | |
|---|---|---|
| committer | 2015-05-21 23:30:00 +0100 | |
| commit | e3f452250e51b7271f3989c7dfd12e4422934942 (patch) | |
| tree | 5aa482f9a6e21eb318a3e23e7d8274d77c73faf6 /src/cuchaz/enigma/mapping | |
| download | enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.gz enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.tar.xz enigma-fork-e3f452250e51b7271f3989c7dfd12e4422934942.zip | |
Support Gradle alongside SSJB
This makes builds faster, simpler and better automated but still keeps
Cuchaz happy. :)
Diffstat (limited to 'src/cuchaz/enigma/mapping')
29 files changed, 3415 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..9d99016 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..a0055a6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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 ArgumentMapping(ArgumentMapping other) { | ||
| 29 | m_index = other.m_index; | ||
| 30 | m_name = other.m_name; | ||
| 31 | } | ||
| 32 | |||
| 33 | public int getIndex() { | ||
| 34 | return m_index; | ||
| 35 | } | ||
| 36 | |||
| 37 | public String getName() { | ||
| 38 | return m_name; | ||
| 39 | } | ||
| 40 | |||
| 41 | public void setName(String val) { | ||
| 42 | m_name = NameValidator.validateArgumentName(val); | ||
| 43 | } | ||
| 44 | |||
| 45 | @Override | ||
| 46 | public int compareTo(ArgumentMapping other) { | ||
| 47 | return Integer.compare(m_index, other.m_index); | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/src/cuchaz/enigma/mapping/BehaviorEntry.java b/src/cuchaz/enigma/mapping/BehaviorEntry.java new file mode 100644 index 0000000..031d267 --- /dev/null +++ b/src/cuchaz/enigma/mapping/BehaviorEntry.java | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public interface BehaviorEntry extends Entry { | ||
| 14 | Signature getSignature(); | ||
| 15 | } | ||
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java new file mode 100644 index 0000000..373203f --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassEntry.java | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.List; | ||
| 15 | |||
| 16 | import com.google.common.collect.Lists; | ||
| 17 | |||
| 18 | public class ClassEntry implements Entry, Serializable { | ||
| 19 | |||
| 20 | private static final long serialVersionUID = 4235460580973955811L; | ||
| 21 | |||
| 22 | private String m_name; | ||
| 23 | |||
| 24 | public ClassEntry(String className) { | ||
| 25 | if (className == null) { | ||
| 26 | throw new IllegalArgumentException("Class name cannot be null!"); | ||
| 27 | } | ||
| 28 | if (className.indexOf('.') >= 0) { | ||
| 29 | throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); | ||
| 30 | } | ||
| 31 | |||
| 32 | m_name = className; | ||
| 33 | |||
| 34 | if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { | ||
| 35 | throw new IllegalArgumentException("Inner class must not have a package: " + className); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | public ClassEntry(ClassEntry other) { | ||
| 40 | m_name = other.m_name; | ||
| 41 | } | ||
| 42 | |||
| 43 | @Override | ||
| 44 | public String getName() { | ||
| 45 | return m_name; | ||
| 46 | } | ||
| 47 | |||
| 48 | @Override | ||
| 49 | public String getClassName() { | ||
| 50 | return m_name; | ||
| 51 | } | ||
| 52 | |||
| 53 | @Override | ||
| 54 | public ClassEntry getClassEntry() { | ||
| 55 | return this; | ||
| 56 | } | ||
| 57 | |||
| 58 | @Override | ||
| 59 | public ClassEntry cloneToNewClass(ClassEntry classEntry) { | ||
| 60 | return classEntry; | ||
| 61 | } | ||
| 62 | |||
| 63 | @Override | ||
| 64 | public int hashCode() { | ||
| 65 | return m_name.hashCode(); | ||
| 66 | } | ||
| 67 | |||
| 68 | @Override | ||
| 69 | public boolean equals(Object other) { | ||
| 70 | if (other instanceof ClassEntry) { | ||
| 71 | return equals((ClassEntry)other); | ||
| 72 | } | ||
| 73 | return false; | ||
| 74 | } | ||
| 75 | |||
| 76 | public boolean equals(ClassEntry other) { | ||
| 77 | return m_name.equals(other.m_name); | ||
| 78 | } | ||
| 79 | |||
| 80 | @Override | ||
| 81 | public String toString() { | ||
| 82 | return m_name; | ||
| 83 | } | ||
| 84 | |||
| 85 | public boolean isInnerClass() { | ||
| 86 | return m_name.lastIndexOf('$') >= 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | public List<String> getClassChainNames() { | ||
| 90 | return Lists.newArrayList(m_name.split("\\$")); | ||
| 91 | } | ||
| 92 | |||
| 93 | public List<ClassEntry> getClassChain() { | ||
| 94 | List<ClassEntry> entries = Lists.newArrayList(); | ||
| 95 | StringBuilder buf = new StringBuilder(); | ||
| 96 | for (String name : getClassChainNames()) { | ||
| 97 | if (buf.length() > 0) { | ||
| 98 | buf.append("$"); | ||
| 99 | } | ||
| 100 | buf.append(name); | ||
| 101 | entries.add(new ClassEntry(buf.toString())); | ||
| 102 | } | ||
| 103 | return entries; | ||
| 104 | } | ||
| 105 | |||
| 106 | public String getOutermostClassName() { | ||
| 107 | if (isInnerClass()) { | ||
| 108 | return m_name.substring(0, m_name.indexOf('$')); | ||
| 109 | } | ||
| 110 | return m_name; | ||
| 111 | } | ||
| 112 | |||
| 113 | public ClassEntry getOutermostClassEntry() { | ||
| 114 | return new ClassEntry(getOutermostClassName()); | ||
| 115 | } | ||
| 116 | |||
| 117 | public String getOuterClassName() { | ||
| 118 | if (!isInnerClass()) { | ||
| 119 | throw new Error("This is not an inner class!"); | ||
| 120 | } | ||
| 121 | return m_name.substring(0, m_name.lastIndexOf('$')); | ||
| 122 | } | ||
| 123 | |||
| 124 | public ClassEntry getOuterClassEntry() { | ||
| 125 | return new ClassEntry(getOuterClassName()); | ||
| 126 | } | ||
| 127 | |||
| 128 | public String getInnermostClassName() { | ||
| 129 | if (!isInnerClass()) { | ||
| 130 | throw new Error("This is not an inner class!"); | ||
| 131 | } | ||
| 132 | return m_name.substring(m_name.lastIndexOf('$') + 1); | ||
| 133 | } | ||
| 134 | |||
| 135 | public boolean isInDefaultPackage() { | ||
| 136 | return m_name.indexOf('/') < 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | public String getPackageName() { | ||
| 140 | int pos = m_name.lastIndexOf('/'); | ||
| 141 | if (pos > 0) { | ||
| 142 | return m_name.substring(0, pos); | ||
| 143 | } | ||
| 144 | return null; | ||
| 145 | } | ||
| 146 | |||
| 147 | public String getSimpleName() { | ||
| 148 | int pos = m_name.lastIndexOf('/'); | ||
| 149 | if (pos > 0) { | ||
| 150 | return m_name.substring(pos + 1); | ||
| 151 | } | ||
| 152 | return m_name; | ||
| 153 | } | ||
| 154 | |||
| 155 | public ClassEntry buildClassEntry(List<ClassEntry> classChain) { | ||
| 156 | assert(classChain.contains(this)); | ||
| 157 | StringBuilder buf = new StringBuilder(); | ||
| 158 | for (ClassEntry chainEntry : classChain) { | ||
| 159 | if (buf.length() == 0) { | ||
| 160 | buf.append(chainEntry.getName()); | ||
| 161 | } else { | ||
| 162 | buf.append("$"); | ||
| 163 | buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); | ||
| 164 | } | ||
| 165 | |||
| 166 | if (chainEntry == this) { | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | return new ClassEntry(buf.toString()); | ||
| 171 | } | ||
| 172 | } | ||
diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java new file mode 100644 index 0000000..0b0105e --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassMapping.java | |||
| @@ -0,0 +1,460 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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_obfFullName; | ||
| 24 | private String m_obfSimpleName; | ||
| 25 | private String m_deobfName; | ||
| 26 | private Map<String,ClassMapping> m_innerClassesByObfSimple; | ||
| 27 | private Map<String,ClassMapping> m_innerClassesByDeobf; | ||
| 28 | private Map<String,FieldMapping> m_fieldsByObf; | ||
| 29 | private Map<String,FieldMapping> m_fieldsByDeobf; | ||
| 30 | private Map<String,MethodMapping> m_methodsByObf; | ||
| 31 | private Map<String,MethodMapping> m_methodsByDeobf; | ||
| 32 | |||
| 33 | public ClassMapping(String obfFullName) { | ||
| 34 | this(obfFullName, null); | ||
| 35 | } | ||
| 36 | |||
| 37 | public ClassMapping(String obfFullName, String deobfName) { | ||
| 38 | m_obfFullName = obfFullName; | ||
| 39 | ClassEntry classEntry = new ClassEntry(obfFullName); | ||
| 40 | m_obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); | ||
| 41 | m_deobfName = NameValidator.validateClassName(deobfName, false); | ||
| 42 | m_innerClassesByObfSimple = Maps.newHashMap(); | ||
| 43 | m_innerClassesByDeobf = Maps.newHashMap(); | ||
| 44 | m_fieldsByObf = Maps.newHashMap(); | ||
| 45 | m_fieldsByDeobf = Maps.newHashMap(); | ||
| 46 | m_methodsByObf = Maps.newHashMap(); | ||
| 47 | m_methodsByDeobf = Maps.newHashMap(); | ||
| 48 | } | ||
| 49 | |||
| 50 | public String getObfFullName() { | ||
| 51 | return m_obfFullName; | ||
| 52 | } | ||
| 53 | |||
| 54 | public String getObfSimpleName() { | ||
| 55 | return m_obfSimpleName; | ||
| 56 | } | ||
| 57 | |||
| 58 | public String getDeobfName() { | ||
| 59 | return m_deobfName; | ||
| 60 | } | ||
| 61 | |||
| 62 | public void setDeobfName(String val) { | ||
| 63 | m_deobfName = NameValidator.validateClassName(val, false); | ||
| 64 | } | ||
| 65 | |||
| 66 | //// INNER CLASSES //////// | ||
| 67 | |||
| 68 | public Iterable<ClassMapping> innerClasses() { | ||
| 69 | assert (m_innerClassesByObfSimple.size() >= m_innerClassesByDeobf.size()); | ||
| 70 | return m_innerClassesByObfSimple.values(); | ||
| 71 | } | ||
| 72 | |||
| 73 | public void addInnerClassMapping(ClassMapping classMapping) { | ||
| 74 | boolean obfWasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; | ||
| 75 | assert (obfWasAdded); | ||
| 76 | if (classMapping.getDeobfName() != null) { | ||
| 77 | assert (isSimpleClassName(classMapping.getDeobfName())); | ||
| 78 | boolean deobfWasAdded = m_innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; | ||
| 79 | assert (deobfWasAdded); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | public void removeInnerClassMapping(ClassMapping classMapping) { | ||
| 84 | boolean obfWasRemoved = m_innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; | ||
| 85 | assert (obfWasRemoved); | ||
| 86 | if (classMapping.getDeobfName() != null) { | ||
| 87 | boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 88 | assert (deobfWasRemoved); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { | ||
| 93 | ClassMapping classMapping = m_innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); | ||
| 94 | if (classMapping == null) { | ||
| 95 | classMapping = new ClassMapping(obfInnerClass.getName()); | ||
| 96 | boolean wasAdded = m_innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; | ||
| 97 | assert (wasAdded); | ||
| 98 | } | ||
| 99 | return classMapping; | ||
| 100 | } | ||
| 101 | |||
| 102 | public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { | ||
| 103 | assert (isSimpleClassName(obfSimpleName)); | ||
| 104 | return m_innerClassesByObfSimple.get(obfSimpleName); | ||
| 105 | } | ||
| 106 | |||
| 107 | public ClassMapping getInnerClassByDeobf(String deobfName) { | ||
| 108 | assert (isSimpleClassName(deobfName)); | ||
| 109 | return m_innerClassesByDeobf.get(deobfName); | ||
| 110 | } | ||
| 111 | |||
| 112 | public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { | ||
| 113 | ClassMapping classMapping = getInnerClassByDeobf(name); | ||
| 114 | if (classMapping == null) { | ||
| 115 | classMapping = getInnerClassByObfSimple(name); | ||
| 116 | } | ||
| 117 | return classMapping; | ||
| 118 | } | ||
| 119 | |||
| 120 | public String getDeobfInnerClassName(String obfSimpleName) { | ||
| 121 | assert (isSimpleClassName(obfSimpleName)); | ||
| 122 | ClassMapping classMapping = m_innerClassesByObfSimple.get(obfSimpleName); | ||
| 123 | if (classMapping != null) { | ||
| 124 | return classMapping.getDeobfName(); | ||
| 125 | } | ||
| 126 | return null; | ||
| 127 | } | ||
| 128 | |||
| 129 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { | ||
| 130 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); | ||
| 131 | if (classMapping.getDeobfName() != null) { | ||
| 132 | boolean wasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 133 | assert (wasRemoved); | ||
| 134 | } | ||
| 135 | classMapping.setDeobfName(deobfName); | ||
| 136 | if (deobfName != null) { | ||
| 137 | assert (isSimpleClassName(deobfName)); | ||
| 138 | boolean wasAdded = m_innerClassesByDeobf.put(deobfName, classMapping) == null; | ||
| 139 | assert (wasAdded); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | public boolean hasInnerClassByObfSimple(String obfSimpleName) { | ||
| 144 | return m_innerClassesByObfSimple.containsKey(obfSimpleName); | ||
| 145 | } | ||
| 146 | |||
| 147 | public boolean hasInnerClassByDeobf(String deobfName) { | ||
| 148 | return m_innerClassesByDeobf.containsKey(deobfName); | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | //// FIELDS //////// | ||
| 153 | |||
| 154 | public Iterable<FieldMapping> fields() { | ||
| 155 | assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); | ||
| 156 | return m_fieldsByObf.values(); | ||
| 157 | } | ||
| 158 | |||
| 159 | public boolean containsObfField(String obfName, Type obfType) { | ||
| 160 | return m_fieldsByObf.containsKey(getFieldKey(obfName, obfType)); | ||
| 161 | } | ||
| 162 | |||
| 163 | public boolean containsDeobfField(String deobfName, Type deobfType) { | ||
| 164 | return m_fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); | ||
| 165 | } | ||
| 166 | |||
| 167 | public void addFieldMapping(FieldMapping fieldMapping) { | ||
| 168 | String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | ||
| 169 | if (m_fieldsByObf.containsKey(obfKey)) { | ||
| 170 | throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); | ||
| 171 | } | ||
| 172 | String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); | ||
| 173 | if (m_fieldsByDeobf.containsKey(deobfKey)) { | ||
| 174 | throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); | ||
| 175 | } | ||
| 176 | boolean obfWasAdded = m_fieldsByObf.put(obfKey, fieldMapping) == null; | ||
| 177 | assert (obfWasAdded); | ||
| 178 | boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null; | ||
| 179 | assert (deobfWasAdded); | ||
| 180 | assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); | ||
| 181 | } | ||
| 182 | |||
| 183 | public void removeFieldMapping(FieldMapping fieldMapping) { | ||
| 184 | boolean obfWasRemoved = m_fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; | ||
| 185 | assert (obfWasRemoved); | ||
| 186 | if (fieldMapping.getDeobfName() != null) { | ||
| 187 | boolean deobfWasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; | ||
| 188 | assert (deobfWasRemoved); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | public FieldMapping getFieldByObf(String obfName, Type obfType) { | ||
| 193 | return m_fieldsByObf.get(getFieldKey(obfName, obfType)); | ||
| 194 | } | ||
| 195 | |||
| 196 | public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { | ||
| 197 | return m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | ||
| 198 | } | ||
| 199 | |||
| 200 | public String getObfFieldName(String deobfName, Type obfType) { | ||
| 201 | FieldMapping fieldMapping = m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | ||
| 202 | if (fieldMapping != null) { | ||
| 203 | return fieldMapping.getObfName(); | ||
| 204 | } | ||
| 205 | return null; | ||
| 206 | } | ||
| 207 | |||
| 208 | public String getDeobfFieldName(String obfName, Type obfType) { | ||
| 209 | FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); | ||
| 210 | if (fieldMapping != null) { | ||
| 211 | return fieldMapping.getDeobfName(); | ||
| 212 | } | ||
| 213 | return null; | ||
| 214 | } | ||
| 215 | |||
| 216 | private String getFieldKey(String name, Type type) { | ||
| 217 | if (name == null) { | ||
| 218 | throw new IllegalArgumentException("name cannot be null!"); | ||
| 219 | } | ||
| 220 | if (type == null) { | ||
| 221 | throw new IllegalArgumentException("type cannot be null!"); | ||
| 222 | } | ||
| 223 | return name + ":" + type; | ||
| 224 | } | ||
| 225 | |||
| 226 | |||
| 227 | public void setFieldName(String obfName, Type obfType, String deobfName) { | ||
| 228 | assert(deobfName != null); | ||
| 229 | FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); | ||
| 230 | if (fieldMapping == null) { | ||
| 231 | fieldMapping = new FieldMapping(obfName, obfType, deobfName); | ||
| 232 | boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; | ||
| 233 | assert (obfWasAdded); | ||
| 234 | } else { | ||
| 235 | boolean wasRemoved = m_fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; | ||
| 236 | assert (wasRemoved); | ||
| 237 | } | ||
| 238 | fieldMapping.setDeobfName(deobfName); | ||
| 239 | if (deobfName != null) { | ||
| 240 | boolean wasAdded = m_fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; | ||
| 241 | assert (wasAdded); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { | ||
| 246 | assert(newObfName != null); | ||
| 247 | FieldMapping fieldMapping = m_fieldsByObf.remove(getFieldKey(oldObfName, obfType)); | ||
| 248 | assert(fieldMapping != null); | ||
| 249 | fieldMapping.setObfName(newObfName); | ||
| 250 | fieldMapping.setObfType(newObfType); | ||
| 251 | boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; | ||
| 252 | assert(obfWasAdded); | ||
| 253 | } | ||
| 254 | |||
| 255 | |||
| 256 | //// METHODS //////// | ||
| 257 | |||
| 258 | public Iterable<MethodMapping> methods() { | ||
| 259 | assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); | ||
| 260 | return m_methodsByObf.values(); | ||
| 261 | } | ||
| 262 | |||
| 263 | public boolean containsObfMethod(String obfName, Signature obfSignature) { | ||
| 264 | return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); | ||
| 265 | } | ||
| 266 | |||
| 267 | public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { | ||
| 268 | return m_methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); | ||
| 269 | } | ||
| 270 | |||
| 271 | public void addMethodMapping(MethodMapping methodMapping) { | ||
| 272 | String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | ||
| 273 | if (m_methodsByObf.containsKey(obfKey)) { | ||
| 274 | throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); | ||
| 275 | } | ||
| 276 | boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; | ||
| 277 | assert (wasAdded); | ||
| 278 | if (methodMapping.getDeobfName() != null) { | ||
| 279 | String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); | ||
| 280 | if (m_methodsByDeobf.containsKey(deobfKey)) { | ||
| 281 | throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); | ||
| 282 | } | ||
| 283 | boolean deobfWasAdded = m_methodsByDeobf.put(deobfKey, methodMapping) == null; | ||
| 284 | assert (deobfWasAdded); | ||
| 285 | } | ||
| 286 | assert (m_methodsByObf.size() >= m_methodsByDeobf.size()); | ||
| 287 | } | ||
| 288 | |||
| 289 | public void removeMethodMapping(MethodMapping methodMapping) { | ||
| 290 | boolean obfWasRemoved = m_methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; | ||
| 291 | assert (obfWasRemoved); | ||
| 292 | if (methodMapping.getDeobfName() != null) { | ||
| 293 | boolean deobfWasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; | ||
| 294 | assert (deobfWasRemoved); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { | ||
| 299 | return m_methodsByObf.get(getMethodKey(obfName, obfSignature)); | ||
| 300 | } | ||
| 301 | |||
| 302 | public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { | ||
| 303 | return m_methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); | ||
| 304 | } | ||
| 305 | |||
| 306 | private String getMethodKey(String name, Signature signature) { | ||
| 307 | if (name == null) { | ||
| 308 | throw new IllegalArgumentException("name cannot be null!"); | ||
| 309 | } | ||
| 310 | if (signature == null) { | ||
| 311 | throw new IllegalArgumentException("signature cannot be null!"); | ||
| 312 | } | ||
| 313 | return name + signature; | ||
| 314 | } | ||
| 315 | |||
| 316 | public void setMethodName(String obfName, Signature obfSignature, String deobfName) { | ||
| 317 | MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, obfSignature)); | ||
| 318 | if (methodMapping == null) { | ||
| 319 | methodMapping = createMethodMapping(obfName, obfSignature); | ||
| 320 | } else if (methodMapping.getDeobfName() != null) { | ||
| 321 | boolean wasRemoved = m_methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; | ||
| 322 | assert (wasRemoved); | ||
| 323 | } | ||
| 324 | methodMapping.setDeobfName(deobfName); | ||
| 325 | if (deobfName != null) { | ||
| 326 | boolean wasAdded = m_methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; | ||
| 327 | assert (wasAdded); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { | ||
| 332 | assert(newObfName != null); | ||
| 333 | MethodMapping methodMapping = m_methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); | ||
| 334 | assert(methodMapping != null); | ||
| 335 | methodMapping.setObfName(newObfName); | ||
| 336 | methodMapping.setObfSignature(newObfSignature); | ||
| 337 | boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; | ||
| 338 | assert(obfWasAdded); | ||
| 339 | } | ||
| 340 | |||
| 341 | //// ARGUMENTS //////// | ||
| 342 | |||
| 343 | public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { | ||
| 344 | assert(argumentName != null); | ||
| 345 | MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); | ||
| 346 | if (methodMapping == null) { | ||
| 347 | methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); | ||
| 348 | } | ||
| 349 | methodMapping.setArgumentName(argumentIndex, argumentName); | ||
| 350 | } | ||
| 351 | |||
| 352 | public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { | ||
| 353 | m_methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); | ||
| 354 | } | ||
| 355 | |||
| 356 | private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { | ||
| 357 | MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); | ||
| 358 | boolean wasAdded = m_methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; | ||
| 359 | assert (wasAdded); | ||
| 360 | return methodMapping; | ||
| 361 | } | ||
| 362 | |||
| 363 | @Override | ||
| 364 | public String toString() { | ||
| 365 | StringBuilder buf = new StringBuilder(); | ||
| 366 | buf.append(m_obfFullName); | ||
| 367 | buf.append(" <-> "); | ||
| 368 | buf.append(m_deobfName); | ||
| 369 | buf.append("\n"); | ||
| 370 | buf.append("Fields:\n"); | ||
| 371 | for (FieldMapping fieldMapping : fields()) { | ||
| 372 | buf.append("\t"); | ||
| 373 | buf.append(fieldMapping.getObfName()); | ||
| 374 | buf.append(" <-> "); | ||
| 375 | buf.append(fieldMapping.getDeobfName()); | ||
| 376 | buf.append("\n"); | ||
| 377 | } | ||
| 378 | buf.append("Methods:\n"); | ||
| 379 | for (MethodMapping methodMapping : m_methodsByObf.values()) { | ||
| 380 | buf.append(methodMapping.toString()); | ||
| 381 | buf.append("\n"); | ||
| 382 | } | ||
| 383 | buf.append("Inner Classes:\n"); | ||
| 384 | for (ClassMapping classMapping : m_innerClassesByObfSimple.values()) { | ||
| 385 | buf.append("\t"); | ||
| 386 | buf.append(classMapping.getObfSimpleName()); | ||
| 387 | buf.append(" <-> "); | ||
| 388 | buf.append(classMapping.getDeobfName()); | ||
| 389 | buf.append("\n"); | ||
| 390 | } | ||
| 391 | return buf.toString(); | ||
| 392 | } | ||
| 393 | |||
| 394 | @Override | ||
| 395 | public int compareTo(ClassMapping other) { | ||
| 396 | // sort by a, b, c, ... aa, ab, etc | ||
| 397 | if (m_obfFullName.length() != other.m_obfFullName.length()) { | ||
| 398 | return m_obfFullName.length() - other.m_obfFullName.length(); | ||
| 399 | } | ||
| 400 | return m_obfFullName.compareTo(other.m_obfFullName); | ||
| 401 | } | ||
| 402 | |||
| 403 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { | ||
| 404 | |||
| 405 | // rename inner classes | ||
| 406 | for (ClassMapping innerClassMapping : new ArrayList<ClassMapping>(m_innerClassesByObfSimple.values())) { | ||
| 407 | if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 408 | boolean wasRemoved = m_innerClassesByObfSimple.remove(oldObfClassName) != null; | ||
| 409 | assert (wasRemoved); | ||
| 410 | boolean wasAdded = m_innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; | ||
| 411 | assert (wasAdded); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | // rename field types | ||
| 416 | for (FieldMapping fieldMapping : new ArrayList<FieldMapping>(m_fieldsByObf.values())) { | ||
| 417 | String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | ||
| 418 | if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 419 | boolean wasRemoved = m_fieldsByObf.remove(oldFieldKey) != null; | ||
| 420 | assert (wasRemoved); | ||
| 421 | boolean wasAdded = m_fieldsByObf.put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; | ||
| 422 | assert (wasAdded); | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | // rename method signatures | ||
| 427 | for (MethodMapping methodMapping : new ArrayList<MethodMapping>(m_methodsByObf.values())) { | ||
| 428 | String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | ||
| 429 | if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 430 | boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null; | ||
| 431 | assert (wasRemoved); | ||
| 432 | boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; | ||
| 433 | assert (wasAdded); | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | if (m_obfFullName.equals(oldObfClassName)) { | ||
| 438 | // rename this class | ||
| 439 | m_obfFullName = newObfClassName; | ||
| 440 | return true; | ||
| 441 | } | ||
| 442 | return false; | ||
| 443 | } | ||
| 444 | |||
| 445 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { | ||
| 446 | MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); | ||
| 447 | if (methodMapping != null) { | ||
| 448 | return methodMapping.containsArgument(name); | ||
| 449 | } | ||
| 450 | return false; | ||
| 451 | } | ||
| 452 | |||
| 453 | public static boolean isSimpleClassName(String name) { | ||
| 454 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; | ||
| 455 | } | ||
| 456 | |||
| 457 | public ClassEntry getObfEntry() { | ||
| 458 | return new ClassEntry(m_obfFullName); | ||
| 459 | } | ||
| 460 | } | ||
diff --git a/src/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/cuchaz/enigma/mapping/ClassNameReplacer.java new file mode 100644 index 0000000..f00d811 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ClassNameReplacer.java | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | public interface ClassNameReplacer { | ||
| 14 | String replace(String className); | ||
| 15 | } | ||
diff --git a/src/cuchaz/enigma/mapping/ConstructorEntry.java b/src/cuchaz/enigma/mapping/ConstructorEntry.java new file mode 100644 index 0000000..7cde8f6 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ConstructorEntry.java | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..3c94a95 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Entry.java | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java new file mode 100644 index 0000000..03d97ba --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import javassist.CtBehavior; | ||
| 14 | import javassist.CtClass; | ||
| 15 | import javassist.CtConstructor; | ||
| 16 | import javassist.CtField; | ||
| 17 | import javassist.CtMethod; | ||
| 18 | import javassist.bytecode.Descriptor; | ||
| 19 | import javassist.expr.ConstructorCall; | ||
| 20 | import javassist.expr.FieldAccess; | ||
| 21 | import javassist.expr.MethodCall; | ||
| 22 | import javassist.expr.NewExpr; | ||
| 23 | |||
| 24 | import cuchaz.enigma.analysis.JarIndex; | ||
| 25 | |||
| 26 | public class EntryFactory { | ||
| 27 | |||
| 28 | public static ClassEntry getClassEntry(CtClass c) { | ||
| 29 | return new ClassEntry(Descriptor.toJvmName(c.getName())); | ||
| 30 | } | ||
| 31 | |||
| 32 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { | ||
| 33 | ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); | ||
| 34 | return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); | ||
| 35 | } | ||
| 36 | |||
| 37 | private static ClassEntry getObfClassEntry(ClassMapping classMapping) { | ||
| 38 | return new ClassEntry(classMapping.getObfFullName()); | ||
| 39 | } | ||
| 40 | |||
| 41 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | ||
| 42 | return new ClassEntry(classMapping.getDeobfName()); | ||
| 43 | } | ||
| 44 | |||
| 45 | public static ClassEntry getSuperclassEntry(CtClass c) { | ||
| 46 | return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); | ||
| 47 | } | ||
| 48 | |||
| 49 | public static FieldEntry getFieldEntry(CtField field) { | ||
| 50 | return new FieldEntry( | ||
| 51 | getClassEntry(field.getDeclaringClass()), | ||
| 52 | field.getName(), | ||
| 53 | new Type(field.getFieldInfo().getDescriptor()) | ||
| 54 | ); | ||
| 55 | } | ||
| 56 | |||
| 57 | public static FieldEntry getFieldEntry(FieldAccess call) { | ||
| 58 | return new FieldEntry( | ||
| 59 | new ClassEntry(Descriptor.toJvmName(call.getClassName())), | ||
| 60 | call.getFieldName(), | ||
| 61 | new Type(call.getSignature()) | ||
| 62 | ); | ||
| 63 | } | ||
| 64 | |||
| 65 | public static FieldEntry getFieldEntry(String className, String name, String type) { | ||
| 66 | return new FieldEntry(new ClassEntry(className), name, new Type(type)); | ||
| 67 | } | ||
| 68 | |||
| 69 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { | ||
| 70 | return new FieldEntry( | ||
| 71 | getObfClassEntry(classMapping), | ||
| 72 | fieldMapping.getObfName(), | ||
| 73 | fieldMapping.getObfType() | ||
| 74 | ); | ||
| 75 | } | ||
| 76 | |||
| 77 | public static MethodEntry getMethodEntry(CtMethod method) { | ||
| 78 | return new MethodEntry( | ||
| 79 | getClassEntry(method.getDeclaringClass()), | ||
| 80 | method.getName(), | ||
| 81 | new Signature(method.getMethodInfo().getDescriptor()) | ||
| 82 | ); | ||
| 83 | } | ||
| 84 | |||
| 85 | public static MethodEntry getMethodEntry(MethodCall call) { | ||
| 86 | return new MethodEntry( | ||
| 87 | new ClassEntry(Descriptor.toJvmName(call.getClassName())), | ||
| 88 | call.getMethodName(), | ||
| 89 | new Signature(call.getSignature()) | ||
| 90 | ); | ||
| 91 | } | ||
| 92 | |||
| 93 | public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { | ||
| 94 | if (constructor.isClassInitializer()) { | ||
| 95 | return new ConstructorEntry( | ||
| 96 | getClassEntry(constructor.getDeclaringClass()) | ||
| 97 | ); | ||
| 98 | } else { | ||
| 99 | return new ConstructorEntry( | ||
| 100 | getClassEntry(constructor.getDeclaringClass()), | ||
| 101 | new Signature(constructor.getMethodInfo().getDescriptor()) | ||
| 102 | ); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | public static ConstructorEntry getConstructorEntry(ConstructorCall call) { | ||
| 107 | return new ConstructorEntry( | ||
| 108 | new ClassEntry(Descriptor.toJvmName(call.getClassName())), | ||
| 109 | new Signature(call.getSignature()) | ||
| 110 | ); | ||
| 111 | } | ||
| 112 | |||
| 113 | public static ConstructorEntry getConstructorEntry(NewExpr call) { | ||
| 114 | return new ConstructorEntry( | ||
| 115 | new ClassEntry(Descriptor.toJvmName(call.getClassName())), | ||
| 116 | new Signature(call.getSignature()) | ||
| 117 | ); | ||
| 118 | } | ||
| 119 | |||
| 120 | public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { | ||
| 121 | if (behavior instanceof CtMethod) { | ||
| 122 | return getMethodEntry((CtMethod)behavior); | ||
| 123 | } else if (behavior instanceof CtConstructor) { | ||
| 124 | return getConstructorEntry((CtConstructor)behavior); | ||
| 125 | } | ||
| 126 | throw new Error("behavior is neither Method nor Constructor!"); | ||
| 127 | } | ||
| 128 | |||
| 129 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { | ||
| 130 | return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); | ||
| 131 | } | ||
| 132 | |||
| 133 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { | ||
| 134 | return getBehaviorEntry(new ClassEntry(className), behaviorName); | ||
| 135 | } | ||
| 136 | |||
| 137 | public static BehaviorEntry getBehaviorEntry(String className) { | ||
| 138 | return new ConstructorEntry(new ClassEntry(className)); | ||
| 139 | } | ||
| 140 | |||
| 141 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { | ||
| 142 | if (behaviorName.equals("<init>")) { | ||
| 143 | return new ConstructorEntry(classEntry, behaviorSignature); | ||
| 144 | } else if(behaviorName.equals("<clinit>")) { | ||
| 145 | return new ConstructorEntry(classEntry); | ||
| 146 | } else { | ||
| 147 | return new MethodEntry(classEntry, behaviorName, behaviorSignature); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { | ||
| 152 | if(behaviorName.equals("<clinit>")) { | ||
| 153 | return new ConstructorEntry(classEntry); | ||
| 154 | } else { | ||
| 155 | throw new IllegalArgumentException("Only class initializers don't have signatures"); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { | ||
| 160 | return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); | ||
| 161 | } | ||
| 162 | |||
| 163 | public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { | ||
| 164 | return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); | ||
| 165 | } | ||
| 166 | } | ||
diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java new file mode 100644 index 0000000..82b28cd --- /dev/null +++ b/src/cuchaz/enigma/mapping/EntryPair.java | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..e4a74f4 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldEntry.java | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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 | private Type m_type; | ||
| 24 | |||
| 25 | // NOTE: this argument order is important for the MethodReader/MethodWriter | ||
| 26 | public FieldEntry(ClassEntry classEntry, String name, Type type) { | ||
| 27 | if (classEntry == null) { | ||
| 28 | throw new IllegalArgumentException("Class cannot be null!"); | ||
| 29 | } | ||
| 30 | if (name == null) { | ||
| 31 | throw new IllegalArgumentException("Field name cannot be null!"); | ||
| 32 | } | ||
| 33 | if (type == null) { | ||
| 34 | throw new IllegalArgumentException("Field type cannot be null!"); | ||
| 35 | } | ||
| 36 | |||
| 37 | m_classEntry = classEntry; | ||
| 38 | m_name = name; | ||
| 39 | m_type = type; | ||
| 40 | } | ||
| 41 | |||
| 42 | public FieldEntry(FieldEntry other) { | ||
| 43 | this(other, new ClassEntry(other.m_classEntry)); | ||
| 44 | } | ||
| 45 | |||
| 46 | public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { | ||
| 47 | m_classEntry = newClassEntry; | ||
| 48 | m_name = other.m_name; | ||
| 49 | m_type = other.m_type; | ||
| 50 | } | ||
| 51 | |||
| 52 | @Override | ||
| 53 | public ClassEntry getClassEntry() { | ||
| 54 | return m_classEntry; | ||
| 55 | } | ||
| 56 | |||
| 57 | @Override | ||
| 58 | public String getName() { | ||
| 59 | return m_name; | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public String getClassName() { | ||
| 64 | return m_classEntry.getName(); | ||
| 65 | } | ||
| 66 | |||
| 67 | public Type getType() { | ||
| 68 | return m_type; | ||
| 69 | } | ||
| 70 | |||
| 71 | @Override | ||
| 72 | public FieldEntry cloneToNewClass(ClassEntry classEntry) { | ||
| 73 | return new FieldEntry(this, classEntry); | ||
| 74 | } | ||
| 75 | |||
| 76 | @Override | ||
| 77 | public int hashCode() { | ||
| 78 | return Util.combineHashesOrdered(m_classEntry, m_name, m_type); | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public boolean equals(Object other) { | ||
| 83 | if (other instanceof FieldEntry) { | ||
| 84 | return equals((FieldEntry)other); | ||
| 85 | } | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | public boolean equals(FieldEntry other) { | ||
| 90 | return m_classEntry.equals(other.m_classEntry) | ||
| 91 | && m_name.equals(other.m_name) | ||
| 92 | && m_type.equals(other.m_type); | ||
| 93 | } | ||
| 94 | |||
| 95 | @Override | ||
| 96 | public String toString() { | ||
| 97 | return m_classEntry.getName() + "." + m_name + ":" + m_type; | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java new file mode 100644 index 0000000..2855740 --- /dev/null +++ b/src/cuchaz/enigma/mapping/FieldMapping.java | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | public class FieldMapping implements Serializable, Comparable<FieldMapping>, MemberMapping<FieldEntry> { | ||
| 16 | |||
| 17 | private static final long serialVersionUID = 8610742471440861315L; | ||
| 18 | |||
| 19 | private String m_obfName; | ||
| 20 | private String m_deobfName; | ||
| 21 | private Type m_obfType; | ||
| 22 | |||
| 23 | public FieldMapping(String obfName, Type obfType, String deobfName) { | ||
| 24 | m_obfName = obfName; | ||
| 25 | m_deobfName = NameValidator.validateFieldName(deobfName); | ||
| 26 | m_obfType = obfType; | ||
| 27 | } | ||
| 28 | |||
| 29 | public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { | ||
| 30 | m_obfName = other.m_obfName; | ||
| 31 | m_deobfName = other.m_deobfName; | ||
| 32 | m_obfType = new Type(other.m_obfType, obfClassNameReplacer); | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public String getObfName() { | ||
| 37 | return m_obfName; | ||
| 38 | } | ||
| 39 | |||
| 40 | public void setObfName(String val) { | ||
| 41 | m_obfName = NameValidator.validateFieldName(val); | ||
| 42 | } | ||
| 43 | |||
| 44 | public String getDeobfName() { | ||
| 45 | return m_deobfName; | ||
| 46 | } | ||
| 47 | |||
| 48 | public void setDeobfName(String val) { | ||
| 49 | m_deobfName = NameValidator.validateFieldName(val); | ||
| 50 | } | ||
| 51 | |||
| 52 | public Type getObfType() { | ||
| 53 | return m_obfType; | ||
| 54 | } | ||
| 55 | |||
| 56 | public void setObfType(Type val) { | ||
| 57 | m_obfType = val; | ||
| 58 | } | ||
| 59 | |||
| 60 | @Override | ||
| 61 | public int compareTo(FieldMapping other) { | ||
| 62 | return (m_obfName + m_obfType).compareTo(other.m_obfName + other.m_obfType); | ||
| 63 | } | ||
| 64 | |||
| 65 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 66 | |||
| 67 | // rename obf classes in the type | ||
| 68 | Type newType = new Type(m_obfType, new ClassNameReplacer() { | ||
| 69 | @Override | ||
| 70 | public String replace(String className) { | ||
| 71 | if (className.equals(oldObfClassName)) { | ||
| 72 | return newObfClassName; | ||
| 73 | } | ||
| 74 | return null; | ||
| 75 | } | ||
| 76 | }); | ||
| 77 | |||
| 78 | if (!newType.equals(m_obfType)) { | ||
| 79 | m_obfType = newType; | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 85 | @Override | ||
| 86 | public FieldEntry getObfEntry(ClassEntry classEntry) { | ||
| 87 | return new FieldEntry(classEntry, m_obfName, new Type(m_obfType)); | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java new file mode 100644 index 0000000..f62df7c --- /dev/null +++ b/src/cuchaz/enigma/mapping/IllegalNameException.java | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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/MappingParseException.java b/src/cuchaz/enigma/mapping/MappingParseException.java new file mode 100644 index 0000000..73fca94 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingParseException.java | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..11ed5d0 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Mappings.java | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.ArrayList; | ||
| 15 | import java.util.Collection; | ||
| 16 | import java.util.List; | ||
| 17 | import java.util.Map; | ||
| 18 | import java.util.Set; | ||
| 19 | |||
| 20 | import com.google.common.collect.Lists; | ||
| 21 | import com.google.common.collect.Maps; | ||
| 22 | import com.google.common.collect.Sets; | ||
| 23 | |||
| 24 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 25 | |||
| 26 | public class Mappings implements Serializable { | ||
| 27 | |||
| 28 | private static final long serialVersionUID = 4649790259460259026L; | ||
| 29 | |||
| 30 | protected Map<String,ClassMapping> m_classesByObf; | ||
| 31 | protected Map<String,ClassMapping> m_classesByDeobf; | ||
| 32 | |||
| 33 | public Mappings() { | ||
| 34 | m_classesByObf = Maps.newHashMap(); | ||
| 35 | m_classesByDeobf = Maps.newHashMap(); | ||
| 36 | } | ||
| 37 | |||
| 38 | public Mappings(Iterable<ClassMapping> classes) { | ||
| 39 | this(); | ||
| 40 | |||
| 41 | for (ClassMapping classMapping : classes) { | ||
| 42 | m_classesByObf.put(classMapping.getObfFullName(), classMapping); | ||
| 43 | if (classMapping.getDeobfName() != null) { | ||
| 44 | m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | public Collection<ClassMapping> classes() { | ||
| 50 | assert (m_classesByObf.size() >= m_classesByDeobf.size()); | ||
| 51 | return m_classesByObf.values(); | ||
| 52 | } | ||
| 53 | |||
| 54 | public void addClassMapping(ClassMapping classMapping) { | ||
| 55 | if (m_classesByObf.containsKey(classMapping.getObfFullName())) { | ||
| 56 | throw new Error("Already have mapping for " + classMapping.getObfFullName()); | ||
| 57 | } | ||
| 58 | boolean obfWasAdded = m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; | ||
| 59 | assert (obfWasAdded); | ||
| 60 | if (classMapping.getDeobfName() != null) { | ||
| 61 | if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { | ||
| 62 | throw new Error("Already have mapping for " + classMapping.getDeobfName()); | ||
| 63 | } | ||
| 64 | boolean deobfWasAdded = m_classesByDeobf.put(classMapping.getDeobfName(), classMapping) == null; | ||
| 65 | assert (deobfWasAdded); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | public void removeClassMapping(ClassMapping classMapping) { | ||
| 70 | boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfFullName()) != null; | ||
| 71 | assert (obfWasRemoved); | ||
| 72 | if (classMapping.getDeobfName() != null) { | ||
| 73 | boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 74 | assert (deobfWasRemoved); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | public ClassMapping getClassByObf(ClassEntry entry) { | ||
| 79 | return getClassByObf(entry.getName()); | ||
| 80 | } | ||
| 81 | |||
| 82 | public ClassMapping getClassByObf(String obfName) { | ||
| 83 | return m_classesByObf.get(obfName); | ||
| 84 | } | ||
| 85 | |||
| 86 | public ClassMapping getClassByDeobf(ClassEntry entry) { | ||
| 87 | return getClassByDeobf(entry.getName()); | ||
| 88 | } | ||
| 89 | |||
| 90 | public ClassMapping getClassByDeobf(String deobfName) { | ||
| 91 | return m_classesByDeobf.get(deobfName); | ||
| 92 | } | ||
| 93 | |||
| 94 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { | ||
| 95 | if (classMapping.getDeobfName() != null) { | ||
| 96 | boolean wasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; | ||
| 97 | assert (wasRemoved); | ||
| 98 | } | ||
| 99 | classMapping.setDeobfName(deobfName); | ||
| 100 | if (deobfName != null) { | ||
| 101 | boolean wasAdded = m_classesByDeobf.put(deobfName, classMapping) == null; | ||
| 102 | assert (wasAdded); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { | ||
| 107 | switch (direction) { | ||
| 108 | case Deobfuscating: | ||
| 109 | |||
| 110 | return new Translator(direction, m_classesByObf, index); | ||
| 111 | |||
| 112 | case Obfuscating: | ||
| 113 | |||
| 114 | // fill in the missing deobf class entries with obf entries | ||
| 115 | Map<String,ClassMapping> classes = Maps.newHashMap(); | ||
| 116 | for (ClassMapping classMapping : classes()) { | ||
| 117 | if (classMapping.getDeobfName() != null) { | ||
| 118 | classes.put(classMapping.getDeobfName(), classMapping); | ||
| 119 | } else { | ||
| 120 | classes.put(classMapping.getObfFullName(), classMapping); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | // translate the translation index | ||
| 125 | // NOTE: this isn't actually recursive | ||
| 126 | TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); | ||
| 127 | |||
| 128 | return new Translator(direction, classes, deobfIndex); | ||
| 129 | |||
| 130 | default: | ||
| 131 | throw new Error("Invalid translation direction!"); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | @Override | ||
| 136 | public String toString() { | ||
| 137 | StringBuilder buf = new StringBuilder(); | ||
| 138 | for (ClassMapping classMapping : m_classesByObf.values()) { | ||
| 139 | buf.append(classMapping.toString()); | ||
| 140 | buf.append("\n"); | ||
| 141 | } | ||
| 142 | return buf.toString(); | ||
| 143 | } | ||
| 144 | |||
| 145 | public void renameObfClass(String oldObfName, String newObfName) { | ||
| 146 | for (ClassMapping classMapping : new ArrayList<ClassMapping>(classes())) { | ||
| 147 | if (classMapping.renameObfClass(oldObfName, newObfName)) { | ||
| 148 | boolean wasRemoved = m_classesByObf.remove(oldObfName) != null; | ||
| 149 | assert (wasRemoved); | ||
| 150 | boolean wasAdded = m_classesByObf.put(newObfName, classMapping) == null; | ||
| 151 | assert (wasAdded); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | public Set<String> getAllObfClassNames() { | ||
| 157 | final Set<String> classNames = Sets.newHashSet(); | ||
| 158 | for (ClassMapping classMapping : classes()) { | ||
| 159 | |||
| 160 | // add the class name | ||
| 161 | classNames.add(classMapping.getObfFullName()); | ||
| 162 | |||
| 163 | // add classes from method signatures | ||
| 164 | for (MethodMapping methodMapping : classMapping.methods()) { | ||
| 165 | for (Type type : methodMapping.getObfSignature().types()) { | ||
| 166 | if (type.hasClass()) { | ||
| 167 | classNames.add(type.getClassEntry().getClassName()); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | return classNames; | ||
| 173 | } | ||
| 174 | |||
| 175 | public boolean containsDeobfClass(String deobfName) { | ||
| 176 | return m_classesByDeobf.containsKey(deobfName); | ||
| 177 | } | ||
| 178 | |||
| 179 | public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { | ||
| 180 | ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); | ||
| 181 | if (classMapping != null) { | ||
| 182 | return classMapping.containsDeobfField(deobfName, obfType); | ||
| 183 | } | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | |||
| 187 | public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature deobfSignature) { | ||
| 188 | ClassMapping classMapping = m_classesByObf.get(obfClassEntry.getName()); | ||
| 189 | if (classMapping != null) { | ||
| 190 | return classMapping.containsDeobfMethod(deobfName, deobfSignature); | ||
| 191 | } | ||
| 192 | return false; | ||
| 193 | } | ||
| 194 | |||
| 195 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { | ||
| 196 | ClassMapping classMapping = m_classesByObf.get(obfBehaviorEntry.getClassName()); | ||
| 197 | if (classMapping != null) { | ||
| 198 | return classMapping.containsArgument(obfBehaviorEntry, name); | ||
| 199 | } | ||
| 200 | return false; | ||
| 201 | } | ||
| 202 | |||
| 203 | public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { | ||
| 204 | List<ClassMapping> mappingChain = Lists.newArrayList(); | ||
| 205 | ClassMapping classMapping = null; | ||
| 206 | for (ClassEntry obfClassEntry : obfClass.getClassChain()) { | ||
| 207 | if (mappingChain.isEmpty()) { | ||
| 208 | classMapping = m_classesByObf.get(obfClassEntry.getName()); | ||
| 209 | } else if (classMapping != null) { | ||
| 210 | classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); | ||
| 211 | } | ||
| 212 | mappingChain.add(classMapping); | ||
| 213 | } | ||
| 214 | return mappingChain; | ||
| 215 | } | ||
| 216 | } | ||
diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java new file mode 100644 index 0000000..b25ea3c --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | |||
| 18 | import cuchaz.enigma.analysis.JarIndex; | ||
| 19 | import cuchaz.enigma.analysis.RelatedMethodChecker; | ||
| 20 | |||
| 21 | |||
| 22 | public class MappingsChecker { | ||
| 23 | |||
| 24 | private JarIndex m_index; | ||
| 25 | private RelatedMethodChecker m_relatedMethodChecker; | ||
| 26 | private Map<ClassEntry,ClassMapping> m_droppedClassMappings; | ||
| 27 | private Map<ClassEntry,ClassMapping> m_droppedInnerClassMappings; | ||
| 28 | private Map<FieldEntry,FieldMapping> m_droppedFieldMappings; | ||
| 29 | private Map<BehaviorEntry,MethodMapping> m_droppedMethodMappings; | ||
| 30 | |||
| 31 | public MappingsChecker(JarIndex index) { | ||
| 32 | m_index = index; | ||
| 33 | m_relatedMethodChecker = new RelatedMethodChecker(m_index); | ||
| 34 | m_droppedClassMappings = Maps.newHashMap(); | ||
| 35 | m_droppedInnerClassMappings = Maps.newHashMap(); | ||
| 36 | m_droppedFieldMappings = Maps.newHashMap(); | ||
| 37 | m_droppedMethodMappings = Maps.newHashMap(); | ||
| 38 | } | ||
| 39 | |||
| 40 | public RelatedMethodChecker getRelatedMethodChecker() { | ||
| 41 | return m_relatedMethodChecker; | ||
| 42 | } | ||
| 43 | |||
| 44 | public Map<ClassEntry,ClassMapping> getDroppedClassMappings() { | ||
| 45 | return m_droppedClassMappings; | ||
| 46 | } | ||
| 47 | |||
| 48 | public Map<ClassEntry,ClassMapping> getDroppedInnerClassMappings() { | ||
| 49 | return m_droppedInnerClassMappings; | ||
| 50 | } | ||
| 51 | |||
| 52 | public Map<FieldEntry,FieldMapping> getDroppedFieldMappings() { | ||
| 53 | return m_droppedFieldMappings; | ||
| 54 | } | ||
| 55 | |||
| 56 | public Map<BehaviorEntry,MethodMapping> getDroppedMethodMappings() { | ||
| 57 | return m_droppedMethodMappings; | ||
| 58 | } | ||
| 59 | |||
| 60 | public void dropBrokenMappings(Mappings mappings) { | ||
| 61 | for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { | ||
| 62 | if (!checkClassMapping(classMapping)) { | ||
| 63 | mappings.removeClassMapping(classMapping); | ||
| 64 | m_droppedClassMappings.put(EntryFactory.getObfClassEntry(m_index, classMapping), classMapping); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | private boolean checkClassMapping(ClassMapping classMapping) { | ||
| 70 | |||
| 71 | // check the class | ||
| 72 | ClassEntry classEntry = EntryFactory.getObfClassEntry(m_index, classMapping); | ||
| 73 | if (!m_index.getObfClassEntries().contains(classEntry)) { | ||
| 74 | return false; | ||
| 75 | } | ||
| 76 | |||
| 77 | // check the fields | ||
| 78 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { | ||
| 79 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); | ||
| 80 | if (!m_index.containsObfField(obfFieldEntry)) { | ||
| 81 | classMapping.removeFieldMapping(fieldMapping); | ||
| 82 | m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | // check methods | ||
| 87 | for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { | ||
| 88 | BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); | ||
| 89 | if (!m_index.containsObfBehavior(obfBehaviorEntry)) { | ||
| 90 | classMapping.removeMethodMapping(methodMapping); | ||
| 91 | m_droppedMethodMappings.put(obfBehaviorEntry, methodMapping); | ||
| 92 | } | ||
| 93 | |||
| 94 | m_relatedMethodChecker.checkMethod(classEntry, methodMapping); | ||
| 95 | } | ||
| 96 | |||
| 97 | // check inner classes | ||
| 98 | for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { | ||
| 99 | if (!checkClassMapping(innerClassMapping)) { | ||
| 100 | classMapping.removeInnerClassMapping(innerClassMapping); | ||
| 101 | m_droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(m_index, innerClassMapping), innerClassMapping); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | return true; | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/src/cuchaz/enigma/mapping/MappingsReader.java b/src/cuchaz/enigma/mapping/MappingsReader.java new file mode 100644 index 0000000..0a4b117 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsReader.java | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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 | public class MappingsReader { | ||
| 21 | |||
| 22 | public Mappings read(Reader in) | ||
| 23 | throws IOException, MappingParseException { | ||
| 24 | return read(new BufferedReader(in)); | ||
| 25 | } | ||
| 26 | |||
| 27 | public Mappings read(BufferedReader in) | ||
| 28 | 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 { | ||
| 74 | |||
| 75 | // inner class | ||
| 76 | if (!(mappingStack.peek() instanceof ClassMapping)) { | ||
| 77 | throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); | ||
| 78 | } | ||
| 79 | |||
| 80 | classMapping = readClass(parts, true); | ||
| 81 | ((ClassMapping)mappingStack.peek()).addInnerClassMapping(classMapping); | ||
| 82 | } | ||
| 83 | mappingStack.push(classMapping); | ||
| 84 | } else if (token.equalsIgnoreCase("FIELD")) { | ||
| 85 | if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof ClassMapping)) { | ||
| 86 | throw new MappingParseException(lineNumber, "Unexpected FIELD entry here!"); | ||
| 87 | } | ||
| 88 | ((ClassMapping)mappingStack.peek()).addFieldMapping(readField(parts)); | ||
| 89 | } else if (token.equalsIgnoreCase("METHOD")) { | ||
| 90 | if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof ClassMapping)) { | ||
| 91 | throw new MappingParseException(lineNumber, "Unexpected METHOD entry here!"); | ||
| 92 | } | ||
| 93 | MethodMapping methodMapping = readMethod(parts); | ||
| 94 | ((ClassMapping)mappingStack.peek()).addMethodMapping(methodMapping); | ||
| 95 | mappingStack.push(methodMapping); | ||
| 96 | } else if (token.equalsIgnoreCase("ARG")) { | ||
| 97 | if (mappingStack.isEmpty() || ! (mappingStack.peek() instanceof MethodMapping)) { | ||
| 98 | throw new MappingParseException(lineNumber, "Unexpected ARG entry here!"); | ||
| 99 | } | ||
| 100 | ((MethodMapping)mappingStack.peek()).addArgumentMapping(readArgument(parts)); | ||
| 101 | } | ||
| 102 | } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { | ||
| 103 | throw new MappingParseException(lineNumber, "Malformed line:\n" + line); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | return mappings; | ||
| 108 | } | ||
| 109 | |||
| 110 | private ArgumentMapping readArgument(String[] parts) { | ||
| 111 | return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); | ||
| 112 | } | ||
| 113 | |||
| 114 | private ClassMapping readClass(String[] parts, boolean makeSimple) { | ||
| 115 | if (parts.length == 2) { | ||
| 116 | return new ClassMapping(parts[1]); | ||
| 117 | } else { | ||
| 118 | return new ClassMapping(parts[1], parts[2]); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /* TEMP */ | ||
| 123 | protected FieldMapping readField(String[] parts) { | ||
| 124 | return new FieldMapping(parts[1], new Type(parts[3]), parts[2]); | ||
| 125 | } | ||
| 126 | |||
| 127 | private MethodMapping readMethod(String[] parts) { | ||
| 128 | if (parts.length == 3) { | ||
| 129 | return new MethodMapping(parts[1], new Signature(parts[2])); | ||
| 130 | } else { | ||
| 131 | return new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java new file mode 100644 index 0000000..47e5738 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.ObjectOutputStream; | ||
| 15 | import java.io.OutputStream; | ||
| 16 | import java.util.List; | ||
| 17 | import java.util.Set; | ||
| 18 | import java.util.zip.GZIPOutputStream; | ||
| 19 | |||
| 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 | |||
| 34 | deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); | ||
| 35 | |||
| 36 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 37 | if (mappingChain.size() == 1) { | ||
| 38 | |||
| 39 | if (deobfName != null) { | ||
| 40 | // make sure we don't rename to an existing obf or deobf class | ||
| 41 | if (m_mappings.containsDeobfClass(deobfName) || m_index.containsObfClass(new ClassEntry(deobfName))) { | ||
| 42 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | ClassMapping classMapping = mappingChain.get(0); | ||
| 47 | m_mappings.setClassDeobfName(classMapping, deobfName); | ||
| 48 | |||
| 49 | } else { | ||
| 50 | |||
| 51 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 52 | |||
| 53 | if (deobfName != null) { | ||
| 54 | // make sure we don't rename to an existing obf or deobf inner class | ||
| 55 | if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { | ||
| 56 | throw new IllegalNameException(deobfName, "There is already a class with that name"); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | public void removeClassMapping(ClassEntry obf) { | ||
| 65 | setClassName(obf, null); | ||
| 66 | } | ||
| 67 | |||
| 68 | public void markClassAsDeobfuscated(ClassEntry obf) { | ||
| 69 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); | ||
| 70 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 71 | if (mappingChain.size() == 1) { | ||
| 72 | ClassMapping classMapping = mappingChain.get(0); | ||
| 73 | m_mappings.setClassDeobfName(classMapping, deobfName); | ||
| 74 | } else { | ||
| 75 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 76 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | public void setFieldName(FieldEntry obf, String deobfName) { | ||
| 81 | deobfName = NameValidator.validateFieldName(deobfName); | ||
| 82 | FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); | ||
| 83 | if (m_mappings.containsDeobfField(obf.getClassEntry(), deobfName, obf.getType()) || m_index.containsObfField(targetEntry)) { | ||
| 84 | throw new IllegalNameException(deobfName, "There is already a field with that name"); | ||
| 85 | } | ||
| 86 | |||
| 87 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 88 | classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); | ||
| 89 | } | ||
| 90 | |||
| 91 | public void removeFieldMapping(FieldEntry obf) { | ||
| 92 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 93 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); | ||
| 94 | } | ||
| 95 | |||
| 96 | public void markFieldAsDeobfuscated(FieldEntry obf) { | ||
| 97 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 98 | classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); | ||
| 99 | } | ||
| 100 | |||
| 101 | public void setMethodTreeName(MethodEntry obf, String deobfName) { | ||
| 102 | Set<MethodEntry> implementations = m_index.getRelatedMethodImplementations(obf); | ||
| 103 | |||
| 104 | deobfName = NameValidator.validateMethodName(deobfName); | ||
| 105 | for (MethodEntry entry : implementations) { | ||
| 106 | Signature deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature()); | ||
| 107 | MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); | ||
| 108 | if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { | ||
| 109 | String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName()); | ||
| 110 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | for (MethodEntry entry : implementations) { | ||
| 115 | setMethodName(entry, deobfName); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | public void setMethodName(MethodEntry obf, String deobfName) { | ||
| 120 | deobfName = NameValidator.validateMethodName(deobfName); | ||
| 121 | MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); | ||
| 122 | if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { | ||
| 123 | String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName()); | ||
| 124 | throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); | ||
| 125 | } | ||
| 126 | |||
| 127 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 128 | classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); | ||
| 129 | } | ||
| 130 | |||
| 131 | public void removeMethodTreeMapping(MethodEntry obf) { | ||
| 132 | for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { | ||
| 133 | removeMethodMapping(implementation); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | public void removeMethodMapping(MethodEntry obf) { | ||
| 138 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 139 | classMapping.setMethodName(obf.getName(), obf.getSignature(), null); | ||
| 140 | } | ||
| 141 | |||
| 142 | public void markMethodTreeAsDeobfuscated(MethodEntry obf) { | ||
| 143 | for (MethodEntry implementation : m_index.getRelatedMethodImplementations(obf)) { | ||
| 144 | markMethodAsDeobfuscated(implementation); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | public void markMethodAsDeobfuscated(MethodEntry obf) { | ||
| 149 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 150 | classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); | ||
| 151 | } | ||
| 152 | |||
| 153 | public void setArgumentName(ArgumentEntry obf, String deobfName) { | ||
| 154 | deobfName = NameValidator.validateArgumentName(deobfName); | ||
| 155 | // NOTE: don't need to check arguments for name collisions with names determined by Procyon | ||
| 156 | if (m_mappings.containsArgument(obf.getBehaviorEntry(), deobfName)) { | ||
| 157 | throw new IllegalNameException(deobfName, "There is already an argument with that name"); | ||
| 158 | } | ||
| 159 | |||
| 160 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 161 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); | ||
| 162 | } | ||
| 163 | |||
| 164 | public void removeArgumentMapping(ArgumentEntry obf) { | ||
| 165 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 166 | classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); | ||
| 167 | } | ||
| 168 | |||
| 169 | public void markArgumentAsDeobfuscated(ArgumentEntry obf) { | ||
| 170 | ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); | ||
| 171 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); | ||
| 172 | } | ||
| 173 | |||
| 174 | public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { | ||
| 175 | classMapping.removeFieldMapping(fieldMapping); | ||
| 176 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 177 | if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { | ||
| 178 | if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { | ||
| 179 | targetClassMapping.addFieldMapping(fieldMapping); | ||
| 180 | return true; | ||
| 181 | } else { | ||
| 182 | System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | return false; | ||
| 186 | } | ||
| 187 | |||
| 188 | public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { | ||
| 189 | classMapping.removeMethodMapping(methodMapping); | ||
| 190 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 191 | if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { | ||
| 192 | if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { | ||
| 193 | targetClassMapping.addMethodMapping(methodMapping); | ||
| 194 | return true; | ||
| 195 | } else { | ||
| 196 | System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | return false; | ||
| 200 | } | ||
| 201 | |||
| 202 | public void write(OutputStream out) throws IOException { | ||
| 203 | // TEMP: just use the object output for now. We can find a more efficient storage format later | ||
| 204 | GZIPOutputStream gzipout = new GZIPOutputStream(out); | ||
| 205 | ObjectOutputStream oout = new ObjectOutputStream(gzipout); | ||
| 206 | oout.writeObject(this); | ||
| 207 | gzipout.finish(); | ||
| 208 | } | ||
| 209 | |||
| 210 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { | ||
| 211 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); | ||
| 212 | return mappingChain.get(mappingChain.size() - 1); | ||
| 213 | } | ||
| 214 | |||
| 215 | private List<ClassMapping> getOrCreateClassMappingChain(ClassEntry obfClassEntry) { | ||
| 216 | List<ClassEntry> classChain = obfClassEntry.getClassChain(); | ||
| 217 | List<ClassMapping> mappingChain = m_mappings.getClassMappingChain(obfClassEntry); | ||
| 218 | for (int i=0; i<classChain.size(); i++) { | ||
| 219 | ClassEntry classEntry = classChain.get(i); | ||
| 220 | ClassMapping classMapping = mappingChain.get(i); | ||
| 221 | if (classMapping == null) { | ||
| 222 | |||
| 223 | // create it | ||
| 224 | classMapping = new ClassMapping(classEntry.getName()); | ||
| 225 | mappingChain.set(i, classMapping); | ||
| 226 | |||
| 227 | // add it to the right parent | ||
| 228 | if (i == 0) { | ||
| 229 | m_mappings.addClassMapping(classMapping); | ||
| 230 | } else { | ||
| 231 | mappingChain.get(i-1).addInnerClassMapping(classMapping); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | return mappingChain; | ||
| 236 | } | ||
| 237 | } | ||
diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java new file mode 100644 index 0000000..1ebefef --- /dev/null +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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.getObfFullName()); | ||
| 35 | } else { | ||
| 36 | out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfFullName(), 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 %s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString()); | ||
| 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/MemberMapping.java b/src/cuchaz/enigma/mapping/MemberMapping.java new file mode 100644 index 0000000..8378297 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MemberMapping.java | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | |||
| 14 | public interface MemberMapping<T extends Entry> { | ||
| 15 | T getObfEntry(ClassEntry classEntry); | ||
| 16 | String getObfName(); | ||
| 17 | } | ||
diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java new file mode 100644 index 0000000..eb9e204 --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodEntry.java | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..055e1fe --- /dev/null +++ b/src/cuchaz/enigma/mapping/MethodMapping.java | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.Map; | ||
| 15 | import java.util.Map.Entry; | ||
| 16 | |||
| 17 | import com.google.common.collect.Maps; | ||
| 18 | |||
| 19 | public class MethodMapping implements Serializable, Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { | ||
| 20 | |||
| 21 | private static final long serialVersionUID = -4409570216084263978L; | ||
| 22 | |||
| 23 | private String m_obfName; | ||
| 24 | private String m_deobfName; | ||
| 25 | private Signature m_obfSignature; | ||
| 26 | private Map<Integer,ArgumentMapping> m_arguments; | ||
| 27 | |||
| 28 | public MethodMapping(String obfName, Signature obfSignature) { | ||
| 29 | this(obfName, obfSignature, null); | ||
| 30 | } | ||
| 31 | |||
| 32 | public MethodMapping(String obfName, Signature obfSignature, String deobfName) { | ||
| 33 | if (obfName == null) { | ||
| 34 | throw new IllegalArgumentException("obf name cannot be null!"); | ||
| 35 | } | ||
| 36 | if (obfSignature == null) { | ||
| 37 | throw new IllegalArgumentException("obf signature cannot be null!"); | ||
| 38 | } | ||
| 39 | m_obfName = obfName; | ||
| 40 | m_deobfName = NameValidator.validateMethodName(deobfName); | ||
| 41 | m_obfSignature = obfSignature; | ||
| 42 | m_arguments = Maps.newTreeMap(); | ||
| 43 | } | ||
| 44 | |||
| 45 | public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { | ||
| 46 | m_obfName = other.m_obfName; | ||
| 47 | m_deobfName = other.m_deobfName; | ||
| 48 | m_obfSignature = new Signature(other.m_obfSignature, obfClassNameReplacer); | ||
| 49 | m_arguments = Maps.newTreeMap(); | ||
| 50 | for (Entry<Integer,ArgumentMapping> entry : other.m_arguments.entrySet()) { | ||
| 51 | m_arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | @Override | ||
| 56 | public String getObfName() { | ||
| 57 | return m_obfName; | ||
| 58 | } | ||
| 59 | |||
| 60 | public void setObfName(String val) { | ||
| 61 | m_obfName = NameValidator.validateMethodName(val); | ||
| 62 | } | ||
| 63 | |||
| 64 | public String getDeobfName() { | ||
| 65 | return m_deobfName; | ||
| 66 | } | ||
| 67 | |||
| 68 | public void setDeobfName(String val) { | ||
| 69 | m_deobfName = NameValidator.validateMethodName(val); | ||
| 70 | } | ||
| 71 | |||
| 72 | public Signature getObfSignature() { | ||
| 73 | return m_obfSignature; | ||
| 74 | } | ||
| 75 | |||
| 76 | public void setObfSignature(Signature val) { | ||
| 77 | m_obfSignature = val; | ||
| 78 | } | ||
| 79 | |||
| 80 | public Iterable<ArgumentMapping> arguments() { | ||
| 81 | return m_arguments.values(); | ||
| 82 | } | ||
| 83 | |||
| 84 | public boolean isConstructor() { | ||
| 85 | return m_obfName.startsWith("<"); | ||
| 86 | } | ||
| 87 | |||
| 88 | public void addArgumentMapping(ArgumentMapping argumentMapping) { | ||
| 89 | boolean wasAdded = m_arguments.put(argumentMapping.getIndex(), argumentMapping) == null; | ||
| 90 | assert (wasAdded); | ||
| 91 | } | ||
| 92 | |||
| 93 | public String getObfArgumentName(int index) { | ||
| 94 | ArgumentMapping argumentMapping = m_arguments.get(index); | ||
| 95 | if (argumentMapping != null) { | ||
| 96 | return argumentMapping.getName(); | ||
| 97 | } | ||
| 98 | |||
| 99 | return null; | ||
| 100 | } | ||
| 101 | |||
| 102 | public String getDeobfArgumentName(int index) { | ||
| 103 | ArgumentMapping argumentMapping = m_arguments.get(index); | ||
| 104 | if (argumentMapping != null) { | ||
| 105 | return argumentMapping.getName(); | ||
| 106 | } | ||
| 107 | |||
| 108 | return null; | ||
| 109 | } | ||
| 110 | |||
| 111 | public void setArgumentName(int index, String name) { | ||
| 112 | ArgumentMapping argumentMapping = m_arguments.get(index); | ||
| 113 | if (argumentMapping == null) { | ||
| 114 | argumentMapping = new ArgumentMapping(index, name); | ||
| 115 | boolean wasAdded = m_arguments.put(index, argumentMapping) == null; | ||
| 116 | assert (wasAdded); | ||
| 117 | } else { | ||
| 118 | argumentMapping.setName(name); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | public void removeArgumentName(int index) { | ||
| 123 | boolean wasRemoved = m_arguments.remove(index) != null; | ||
| 124 | assert (wasRemoved); | ||
| 125 | } | ||
| 126 | |||
| 127 | @Override | ||
| 128 | public String toString() { | ||
| 129 | StringBuilder buf = new StringBuilder(); | ||
| 130 | buf.append("\t"); | ||
| 131 | buf.append(m_obfName); | ||
| 132 | buf.append(" <-> "); | ||
| 133 | buf.append(m_deobfName); | ||
| 134 | buf.append("\n"); | ||
| 135 | buf.append("\t"); | ||
| 136 | buf.append(m_obfSignature); | ||
| 137 | buf.append("\n"); | ||
| 138 | buf.append("\tArguments:\n"); | ||
| 139 | for (ArgumentMapping argumentMapping : m_arguments.values()) { | ||
| 140 | buf.append("\t\t"); | ||
| 141 | buf.append(argumentMapping.getIndex()); | ||
| 142 | buf.append(" -> "); | ||
| 143 | buf.append(argumentMapping.getName()); | ||
| 144 | buf.append("\n"); | ||
| 145 | } | ||
| 146 | return buf.toString(); | ||
| 147 | } | ||
| 148 | |||
| 149 | @Override | ||
| 150 | public int compareTo(MethodMapping other) { | ||
| 151 | return (m_obfName + m_obfSignature).compareTo(other.m_obfName + other.m_obfSignature); | ||
| 152 | } | ||
| 153 | |||
| 154 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 155 | |||
| 156 | // rename obf classes in the signature | ||
| 157 | Signature newSignature = new Signature(m_obfSignature, new ClassNameReplacer() { | ||
| 158 | @Override | ||
| 159 | public String replace(String className) { | ||
| 160 | if (className.equals(oldObfClassName)) { | ||
| 161 | return newObfClassName; | ||
| 162 | } | ||
| 163 | return null; | ||
| 164 | } | ||
| 165 | }); | ||
| 166 | |||
| 167 | if (!newSignature.equals(m_obfSignature)) { | ||
| 168 | m_obfSignature = newSignature; | ||
| 169 | return true; | ||
| 170 | } | ||
| 171 | return false; | ||
| 172 | } | ||
| 173 | |||
| 174 | public boolean containsArgument(String name) { | ||
| 175 | for (ArgumentMapping argumentMapping : m_arguments.values()) { | ||
| 176 | if (argumentMapping.getName().equals(name)) { | ||
| 177 | return true; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | return false; | ||
| 181 | } | ||
| 182 | |||
| 183 | @Override | ||
| 184 | public BehaviorEntry getObfEntry(ClassEntry classEntry) { | ||
| 185 | if (isConstructor()) { | ||
| 186 | return new ConstructorEntry(classEntry, m_obfSignature); | ||
| 187 | } else { | ||
| 188 | return new MethodEntry(classEntry, m_obfName, m_obfSignature); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java new file mode 100644 index 0000000..12520e1 --- /dev/null +++ b/src/cuchaz/enigma/mapping/NameValidator.java | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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/ProcyonEntryFactory.java b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java new file mode 100644 index 0000000..777a12e --- /dev/null +++ b/src/cuchaz/enigma/mapping/ProcyonEntryFactory.java | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 14 | import com.strobel.assembler.metadata.MethodDefinition; | ||
| 15 | |||
| 16 | |||
| 17 | public class ProcyonEntryFactory { | ||
| 18 | |||
| 19 | public static FieldEntry getFieldEntry(FieldDefinition def) { | ||
| 20 | return new FieldEntry( | ||
| 21 | new ClassEntry(def.getDeclaringType().getInternalName()), | ||
| 22 | def.getName(), | ||
| 23 | new Type(def.getErasedSignature()) | ||
| 24 | ); | ||
| 25 | } | ||
| 26 | |||
| 27 | public static MethodEntry getMethodEntry(MethodDefinition def) { | ||
| 28 | return new MethodEntry( | ||
| 29 | new ClassEntry(def.getDeclaringType().getInternalName()), | ||
| 30 | def.getName(), | ||
| 31 | new Signature(def.getErasedSignature()) | ||
| 32 | ); | ||
| 33 | } | ||
| 34 | |||
| 35 | public static ConstructorEntry getConstructorEntry(MethodDefinition def) { | ||
| 36 | if (def.isTypeInitializer()) { | ||
| 37 | return new ConstructorEntry( | ||
| 38 | new ClassEntry(def.getDeclaringType().getInternalName()) | ||
| 39 | ); | ||
| 40 | } else { | ||
| 41 | return new ConstructorEntry( | ||
| 42 | new ClassEntry(def.getDeclaringType().getInternalName()), | ||
| 43 | new Signature(def.getErasedSignature()) | ||
| 44 | ); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | public static BehaviorEntry getBehaviorEntry(MethodDefinition def) { | ||
| 49 | if (def.isConstructor() || def.isTypeInitializer()) { | ||
| 50 | return getConstructorEntry(def); | ||
| 51 | } else { | ||
| 52 | return getMethodEntry(def); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java new file mode 100644 index 0000000..8f2b6b2 --- /dev/null +++ b/src/cuchaz/enigma/mapping/Signature.java | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.List; | ||
| 15 | |||
| 16 | import com.google.common.collect.Lists; | ||
| 17 | |||
| 18 | import cuchaz.enigma.Util; | ||
| 19 | |||
| 20 | public class Signature implements Serializable { | ||
| 21 | |||
| 22 | private static final long serialVersionUID = -5843719505729497539L; | ||
| 23 | |||
| 24 | private List<Type> m_argumentTypes; | ||
| 25 | private Type m_returnType; | ||
| 26 | |||
| 27 | public Signature(String signature) { | ||
| 28 | try { | ||
| 29 | m_argumentTypes = Lists.newArrayList(); | ||
| 30 | int i=0; | ||
| 31 | while (i<signature.length()) { | ||
| 32 | char c = signature.charAt(i); | ||
| 33 | if (c == '(') { | ||
| 34 | assert(m_argumentTypes.isEmpty()); | ||
| 35 | assert(m_returnType == null); | ||
| 36 | i++; | ||
| 37 | } else if (c == ')') { | ||
| 38 | i++; | ||
| 39 | break; | ||
| 40 | } else { | ||
| 41 | String type = Type.parseFirst(signature.substring(i)); | ||
| 42 | m_argumentTypes.add(new Type(type)); | ||
| 43 | i += type.length(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | m_returnType = new Type(Type.parseFirst(signature.substring(i))); | ||
| 47 | } catch (Exception ex) { | ||
| 48 | throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | public Signature(Signature other) { | ||
| 53 | m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); | ||
| 54 | m_returnType = new Type(other.m_returnType); | ||
| 55 | } | ||
| 56 | |||
| 57 | public Signature(Signature other, ClassNameReplacer replacer) { | ||
| 58 | m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); | ||
| 59 | for (int i=0; i<m_argumentTypes.size(); i++) { | ||
| 60 | m_argumentTypes.set(i, new Type(m_argumentTypes.get(i), replacer)); | ||
| 61 | } | ||
| 62 | m_returnType = new Type(other.m_returnType, replacer); | ||
| 63 | } | ||
| 64 | |||
| 65 | public List<Type> getArgumentTypes() { | ||
| 66 | return m_argumentTypes; | ||
| 67 | } | ||
| 68 | |||
| 69 | public Type getReturnType() { | ||
| 70 | return m_returnType; | ||
| 71 | } | ||
| 72 | |||
| 73 | @Override | ||
| 74 | public String toString() { | ||
| 75 | StringBuilder buf = new StringBuilder(); | ||
| 76 | buf.append("("); | ||
| 77 | for (Type type : m_argumentTypes) { | ||
| 78 | buf.append(type.toString()); | ||
| 79 | } | ||
| 80 | buf.append(")"); | ||
| 81 | buf.append(m_returnType.toString()); | ||
| 82 | return buf.toString(); | ||
| 83 | } | ||
| 84 | |||
| 85 | public Iterable<Type> types() { | ||
| 86 | List<Type> types = Lists.newArrayList(); | ||
| 87 | types.addAll(m_argumentTypes); | ||
| 88 | types.add(m_returnType); | ||
| 89 | return types; | ||
| 90 | } | ||
| 91 | |||
| 92 | @Override | ||
| 93 | public boolean equals(Object other) { | ||
| 94 | if (other instanceof Signature) { | ||
| 95 | return equals((Signature)other); | ||
| 96 | } | ||
| 97 | return false; | ||
| 98 | } | ||
| 99 | |||
| 100 | public boolean equals(Signature other) { | ||
| 101 | return m_argumentTypes.equals(other.m_argumentTypes) && m_returnType.equals(other.m_returnType); | ||
| 102 | } | ||
| 103 | |||
| 104 | @Override | ||
| 105 | public int hashCode() { | ||
| 106 | return Util.combineHashesOrdered(m_argumentTypes.hashCode(), m_returnType.hashCode()); | ||
| 107 | } | ||
| 108 | |||
| 109 | public boolean hasClass(ClassEntry classEntry) { | ||
| 110 | for (Type type : types()) { | ||
| 111 | if (type.hasClass() && type.getClassEntry().equals(classEntry)) { | ||
| 112 | return true; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java new file mode 100644 index 0000000..eb53233 --- /dev/null +++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..bc3aaa1 --- /dev/null +++ b/src/cuchaz/enigma/mapping/TranslationDirection.java | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | 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..41c7d7c --- /dev/null +++ b/src/cuchaz/enigma/mapping/Translator.java | |||
| @@ -0,0 +1,289 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.List; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | import com.google.common.collect.Lists; | ||
| 17 | import com.google.common.collect.Maps; | ||
| 18 | |||
| 19 | import cuchaz.enigma.analysis.TranslationIndex; | ||
| 20 | |||
| 21 | public class Translator { | ||
| 22 | |||
| 23 | private TranslationDirection m_direction; | ||
| 24 | private Map<String,ClassMapping> m_classes; | ||
| 25 | private TranslationIndex m_index; | ||
| 26 | |||
| 27 | private ClassNameReplacer m_classNameReplacer = new ClassNameReplacer() { | ||
| 28 | @Override | ||
| 29 | public String replace(String className) { | ||
| 30 | return translateEntry(new ClassEntry(className)).getName(); | ||
| 31 | } | ||
| 32 | }; | ||
| 33 | |||
| 34 | public Translator() { | ||
| 35 | m_direction = null; | ||
| 36 | m_classes = Maps.newHashMap(); | ||
| 37 | m_index = new TranslationIndex(); | ||
| 38 | } | ||
| 39 | |||
| 40 | public Translator(TranslationDirection direction, Map<String,ClassMapping> classes, TranslationIndex index) { | ||
| 41 | m_direction = direction; | ||
| 42 | m_classes = classes; | ||
| 43 | m_index = index; | ||
| 44 | } | ||
| 45 | |||
| 46 | public TranslationDirection getDirection() { | ||
| 47 | return m_direction; | ||
| 48 | } | ||
| 49 | |||
| 50 | public TranslationIndex getTranslationIndex() { | ||
| 51 | return m_index; | ||
| 52 | } | ||
| 53 | |||
| 54 | @SuppressWarnings("unchecked") | ||
| 55 | public <T extends Entry> T translateEntry(T entry) { | ||
| 56 | if (entry instanceof ClassEntry) { | ||
| 57 | return (T)translateEntry((ClassEntry)entry); | ||
| 58 | } else if (entry instanceof FieldEntry) { | ||
| 59 | return (T)translateEntry((FieldEntry)entry); | ||
| 60 | } else if (entry instanceof MethodEntry) { | ||
| 61 | return (T)translateEntry((MethodEntry)entry); | ||
| 62 | } else if (entry instanceof ConstructorEntry) { | ||
| 63 | return (T)translateEntry((ConstructorEntry)entry); | ||
| 64 | } else if (entry instanceof ArgumentEntry) { | ||
| 65 | return (T)translateEntry((ArgumentEntry)entry); | ||
| 66 | } else { | ||
| 67 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | public <T extends Entry> String translate(T entry) { | ||
| 72 | if (entry instanceof ClassEntry) { | ||
| 73 | return translate((ClassEntry)entry); | ||
| 74 | } else if (entry instanceof FieldEntry) { | ||
| 75 | return translate((FieldEntry)entry); | ||
| 76 | } else if (entry instanceof MethodEntry) { | ||
| 77 | return translate((MethodEntry)entry); | ||
| 78 | } else if (entry instanceof ConstructorEntry) { | ||
| 79 | return translate((ConstructorEntry)entry); | ||
| 80 | } else if (entry instanceof ArgumentEntry) { | ||
| 81 | return translate((ArgumentEntry)entry); | ||
| 82 | } else { | ||
| 83 | throw new Error("Unknown entry type: " + entry.getClass().getName()); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | public String translate(ClassEntry in) { | ||
| 88 | ClassEntry translated = translateEntry(in); | ||
| 89 | if (translated.equals(in)) { | ||
| 90 | return null; | ||
| 91 | } | ||
| 92 | return translated.getName(); | ||
| 93 | } | ||
| 94 | |||
| 95 | public String translateClass(String className) { | ||
| 96 | return translate(new ClassEntry(className)); | ||
| 97 | } | ||
| 98 | |||
| 99 | public ClassEntry translateEntry(ClassEntry in) { | ||
| 100 | |||
| 101 | if (in.isInnerClass()) { | ||
| 102 | |||
| 103 | // translate as much of the class chain as we can | ||
| 104 | List<ClassMapping> mappingsChain = getClassMappingChain(in); | ||
| 105 | String[] obfClassNames = in.getName().split("\\$"); | ||
| 106 | StringBuilder buf = new StringBuilder(); | ||
| 107 | for (int i=0; i<obfClassNames.length; i++) { | ||
| 108 | boolean isFirstClass = buf.length() == 0; | ||
| 109 | String className = null; | ||
| 110 | ClassMapping classMapping = mappingsChain.get(i); | ||
| 111 | if (classMapping != null) { | ||
| 112 | className = m_direction.choose( | ||
| 113 | classMapping.getDeobfName(), | ||
| 114 | isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() | ||
| 115 | ); | ||
| 116 | } | ||
| 117 | if (className == null) { | ||
| 118 | className = obfClassNames[i]; | ||
| 119 | } | ||
| 120 | if (!isFirstClass) { | ||
| 121 | buf.append("$"); | ||
| 122 | } | ||
| 123 | buf.append(className); | ||
| 124 | } | ||
| 125 | return new ClassEntry(buf.toString()); | ||
| 126 | |||
| 127 | } else { | ||
| 128 | |||
| 129 | // normal classes are easy | ||
| 130 | ClassMapping classMapping = m_classes.get(in.getName()); | ||
| 131 | if (classMapping == null) { | ||
| 132 | return in; | ||
| 133 | } | ||
| 134 | return m_direction.choose( | ||
| 135 | classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, | ||
| 136 | new ClassEntry(classMapping.getObfFullName()) | ||
| 137 | ); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | public String translate(FieldEntry in) { | ||
| 142 | |||
| 143 | // resolve the class entry | ||
| 144 | ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); | ||
| 145 | if (resolvedClassEntry != null) { | ||
| 146 | |||
| 147 | // look for the class | ||
| 148 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | ||
| 149 | if (classMapping != null) { | ||
| 150 | |||
| 151 | // look for the field | ||
| 152 | String translatedName = m_direction.choose( | ||
| 153 | classMapping.getDeobfFieldName(in.getName(), in.getType()), | ||
| 154 | classMapping.getObfFieldName(in.getName(), translateType(in.getType())) | ||
| 155 | ); | ||
| 156 | if (translatedName != null) { | ||
| 157 | return translatedName; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return null; | ||
| 162 | } | ||
| 163 | |||
| 164 | public FieldEntry translateEntry(FieldEntry in) { | ||
| 165 | String name = translate(in); | ||
| 166 | if (name == null) { | ||
| 167 | name = in.getName(); | ||
| 168 | } | ||
| 169 | return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); | ||
| 170 | } | ||
| 171 | |||
| 172 | public String translate(MethodEntry in) { | ||
| 173 | |||
| 174 | // resolve the class entry | ||
| 175 | ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in); | ||
| 176 | if (resolvedClassEntry != null) { | ||
| 177 | |||
| 178 | // look for class | ||
| 179 | ClassMapping classMapping = findClassMapping(resolvedClassEntry); | ||
| 180 | if (classMapping != null) { | ||
| 181 | |||
| 182 | // look for the method | ||
| 183 | MethodMapping methodMapping = m_direction.choose( | ||
| 184 | classMapping.getMethodByObf(in.getName(), in.getSignature()), | ||
| 185 | classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) | ||
| 186 | ); | ||
| 187 | if (methodMapping != null) { | ||
| 188 | return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | return null; | ||
| 193 | } | ||
| 194 | |||
| 195 | public MethodEntry translateEntry(MethodEntry in) { | ||
| 196 | String name = translate(in); | ||
| 197 | if (name == null) { | ||
| 198 | name = in.getName(); | ||
| 199 | } | ||
| 200 | return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); | ||
| 201 | } | ||
| 202 | |||
| 203 | public ConstructorEntry translateEntry(ConstructorEntry in) { | ||
| 204 | if (in.isStatic()) { | ||
| 205 | return new ConstructorEntry(translateEntry(in.getClassEntry())); | ||
| 206 | } else { | ||
| 207 | return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | public BehaviorEntry translateEntry(BehaviorEntry in) { | ||
| 212 | if (in instanceof MethodEntry) { | ||
| 213 | return translateEntry((MethodEntry)in); | ||
| 214 | } else if (in instanceof ConstructorEntry) { | ||
| 215 | return translateEntry((ConstructorEntry)in); | ||
| 216 | } | ||
| 217 | throw new Error("Wrong entry type!"); | ||
| 218 | } | ||
| 219 | |||
| 220 | public String translate(ArgumentEntry in) { | ||
| 221 | |||
| 222 | // look for the class | ||
| 223 | ClassMapping classMapping = findClassMapping(in.getClassEntry()); | ||
| 224 | if (classMapping != null) { | ||
| 225 | |||
| 226 | // look for the method | ||
| 227 | MethodMapping methodMapping = m_direction.choose( | ||
| 228 | classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), | ||
| 229 | classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) | ||
| 230 | ); | ||
| 231 | if (methodMapping != null) { | ||
| 232 | return m_direction.choose( | ||
| 233 | methodMapping.getDeobfArgumentName(in.getIndex()), | ||
| 234 | methodMapping.getObfArgumentName(in.getIndex()) | ||
| 235 | ); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | return null; | ||
| 239 | } | ||
| 240 | |||
| 241 | public ArgumentEntry translateEntry(ArgumentEntry in) { | ||
| 242 | String name = translate(in); | ||
| 243 | if (name == null) { | ||
| 244 | name = in.getName(); | ||
| 245 | } | ||
| 246 | return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); | ||
| 247 | } | ||
| 248 | |||
| 249 | public Type translateType(Type type) { | ||
| 250 | return new Type(type, m_classNameReplacer); | ||
| 251 | } | ||
| 252 | |||
| 253 | public Signature translateSignature(Signature signature) { | ||
| 254 | return new Signature(signature, m_classNameReplacer); | ||
| 255 | } | ||
| 256 | |||
| 257 | private ClassMapping findClassMapping(ClassEntry in) { | ||
| 258 | List<ClassMapping> mappingChain = getClassMappingChain(in); | ||
| 259 | return mappingChain.get(mappingChain.size() - 1); | ||
| 260 | } | ||
| 261 | |||
| 262 | private List<ClassMapping> getClassMappingChain(ClassEntry in) { | ||
| 263 | |||
| 264 | // get a list of all the classes in the hierarchy | ||
| 265 | String[] parts = in.getName().split("\\$"); | ||
| 266 | List<ClassMapping> mappingsChain = Lists.newArrayList(); | ||
| 267 | |||
| 268 | // get mappings for the outer class | ||
| 269 | ClassMapping outerClassMapping = m_classes.get(parts[0]); | ||
| 270 | mappingsChain.add(outerClassMapping); | ||
| 271 | |||
| 272 | for (int i=1; i<parts.length; i++) { | ||
| 273 | |||
| 274 | // get mappings for the inner class | ||
| 275 | ClassMapping innerClassMapping = null; | ||
| 276 | if (outerClassMapping != null) { | ||
| 277 | innerClassMapping = m_direction.choose( | ||
| 278 | outerClassMapping.getInnerClassByObfSimple(parts[i]), | ||
| 279 | outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) | ||
| 280 | ); | ||
| 281 | } | ||
| 282 | mappingsChain.add(innerClassMapping); | ||
| 283 | outerClassMapping = innerClassMapping; | ||
| 284 | } | ||
| 285 | |||
| 286 | assert(mappingsChain.size() == parts.length); | ||
| 287 | return mappingsChain; | ||
| 288 | } | ||
| 289 | } | ||
diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java new file mode 100644 index 0000000..f86a5cc --- /dev/null +++ b/src/cuchaz/enigma/mapping/Type.java | |||
| @@ -0,0 +1,247 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | |||
| 18 | public class Type implements Serializable { | ||
| 19 | |||
| 20 | private static final long serialVersionUID = 7862257669347104063L; | ||
| 21 | |||
| 22 | public enum Primitive { | ||
| 23 | Byte('B'), | ||
| 24 | Character('C'), | ||
| 25 | Short('S'), | ||
| 26 | Integer('I'), | ||
| 27 | Long('J'), | ||
| 28 | Float('F'), | ||
| 29 | Double('D'), | ||
| 30 | Boolean('Z'); | ||
| 31 | |||
| 32 | private static final Map<Character,Primitive> m_lookup; | ||
| 33 | |||
| 34 | static { | ||
| 35 | m_lookup = Maps.newTreeMap(); | ||
| 36 | for (Primitive val : values()) { | ||
| 37 | m_lookup.put(val.getCode(), val); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | public static Primitive get(char code) { | ||
| 42 | return m_lookup.get(code); | ||
| 43 | } | ||
| 44 | |||
| 45 | private char m_code; | ||
| 46 | |||
| 47 | private Primitive(char code) { | ||
| 48 | m_code = code; | ||
| 49 | } | ||
| 50 | |||
| 51 | public char getCode() { | ||
| 52 | return m_code; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | public static String parseFirst(String in) { | ||
| 57 | |||
| 58 | if (in == null || in.length() <= 0) { | ||
| 59 | throw new IllegalArgumentException("No type to parse, input is empty!"); | ||
| 60 | } | ||
| 61 | |||
| 62 | // read one type from the input | ||
| 63 | |||
| 64 | char c = in.charAt(0); | ||
| 65 | |||
| 66 | // first check for void | ||
| 67 | if (c == 'V') { | ||
| 68 | return "V"; | ||
| 69 | } | ||
| 70 | |||
| 71 | // then check for primitives | ||
| 72 | Primitive primitive = Primitive.get(c); | ||
| 73 | if (primitive != null) { | ||
| 74 | return in.substring(0, 1); | ||
| 75 | } | ||
| 76 | |||
| 77 | // then check for classes | ||
| 78 | if (c == 'L') { | ||
| 79 | return readClass(in); | ||
| 80 | } | ||
| 81 | |||
| 82 | // then check for templates | ||
| 83 | if (c == 'T') { | ||
| 84 | return readClass(in); | ||
| 85 | } | ||
| 86 | |||
| 87 | // then check for arrays | ||
| 88 | int dim = countArrayDimension(in); | ||
| 89 | if (dim > 0) { | ||
| 90 | String arrayType = Type.parseFirst(in.substring(dim)); | ||
| 91 | return in.substring(0, dim + arrayType.length()); | ||
| 92 | } | ||
| 93 | |||
| 94 | throw new IllegalArgumentException("don't know how to parse: " + in); | ||
| 95 | } | ||
| 96 | |||
| 97 | protected String m_name; | ||
| 98 | |||
| 99 | public Type(String name) { | ||
| 100 | |||
| 101 | // don't deal with generics | ||
| 102 | // this is just for raw jvm types | ||
| 103 | if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { | ||
| 104 | throw new IllegalArgumentException("don't use with generic types or templates: " + name); | ||
| 105 | } | ||
| 106 | |||
| 107 | m_name = name; | ||
| 108 | } | ||
| 109 | |||
| 110 | public Type(Type other) { | ||
| 111 | m_name = other.m_name; | ||
| 112 | } | ||
| 113 | |||
| 114 | public Type(ClassEntry classEntry) { | ||
| 115 | m_name = "L" + classEntry.getClassName() + ";"; | ||
| 116 | } | ||
| 117 | |||
| 118 | public Type(Type other, ClassNameReplacer replacer) { | ||
| 119 | m_name = other.m_name; | ||
| 120 | if (other.isClass()) { | ||
| 121 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); | ||
| 122 | if (replacedName != null) { | ||
| 123 | m_name = "L" + replacedName + ";"; | ||
| 124 | } | ||
| 125 | } else if (other.isArray() && other.hasClass()) { | ||
| 126 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); | ||
| 127 | if (replacedName != null) { | ||
| 128 | m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | @Override | ||
| 134 | public String toString() { | ||
| 135 | return m_name; | ||
| 136 | } | ||
| 137 | |||
| 138 | public boolean isVoid() { | ||
| 139 | return m_name.length() == 1 && m_name.charAt(0) == 'V'; | ||
| 140 | } | ||
| 141 | |||
| 142 | public boolean isPrimitive() { | ||
| 143 | return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null; | ||
| 144 | } | ||
| 145 | |||
| 146 | public Primitive getPrimitive() { | ||
| 147 | if (!isPrimitive()) { | ||
| 148 | throw new IllegalStateException("not a primitive"); | ||
| 149 | } | ||
| 150 | return Primitive.get(m_name.charAt(0)); | ||
| 151 | } | ||
| 152 | |||
| 153 | public boolean isClass() { | ||
| 154 | return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; | ||
| 155 | } | ||
| 156 | |||
| 157 | public ClassEntry getClassEntry() { | ||
| 158 | if (isClass()) { | ||
| 159 | String name = m_name.substring(1, m_name.length() - 1); | ||
| 160 | |||
| 161 | int pos = name.indexOf('<'); | ||
| 162 | if (pos >= 0) { | ||
| 163 | // remove the parameters from the class name | ||
| 164 | name = name.substring(0, pos); | ||
| 165 | } | ||
| 166 | |||
| 167 | return new ClassEntry(name); | ||
| 168 | |||
| 169 | } else if (isArray() && getArrayType().isClass()) { | ||
| 170 | return getArrayType().getClassEntry(); | ||
| 171 | } else { | ||
| 172 | throw new IllegalStateException("type doesn't have a class"); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | public boolean isArray() { | ||
| 177 | return m_name.charAt(0) == '['; | ||
| 178 | } | ||
| 179 | |||
| 180 | public int getArrayDimension() { | ||
| 181 | if (!isArray()) { | ||
| 182 | throw new IllegalStateException("not an array"); | ||
| 183 | } | ||
| 184 | return countArrayDimension(m_name); | ||
| 185 | } | ||
| 186 | |||
| 187 | public Type getArrayType() { | ||
| 188 | if (!isArray()) { | ||
| 189 | throw new IllegalStateException("not an array"); | ||
| 190 | } | ||
| 191 | return new Type(m_name.substring(getArrayDimension(), m_name.length())); | ||
| 192 | } | ||
| 193 | |||
| 194 | private static String getArrayPrefix(int dimension) { | ||
| 195 | StringBuilder buf = new StringBuilder(); | ||
| 196 | for (int i=0; i<dimension; i++) { | ||
| 197 | buf.append("["); | ||
| 198 | } | ||
| 199 | return buf.toString(); | ||
| 200 | } | ||
| 201 | |||
| 202 | public boolean hasClass() { | ||
| 203 | return isClass() || (isArray() && getArrayType().hasClass()); | ||
| 204 | } | ||
| 205 | |||
| 206 | @Override | ||
| 207 | public boolean equals(Object other) { | ||
| 208 | if (other instanceof Type) { | ||
| 209 | return equals((Type)other); | ||
| 210 | } | ||
| 211 | return false; | ||
| 212 | } | ||
| 213 | |||
| 214 | public boolean equals(Type other) { | ||
| 215 | return m_name.equals(other.m_name); | ||
| 216 | } | ||
| 217 | |||
| 218 | public int hashCode() { | ||
| 219 | return m_name.hashCode(); | ||
| 220 | } | ||
| 221 | |||
| 222 | private static int countArrayDimension(String in) { | ||
| 223 | int i=0; | ||
| 224 | for(; i < in.length() && in.charAt(i) == '['; i++); | ||
| 225 | return i; | ||
| 226 | } | ||
| 227 | |||
| 228 | private static String readClass(String in) { | ||
| 229 | // read all the characters in the buffer until we hit a ';' | ||
| 230 | // include the parameters too | ||
| 231 | StringBuilder buf = new StringBuilder(); | ||
| 232 | int depth = 0; | ||
| 233 | for (int i=0; i<in.length(); i++) { | ||
| 234 | char c = in.charAt(i); | ||
| 235 | buf.append(c); | ||
| 236 | |||
| 237 | if (c == '<') { | ||
| 238 | depth++; | ||
| 239 | } else if (c == '>') { | ||
| 240 | depth--; | ||
| 241 | } else if (depth == 0 && c == ';') { | ||
| 242 | return buf.toString(); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | return null; | ||
| 246 | } | ||
| 247 | } | ||