diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/mapping')
14 files changed, 428 insertions, 3 deletions
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java index 1409fc4..741849a 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java | |||
| @@ -34,6 +34,12 @@ public class ArgumentEntry implements Entry { | |||
| 34 | this.name = name; | 34 | this.name = name; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | public ArgumentEntry(ArgumentEntry other) { | ||
| 38 | this.behaviorEntry = other.getBehaviorEntry(); | ||
| 39 | this.index = other.index; | ||
| 40 | this.name = other.name; | ||
| 41 | } | ||
| 42 | |||
| 37 | public ArgumentEntry(ArgumentEntry other, String newClassName) { | 43 | public ArgumentEntry(ArgumentEntry other, String newClassName) { |
| 38 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); | 44 | this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); |
| 39 | this.index = other.index; | 45 | this.index = other.index; |
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java index 918395f..d117de0 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java | |||
| @@ -21,6 +21,11 @@ public class ArgumentMapping implements Comparable<ArgumentMapping> { | |||
| 21 | this.name = NameValidator.validateArgumentName(name); | 21 | this.name = NameValidator.validateArgumentName(name); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | public ArgumentMapping(ArgumentMapping other) { | ||
| 25 | this.index = other.index; | ||
| 26 | this.name = other.name; | ||
| 27 | } | ||
| 28 | |||
| 24 | public int getIndex() { | 29 | public int getIndex() { |
| 25 | return this.index; | 30 | return this.index; |
| 26 | } | 31 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index b2c076a..36b35f7 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java | |||
| @@ -12,6 +12,7 @@ package cuchaz.enigma.mapping; | |||
| 12 | 12 | ||
| 13 | import com.google.common.collect.Maps; | 13 | import com.google.common.collect.Maps; |
| 14 | 14 | ||
| 15 | import java.util.ArrayList; | ||
| 15 | import java.util.Map; | 16 | import java.util.Map; |
| 16 | 17 | ||
| 17 | import cuchaz.enigma.throwables.MappingConflict; | 18 | import cuchaz.enigma.throwables.MappingConflict; |
| @@ -119,6 +120,15 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 119 | return classMapping; | 120 | return classMapping; |
| 120 | } | 121 | } |
| 121 | 122 | ||
| 123 | public String getDeobfInnerClassName(String obfSimpleName) { | ||
| 124 | assert (isSimpleClassName(obfSimpleName)); | ||
| 125 | ClassMapping classMapping = m_innerClassesByObfSimple.get(obfSimpleName); | ||
| 126 | if (classMapping != null) { | ||
| 127 | return classMapping.getDeobfName(); | ||
| 128 | } | ||
| 129 | return null; | ||
| 130 | } | ||
| 131 | |||
| 122 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { | 132 | public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { |
| 123 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); | 133 | ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); |
| 124 | if (classMapping.getDeobfName() != null) { | 134 | if (classMapping.getDeobfName() != null) { |
| @@ -149,6 +159,10 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 149 | return m_fieldsByObf.values(); | 159 | return m_fieldsByObf.values(); |
| 150 | } | 160 | } |
| 151 | 161 | ||
| 162 | public boolean containsObfField(String obfName, Type obfType) { | ||
| 163 | return m_fieldsByObf.containsKey(getFieldKey(obfName, obfType)); | ||
| 164 | } | ||
| 165 | |||
| 152 | public boolean containsDeobfField(String deobfName, Type deobfType) { | 166 | public boolean containsDeobfField(String deobfName, Type deobfType) { |
| 153 | return m_fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); | 167 | return m_fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); |
| 154 | } | 168 | } |
| @@ -182,6 +196,10 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 182 | return m_fieldsByObf.get(getFieldKey(obfName, obfType)); | 196 | return m_fieldsByObf.get(getFieldKey(obfName, obfType)); |
| 183 | } | 197 | } |
| 184 | 198 | ||
| 199 | public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { | ||
| 200 | return m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | ||
| 201 | } | ||
| 202 | |||
| 185 | public String getObfFieldName(String deobfName, Type obfType) { | 203 | public String getObfFieldName(String deobfName, Type obfType) { |
| 186 | FieldMapping fieldMapping = m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); | 204 | FieldMapping fieldMapping = m_fieldsByDeobf.get(getFieldKey(deobfName, obfType)); |
| 187 | if (fieldMapping != null) { | 205 | if (fieldMapping != null) { |
| @@ -227,6 +245,16 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 227 | } | 245 | } |
| 228 | } | 246 | } |
| 229 | 247 | ||
| 248 | public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { | ||
| 249 | assert(newObfName != null); | ||
| 250 | FieldMapping fieldMapping = m_fieldsByObf.remove(getFieldKey(oldObfName, obfType)); | ||
| 251 | assert(fieldMapping != null); | ||
| 252 | fieldMapping.setObfName(newObfName); | ||
| 253 | fieldMapping.setObfType(newObfType); | ||
| 254 | boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; | ||
| 255 | assert(obfWasAdded); | ||
| 256 | } | ||
| 257 | |||
| 230 | //// METHODS //////// | 258 | //// METHODS //////// |
| 231 | 259 | ||
| 232 | public Iterable<MethodMapping> methods() { | 260 | public Iterable<MethodMapping> methods() { |
| @@ -234,6 +262,10 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 234 | return m_methodsByObf.values(); | 262 | return m_methodsByObf.values(); |
| 235 | } | 263 | } |
| 236 | 264 | ||
| 265 | public boolean containsObfMethod(String obfName, Signature obfSignature) { | ||
| 266 | return m_methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); | ||
| 267 | } | ||
| 268 | |||
| 237 | public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { | 269 | public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { |
| 238 | return m_methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); | 270 | return m_methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); |
| 239 | } | 271 | } |
| @@ -298,6 +330,16 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 298 | } | 330 | } |
| 299 | } | 331 | } |
| 300 | 332 | ||
| 333 | public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { | ||
| 334 | assert(newObfName != null); | ||
| 335 | MethodMapping methodMapping = m_methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); | ||
| 336 | assert(methodMapping != null); | ||
| 337 | methodMapping.setObfName(newObfName); | ||
| 338 | methodMapping.setObfSignature(newObfSignature); | ||
| 339 | boolean obfWasAdded = m_methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; | ||
| 340 | assert(obfWasAdded); | ||
| 341 | } | ||
| 342 | |||
| 301 | //// ARGUMENTS //////// | 343 | //// ARGUMENTS //////// |
| 302 | 344 | ||
| 303 | public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { | 345 | public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { |
| @@ -360,6 +402,48 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 360 | return m_obfFullName.compareTo(other.m_obfFullName); | 402 | return m_obfFullName.compareTo(other.m_obfFullName); |
| 361 | } | 403 | } |
| 362 | 404 | ||
| 405 | public boolean renameObfClass(String oldObfClassName, String newObfClassName) { | ||
| 406 | |||
| 407 | // rename inner classes | ||
| 408 | for (ClassMapping innerClassMapping : new ArrayList<>(m_innerClassesByObfSimple.values())) { | ||
| 409 | if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 410 | boolean wasRemoved = m_innerClassesByObfSimple.remove(oldObfClassName) != null; | ||
| 411 | assert (wasRemoved); | ||
| 412 | boolean wasAdded = m_innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; | ||
| 413 | assert (wasAdded); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | // rename field types | ||
| 418 | for (FieldMapping fieldMapping : new ArrayList<>(m_fieldsByObf.values())) { | ||
| 419 | String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); | ||
| 420 | if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 421 | boolean wasRemoved = m_fieldsByObf.remove(oldFieldKey) != null; | ||
| 422 | assert (wasRemoved); | ||
| 423 | boolean wasAdded = m_fieldsByObf.put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; | ||
| 424 | assert (wasAdded); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | // rename method signatures | ||
| 429 | for (MethodMapping methodMapping : new ArrayList<>(m_methodsByObf.values())) { | ||
| 430 | String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); | ||
| 431 | if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { | ||
| 432 | boolean wasRemoved = m_methodsByObf.remove(oldMethodKey) != null; | ||
| 433 | assert (wasRemoved); | ||
| 434 | boolean wasAdded = m_methodsByObf.put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; | ||
| 435 | assert (wasAdded); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | if (m_obfFullName.equals(oldObfClassName)) { | ||
| 440 | // rename this class | ||
| 441 | m_obfFullName = newObfClassName; | ||
| 442 | return true; | ||
| 443 | } | ||
| 444 | return false; | ||
| 445 | } | ||
| 446 | |||
| 363 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { | 447 | public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { |
| 364 | MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); | 448 | MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); |
| 365 | return methodMapping != null && methodMapping.containsArgument(name); | 449 | return methodMapping != null && methodMapping.containsArgument(name); |
| @@ -369,4 +453,7 @@ public class ClassMapping implements Comparable<ClassMapping> { | |||
| 369 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; | 453 | return name.indexOf('/') < 0 && name.indexOf('$') < 0; |
| 370 | } | 454 | } |
| 371 | 455 | ||
| 456 | public ClassEntry getObfEntry() { | ||
| 457 | return new ClassEntry(m_obfFullName); | ||
| 458 | } | ||
| 372 | } | 459 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java index 2351dcf..ce4b948 100644 --- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -33,6 +33,10 @@ public class EntryFactory { | |||
| 33 | return new ClassEntry(classMapping.getObfFullName()); | 33 | return new ClassEntry(classMapping.getObfFullName()); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | ||
| 37 | return new ClassEntry(classMapping.getDeobfName()); | ||
| 38 | } | ||
| 39 | |||
| 36 | public static ClassEntry getSuperclassEntry(CtClass c) { | 40 | public static ClassEntry getSuperclassEntry(CtClass c) { |
| 37 | return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); | 41 | return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); |
| 38 | } | 42 | } |
| @@ -90,6 +94,10 @@ public class EntryFactory { | |||
| 90 | return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); | 94 | return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); |
| 91 | } | 95 | } |
| 92 | 96 | ||
| 97 | public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { | ||
| 98 | return getBehaviorEntry(new ClassEntry(className), behaviorName); | ||
| 99 | } | ||
| 100 | |||
| 93 | public static BehaviorEntry getBehaviorEntry(String className) { | 101 | public static BehaviorEntry getBehaviorEntry(String className) { |
| 94 | return new ConstructorEntry(new ClassEntry(className)); | 102 | return new ConstructorEntry(new ClassEntry(className)); |
| 95 | } | 103 | } |
| @@ -105,7 +113,19 @@ public class EntryFactory { | |||
| 105 | } | 113 | } |
| 106 | } | 114 | } |
| 107 | 115 | ||
| 116 | public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { | ||
| 117 | if(behaviorName.equals("<clinit>")) { | ||
| 118 | return new ConstructorEntry(classEntry); | ||
| 119 | } else { | ||
| 120 | throw new IllegalArgumentException("Only class initializers don't have signatures"); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 108 | public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { | 124 | public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { |
| 109 | return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); | 125 | return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); |
| 110 | } | 126 | } |
| 127 | |||
| 128 | public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { | ||
| 129 | return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); | ||
| 130 | } | ||
| 111 | } | 131 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryPair.java b/src/main/java/cuchaz/enigma/mapping/EntryPair.java new file mode 100644 index 0000000..1c93d53 --- /dev/null +++ b/src/main/java/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 | * <p> | ||
| 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/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java index 3ec1af0..1b59660 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java | |||
| @@ -22,6 +22,17 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie | |||
| 22 | this.obfType = obfType; | 22 | this.obfType = obfType; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { | ||
| 26 | this.obfName = other.obfName; | ||
| 27 | this.deobfName = other.deobfName; | ||
| 28 | this.obfType = new Type(other.obfType, obfClassNameReplacer); | ||
| 29 | } | ||
| 30 | |||
| 31 | @Override | ||
| 32 | public FieldEntry getObfEntry(ClassEntry classEntry) { | ||
| 33 | return new FieldEntry(classEntry, this.obfName, this.obfType); | ||
| 34 | } | ||
| 35 | |||
| 25 | @Override | 36 | @Override |
| 26 | public String getObfName() { | 37 | public String getObfName() { |
| 27 | return this.obfName; | 38 | return this.obfName; |
| @@ -35,12 +46,40 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie | |||
| 35 | this.deobfName = NameValidator.validateFieldName(val); | 46 | this.deobfName = NameValidator.validateFieldName(val); |
| 36 | } | 47 | } |
| 37 | 48 | ||
| 49 | public void setObfName(String val) { | ||
| 50 | this.obfName = NameValidator.validateFieldName(val); | ||
| 51 | } | ||
| 52 | |||
| 38 | public Type getObfType() { | 53 | public Type getObfType() { |
| 39 | return this.obfType; | 54 | return this.obfType; |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 57 | public void setObfType(Type val) { | ||
| 58 | this.obfType = val; | ||
| 59 | } | ||
| 60 | |||
| 42 | @Override | 61 | @Override |
| 43 | public int compareTo(FieldMapping other) { | 62 | public int compareTo(FieldMapping other) { |
| 44 | return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); | 63 | return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); |
| 45 | } | 64 | } |
| 65 | |||
| 66 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 67 | // rename obf classes in the type | ||
| 68 | Type newType = new Type(this.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(this.obfType)) { | ||
| 79 | this.obfType = newType; | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 46 | } | 85 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java index 538c67e..171ddf1 100644 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java | |||
| @@ -13,10 +13,13 @@ package cuchaz.enigma.mapping; | |||
| 13 | import com.google.common.collect.Lists; | 13 | import com.google.common.collect.Lists; |
| 14 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 15 | 15 | ||
| 16 | import java.util.ArrayList; | ||
| 16 | import java.util.Collection; | 17 | import java.util.Collection; |
| 17 | import java.util.List; | 18 | import java.util.List; |
| 18 | import java.util.Map; | 19 | import java.util.Map; |
| 20 | import java.util.Set; | ||
| 19 | 21 | ||
| 22 | import com.google.common.collect.Sets; | ||
| 20 | import cuchaz.enigma.analysis.TranslationIndex; | 23 | import cuchaz.enigma.analysis.TranslationIndex; |
| 21 | import cuchaz.enigma.throwables.MappingConflict; | 24 | import cuchaz.enigma.throwables.MappingConflict; |
| 22 | 25 | ||
| @@ -65,10 +68,23 @@ public class Mappings { | |||
| 65 | } | 68 | } |
| 66 | } | 69 | } |
| 67 | 70 | ||
| 71 | |||
| 72 | public ClassMapping getClassByObf(ClassEntry entry) { | ||
| 73 | return getClassByObf(entry.getName()); | ||
| 74 | } | ||
| 75 | |||
| 68 | public ClassMapping getClassByObf(String obfName) { | 76 | public ClassMapping getClassByObf(String obfName) { |
| 69 | return this.classesByObf.get(obfName); | 77 | return this.classesByObf.get(obfName); |
| 70 | } | 78 | } |
| 71 | 79 | ||
| 80 | public ClassMapping getClassByDeobf(ClassEntry entry) { | ||
| 81 | return getClassByDeobf(entry.getName()); | ||
| 82 | } | ||
| 83 | |||
| 84 | public ClassMapping getClassByDeobf(String deobfName) { | ||
| 85 | return this.classesByDeobf.get(deobfName); | ||
| 86 | } | ||
| 87 | |||
| 72 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { | 88 | public void setClassDeobfName(ClassMapping classMapping, String deobfName) { |
| 73 | if (classMapping.getDeobfName() != null) { | 89 | if (classMapping.getDeobfName() != null) { |
| 74 | boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; | 90 | boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; |
| @@ -120,6 +136,34 @@ public class Mappings { | |||
| 120 | return buf.toString(); | 136 | return buf.toString(); |
| 121 | } | 137 | } |
| 122 | 138 | ||
| 139 | public void renameObfClass(String oldObfName, String newObfName) { | ||
| 140 | new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { | ||
| 141 | boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; | ||
| 142 | assert (wasRemoved); | ||
| 143 | boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; | ||
| 144 | assert (wasAdded); | ||
| 145 | }); | ||
| 146 | } | ||
| 147 | |||
| 148 | public Set<String> getAllObfClassNames() { | ||
| 149 | final Set<String> classNames = Sets.newHashSet(); | ||
| 150 | for (ClassMapping classMapping : classes()) { | ||
| 151 | |||
| 152 | // add the class name | ||
| 153 | classNames.add(classMapping.getObfFullName()); | ||
| 154 | |||
| 155 | // add classes from method signatures | ||
| 156 | for (MethodMapping methodMapping : classMapping.methods()) { | ||
| 157 | for (Type type : methodMapping.getObfSignature().types()) { | ||
| 158 | if (type.hasClass()) { | ||
| 159 | classNames.add(type.getClassEntry().getClassName()); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | return classNames; | ||
| 165 | } | ||
| 166 | |||
| 123 | public boolean containsDeobfClass(String deobfName) { | 167 | public boolean containsDeobfClass(String deobfName) { |
| 124 | return this.classesByDeobf.containsKey(deobfName); | 168 | return this.classesByDeobf.containsKey(deobfName); |
| 125 | } | 169 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java index 70f3f18..a27f72e 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java | |||
| @@ -85,7 +85,7 @@ public class MappingsEnigmaReader | |||
| 85 | ClassMapping classMapping; | 85 | ClassMapping classMapping; |
| 86 | if (indent <= 0) { | 86 | if (indent <= 0) { |
| 87 | // outer class | 87 | // outer class |
| 88 | classMapping = readClass(parts); | 88 | classMapping = readClass(parts, false); |
| 89 | mappings.addClassMapping(classMapping); | 89 | mappings.addClassMapping(classMapping); |
| 90 | } else { | 90 | } else { |
| 91 | 91 | ||
| @@ -94,7 +94,7 @@ public class MappingsEnigmaReader | |||
| 94 | throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); | 94 | throw new MappingParseException(lineNumber, "Unexpected CLASS entry here!"); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | classMapping = readClass(parts); | 97 | classMapping = readClass(parts, true); |
| 98 | ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); | 98 | ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); |
| 99 | } | 99 | } |
| 100 | mappingStack.push(classMapping); | 100 | mappingStack.push(classMapping); |
| @@ -130,7 +130,7 @@ public class MappingsEnigmaReader | |||
| 130 | return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); | 130 | return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | private ClassMapping readClass(String[] parts) { | 133 | private ClassMapping readClass(String[] parts, boolean makeSimple) { |
| 134 | if (parts.length == 2) { | 134 | if (parts.length == 2) { |
| 135 | return new ClassMapping(parts[1]); | 135 | return new ClassMapping(parts[1]); |
| 136 | } else { | 136 | } else { |
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java index afb8c97..8002813 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -10,8 +10,12 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.mapping; | 11 | package cuchaz.enigma.mapping; |
| 12 | 12 | ||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.ObjectOutputStream; | ||
| 15 | import java.io.OutputStream; | ||
| 13 | import java.util.List; | 16 | import java.util.List; |
| 14 | import java.util.Set; | 17 | import java.util.Set; |
| 18 | import java.util.zip.GZIPOutputStream; | ||
| 15 | 19 | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | 20 | import cuchaz.enigma.analysis.JarIndex; |
| 17 | import cuchaz.enigma.throwables.IllegalNameException; | 21 | import cuchaz.enigma.throwables.IllegalNameException; |
| @@ -165,6 +169,42 @@ public class MappingsRenamer { | |||
| 165 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); | 169 | classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); |
| 166 | } | 170 | } |
| 167 | 171 | ||
| 172 | public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { | ||
| 173 | classMapping.removeFieldMapping(fieldMapping); | ||
| 174 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 175 | if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { | ||
| 176 | if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { | ||
| 177 | targetClassMapping.addFieldMapping(fieldMapping); | ||
| 178 | return true; | ||
| 179 | } else { | ||
| 180 | System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | return false; | ||
| 184 | } | ||
| 185 | |||
| 186 | public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { | ||
| 187 | classMapping.removeMethodMapping(methodMapping); | ||
| 188 | ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); | ||
| 189 | if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { | ||
| 190 | if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { | ||
| 191 | targetClassMapping.addMethodMapping(methodMapping); | ||
| 192 | return true; | ||
| 193 | } else { | ||
| 194 | System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | return false; | ||
| 198 | } | ||
| 199 | |||
| 200 | public void write(OutputStream out) throws IOException { | ||
| 201 | // TEMP: just use the object output for now. We can find a more efficient storage format later | ||
| 202 | GZIPOutputStream gzipout = new GZIPOutputStream(out); | ||
| 203 | ObjectOutputStream oout = new ObjectOutputStream(gzipout); | ||
| 204 | oout.writeObject(this); | ||
| 205 | gzipout.finish(); | ||
| 206 | } | ||
| 207 | |||
| 168 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { | 208 | private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { |
| 169 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); | 209 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obfClassEntry); |
| 170 | return mappingChain.get(mappingChain.size() - 1); | 210 | return mappingChain.get(mappingChain.size() - 1); |
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java index 590c830..90c096f 100644 --- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java | |||
| @@ -12,5 +12,7 @@ package cuchaz.enigma.mapping; | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | public interface MemberMapping<T extends Entry> { | 14 | public interface MemberMapping<T extends Entry> { |
| 15 | T getObfEntry(ClassEntry classEntry); | ||
| 16 | |||
| 15 | String getObfName(); | 17 | String getObfName(); |
| 16 | } | 18 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java index 6e7c1ef..99b9c88 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java | |||
| @@ -39,6 +39,16 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B | |||
| 39 | this.obfSignature = obfSignature; | 39 | this.obfSignature = obfSignature; |
| 40 | this.arguments = Maps.newTreeMap(); | 40 | this.arguments = Maps.newTreeMap(); |
| 41 | } | 41 | } |
| 42 | |||
| 43 | public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { | ||
| 44 | this.obfName = other.obfName; | ||
| 45 | this.deobfName = other.deobfName; | ||
| 46 | this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); | ||
| 47 | this.arguments = Maps.newTreeMap(); | ||
| 48 | for (Map.Entry<Integer,ArgumentMapping> entry : other.arguments.entrySet()) { | ||
| 49 | this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); | ||
| 50 | } | ||
| 51 | } | ||
| 42 | 52 | ||
| 43 | @Override | 53 | @Override |
| 44 | public String getObfName() { | 54 | public String getObfName() { |
| @@ -57,6 +67,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B | |||
| 57 | return this.obfSignature; | 67 | return this.obfSignature; |
| 58 | } | 68 | } |
| 59 | 69 | ||
| 70 | public void setObfName(String name) { | ||
| 71 | this.obfName = NameValidator.validateMethodName(name); | ||
| 72 | } | ||
| 73 | |||
| 74 | public void setObfSignature(Signature val) { | ||
| 75 | this.obfSignature = val; | ||
| 76 | } | ||
| 77 | |||
| 60 | public Iterable<ArgumentMapping> arguments() { | 78 | public Iterable<ArgumentMapping> arguments() { |
| 61 | return this.arguments.values(); | 79 | return this.arguments.values(); |
| 62 | } | 80 | } |
| @@ -137,4 +155,36 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B | |||
| 137 | } | 155 | } |
| 138 | return false; | 156 | return false; |
| 139 | } | 157 | } |
| 158 | |||
| 159 | public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { | ||
| 160 | // rename obf classes in the signature | ||
| 161 | Signature newSignature = new Signature(this.obfSignature, new ClassNameReplacer() { | ||
| 162 | @Override | ||
| 163 | public String replace(String className) { | ||
| 164 | if (className.equals(oldObfClassName)) { | ||
| 165 | return newObfClassName; | ||
| 166 | } | ||
| 167 | return null; | ||
| 168 | } | ||
| 169 | }); | ||
| 170 | |||
| 171 | if (!newSignature.equals(this.obfSignature)) { | ||
| 172 | this.obfSignature = newSignature; | ||
| 173 | return true; | ||
| 174 | } | ||
| 175 | return false; | ||
| 176 | } | ||
| 177 | |||
| 178 | public boolean isConstructor() { | ||
| 179 | return this.obfName.startsWith("<"); | ||
| 180 | } | ||
| 181 | |||
| 182 | @Override | ||
| 183 | public BehaviorEntry getObfEntry(ClassEntry classEntry) { | ||
| 184 | if (isConstructor()) { | ||
| 185 | return new ConstructorEntry(classEntry, this.obfSignature); | ||
| 186 | } else { | ||
| 187 | return new MethodEntry(classEntry, this.obfName, this.obfSignature); | ||
| 188 | } | ||
| 189 | } | ||
| 140 | } | 190 | } |
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java index 15b0314..7be83c7 100644 --- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java +++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java | |||
| @@ -32,6 +32,17 @@ public class NameValidator { | |||
| 32 | static { | 32 | static { |
| 33 | 33 | ||
| 34 | // java allows all kinds of weird characters... | 34 | // java allows all kinds of weird characters... |
| 35 | StringBuilder startChars = new StringBuilder(); | ||
| 36 | StringBuilder partChars = new StringBuilder(); | ||
| 37 | for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) { | ||
| 38 | if (Character.isJavaIdentifierStart(i)) { | ||
| 39 | startChars.appendCodePoint(i); | ||
| 40 | } | ||
| 41 | if (Character.isJavaIdentifierPart(i)) { | ||
| 42 | partChars.appendCodePoint(i); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 35 | String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; | 46 | String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; |
| 36 | IdentifierPattern = Pattern.compile(identifierRegex); | 47 | IdentifierPattern = Pattern.compile(identifierRegex); |
| 37 | ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); | 48 | ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); |
diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java new file mode 100644 index 0000000..9864333 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import com.google.common.collect.Lists; | ||
| 14 | |||
| 15 | import java.io.IOException; | ||
| 16 | import java.io.StringReader; | ||
| 17 | import java.util.List; | ||
| 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; | ||
| 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; | ||
| 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, className -> { | ||
| 86 | classNames.add(className); | ||
| 87 | return className; | ||
| 88 | }); | ||
| 89 | return classNames; | ||
| 90 | } | ||
| 91 | } | ||
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java index 125e03f..bebac4e 100644 --- a/src/main/java/cuchaz/enigma/mapping/Translator.java +++ b/src/main/java/cuchaz/enigma/mapping/Translator.java | |||
| @@ -38,6 +38,14 @@ public class Translator { | |||
| 38 | this.index = index; | 38 | this.index = index; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | public TranslationDirection getDirection() { | ||
| 42 | return direction; | ||
| 43 | } | ||
| 44 | |||
| 45 | public TranslationIndex getTranslationIndex() { | ||
| 46 | return index; | ||
| 47 | } | ||
| 48 | |||
| 41 | @SuppressWarnings("unchecked") | 49 | @SuppressWarnings("unchecked") |
| 42 | public <T extends Entry> T translateEntry(T entry) { | 50 | public <T extends Entry> T translateEntry(T entry) { |
| 43 | if (entry instanceof ClassEntry) { | 51 | if (entry instanceof ClassEntry) { |