From 6e464ea251cab63c776ece0b2a356f1498ffa294 Mon Sep 17 00:00:00 2001 From: Thog Date: Wed, 8 Mar 2017 08:17:04 +0100 Subject: Follow Fabric guidelines --- .../java/cuchaz/enigma/mapping/ArgumentEntry.java | 185 ++-- .../cuchaz/enigma/mapping/ArgumentMapping.java | 55 +- .../java/cuchaz/enigma/mapping/BehaviorEntry.java | 3 +- .../java/cuchaz/enigma/mapping/ClassEntry.java | 295 +++--- .../java/cuchaz/enigma/mapping/ClassMapping.java | 1042 ++++++++++---------- .../cuchaz/enigma/mapping/ClassNameReplacer.java | 3 +- .../cuchaz/enigma/mapping/ConstructorEntry.java | 175 ++-- src/main/java/cuchaz/enigma/mapping/Entry.java | 9 +- .../java/cuchaz/enigma/mapping/EntryFactory.java | 217 ++-- .../java/cuchaz/enigma/mapping/FieldEntry.java | 139 +-- .../java/cuchaz/enigma/mapping/FieldMapping.java | 180 ++-- .../cuchaz/enigma/mapping/LocalVariableEntry.java | 186 ++-- src/main/java/cuchaz/enigma/mapping/Mappings.java | 453 +++++---- .../cuchaz/enigma/mapping/MappingsChecker.java | 155 ++- .../enigma/mapping/MappingsEnigmaReader.java | 340 ++++--- .../enigma/mapping/MappingsEnigmaWriter.java | 70 +- .../cuchaz/enigma/mapping/MappingsRenamer.java | 633 ++++++------ .../cuchaz/enigma/mapping/MappingsSRGWriter.java | 112 ++- .../java/cuchaz/enigma/mapping/MemberMapping.java | 6 +- .../java/cuchaz/enigma/mapping/MethodEntry.java | 145 +-- .../java/cuchaz/enigma/mapping/MethodMapping.java | 393 ++++---- .../java/cuchaz/enigma/mapping/NameValidator.java | 89 +- .../cuchaz/enigma/mapping/ProcyonEntryFactory.java | 91 +- src/main/java/cuchaz/enigma/mapping/Signature.java | 154 +-- .../cuchaz/enigma/mapping/SignatureUpdater.java | 125 +-- .../enigma/mapping/TranslationDirection.java | 27 +- .../java/cuchaz/enigma/mapping/Translator.java | 655 ++++++------ src/main/java/cuchaz/enigma/mapping/Type.java | 431 ++++---- 28 files changed, 3159 insertions(+), 3209 deletions(-) (limited to 'src/main/java/cuchaz/enigma/mapping') diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java index 662516d..9154cc2 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java @@ -8,102 +8,103 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.utils.Utils; public class ArgumentEntry implements Entry { - private BehaviorEntry behaviorEntry; - private int index; - private String name; - - public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { - if (behaviorEntry == null) { - throw new IllegalArgumentException("Behavior cannot be null!"); - } - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative!"); - } - if (name == null) { - throw new IllegalArgumentException("Argument name cannot be null!"); - } - - this.behaviorEntry = behaviorEntry; - this.index = index; - this.name = name; - } - - public ArgumentEntry(ArgumentEntry other) { - this.behaviorEntry = other.getBehaviorEntry(); - this.index = other.index; - this.name = other.name; - } - - public ArgumentEntry(ArgumentEntry other, String newClassName) { - this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); - this.index = other.index; - this.name = other.name; - } - - public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { - this.behaviorEntry = entry; - this.index = other.index; - this.name = other.name; - } - - public BehaviorEntry getBehaviorEntry() { - return this.behaviorEntry; - } - - public int getIndex() { - return this.index; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public ClassEntry getClassEntry() { - return this.behaviorEntry.getClassEntry(); - } - - @Override - public String getClassName() { - return this.behaviorEntry.getClassName(); - } - - @Override - public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { - return new ArgumentEntry(this, classEntry.getName()); - } - - public String getMethodName() { - return this.behaviorEntry.getName(); - } - - public Signature getMethodSignature() { - return this.behaviorEntry.getSignature(); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); - } - - @Override - public boolean equals(Object other) { - return other instanceof ArgumentEntry && equals((ArgumentEntry) other); - } - - public boolean equals(ArgumentEntry other) { - return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); - } - - @Override - public String toString() { - return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ")"; - } + private BehaviorEntry behaviorEntry; + private int index; + private String name; + + public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { + if (behaviorEntry == null) { + throw new IllegalArgumentException("Behavior cannot be null!"); + } + if (index < 0) { + throw new IllegalArgumentException("Index must be non-negative!"); + } + if (name == null) { + throw new IllegalArgumentException("Argument name cannot be null!"); + } + + this.behaviorEntry = behaviorEntry; + this.index = index; + this.name = name; + } + + public ArgumentEntry(ArgumentEntry other) { + this.behaviorEntry = other.getBehaviorEntry(); + this.index = other.index; + this.name = other.name; + } + + public ArgumentEntry(ArgumentEntry other, String newClassName) { + this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); + this.index = other.index; + this.name = other.name; + } + + public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { + this.behaviorEntry = entry; + this.index = other.index; + this.name = other.name; + } + + public BehaviorEntry getBehaviorEntry() { + return this.behaviorEntry; + } + + public int getIndex() { + return this.index; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public ClassEntry getClassEntry() { + return this.behaviorEntry.getClassEntry(); + } + + @Override + public String getClassName() { + return this.behaviorEntry.getClassName(); + } + + @Override + public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { + return new ArgumentEntry(this, classEntry.getName()); + } + + public String getMethodName() { + return this.behaviorEntry.getName(); + } + + public Signature getMethodSignature() { + return this.behaviorEntry.getSignature(); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); + } + + @Override + public boolean equals(Object other) { + return other instanceof ArgumentEntry && equals((ArgumentEntry) other); + } + + public boolean equals(ArgumentEntry other) { + return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); + } + + @Override + public String toString() { + return this.behaviorEntry + "(" + this.index + ":" + this.name + ")"; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java index e3f8927..91ecd10 100644 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java @@ -8,42 +8,43 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; public class ArgumentMapping implements Comparable { - private int index; - private String name; + private int index; + private String name; - // NOTE: this argument order is important for the MethodReader/MethodWriter - public ArgumentMapping(int index, String name) { - this.index = index; - this.name = NameValidator.validateArgumentName(name); - } + // NOTE: this argument order is important for the MethodReader/MethodWriter + public ArgumentMapping(int index, String name) { + this.index = index; + this.name = NameValidator.validateArgumentName(name); + } - public ArgumentMapping(ArgumentMapping other) { - this.index = other.index; - this.name = other.name; - } + public ArgumentMapping(ArgumentMapping other) { + this.index = other.index; + this.name = other.name; + } - public int getIndex() { - return this.index; - } + public int getIndex() { + return this.index; + } - public String getName() { - return this.name; - } + public String getName() { + return this.name; + } - public void setName(String val) { - this.name = NameValidator.validateArgumentName(val); - } + public void setName(String val) { + this.name = NameValidator.validateArgumentName(val); + } - public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { - return new ArgumentEntry(behaviorEntry, index, name); - } + public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { + return new ArgumentEntry(behaviorEntry, index, name); + } - @Override - public int compareTo(ArgumentMapping other) { - return Integer.compare(this.index, other.index); - } + @Override + public int compareTo(ArgumentMapping other) { + return Integer.compare(this.index, other.index); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java index f5c6c05..04b4ebc 100644 --- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java @@ -8,8 +8,9 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; public interface BehaviorEntry extends Entry { - Signature getSignature(); + Signature getSignature(); } diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java index 398b135..788811f 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; @@ -16,151 +17,151 @@ import java.util.List; public class ClassEntry implements Entry { - private String name; - - public ClassEntry(String className) { - if (className == null) { - throw new IllegalArgumentException("Class name cannot be null!"); - } - if (className.indexOf('.') >= 0) { - throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); - } - - this.name = className; - - if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { - throw new IllegalArgumentException("Inner class must not have a package: " + className); - } - } - - public ClassEntry(ClassEntry other) { - this.name = other.name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getClassName() { - return this.name; - } - - @Override - public ClassEntry getClassEntry() { - return this; - } - - @Override - public ClassEntry cloneToNewClass(ClassEntry classEntry) { - return classEntry; - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - - @Override - public boolean equals(Object other) { - return other instanceof ClassEntry && equals((ClassEntry) other); - } - - public boolean equals(ClassEntry other) { - return other != null && this.name.equals(other.name); - } - - @Override - public String toString() { - return this.name; - } - - public boolean isInnerClass() { - return this.name.lastIndexOf('$') >= 0; - } - - public List getClassChainNames() { - return Lists.newArrayList(this.name.split("\\$")); - } - - public List getClassChain() { - List entries = Lists.newArrayList(); - StringBuilder buf = new StringBuilder(); - for (String name : getClassChainNames()) { - if (buf.length() > 0) { - buf.append("$"); - } - buf.append(name); - entries.add(new ClassEntry(buf.toString())); - } - return entries; - } - - public String getOutermostClassName() { - if (isInnerClass()) { - return this.name.substring(0, this.name.indexOf('$')); - } - return this.name; - } - - public ClassEntry getOutermostClassEntry() { - return new ClassEntry(getOutermostClassName()); - } - - public String getOuterClassName() { - if (!isInnerClass()) { - throw new Error("This is not an inner class!"); - } - return this.name.substring(0, this.name.lastIndexOf('$')); - } - - public ClassEntry getOuterClassEntry() { - return new ClassEntry(getOuterClassName()); - } - - public String getInnermostClassName() { - if (!isInnerClass()) { - throw new Error("This is not an inner class!"); - } - return this.name.substring(this.name.lastIndexOf('$') + 1); - } - - public boolean isInDefaultPackage() { - return this.name.indexOf('/') < 0; - } - - public String getPackageName() { - int pos = this.name.lastIndexOf('/'); - if (pos > 0) { - return this.name.substring(0, pos); - } - return null; - } - - public String getSimpleName() { - int pos = this.name.lastIndexOf('/'); - if (pos > 0) { - return this.name.substring(pos + 1); - } - return this.name; - } - - public ClassEntry buildClassEntry(List classChain) { - assert (classChain.contains(this)); - StringBuilder buf = new StringBuilder(); - for (ClassEntry chainEntry : classChain) { - if (buf.length() == 0) { - buf.append(chainEntry.getName()); - } else { - buf.append("$"); - buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); - } - - if (chainEntry == this) { - break; - } - } - return new ClassEntry(buf.toString()); - } + private String name; + + public ClassEntry(String className) { + if (className == null) { + throw new IllegalArgumentException("Class name cannot be null!"); + } + if (className.indexOf('.') >= 0) { + throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); + } + + this.name = className; + + if (isInnerClass() && getInnermostClassName().indexOf('/') >= 0) { + throw new IllegalArgumentException("Inner class must not have a package: " + className); + } + } + + public ClassEntry(ClassEntry other) { + this.name = other.name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getClassName() { + return this.name; + } + + @Override + public ClassEntry getClassEntry() { + return this; + } + + @Override + public ClassEntry cloneToNewClass(ClassEntry classEntry) { + return classEntry; + } + + @Override + public int hashCode() { + return this.name.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other instanceof ClassEntry && equals((ClassEntry) other); + } + + public boolean equals(ClassEntry other) { + return other != null && this.name.equals(other.name); + } + + @Override + public String toString() { + return this.name; + } + + public boolean isInnerClass() { + return this.name.lastIndexOf('$') >= 0; + } + + public List getClassChainNames() { + return Lists.newArrayList(this.name.split("\\$")); + } + + public List getClassChain() { + List entries = Lists.newArrayList(); + StringBuilder buf = new StringBuilder(); + for (String name : getClassChainNames()) { + if (buf.length() > 0) { + buf.append("$"); + } + buf.append(name); + entries.add(new ClassEntry(buf.toString())); + } + return entries; + } + + public String getOutermostClassName() { + if (isInnerClass()) { + return this.name.substring(0, this.name.indexOf('$')); + } + return this.name; + } + + public ClassEntry getOutermostClassEntry() { + return new ClassEntry(getOutermostClassName()); + } + + public String getOuterClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); + } + return this.name.substring(0, this.name.lastIndexOf('$')); + } + + public ClassEntry getOuterClassEntry() { + return new ClassEntry(getOuterClassName()); + } + + public String getInnermostClassName() { + if (!isInnerClass()) { + throw new Error("This is not an inner class!"); + } + return this.name.substring(this.name.lastIndexOf('$') + 1); + } + + public boolean isInDefaultPackage() { + return this.name.indexOf('/') < 0; + } + + public String getPackageName() { + int pos = this.name.lastIndexOf('/'); + if (pos > 0) { + return this.name.substring(0, pos); + } + return null; + } + + public String getSimpleName() { + int pos = this.name.lastIndexOf('/'); + if (pos > 0) { + return this.name.substring(pos + 1); + } + return this.name; + } + + public ClassEntry buildClassEntry(List classChain) { + assert (classChain.contains(this)); + StringBuilder buf = new StringBuilder(); + for (ClassEntry chainEntry : classChain) { + if (buf.length() == 0) { + buf.append(chainEntry.getName()); + } else { + buf.append("$"); + buf.append(chainEntry.isInnerClass() ? chainEntry.getInnermostClassName() : chainEntry.getSimpleName()); + } + + if (chainEntry == this) { + break; + } + } + return new ClassEntry(buf.toString()); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index a261c91..178dd3c 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java @@ -8,540 +8,530 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Maps; +import cuchaz.enigma.throwables.MappingConflict; import java.util.ArrayList; import java.util.Map; -import cuchaz.enigma.throwables.MappingConflict; - // FIXME: Enigma doesn't support inner classes of inner class????! public class ClassMapping implements Comparable { - private String obfFullName; - private String obfSimpleName; - private String deobfName; - private String previousDeobfName; - private Map innerClassesByObfSimple; - private Map innerClassesByObfFull; - private Map innerClassesByDeobf; - private Map fieldsByObf; - private Map fieldsByDeobf; - private Map methodsByObf; - private Map methodsByDeobf; - private boolean isDirty; - private Mappings.EntryModifier modifier; - - public ClassMapping(String obfFullName) - { - this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); - } - - public ClassMapping(String obfFullName, String deobfName) - { - this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); - } - - public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) - { - this.obfFullName = obfFullName; - ClassEntry classEntry = new ClassEntry(obfFullName); - obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); - previousDeobfName = null; - this.deobfName = NameValidator.validateClassName(deobfName, false); - innerClassesByObfSimple = Maps.newHashMap(); - innerClassesByObfFull = Maps.newHashMap(); - innerClassesByDeobf = Maps.newHashMap(); - fieldsByObf = Maps.newHashMap(); - fieldsByDeobf = Maps.newHashMap(); - methodsByObf = Maps.newHashMap(); - methodsByDeobf = Maps.newHashMap(); - isDirty = true; - this.modifier = modifier; - } - - public String getObfFullName() { - return obfFullName; - } - - public String getObfSimpleName() { - return obfSimpleName; - } - - public String getPreviousDeobfName() { - return previousDeobfName; - } - - public String getDeobfName() { - return deobfName; - } - - public void setDeobfName(String val) { - previousDeobfName = deobfName; - deobfName = NameValidator.validateClassName(val, false); - this.isDirty = true; - } - - //// INNER CLASSES //////// - - public Iterable innerClasses() { - assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); - return innerClassesByObfSimple.values(); - } - - public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { - // FIXME: dirty hack, that can get into issues, but it's a temp fix! - if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { - throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); - } - innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); - innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); - - if (classMapping.getDeobfName() != null) { - if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { - throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); - } - innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); - } - this.isDirty = true; - } - - public void removeInnerClassMapping(ClassMapping classMapping) { - innerClassesByObfFull.remove(classMapping.getObfFullName()); - boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; - assert (obfWasRemoved); - if (classMapping.getDeobfName() != null) { - boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (deobfWasRemoved); - } - this.isDirty = true; - } - - public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { - ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); - if (classMapping == null) { - classMapping = new ClassMapping(obfInnerClass.getName()); - innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); - boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; - assert (wasAdded); - this.isDirty = true; - } - return classMapping; - } - - public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { - assert (isSimpleClassName(obfSimpleName)); - return innerClassesByObfSimple.get(obfSimpleName); - } - - public ClassMapping getInnerClassByDeobf(String deobfName) { - assert (isSimpleClassName(deobfName)); - return innerClassesByDeobf.get(deobfName); - } - - public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { - ClassMapping classMapping = getInnerClassByDeobf(name); - if (classMapping == null) { - classMapping = getInnerClassByObfSimple(name); - } - return classMapping; - } - - public String getDeobfInnerClassName(String obfSimpleName) { - assert (isSimpleClassName(obfSimpleName)); - ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); - if (classMapping != null) { - return classMapping.getDeobfName(); - } - return null; - } - - public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { - ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); - if (classMapping.getDeobfName() != null) { - boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (wasRemoved); - } - classMapping.setDeobfName(deobfName); - if (deobfName != null) { - assert (isSimpleClassName(deobfName)); - boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; - assert (wasAdded); - } - this.isDirty = true; - } - - public boolean hasInnerClassByObfSimple(String obfSimpleName) { - return innerClassesByObfSimple.containsKey(obfSimpleName); - } - - public boolean hasInnerClassByDeobf(String deobfName) { - return innerClassesByDeobf.containsKey(deobfName); - } - - - //// FIELDS //////// - - public Iterable fields() { - assert (fieldsByObf.size() == fieldsByDeobf.size()); - return fieldsByObf.values(); - } - - public boolean containsObfField(String obfName, Type obfType) { - return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); - } - - public boolean containsDeobfField(String deobfName, Type deobfType) { - return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); - } - - public void addFieldMapping(FieldMapping fieldMapping) { - String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); - if (fieldsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + obfFullName + "." + obfKey); - } - if (fieldMapping.getDeobfName() != null) { - String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); - if (fieldsByDeobf.containsKey(deobfKey)) { - throw new Error("Already have mapping for " + deobfName + "." + deobfKey); - } - boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; - assert (deobfWasAdded); - } - boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; - assert (obfWasAdded); - this.isDirty = true; - } - - public void removeFieldMapping(FieldMapping fieldMapping) { - boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; - assert (obfWasRemoved); - if (fieldMapping.getDeobfName() != null) { - boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; - assert (deobfWasRemoved); - } - this.isDirty = true; - } - - public FieldMapping getFieldByObf(String obfName, Type obfType) { - return fieldsByObf.get(getFieldKey(obfName, obfType)); - } - - public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { - return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); - } - - public String getObfFieldName(String deobfName, Type obfType) { - FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); - if (fieldMapping != null) { - return fieldMapping.getObfName(); - } - return null; - } - - public String getDeobfFieldName(String obfName, Type obfType) { - FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); - if (fieldMapping != null) { - return fieldMapping.getDeobfName(); - } - return null; - } - - private String getFieldKey(String name, Type type) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null!"); - } - if (type == null) { - throw new IllegalArgumentException("type cannot be null!"); - } - return name + ":" + type; - } - - public void setFieldName(String obfName, Type obfType, String deobfName) { - assert (deobfName != null); - FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); - if (fieldMapping == null) { - fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); - boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; - assert (obfWasAdded); - } else { - boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; - assert (wasRemoved); - } - fieldMapping.setDeobfName(deobfName); - if (deobfName != null) { - boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; - assert (wasAdded); - } - this.isDirty = true; - } - - public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { - assert(newObfName != null); - FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); - assert(fieldMapping != null); - fieldMapping.setObfName(newObfName); - fieldMapping.setObfType(newObfType); - boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; - assert(obfWasAdded); - this.isDirty = true; - } - - //// METHODS //////// - - public Iterable methods() { - assert (methodsByObf.size() >= methodsByDeobf.size()); - return methodsByObf.values(); - } - - public boolean containsObfMethod(String obfName, Signature obfSignature) { - return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); - } - - public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { - return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); - } - - public void addMethodMapping(MethodMapping methodMapping) { - String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); - if (methodsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + obfFullName + "." + obfKey); - } - boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; - assert (wasAdded); - if (methodMapping.getDeobfName() != null) { - String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); - if (methodsByDeobf.containsKey(deobfKey)) { - throw new Error("Already have mapping for " + deobfName + "." + deobfKey); - } - boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; - assert (deobfWasAdded); - } - this.isDirty = true; - assert (methodsByObf.size() >= methodsByDeobf.size()); - } - - public void removeMethodMapping(MethodMapping methodMapping) { - boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; - assert (obfWasRemoved); - if (methodMapping.getDeobfName() != null) { - boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; - assert (deobfWasRemoved); - } - this.isDirty = true; - } - - public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { - return methodsByObf.get(getMethodKey(obfName, obfSignature)); - } - - public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { - return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); - } - - private String getMethodKey(String name, Signature signature) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null!"); - } - if (signature == null) { - throw new IllegalArgumentException("signature cannot be null!"); - } - return name + signature; - } - - public void setMethodName(String obfName, Signature obfSignature, String deobfName) { - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); - if (methodMapping == null) { - methodMapping = createMethodMapping(obfName, obfSignature); - } else if (methodMapping.getDeobfName() != null) { - boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; - assert (wasRemoved); - } - methodMapping.setDeobfName(deobfName); - if (deobfName != null) { - boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; - assert (wasAdded); - } - this.isDirty = true; - } - - public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { - assert(newObfName != null); - MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); - assert(methodMapping != null); - methodMapping.setObfName(newObfName); - methodMapping.setObfSignature(newObfSignature); - boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; - assert(obfWasAdded); - this.isDirty = true; - } - - //// ARGUMENTS //////// - - public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { - assert (argumentName != null); - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); - if (methodMapping == null) { - methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); - } - methodMapping.setArgumentName(argumentIndex, argumentName); - this.isDirty = true; - } - - public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { - methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); - this.isDirty = true; - } - - private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { - MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); - boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; - assert (wasAdded); - this.isDirty = true; - return methodMapping; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append(obfFullName); - buf.append(" <-> "); - buf.append(deobfName); - buf.append("\n"); - buf.append("Fields:\n"); - for (FieldMapping fieldMapping : fields()) { - buf.append("\t"); - buf.append(fieldMapping.getObfName()); - buf.append(" <-> "); - buf.append(fieldMapping.getDeobfName()); - buf.append("\n"); - } - buf.append("Methods:\n"); - for (MethodMapping methodMapping : methodsByObf.values()) { - buf.append(methodMapping.toString()); - buf.append("\n"); - } - buf.append("Inner Classes:\n"); - for (ClassMapping classMapping : innerClassesByObfSimple.values()) { - buf.append("\t"); - buf.append(classMapping.getObfSimpleName()); - buf.append(" <-> "); - buf.append(classMapping.getDeobfName()); - buf.append("\n"); - } - return buf.toString(); - } - - @Override - public int compareTo(ClassMapping other) { - // sort by a, b, c, ... aa, ab, etc - if (obfFullName.length() != other.obfFullName.length()) { - return obfFullName.length() - other.obfFullName.length(); - } - return obfFullName.compareTo(other.obfFullName); - } - - public boolean renameObfClass(String oldObfClassName, String newObfClassName) { - - // rename inner classes - for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { - if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { - boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; - assert (wasRemoved); - boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; - assert (wasAdded); - } - } - - // rename field types - for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { - String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); - if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { - boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; - assert (wasRemoved); - boolean wasAdded = fieldsByObf - .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; - assert (wasAdded); - } - } - - // rename method signatures - for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { - String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); - if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { - boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; - assert (wasRemoved); - boolean wasAdded = methodsByObf - .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; - assert (wasAdded); - } - } - - if (obfFullName.equals(oldObfClassName)) { - // rename this class - obfFullName = newObfClassName; - return true; - } - this.isDirty = true; - return false; - } - - public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); - return methodMapping != null && methodMapping.containsArgument(name); - } - - public static boolean isSimpleClassName(String name) { - return name.indexOf('/') < 0 && name.indexOf('$') < 0; - } - - public ClassEntry getObfEntry() { - return new ClassEntry(obfFullName); - } - - public boolean isDirty() - { - return isDirty; - } - - public void resetDirty() - { - this.isDirty = false; - } - - public void setModifier(Mappings.EntryModifier modifier) - { - if (this.modifier != modifier) - this.isDirty = true; - this.modifier = modifier; - } - - public Mappings.EntryModifier getModifier() - { - return modifier; - } - - public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { - FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), - k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); - - if (fieldMapping.getModifier() != modifier) - { - fieldMapping.setModifier(modifier); - this.isDirty = true; - } - } - - public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { - MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), - k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); - - if (methodMapping.getModifier() != modifier) - { - methodMapping.setModifier(modifier); - this.isDirty = true; - } - } + private String obfFullName; + private String obfSimpleName; + private String deobfName; + private String previousDeobfName; + private Map innerClassesByObfSimple; + private Map innerClassesByObfFull; + private Map innerClassesByDeobf; + private Map fieldsByObf; + private Map fieldsByDeobf; + private Map methodsByObf; + private Map methodsByDeobf; + private boolean isDirty; + private Mappings.EntryModifier modifier; + + public ClassMapping(String obfFullName) { + this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); + } + + public ClassMapping(String obfFullName, String deobfName) { + this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); + } + + public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) { + this.obfFullName = obfFullName; + ClassEntry classEntry = new ClassEntry(obfFullName); + obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); + previousDeobfName = null; + this.deobfName = NameValidator.validateClassName(deobfName, false); + innerClassesByObfSimple = Maps.newHashMap(); + innerClassesByObfFull = Maps.newHashMap(); + innerClassesByDeobf = Maps.newHashMap(); + fieldsByObf = Maps.newHashMap(); + fieldsByDeobf = Maps.newHashMap(); + methodsByObf = Maps.newHashMap(); + methodsByDeobf = Maps.newHashMap(); + isDirty = true; + this.modifier = modifier; + } + + public static boolean isSimpleClassName(String name) { + return name.indexOf('/') < 0 && name.indexOf('$') < 0; + } + + public String getObfFullName() { + return obfFullName; + } + + public String getObfSimpleName() { + return obfSimpleName; + } + + public String getPreviousDeobfName() { + return previousDeobfName; + } + + public String getDeobfName() { + return deobfName; + } + + //// INNER CLASSES //////// + + public void setDeobfName(String val) { + previousDeobfName = deobfName; + deobfName = NameValidator.validateClassName(val, false); + this.isDirty = true; + } + + public Iterable innerClasses() { + assert (innerClassesByObfSimple.size() >= innerClassesByDeobf.size()); + return innerClassesByObfSimple.values(); + } + + public void addInnerClassMapping(ClassMapping classMapping) throws MappingConflict { + // FIXME: dirty hack, that can get into issues, but it's a temp fix! + if (this.innerClassesByObfFull.containsKey(classMapping.getObfSimpleName())) { + throw new MappingConflict("classes", classMapping.getObfSimpleName(), this.innerClassesByObfSimple.get(classMapping.getObfSimpleName()).getObfSimpleName()); + } + innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); + innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping); + + if (classMapping.getDeobfName() != null) { + if (this.innerClassesByDeobf.containsKey(classMapping.getDeobfName())) { + throw new MappingConflict("classes", classMapping.getDeobfName(), this.innerClassesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); + } + innerClassesByDeobf.put(classMapping.getDeobfName(), classMapping); + } + this.isDirty = true; + } + + public void removeInnerClassMapping(ClassMapping classMapping) { + innerClassesByObfFull.remove(classMapping.getObfFullName()); + boolean obfWasRemoved = innerClassesByObfSimple.remove(classMapping.getObfSimpleName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + this.isDirty = true; + } + + public ClassMapping getOrCreateInnerClass(ClassEntry obfInnerClass) { + ClassMapping classMapping = innerClassesByObfSimple.get(obfInnerClass.getInnermostClassName()); + if (classMapping == null) { + classMapping = new ClassMapping(obfInnerClass.getName()); + innerClassesByObfFull.put(classMapping.getObfFullName(), classMapping); + boolean wasAdded = innerClassesByObfSimple.put(classMapping.getObfSimpleName(), classMapping) == null; + assert (wasAdded); + this.isDirty = true; + } + return classMapping; + } + + public ClassMapping getInnerClassByObfSimple(String obfSimpleName) { + assert (isSimpleClassName(obfSimpleName)); + return innerClassesByObfSimple.get(obfSimpleName); + } + + public ClassMapping getInnerClassByDeobf(String deobfName) { + assert (isSimpleClassName(deobfName)); + return innerClassesByDeobf.get(deobfName); + } + + public ClassMapping getInnerClassByDeobfThenObfSimple(String name) { + ClassMapping classMapping = getInnerClassByDeobf(name); + if (classMapping == null) { + classMapping = getInnerClassByObfSimple(name); + } + return classMapping; + } + + public String getDeobfInnerClassName(String obfSimpleName) { + assert (isSimpleClassName(obfSimpleName)); + ClassMapping classMapping = innerClassesByObfSimple.get(obfSimpleName); + if (classMapping != null) { + return classMapping.getDeobfName(); + } + return null; + } + + public void setInnerClassName(ClassEntry obfInnerClass, String deobfName) { + ClassMapping classMapping = getOrCreateInnerClass(obfInnerClass); + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + assert (isSimpleClassName(deobfName)); + boolean wasAdded = innerClassesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + this.isDirty = true; + } + + public boolean hasInnerClassByObfSimple(String obfSimpleName) { + return innerClassesByObfSimple.containsKey(obfSimpleName); + } + + //// FIELDS //////// + + public boolean hasInnerClassByDeobf(String deobfName) { + return innerClassesByDeobf.containsKey(deobfName); + } + + public Iterable fields() { + assert (fieldsByObf.size() == fieldsByDeobf.size()); + return fieldsByObf.values(); + } + + public boolean containsObfField(String obfName, Type obfType) { + return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); + } + + public boolean containsDeobfField(String deobfName, Type deobfType) { + return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); + } + + public void addFieldMapping(FieldMapping fieldMapping) { + String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + if (fieldsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + obfFullName + "." + obfKey); + } + if (fieldMapping.getDeobfName() != null) { + String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); + if (fieldsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + deobfName + "." + deobfKey); + } + boolean deobfWasAdded = fieldsByDeobf.put(deobfKey, fieldMapping) == null; + assert (deobfWasAdded); + } + boolean obfWasAdded = fieldsByObf.put(obfKey, fieldMapping) == null; + assert (obfWasAdded); + this.isDirty = true; + } + + public void removeFieldMapping(FieldMapping fieldMapping) { + boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; + assert (obfWasRemoved); + if (fieldMapping.getDeobfName() != null) { + boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; + assert (deobfWasRemoved); + } + this.isDirty = true; + } + + public FieldMapping getFieldByObf(String obfName, Type obfType) { + return fieldsByObf.get(getFieldKey(obfName, obfType)); + } + + public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { + return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); + } + + public String getObfFieldName(String deobfName, Type obfType) { + FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); + if (fieldMapping != null) { + return fieldMapping.getObfName(); + } + return null; + } + + public String getDeobfFieldName(String obfName, Type obfType) { + FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); + if (fieldMapping != null) { + return fieldMapping.getDeobfName(); + } + return null; + } + + private String getFieldKey(String name, Type type) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); + } + if (type == null) { + throw new IllegalArgumentException("type cannot be null!"); + } + return name + ":" + type; + } + + public void setFieldName(String obfName, Type obfType, String deobfName) { + assert (deobfName != null); + FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); + if (fieldMapping == null) { + fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); + boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; + assert (obfWasAdded); + } else { + boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; + assert (wasRemoved); + } + fieldMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; + assert (wasAdded); + } + this.isDirty = true; + } + + //// METHODS //////// + + public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { + assert (newObfName != null); + FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); + assert (fieldMapping != null); + fieldMapping.setObfName(newObfName); + fieldMapping.setObfType(newObfType); + boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; + assert (obfWasAdded); + this.isDirty = true; + } + + public Iterable methods() { + assert (methodsByObf.size() >= methodsByDeobf.size()); + return methodsByObf.values(); + } + + public boolean containsObfMethod(String obfName, Signature obfSignature) { + return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); + } + + public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { + return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); + } + + public void addMethodMapping(MethodMapping methodMapping) { + String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (methodsByObf.containsKey(obfKey)) { + throw new Error("Already have mapping for " + obfFullName + "." + obfKey); + } + boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; + assert (wasAdded); + if (methodMapping.getDeobfName() != null) { + String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); + if (methodsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + deobfName + "." + deobfKey); + } + boolean deobfWasAdded = methodsByDeobf.put(deobfKey, methodMapping) == null; + assert (deobfWasAdded); + } + this.isDirty = true; + assert (methodsByObf.size() >= methodsByDeobf.size()); + } + + public void removeMethodMapping(MethodMapping methodMapping) { + boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; + assert (obfWasRemoved); + if (methodMapping.getDeobfName() != null) { + boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (deobfWasRemoved); + } + this.isDirty = true; + } + + public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { + return methodsByObf.get(getMethodKey(obfName, obfSignature)); + } + + public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { + return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); + } + + private String getMethodKey(String name, Signature signature) { + if (name == null) { + throw new IllegalArgumentException("name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("signature cannot be null!"); + } + return name + signature; + } + + public void setMethodName(String obfName, Signature obfSignature, String deobfName) { + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfName, obfSignature); + } else if (methodMapping.getDeobfName() != null) { + boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + assert (wasRemoved); + } + methodMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; + assert (wasAdded); + } + this.isDirty = true; + } + + //// ARGUMENTS //////// + + public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { + assert (newObfName != null); + MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); + assert (methodMapping != null); + methodMapping.setObfName(newObfName); + methodMapping.setObfSignature(newObfSignature); + boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; + assert (obfWasAdded); + this.isDirty = true; + } + + public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { + assert (argumentName != null); + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); + if (methodMapping == null) { + methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); + } + methodMapping.setArgumentName(argumentIndex, argumentName); + this.isDirty = true; + } + + public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { + methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); + this.isDirty = true; + } + + private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { + MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); + boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; + assert (wasAdded); + this.isDirty = true; + return methodMapping; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(obfFullName); + buf.append(" <-> "); + buf.append(deobfName); + buf.append("\n"); + buf.append("Fields:\n"); + for (FieldMapping fieldMapping : fields()) { + buf.append("\t"); + buf.append(fieldMapping.getObfName()); + buf.append(" <-> "); + buf.append(fieldMapping.getDeobfName()); + buf.append("\n"); + } + buf.append("Methods:\n"); + for (MethodMapping methodMapping : methodsByObf.values()) { + buf.append(methodMapping); + buf.append("\n"); + } + buf.append("Inner Classes:\n"); + for (ClassMapping classMapping : innerClassesByObfSimple.values()) { + buf.append("\t"); + buf.append(classMapping.getObfSimpleName()); + buf.append(" <-> "); + buf.append(classMapping.getDeobfName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(ClassMapping other) { + // sort by a, b, c, ... aa, ab, etc + if (obfFullName.length() != other.obfFullName.length()) { + return obfFullName.length() - other.obfFullName.length(); + } + return obfFullName.compareTo(other.obfFullName); + } + + public boolean renameObfClass(String oldObfClassName, String newObfClassName) { + + // rename inner classes + for (ClassMapping innerClassMapping : new ArrayList<>(innerClassesByObfSimple.values())) { + if (innerClassMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = innerClassesByObfSimple.remove(oldObfClassName) != null; + assert (wasRemoved); + boolean wasAdded = innerClassesByObfSimple.put(newObfClassName, innerClassMapping) == null; + assert (wasAdded); + } + } + + // rename field types + for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { + String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; + assert (wasRemoved); + boolean wasAdded = fieldsByObf + .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; + assert (wasAdded); + } + } + + // rename method signatures + for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { + String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; + assert (wasRemoved); + boolean wasAdded = methodsByObf + .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; + assert (wasAdded); + } + } + + if (obfFullName.equals(oldObfClassName)) { + // rename this class + obfFullName = newObfClassName; + return true; + } + this.isDirty = true; + return false; + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); + return methodMapping != null && methodMapping.containsArgument(name); + } + + public ClassEntry getObfEntry() { + return new ClassEntry(obfFullName); + } + + public boolean isDirty() { + return isDirty; + } + + public void resetDirty() { + this.isDirty = false; + } + + public Mappings.EntryModifier getModifier() { + return modifier; + } + + public void setModifier(Mappings.EntryModifier modifier) { + if (this.modifier != modifier) + this.isDirty = true; + this.modifier = modifier; + } + + public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { + FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), + k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); + + if (fieldMapping.getModifier() != modifier) { + fieldMapping.setModifier(modifier); + this.isDirty = true; + } + } + + public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { + MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), + k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); + + if (methodMapping.getModifier() != modifier) { + methodMapping.setModifier(modifier); + this.isDirty = true; + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java index dc833bb..801c410 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java @@ -8,8 +8,9 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; public interface ClassNameReplacer { - String replace(String className); + String replace(String className); } diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java index 4c79820..20e5113 100644 --- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java @@ -8,97 +8,98 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.utils.Utils; public class ConstructorEntry implements BehaviorEntry { - private ClassEntry classEntry; - private Signature signature; - - public ConstructorEntry(ClassEntry classEntry) { - this(classEntry, null); - } - - public ConstructorEntry(ClassEntry classEntry, Signature signature) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - - this.classEntry = classEntry; - this.signature = signature; - } - - public ConstructorEntry(ConstructorEntry other, String newClassName) { - this.classEntry = new ClassEntry(newClassName); - this.signature = other.signature; - } - - @Override - public ClassEntry getClassEntry() { - return this.classEntry; - } - - @Override - public String getName() { - if (isStatic()) { - return ""; - } - return ""; - } - - public boolean isStatic() { - return this.signature == null; - } - - @Override - public Signature getSignature() { - return this.signature; - } - - @Override - public String getClassName() { - return this.classEntry.getName(); - } - - @Override - public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { - return new ConstructorEntry(this, classEntry.getName()); - } - - @Override - public int hashCode() { - if (isStatic()) { - return Utils.combineHashesOrdered(this.classEntry); - } else { - return Utils.combineHashesOrdered(this.classEntry, this.signature); - } - } - - @Override - public boolean equals(Object other) { - return other instanceof ConstructorEntry && equals((ConstructorEntry) other); - } - - public boolean equals(ConstructorEntry other) { - if (isStatic() != other.isStatic()) { - return false; - } - - if (isStatic()) { - return this.classEntry.equals(other.classEntry); - } else { - return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); - } - } - - @Override - public String toString() { - if (isStatic()) { - return this.classEntry.getName() + "." + getName(); - } else { - return this.classEntry.getName() + "." + getName() + this.signature; - } - } + private ClassEntry classEntry; + private Signature signature; + + public ConstructorEntry(ClassEntry classEntry) { + this(classEntry, null); + } + + public ConstructorEntry(ClassEntry classEntry, Signature signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + + this.classEntry = classEntry; + this.signature = signature; + } + + public ConstructorEntry(ConstructorEntry other, String newClassName) { + this.classEntry = new ClassEntry(newClassName); + this.signature = other.signature; + } + + @Override + public ClassEntry getClassEntry() { + return this.classEntry; + } + + @Override + public String getName() { + if (isStatic()) { + return ""; + } + return ""; + } + + public boolean isStatic() { + return this.signature == null; + } + + @Override + public Signature getSignature() { + return this.signature; + } + + @Override + public String getClassName() { + return this.classEntry.getName(); + } + + @Override + public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { + return new ConstructorEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + if (isStatic()) { + return Utils.combineHashesOrdered(this.classEntry); + } else { + return Utils.combineHashesOrdered(this.classEntry, this.signature); + } + } + + @Override + public boolean equals(Object other) { + return other instanceof ConstructorEntry && equals((ConstructorEntry) other); + } + + public boolean equals(ConstructorEntry other) { + if (isStatic() != other.isStatic()) { + return false; + } + + if (isStatic()) { + return this.classEntry.equals(other.classEntry); + } else { + return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); + } + } + + @Override + public String toString() { + if (isStatic()) { + return this.classEntry.getName() + "." + getName(); + } else { + return this.classEntry.getName() + "." + getName() + this.signature; + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java index 95747d5..c79510b 100644 --- a/src/main/java/cuchaz/enigma/mapping/Entry.java +++ b/src/main/java/cuchaz/enigma/mapping/Entry.java @@ -8,14 +8,15 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; public interface Entry { - String getName(); + String getName(); - String getClassName(); + String getClassName(); - ClassEntry getClassEntry(); + ClassEntry getClassEntry(); - Entry cloneToNewClass(ClassEntry classEntry); + Entry cloneToNewClass(ClassEntry classEntry); } diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java index ce4b948..993bb64 100644 --- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.analysis.JarIndex; @@ -20,112 +21,112 @@ import javassist.expr.NewExpr; public class EntryFactory { - public static ClassEntry getClassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getName())); - } - - public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { - ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); - return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); - } - - private static ClassEntry getObfClassEntry(ClassMapping classMapping) { - return new ClassEntry(classMapping.getObfFullName()); - } - - public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { - return new ClassEntry(classMapping.getDeobfName()); - } - - public static ClassEntry getSuperclassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); - } - - public static FieldEntry getFieldEntry(CtField field) { - return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); - } - - public static FieldEntry getFieldEntry(FieldAccess call) { - return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); - } - - public static FieldEntry getFieldEntry(String className, String name, String type) { - return new FieldEntry(new ClassEntry(className), name, new Type(type)); - } - - public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { - return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); - } - - public static MethodEntry getMethodEntry(CtMethod method) { - return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); - } - - public static MethodEntry getMethodEntry(MethodCall call) { - return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); - } - - public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { - if (constructor.isClassInitializer()) { - return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); - } else { - return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); - } - } - - public static ConstructorEntry getConstructorEntry(ConstructorCall call) { - return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); - } - - public static ConstructorEntry getConstructorEntry(NewExpr call) { - return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); - } - - public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { - if (behavior instanceof CtMethod) { - return getMethodEntry((CtMethod) behavior); - } else if (behavior instanceof CtConstructor) { - return getConstructorEntry((CtConstructor) behavior); - } - throw new Error("behavior is neither Method nor Constructor!"); - } - - public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { - return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); - } - - public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { - return getBehaviorEntry(new ClassEntry(className), behaviorName); - } - - public static BehaviorEntry getBehaviorEntry(String className) { - return new ConstructorEntry(new ClassEntry(className)); - } - - public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { - switch (behaviorName) { - case "": - return new ConstructorEntry(classEntry, behaviorSignature); - case "": - return new ConstructorEntry(classEntry); - default: - return new MethodEntry(classEntry, behaviorName, behaviorSignature); - } - } - - public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { - if(behaviorName.equals("")) { - return new ConstructorEntry(classEntry); - } else { - throw new IllegalArgumentException("Only class initializers don't have signatures"); - } - } - - public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { - return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); - } - - public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { - return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); - } + public static ClassEntry getClassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getName())); + } + + public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { + ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); + return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); + } + + private static ClassEntry getObfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getObfFullName()); + } + + public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { + return new ClassEntry(classMapping.getDeobfName()); + } + + public static ClassEntry getSuperclassEntry(CtClass c) { + return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); + } + + public static FieldEntry getFieldEntry(CtField field) { + return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); + } + + public static FieldEntry getFieldEntry(FieldAccess call) { + return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); + } + + public static FieldEntry getFieldEntry(String className, String name, String type) { + return new FieldEntry(new ClassEntry(className), name, new Type(type)); + } + + public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { + return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); + } + + public static MethodEntry getMethodEntry(CtMethod method) { + return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); + } + + public static MethodEntry getMethodEntry(MethodCall call) { + return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); + } + + public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { + if (constructor.isClassInitializer()) { + return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); + } else { + return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); + } + } + + public static ConstructorEntry getConstructorEntry(ConstructorCall call) { + return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); + } + + public static ConstructorEntry getConstructorEntry(NewExpr call) { + return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); + } + + public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { + if (behavior instanceof CtMethod) { + return getMethodEntry((CtMethod) behavior); + } else if (behavior instanceof CtConstructor) { + return getConstructorEntry((CtConstructor) behavior); + } + throw new Error("behavior is neither Method nor Constructor!"); + } + + public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { + return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); + } + + public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { + return getBehaviorEntry(new ClassEntry(className), behaviorName); + } + + public static BehaviorEntry getBehaviorEntry(String className) { + return new ConstructorEntry(new ClassEntry(className)); + } + + public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { + switch (behaviorName) { + case "": + return new ConstructorEntry(classEntry, behaviorSignature); + case "": + return new ConstructorEntry(classEntry); + default: + return new MethodEntry(classEntry, behaviorName, behaviorSignature); + } + } + + public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { + if (behaviorName.equals("")) { + return new ConstructorEntry(classEntry); + } else { + throw new IllegalArgumentException("Only class initializers don't have signatures"); + } + } + + public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { + return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); + } + + public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { + return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java index 9980e8e..0f1f506 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java @@ -8,79 +8,80 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.utils.Utils; public class FieldEntry implements Entry { - private ClassEntry classEntry; - private String name; - private Type type; - - // NOTE: this argument order is important for the MethodReader/MethodWriter - public FieldEntry(ClassEntry classEntry, String name, Type type) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - if (name == null) { - throw new IllegalArgumentException("Field name cannot be null!"); - } - if (type == null) { - throw new IllegalArgumentException("Field type cannot be null!"); - } - - this.classEntry = classEntry; - this.name = name; - this.type = type; - } - - public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { - this.classEntry = newClassEntry; - this.name = other.name; - this.type = other.type; - } - - @Override - public ClassEntry getClassEntry() { - return this.classEntry; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getClassName() { - return this.classEntry.getName(); - } - - public Type getType() { - return this.type; - } - - @Override - public FieldEntry cloneToNewClass(ClassEntry classEntry) { - return new FieldEntry(this, classEntry); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); - } - - @Override - public boolean equals(Object other) { - return other instanceof FieldEntry && equals((FieldEntry) other); - } - - public boolean equals(FieldEntry other) { - return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); - } - - @Override - public String toString() { - return this.classEntry.getName() + "." + this.name + ":" + this.type; - } + private ClassEntry classEntry; + private String name; + private Type type; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public FieldEntry(ClassEntry classEntry, String name, Type type) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Field name cannot be null!"); + } + if (type == null) { + throw new IllegalArgumentException("Field type cannot be null!"); + } + + this.classEntry = classEntry; + this.name = name; + this.type = type; + } + + public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { + this.classEntry = newClassEntry; + this.name = other.name; + this.type = other.type; + } + + @Override + public ClassEntry getClassEntry() { + return this.classEntry; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getClassName() { + return this.classEntry.getName(); + } + + public Type getType() { + return this.type; + } + + @Override + public FieldEntry cloneToNewClass(ClassEntry classEntry) { + return new FieldEntry(this, classEntry); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); + } + + @Override + public boolean equals(Object other) { + return other instanceof FieldEntry && equals((FieldEntry) other); + } + + public boolean equals(FieldEntry other) { + return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); + } + + @Override + public String toString() { + return this.classEntry.getName() + "." + this.name + ":" + this.type; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java index 22ba307..cd761b4 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java @@ -8,103 +8,99 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.throwables.IllegalNameException; public class FieldMapping implements Comparable, MemberMapping { - private String obfName; - private String deobfName; - private Type obfType; - private Mappings.EntryModifier modifier; - - public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { - this.obfName = obfName; - this.deobfName = NameValidator.validateFieldName(deobfName); - this.obfType = obfType; - this.modifier = modifier; - } - - public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { - this.obfName = other.obfName; - this.deobfName = other.deobfName; - this.modifier = other.modifier; - this.obfType = new Type(other.obfType, obfClassNameReplacer); - } - - @Override - public FieldEntry getObfEntry(ClassEntry classEntry) { - return new FieldEntry(classEntry, this.obfName, this.obfType); - } - - @Override - public String getObfName() { - return this.obfName; - } - - public String getDeobfName() { - return this.deobfName; - } - - public void setDeobfName(String val) { - this.deobfName = NameValidator.validateFieldName(val); - } - - public void setObfName(String name) { - try - { - NameValidator.validateMethodName(name); - } catch (IllegalNameException ex) - { - // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues - if (this.deobfName == null) - { - System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); - setDeobfName(name + "_auto_deob"); - } - } - this.obfName = name; - } - - public Type getObfType() { - return this.obfType; - } - - public void setObfType(Type val) { - this.obfType = val; - } - - public void setModifier(Mappings.EntryModifier modifier) - { - this.modifier = modifier; - } - - public Mappings.EntryModifier getModifier() - { - return modifier; - } - - @Override - public int compareTo(FieldMapping other) { - return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); - } - - public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { - // rename obf classes in the type - Type newType = new Type(this.obfType, className -> - { - if (className.equals(oldObfClassName)) { - return newObfClassName; - } - return null; - }); - - if (!newType.equals(this.obfType)) { - this.obfType = newType; - return true; - } - return false; - } + private String obfName; + private String deobfName; + private Type obfType; + private Mappings.EntryModifier modifier; + + public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { + this.obfName = obfName; + this.deobfName = NameValidator.validateFieldName(deobfName); + this.obfType = obfType; + this.modifier = modifier; + } + + public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { + this.obfName = other.obfName; + this.deobfName = other.deobfName; + this.modifier = other.modifier; + this.obfType = new Type(other.obfType, obfClassNameReplacer); + } + + @Override + public FieldEntry getObfEntry(ClassEntry classEntry) { + return new FieldEntry(classEntry, this.obfName, this.obfType); + } + + @Override + public String getObfName() { + return this.obfName; + } + + public void setObfName(String name) { + try { + NameValidator.validateMethodName(name); + } catch (IllegalNameException ex) { + // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues + if (this.deobfName == null) { + System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); + setDeobfName(name + "_auto_deob"); + } + } + this.obfName = name; + } + + public String getDeobfName() { + return this.deobfName; + } + + public void setDeobfName(String val) { + this.deobfName = NameValidator.validateFieldName(val); + } + + public Type getObfType() { + return this.obfType; + } + + public void setObfType(Type val) { + this.obfType = val; + } + + public Mappings.EntryModifier getModifier() { + return modifier; + } + + public void setModifier(Mappings.EntryModifier modifier) { + this.modifier = modifier; + } + + @Override + public int compareTo(FieldMapping other) { + return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); + } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + // rename obf classes in the type + Type newType = new Type(this.obfType, className -> + { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return null; + }); + + if (!newType.equals(this.obfType)) { + this.obfType = newType; + return true; + } + return false; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java index 8bbaaaf..2bb5e3f 100644 --- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java @@ -7,98 +7,96 @@ import cuchaz.enigma.utils.Utils; * Created by Thog * 19/10/2016 */ -public class LocalVariableEntry implements Entry -{ - - protected final BehaviorEntry behaviorEntry; - protected final String name; - protected final Type type; - protected final int index; - - public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { - if (behaviorEntry == null) { - throw new IllegalArgumentException("Behavior cannot be null!"); - } - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative!"); - } - if (name == null) { - throw new IllegalArgumentException("Variable name cannot be null!"); - } - if (type == null) { - throw new IllegalArgumentException("Variable type cannot be null!"); - } - - this.behaviorEntry = behaviorEntry; - this.name = name; - this.type = type; - this.index = index; - } - - - public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { - this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); - this.name = other.name; - this.type = other.type; - this.index = other.index; - } - - public BehaviorEntry getBehaviorEntry() { - return this.behaviorEntry; - } - - public Type getType() { - return type; - } - - public int getIndex() { - return index; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public ClassEntry getClassEntry() { - return this.behaviorEntry.getClassEntry(); - } - - @Override - public String getClassName() { - return this.behaviorEntry.getClassName(); - } - - @Override - public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { - return new LocalVariableEntry(this, classEntry); - } - - public String getMethodName() { - return this.behaviorEntry.getName(); - } - - public Signature getMethodSignature() { - return this.behaviorEntry.getSignature(); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); - } - - @Override - public boolean equals(Object other) { - return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); - } - - public boolean equals(LocalVariableEntry other) { - return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; - } - - @Override - public String toString() { - return this.behaviorEntry.toString() + "(" + this.index + ":" + this.name + ":" + this.type + ")"; - } +public class LocalVariableEntry implements Entry { + + protected final BehaviorEntry behaviorEntry; + protected final String name; + protected final Type type; + protected final int index; + + public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { + if (behaviorEntry == null) { + throw new IllegalArgumentException("Behavior cannot be null!"); + } + if (index < 0) { + throw new IllegalArgumentException("Index must be non-negative!"); + } + if (name == null) { + throw new IllegalArgumentException("Variable name cannot be null!"); + } + if (type == null) { + throw new IllegalArgumentException("Variable type cannot be null!"); + } + + this.behaviorEntry = behaviorEntry; + this.name = name; + this.type = type; + this.index = index; + } + + public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { + this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); + this.name = other.name; + this.type = other.type; + this.index = other.index; + } + + public BehaviorEntry getBehaviorEntry() { + return this.behaviorEntry; + } + + public Type getType() { + return type; + } + + public int getIndex() { + return index; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public ClassEntry getClassEntry() { + return this.behaviorEntry.getClassEntry(); + } + + @Override + public String getClassName() { + return this.behaviorEntry.getClassName(); + } + + @Override + public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { + return new LocalVariableEntry(this, classEntry); + } + + public String getMethodName() { + return this.behaviorEntry.getName(); + } + + public Signature getMethodSignature() { + return this.behaviorEntry.getSignature(); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); + } + + @Override + public boolean equals(Object other) { + return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other); + } + + public boolean equals(LocalVariableEntry other) { + return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; + } + + @Override + public String toString() { + return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")"; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java index d493dcf..33dd3c5 100644 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java @@ -8,246 +8,237 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.throwables.MappingConflict; import java.io.File; import java.io.IOException; import java.util.*; -import com.google.common.collect.Sets; -import cuchaz.enigma.analysis.TranslationIndex; -import cuchaz.enigma.throwables.MappingConflict; - public class Mappings { - protected Map classesByObf; - protected Map classesByDeobf; - private final FormatType originMapping; - private Mappings previousState; - - public Mappings() - { - this(FormatType.ENIGMA_DIRECTORY); - } - - public Mappings(FormatType originMapping) { - this.originMapping = originMapping; - this.classesByObf = Maps.newHashMap(); - this.classesByDeobf = Maps.newHashMap(); - this.previousState = null; - } - - public Collection classes() { - assert (this.classesByObf.size() >= this.classesByDeobf.size()); - return this.classesByObf.values(); - } - - public void addClassMapping(ClassMapping classMapping) throws MappingConflict { - if (this.classesByObf.containsKey(classMapping.getObfFullName())) { - throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); - } - this.classesByObf.put(classMapping.getObfFullName(), classMapping); - - if (classMapping.getDeobfName() != null) { - if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { - throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); - } - this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); - } - } - - public void removeClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; - assert (obfWasRemoved); - if (classMapping.getDeobfName() != null) { - boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (deobfWasRemoved); - } - } - - - public ClassMapping getClassByObf(ClassEntry entry) { - return getClassByObf(entry.getName()); - } - - public ClassMapping getClassByObf(String obfName) { - return this.classesByObf.get(obfName); - } - - public ClassMapping getClassByDeobf(ClassEntry entry) { - return getClassByDeobf(entry.getName()); - } - - public ClassMapping getClassByDeobf(String deobfName) { - return this.classesByDeobf.get(deobfName); - } - - public void setClassDeobfName(ClassMapping classMapping, String deobfName) { - if (classMapping.getDeobfName() != null) { - boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; - assert (wasRemoved); - } - classMapping.setDeobfName(deobfName); - if (deobfName != null) { - boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; - assert (wasAdded); - } - } - - public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { - switch (direction) { - case Deobfuscating: - - return new Translator(direction, this.classesByObf, index); - - case Obfuscating: - - // fill in the missing deobf class entries with obf entries - Map classes = Maps.newHashMap(); - for (ClassMapping classMapping : classes()) { - if (classMapping.getDeobfName() != null) { - classes.put(classMapping.getDeobfName(), classMapping); - } else { - classes.put(classMapping.getObfFullName(), classMapping); - } - } - - // translate the translation index - // NOTE: this isn't actually recursive - TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); - - return new Translator(direction, classes, deobfIndex); - - default: - throw new Error("Invalid translation direction!"); - } - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - for (ClassMapping classMapping : this.classesByObf.values()) { - buf.append(classMapping.toString()); - buf.append("\n"); - } - return buf.toString(); - } - - public void renameObfClass(String oldObfName, String newObfName) { - new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { - boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; - assert (wasRemoved); - boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; - assert (wasAdded); - }); - } - - public Set getAllObfClassNames() { - final Set classNames = Sets.newHashSet(); - for (ClassMapping classMapping : classes()) { - - // add the class name - classNames.add(classMapping.getObfFullName()); - - // add classes from method signatures - for (MethodMapping methodMapping : classMapping.methods()) { - for (Type type : methodMapping.getObfSignature().types()) { - if (type.hasClass()) { - classNames.add(type.getClassEntry().getClassName()); - } - } - } - } - return classNames; - } - - public boolean containsDeobfClass(String deobfName) { - return this.classesByDeobf.containsKey(deobfName); - } - - public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { - ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); - return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); - } - - public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { - ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); - if (classMapping != null) - for (FieldMapping fieldMapping : classMapping.fields()) - if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) - return true; - - return false; - } - - public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { - ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); - return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); - } - - public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { - ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); - return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); - } - - public List getClassMappingChain(ClassEntry obfClass) { - List mappingChain = Lists.newArrayList(); - ClassMapping classMapping = null; - for (ClassEntry obfClassEntry : obfClass.getClassChain()) { - if (mappingChain.isEmpty()) { - classMapping = this.classesByObf.get(obfClassEntry.getName()); - } else if (classMapping != null) { - classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); - } - mappingChain.add(classMapping); - } - return mappingChain; - } - - public FormatType getOriginMappingFormat() - { - return originMapping; - } - - public void savePreviousState() - { - this.previousState = new Mappings(this.originMapping); - this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); - this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); - classesByDeobf.values().forEach(ClassMapping::resetDirty); - classesByObf.values().forEach(ClassMapping::resetDirty); - } - - public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException - { - new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); - this.savePreviousState(); - } - - public void saveSRGMappings(File file) throws IOException - { - new MappingsSRGWriter().write(file, this); - } - - public Mappings getPreviousState() { - return previousState; - } - - public enum FormatType - { - ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE - } - - public enum EntryModifier - { - UNCHANGED, PUBLIC, PROTECTED, PRIVATE; - - public String getFormattedName() - { - return " ACC:" + super.toString(); - } - } + private final FormatType originMapping; + protected Map classesByObf; + protected Map classesByDeobf; + private Mappings previousState; + + public Mappings() { + this(FormatType.ENIGMA_DIRECTORY); + } + + public Mappings(FormatType originMapping) { + this.originMapping = originMapping; + this.classesByObf = Maps.newHashMap(); + this.classesByDeobf = Maps.newHashMap(); + this.previousState = null; + } + + public Collection classes() { + assert (this.classesByObf.size() >= this.classesByDeobf.size()); + return this.classesByObf.values(); + } + + public void addClassMapping(ClassMapping classMapping) throws MappingConflict { + if (this.classesByObf.containsKey(classMapping.getObfFullName())) { + throw new MappingConflict("class", classMapping.getObfFullName(), this.classesByObf.get(classMapping.getObfFullName()).getObfFullName()); + } + this.classesByObf.put(classMapping.getObfFullName(), classMapping); + + if (classMapping.getDeobfName() != null) { + if (this.classesByDeobf.containsKey(classMapping.getDeobfName())) { + throw new MappingConflict("class", classMapping.getDeobfName(), this.classesByDeobf.get(classMapping.getDeobfName()).getDeobfName()); + } + this.classesByDeobf.put(classMapping.getDeobfName(), classMapping); + } + } + + public void removeClassMapping(ClassMapping classMapping) { + boolean obfWasRemoved = this.classesByObf.remove(classMapping.getObfFullName()) != null; + assert (obfWasRemoved); + if (classMapping.getDeobfName() != null) { + boolean deobfWasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (deobfWasRemoved); + } + } + + public ClassMapping getClassByObf(ClassEntry entry) { + return getClassByObf(entry.getName()); + } + + public ClassMapping getClassByObf(String obfName) { + return this.classesByObf.get(obfName); + } + + public ClassMapping getClassByDeobf(ClassEntry entry) { + return getClassByDeobf(entry.getName()); + } + + public ClassMapping getClassByDeobf(String deobfName) { + return this.classesByDeobf.get(deobfName); + } + + public void setClassDeobfName(ClassMapping classMapping, String deobfName) { + if (classMapping.getDeobfName() != null) { + boolean wasRemoved = this.classesByDeobf.remove(classMapping.getDeobfName()) != null; + assert (wasRemoved); + } + classMapping.setDeobfName(deobfName); + if (deobfName != null) { + boolean wasAdded = this.classesByDeobf.put(deobfName, classMapping) == null; + assert (wasAdded); + } + } + + public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { + switch (direction) { + case Deobfuscating: + + return new Translator(direction, this.classesByObf, index); + + case Obfuscating: + + // fill in the missing deobf class entries with obf entries + Map classes = Maps.newHashMap(); + for (ClassMapping classMapping : classes()) { + if (classMapping.getDeobfName() != null) { + classes.put(classMapping.getDeobfName(), classMapping); + } else { + classes.put(classMapping.getObfFullName(), classMapping); + } + } + + // translate the translation index + // NOTE: this isn't actually recursive + TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); + + return new Translator(direction, classes, deobfIndex); + + default: + throw new Error("Invalid translation direction!"); + } + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + for (ClassMapping classMapping : this.classesByObf.values()) { + buf.append(classMapping); + buf.append("\n"); + } + return buf.toString(); + } + + public void renameObfClass(String oldObfName, String newObfName) { + new ArrayList<>(classes()).stream().filter(classMapping -> classMapping.renameObfClass(oldObfName, newObfName)).forEach(classMapping -> { + boolean wasRemoved = this.classesByObf.remove(oldObfName) != null; + assert (wasRemoved); + boolean wasAdded = this.classesByObf.put(newObfName, classMapping) == null; + assert (wasAdded); + }); + } + + public Set getAllObfClassNames() { + final Set classNames = Sets.newHashSet(); + for (ClassMapping classMapping : classes()) { + + // add the class name + classNames.add(classMapping.getObfFullName()); + + // add classes from method signatures + for (MethodMapping methodMapping : classMapping.methods()) { + for (Type type : methodMapping.getObfSignature().types()) { + if (type.hasClass()) { + classNames.add(type.getClassEntry().getClassName()); + } + } + } + } + return classNames; + } + + public boolean containsDeobfClass(String deobfName) { + return this.classesByDeobf.containsKey(deobfName); + } + + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { + ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); + return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); + } + + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { + ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); + if (classMapping != null) + for (FieldMapping fieldMapping : classMapping.fields()) + if (deobfName.equals(fieldMapping.getDeobfName()) || deobfName.equals(fieldMapping.getObfName())) + return true; + + return false; + } + + public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { + ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); + return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); + } + + public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { + ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); + return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); + } + + public List getClassMappingChain(ClassEntry obfClass) { + List mappingChain = Lists.newArrayList(); + ClassMapping classMapping = null; + for (ClassEntry obfClassEntry : obfClass.getClassChain()) { + if (mappingChain.isEmpty()) { + classMapping = this.classesByObf.get(obfClassEntry.getName()); + } else if (classMapping != null) { + classMapping = classMapping.getInnerClassByObfSimple(obfClassEntry.getInnermostClassName()); + } + mappingChain.add(classMapping); + } + return mappingChain; + } + + public FormatType getOriginMappingFormat() { + return originMapping; + } + + public void savePreviousState() { + this.previousState = new Mappings(this.originMapping); + this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); + this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); + classesByDeobf.values().forEach(ClassMapping::resetDirty); + classesByObf.values().forEach(ClassMapping::resetDirty); + } + + public void saveEnigmaMappings(File file, boolean isDirectoryFormat) throws IOException { + new MappingsEnigmaWriter().write(file, this, isDirectoryFormat); + this.savePreviousState(); + } + + public void saveSRGMappings(File file) throws IOException { + new MappingsSRGWriter().write(file, this); + } + + public Mappings getPreviousState() { + return previousState; + } + + public enum FormatType { + ENIGMA_FILE, ENIGMA_DIRECTORY, SRG_FILE + } + + public enum EntryModifier { + UNCHANGED, PUBLIC, PROTECTED, PRIVATE; + + public String getFormattedName() { + return " ACC:" + super.toString(); + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java index 6cf279d..172641b 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java @@ -8,91 +8,90 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; import com.google.common.collect.Maps; - -import java.util.Map; - import cuchaz.enigma.analysis.JarIndex; +import java.util.Map; public class MappingsChecker { - private JarIndex index; - private Map droppedClassMappings; - private Map droppedInnerClassMappings; - private Map droppedFieldMappings; - private Map droppedMethodMappings; - - public MappingsChecker(JarIndex index) { - this.index = index; - this.droppedClassMappings = Maps.newHashMap(); - this.droppedInnerClassMappings = Maps.newHashMap(); - this.droppedFieldMappings = Maps.newHashMap(); - this.droppedMethodMappings = Maps.newHashMap(); - } - - public Map getDroppedClassMappings() { - return this.droppedClassMappings; - } - - public Map getDroppedInnerClassMappings() { - return this.droppedInnerClassMappings; - } - - public Map getDroppedFieldMappings() { - return this.droppedFieldMappings; - } - - public Map getDroppedMethodMappings() { - return this.droppedMethodMappings; - } - - public void dropBrokenMappings(Mappings mappings) { - for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { - if (!checkClassMapping(classMapping)) { - mappings.removeClassMapping(classMapping); - this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); - } - } - } - - private boolean checkClassMapping(ClassMapping classMapping) { - - // check the class - ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); - if (!this.index.getObfClassEntries().contains(classEntry)) { - return false; - } - - // check the fields - for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { - FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); - if (!this.index.containsObfField(obfFieldEntry)) { - classMapping.removeFieldMapping(fieldMapping); - this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); - } - } - - // check methods - for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); - if (!this.index.containsObfBehavior(obfBehaviorEntry)) { - classMapping.removeMethodMapping(methodMapping); - this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); - } - } - - // check inner classes - for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { - if (!checkClassMapping(innerClassMapping)) { - classMapping.removeInnerClassMapping(innerClassMapping); - this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); - } - } - - return true; - } + private JarIndex index; + private Map droppedClassMappings; + private Map droppedInnerClassMappings; + private Map droppedFieldMappings; + private Map droppedMethodMappings; + + public MappingsChecker(JarIndex index) { + this.index = index; + this.droppedClassMappings = Maps.newHashMap(); + this.droppedInnerClassMappings = Maps.newHashMap(); + this.droppedFieldMappings = Maps.newHashMap(); + this.droppedMethodMappings = Maps.newHashMap(); + } + + public Map getDroppedClassMappings() { + return this.droppedClassMappings; + } + + public Map getDroppedInnerClassMappings() { + return this.droppedInnerClassMappings; + } + + public Map getDroppedFieldMappings() { + return this.droppedFieldMappings; + } + + public Map getDroppedMethodMappings() { + return this.droppedMethodMappings; + } + + public void dropBrokenMappings(Mappings mappings) { + for (ClassMapping classMapping : Lists.newArrayList(mappings.classes())) { + if (!checkClassMapping(classMapping)) { + mappings.removeClassMapping(classMapping); + this.droppedClassMappings.put(EntryFactory.getObfClassEntry(this.index, classMapping), classMapping); + } + } + } + + private boolean checkClassMapping(ClassMapping classMapping) { + + // check the class + ClassEntry classEntry = EntryFactory.getObfClassEntry(this.index, classMapping); + if (!this.index.getObfClassEntries().contains(classEntry)) { + return false; + } + + // check the fields + for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { + FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); + if (!this.index.containsObfField(obfFieldEntry)) { + classMapping.removeFieldMapping(fieldMapping); + this.droppedFieldMappings.put(obfFieldEntry, fieldMapping); + } + } + + // check methods + for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { + BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); + if (!this.index.containsObfBehavior(obfBehaviorEntry)) { + classMapping.removeMethodMapping(methodMapping); + this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); + } + } + + // check inner classes + for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { + if (!checkClassMapping(innerClassMapping)) { + classMapping.removeInnerClassMapping(innerClassMapping); + this.droppedInnerClassMappings.put(EntryFactory.getObfClassEntry(this.index, innerClassMapping), innerClassMapping); + } + } + + return true; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java index cdfed72..a0d4313 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java @@ -8,178 +8,170 @@ import cuchaz.enigma.throwables.MappingParseException; import java.io.*; import java.util.Deque; -public class MappingsEnigmaReader -{ - - public Mappings read(File file) throws IOException, MappingParseException { - Mappings mappings; - - // Multiple file - if (file.isDirectory()) - { - mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); - readDirectory(mappings, file); - } - else - { - mappings = new Mappings(); - readFile(mappings, file); - } - return mappings; - } - - public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) - readFile(mappings, file); - else if (file.isDirectory()) - readDirectory(mappings, file.getAbsoluteFile()); - } - mappings.savePreviousState(); - } - else - throw new IOException("Cannot access directory" + directory.getAbsolutePath()); - } - - public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { - - BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); - Deque mappingStack = Queues.newArrayDeque(); - - int lineNumber = 0; - String line; - while ((line = in.readLine()) != null) { - lineNumber++; - - // strip comments - int commentPos = line.indexOf('#'); - if (commentPos >= 0) { - line = line.substring(0, commentPos); - } - - // skip blank lines - if (line.trim().length() <= 0) { - continue; - } - - // get the indent of this line - int indent = 0; - for (int i = 0; i < line.length(); i++) { - if (line.charAt(i) != '\t') { - break; - } - indent++; - } - - // handle stack pops - while (indent < mappingStack.size()) { - mappingStack.pop(); - } - - String[] parts = line.trim().split("\\s"); - try { - // read the first token - String token = parts[0]; - - if (token.equalsIgnoreCase("CLASS")) { - ClassMapping classMapping; - if (indent <= 0) { - // outer class - classMapping = readClass(parts, false); - mappings.addClassMapping(classMapping); - } else { - - // inner class - if (!(mappingStack.peek() instanceof ClassMapping)) { - throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); - } - - classMapping = readClass(parts, true); - ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); - } - mappingStack.push(classMapping); - } else if (token.equalsIgnoreCase("FIELD")) { - if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { - throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); - } - ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); - } else if (token.equalsIgnoreCase("METHOD")) { - if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { - throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); - } - MethodMapping methodMapping = readMethod(parts); - ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); - mappingStack.push(methodMapping); - } else if (token.equalsIgnoreCase("ARG")) { - if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { - throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!"); - } - ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); - } - } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { - throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); - } catch (MappingConflict e) { - throw new MappingParseException(file, lineNumber, e.getMessage()); - } - } - in.close(); - return mappings; - } - - private ArgumentMapping readArgument(String[] parts) { - return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); - } - - private ClassMapping readClass(String[] parts, boolean makeSimple) { - if (parts.length == 2) { - return new ClassMapping(parts[1]); - } else if (parts.length == 3) { - boolean access = parts[2].startsWith("ACC:"); - ClassMapping mapping; - if (access) - mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); - else - mapping = new ClassMapping(parts[1], parts[2]); - - return mapping; - } else if (parts.length == 4) - return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); - return null; - } - - /* TEMP */ - protected FieldMapping readField(String[] parts) { - FieldMapping mapping = null; - if (parts.length == 4) - { - boolean access = parts[3].startsWith("ACC:"); - if (access) - mapping = new FieldMapping(parts[1], new Type(parts[2]), null, - Mappings.EntryModifier.valueOf(parts[3].substring(4))); - else - mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); - } - else if (parts.length == 5) - mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); - return mapping; - } - - private MethodMapping readMethod(String[] parts) { - MethodMapping mapping = null; - if (parts.length == 3) - mapping = new MethodMapping(parts[1], new Signature(parts[2])); - else if (parts.length == 4){ - boolean access = parts[3].startsWith("ACC:"); - if (access) - mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); - else - mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); - } - else if (parts.length == 5) - mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], - Mappings.EntryModifier.valueOf(parts[4].substring(4))); - return mapping; - } +public class MappingsEnigmaReader { + + public Mappings read(File file) throws IOException, MappingParseException { + Mappings mappings; + + // Multiple file + if (file.isDirectory()) { + mappings = new Mappings(Mappings.FormatType.ENIGMA_DIRECTORY); + readDirectory(mappings, file); + } else { + mappings = new Mappings(); + readFile(mappings, file); + } + return mappings; + } + + public void readDirectory(Mappings mappings, File directory) throws IOException, MappingParseException { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile() && !file.getName().startsWith(".") && file.getName().endsWith(".mapping")) + readFile(mappings, file); + else if (file.isDirectory()) + readDirectory(mappings, file.getAbsoluteFile()); + } + mappings.savePreviousState(); + } else + throw new IOException("Cannot access directory" + directory.getAbsolutePath()); + } + + public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { + + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); + Deque mappingStack = Queues.newArrayDeque(); + + int lineNumber = 0; + String line; + while ((line = in.readLine()) != null) { + lineNumber++; + + // strip comments + int commentPos = line.indexOf('#'); + if (commentPos >= 0) { + line = line.substring(0, commentPos); + } + + // skip blank lines + if (line.trim().length() <= 0) { + continue; + } + + // get the indent of this line + int indent = 0; + for (int i = 0; i < line.length(); i++) { + if (line.charAt(i) != '\t') { + break; + } + indent++; + } + + // handle stack pops + while (indent < mappingStack.size()) { + mappingStack.pop(); + } + + String[] parts = line.trim().split("\\s"); + try { + // read the first token + String token = parts[0]; + + if (token.equalsIgnoreCase("CLASS")) { + ClassMapping classMapping; + if (indent <= 0) { + // outer class + classMapping = readClass(parts, false); + mappings.addClassMapping(classMapping); + } else { + + // inner class + if (!(mappingStack.peek() instanceof ClassMapping)) { + throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); + } + + classMapping = readClass(parts, true); + ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); + } + mappingStack.push(classMapping); + } else if (token.equalsIgnoreCase("FIELD")) { + if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { + throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); + } + ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); + } else if (token.equalsIgnoreCase("METHOD")) { + if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { + throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); + } + MethodMapping methodMapping = readMethod(parts); + ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); + mappingStack.push(methodMapping); + } else if (token.equalsIgnoreCase("ARG")) { + if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) { + throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!"); + } + ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); + } + } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) { + throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line); + } catch (MappingConflict e) { + throw new MappingParseException(file, lineNumber, e.getMessage()); + } + } + in.close(); + return mappings; + } + + private ArgumentMapping readArgument(String[] parts) { + return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); + } + + private ClassMapping readClass(String[] parts, boolean makeSimple) { + if (parts.length == 2) { + return new ClassMapping(parts[1]); + } else if (parts.length == 3) { + boolean access = parts[2].startsWith("ACC:"); + ClassMapping mapping; + if (access) + mapping = new ClassMapping(parts[1], null, Mappings.EntryModifier.valueOf(parts[2].substring(4))); + else + mapping = new ClassMapping(parts[1], parts[2]); + + return mapping; + } else if (parts.length == 4) + return new ClassMapping(parts[1], parts[2], Mappings.EntryModifier.valueOf(parts[3].substring(4))); + return null; + } + + /* TEMP */ + protected FieldMapping readField(String[] parts) { + FieldMapping mapping = null; + if (parts.length == 4) { + boolean access = parts[3].startsWith("ACC:"); + if (access) + mapping = new FieldMapping(parts[1], new Type(parts[2]), null, + Mappings.EntryModifier.valueOf(parts[3].substring(4))); + else + mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); + } else if (parts.length == 5) + mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); + return mapping; + } + + private MethodMapping readMethod(String[] parts) { + MethodMapping mapping = null; + if (parts.length == 3) + mapping = new MethodMapping(parts[1], new Signature(parts[2])); + else if (parts.length == 4) { + boolean access = parts[3].startsWith("ACC:"); + if (access) + mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); + else + mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); + } else if (parts.length == 5) + mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], + Mappings.EntryModifier.valueOf(parts[4].substring(4))); + return mapping; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java index 6c57200..ba1b258 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java @@ -4,10 +4,11 @@ * are made available under the terms of the GNU Lesser General Public * License v3.0 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html - * + * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.base.Charsets; @@ -18,15 +19,13 @@ import java.util.Collections; import java.util.List; public class MappingsEnigmaWriter { - + public void write(File out, Mappings mappings, boolean isDirectoryFormat) throws IOException { - if (!isDirectoryFormat) - { + if (!isDirectoryFormat) { PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), Charsets.UTF_8)); write(outputWriter, mappings); outputWriter.close(); - } - else + } else writeAsDirectory(out, mappings); } @@ -42,8 +41,7 @@ public class MappingsEnigmaWriter { File result; if (classMapping.getDeobfName() == null) result = obFile; - else - { + else { // Make sure that old version of the file doesn't exist if (obFile.exists()) obFile.delete(); @@ -59,19 +57,16 @@ public class MappingsEnigmaWriter { } // Remove dropped mappings - if (mappings.getPreviousState() != null) - { + if (mappings.getPreviousState() != null) { List droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); List classMappings = new ArrayList<>(mappings.classes()); droppedClassMappings.removeAll(classMappings); - for (ClassMapping classMapping : droppedClassMappings) - { + for (ClassMapping classMapping : droppedClassMappings) { File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); File result; if (classMapping.getDeobfName() == null) result = obFile; - else - { + else { // Make sure that old version of the file doesn't exist if (obFile.exists()) obFile.delete(); @@ -86,18 +81,15 @@ public class MappingsEnigmaWriter { private void deletePreviousClassMapping(File target, ClassMapping classMapping) { File prevFile = null; // Deob rename - if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) - { + if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) { prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); } // Deob to ob rename - else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) - { + else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) { prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping"); } // Ob to Deob rename - else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) - { + else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) { prevFile = new File(target, classMapping.getObfFullName() + ".mapping"); } @@ -110,50 +102,56 @@ public class MappingsEnigmaWriter { write(out, classMapping, 0); } } - + private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { if (classMapping.getDeobfName() == null) { - out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); + out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), + classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); } else { - out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); + out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), + classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); } - + for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { write(out, innerClassMapping, depth + 1); } - + for (FieldMapping fieldMapping : sorted(classMapping.fields())) { write(out, fieldMapping, depth + 1); } - + for (MethodMapping methodMapping : sorted(classMapping.methods())) { write(out, methodMapping, depth + 1); } } - + private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { if (fieldMapping.getDeobfName() == null) - out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); + out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), + fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); else - out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); + out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), + fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); } - + private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { if (methodMapping.getDeobfName() == null) { - out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" :methodMapping.getModifier().getFormattedName()); + out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), + methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); } else { - out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); + out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), + methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); } - + for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { write(out, argumentMapping, depth + 1); } } - + private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); } - + private > List sorted(Iterable classes) { List out = new ArrayList<>(); for (T t : classes) { @@ -162,7 +160,7 @@ public class MappingsEnigmaWriter { Collections.sort(out); return out; } - + private String getIndent(int depth) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < depth; i++) { diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java index e1428ea..7126d2b 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java @@ -8,8 +8,14 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; +import com.google.common.collect.Lists; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.throwables.IllegalNameException; +import cuchaz.enigma.throwables.MappingConflict; + import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -17,324 +23,315 @@ import java.util.List; import java.util.Set; import java.util.zip.GZIPOutputStream; -import com.google.common.collect.Lists; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.throwables.IllegalNameException; -import cuchaz.enigma.throwables.MappingConflict; - public class MappingsRenamer { - private JarIndex index; - private Mappings mappings; - - public MappingsRenamer(JarIndex index, Mappings mappings) { - this.index = index; - this.mappings = mappings; - } - - public void setMappings(Mappings mappings) - { - this.mappings = mappings; - } - - public void setClassName(ClassEntry obf, String deobfName) { - - deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); - - List mappingChain = getOrCreateClassMappingChain(obf); - if (mappingChain.size() == 1) { - - if (deobfName != null) { - // make sure we don't rename to an existing obf or deobf class - if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { - throw new IllegalNameException(deobfName, "There is already a class with that name"); - } - } - - ClassMapping classMapping = mappingChain.get(0); - mappings.setClassDeobfName(classMapping, deobfName); - - } else { - - ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); - - if (deobfName != null) { - // make sure we don't rename to an existing obf or deobf inner class - if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { - throw new IllegalNameException(deobfName, "There is already a class with that name"); - } - } - - outerClassMapping.setInnerClassName(obf, deobfName); - } - } - - public void removeClassMapping(ClassEntry obf) { - setClassName(obf, null); - } - - public void markClassAsDeobfuscated(ClassEntry obf) { - String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); - List mappingChain = getOrCreateClassMappingChain(obf); - if (mappingChain.size() == 1) { - ClassMapping classMapping = mappingChain.get(0); - mappings.setClassDeobfName(classMapping, deobfName); - } else { - ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); - outerClassMapping.setInnerClassName(obf, deobfName); - } - } - - public void setFieldName(FieldEntry obf, String deobfName) { - deobfName = NameValidator.validateFieldName(deobfName); - FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); - ClassEntry definedClass = null; - if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) - definedClass = obf.getClassEntry(); - else { - for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { - if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { - definedClass = ancestorEntry; - break; - } - } - } - - if (definedClass != null) { - String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); - if (className == null) - className = definedClass.getClassName(); - throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); - } - - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); - } - - public void removeFieldMapping(FieldEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); - } - - public void markFieldAsDeobfuscated(FieldEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); - } - - private void validateMethodTreeName(MethodEntry entry, String deobfName) { - MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); - - // TODO: Verify if I don't break things - ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); - if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) - || index.containsObfBehavior(targetEntry)) { - String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); - if (deobfClassName == null) { - deobfClassName = entry.getClassName(); - } - throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); - } - - for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { - validateMethodTreeName(entry.cloneToNewClass(child), deobfName); - } - } - - public void setMethodTreeName(MethodEntry obf, String deobfName) { - Set implementations = index.getRelatedMethodImplementations(obf); - - deobfName = NameValidator.validateMethodName(deobfName); - for (MethodEntry entry : implementations) { - validateMethodTreeName(entry, deobfName); - } - - for (MethodEntry entry : implementations) { - setMethodName(entry, deobfName); - } - } - - public void setMethodName(MethodEntry obf, String deobfName) { - deobfName = NameValidator.validateMethodName(deobfName); - MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - - // TODO: Verify if I don't break things - if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) - || index.containsObfBehavior(targetEntry)) { - String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); - if (deobfClassName == null) { - deobfClassName = obf.getClassName(); - } - throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); - } - - classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); - } - - public void removeMethodTreeMapping(MethodEntry obf) { - index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); - } - - public void removeMethodMapping(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setMethodName(obf.getName(), obf.getSignature(), null); - } - - public void markMethodTreeAsDeobfuscated(MethodEntry obf) { - index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); - } - - public void markMethodAsDeobfuscated(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); - } - - public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { - if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { - setArgumentName(obf, deobfName); - return; - } - - MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); - - Set implementations = index.getRelatedMethodImplementations(obfMethod); - for (MethodEntry entry : implementations) { - ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); - if (classMapping != null) { - MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); - // NOTE: don't need to check arguments for name collisions with names determined by Procyon - // TODO: Verify if I don't break things - if (mapping != null) { - for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { - if (argumentMapping.getIndex() != obf.getIndex()) { - if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) - || argumentMapping.getName().equals(deobfName)) { - throw new IllegalNameException(deobfName, "There is already an argument with that name"); - } - } - } - } - } - } - - for (MethodEntry entry : implementations) { - setArgumentName(new ArgumentEntry(obf, entry), deobfName); - } - } - - public void setArgumentName(ArgumentEntry obf, String deobfName) { - deobfName = NameValidator.validateArgumentName(deobfName); - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); - // NOTE: don't need to check arguments for name collisions with names determined by Procyon - // TODO: Verify if I don't break things - if (mapping != null) { - for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { - if (argumentMapping.getIndex() != obf.getIndex()) { - if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) - || argumentMapping.getName().equals(deobfName)) { - throw new IllegalNameException(deobfName, "There is already an argument with that name"); - } - } - } - } - - classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); - } - - public void removeArgumentMapping(ArgumentEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); - } - - public void markArgumentAsDeobfuscated(ArgumentEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); - } - - public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { - classMapping.removeFieldMapping(fieldMapping); - ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); - if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { - if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { - targetClassMapping.addFieldMapping(fieldMapping); - return true; - } else { - System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); - } - } - return false; - } - - public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { - classMapping.removeMethodMapping(methodMapping); - ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); - if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { - if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { - targetClassMapping.addMethodMapping(methodMapping); - return true; - } else { - System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); - } - } - return false; - } - - public void write(OutputStream out) throws IOException { - // TEMP: just use the object output for now. We can find a more efficient storage format later - GZIPOutputStream gzipout = new GZIPOutputStream(out); - ObjectOutputStream oout = new ObjectOutputStream(gzipout); - oout.writeObject(this); - gzipout.finish(); - } - - private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { - List mappingChain = getOrCreateClassMappingChain(obfClassEntry); - return mappingChain.get(mappingChain.size() - 1); - } - - private List getOrCreateClassMappingChain(ClassEntry obfClassEntry) { - List classChain = obfClassEntry.getClassChain(); - List mappingChain = mappings.getClassMappingChain(obfClassEntry); - for (int i = 0; i < classChain.size(); i++) { - ClassEntry classEntry = classChain.get(i); - ClassMapping classMapping = mappingChain.get(i); - if (classMapping == null) { - - // create it - classMapping = new ClassMapping(classEntry.getName()); - mappingChain.set(i, classMapping); - - // add it to the right parent - try { - if (i == 0) { - mappings.addClassMapping(classMapping); - } else { - mappingChain.get(i - 1).addInnerClassMapping(classMapping); - } - } catch (MappingConflict mappingConflict) { - mappingConflict.printStackTrace(); - } - } - } - return mappingChain; - } - - public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) - { - ClassMapping classMapping = getOrCreateClassMapping(obEntry); - classMapping.setModifier(modifier); - } - - public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) - { - ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); - classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); - } - - public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) - { - ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); - classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); - } + private JarIndex index; + private Mappings mappings; + + public MappingsRenamer(JarIndex index, Mappings mappings) { + this.index = index; + this.mappings = mappings; + } + + public void setMappings(Mappings mappings) { + this.mappings = mappings; + } + + public void setClassName(ClassEntry obf, String deobfName) { + + deobfName = NameValidator.validateClassName(deobfName, !obf.isInnerClass()); + + List mappingChain = getOrCreateClassMappingChain(obf); + if (mappingChain.size() == 1) { + + if (deobfName != null) { + // make sure we don't rename to an existing obf or deobf class + if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } + } + + ClassMapping classMapping = mappingChain.get(0); + mappings.setClassDeobfName(classMapping, deobfName); + + } else { + + ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); + + if (deobfName != null) { + // make sure we don't rename to an existing obf or deobf inner class + if (outerClassMapping.hasInnerClassByDeobf(deobfName) || outerClassMapping.hasInnerClassByObfSimple(deobfName)) { + throw new IllegalNameException(deobfName, "There is already a class with that name"); + } + } + + outerClassMapping.setInnerClassName(obf, deobfName); + } + } + + public void removeClassMapping(ClassEntry obf) { + setClassName(obf, null); + } + + public void markClassAsDeobfuscated(ClassEntry obf) { + String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); + List mappingChain = getOrCreateClassMappingChain(obf); + if (mappingChain.size() == 1) { + ClassMapping classMapping = mappingChain.get(0); + mappings.setClassDeobfName(classMapping, deobfName); + } else { + ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); + outerClassMapping.setInnerClassName(obf, deobfName); + } + } + + public void setFieldName(FieldEntry obf, String deobfName) { + deobfName = NameValidator.validateFieldName(deobfName); + FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); + ClassEntry definedClass = null; + if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) + definedClass = obf.getClassEntry(); + else { + for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { + if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { + definedClass = ancestorEntry; + break; + } + } + } + + if (definedClass != null) { + String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); + if (className == null) + className = definedClass.getClassName(); + throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); + } + + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); + } + + public void removeFieldMapping(FieldEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); + } + + public void markFieldAsDeobfuscated(FieldEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); + } + + private void validateMethodTreeName(MethodEntry entry, String deobfName) { + MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); + + // TODO: Verify if I don't break things + ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); + if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) + || index.containsObfBehavior(targetEntry)) { + String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); + if (deobfClassName == null) { + deobfClassName = entry.getClassName(); + } + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + + for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { + validateMethodTreeName(entry.cloneToNewClass(child), deobfName); + } + } + + public void setMethodTreeName(MethodEntry obf, String deobfName) { + Set implementations = index.getRelatedMethodImplementations(obf); + + deobfName = NameValidator.validateMethodName(deobfName); + for (MethodEntry entry : implementations) { + validateMethodTreeName(entry, deobfName); + } + + for (MethodEntry entry : implementations) { + setMethodName(entry, deobfName); + } + } + + public void setMethodName(MethodEntry obf, String deobfName) { + deobfName = NameValidator.validateMethodName(deobfName); + MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + + // TODO: Verify if I don't break things + if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) + || index.containsObfBehavior(targetEntry)) { + String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); + if (deobfClassName == null) { + deobfClassName = obf.getClassName(); + } + throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); + } + + classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); + } + + public void removeMethodTreeMapping(MethodEntry obf) { + index.getRelatedMethodImplementations(obf).forEach(this::removeMethodMapping); + } + + public void removeMethodMapping(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), null); + } + + public void markMethodTreeAsDeobfuscated(MethodEntry obf) { + index.getRelatedMethodImplementations(obf).forEach(this::markMethodAsDeobfuscated); + } + + public void markMethodAsDeobfuscated(MethodEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); + } + + public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { + if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { + setArgumentName(obf, deobfName); + return; + } + + MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); + + Set implementations = index.getRelatedMethodImplementations(obfMethod); + for (MethodEntry entry : implementations) { + ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); + if (classMapping != null) { + MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + // TODO: Verify if I don't break things + if (mapping != null) { + for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { + if (argumentMapping.getIndex() != obf.getIndex()) { + if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) + || argumentMapping.getName().equals(deobfName)) { + throw new IllegalNameException(deobfName, "There is already an argument with that name"); + } + } + } + } + } + } + + for (MethodEntry entry : implementations) { + setArgumentName(new ArgumentEntry(obf, entry), deobfName); + } + } + + public void setArgumentName(ArgumentEntry obf, String deobfName) { + deobfName = NameValidator.validateArgumentName(deobfName); + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); + // NOTE: don't need to check arguments for name collisions with names determined by Procyon + // TODO: Verify if I don't break things + if (mapping != null) { + for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { + if (argumentMapping.getIndex() != obf.getIndex()) { + if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) + || argumentMapping.getName().equals(deobfName)) { + throw new IllegalNameException(deobfName, "There is already an argument with that name"); + } + } + } + } + + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); + } + + public void removeArgumentMapping(ArgumentEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); + } + + public void markArgumentAsDeobfuscated(ArgumentEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); + } + + public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { + classMapping.removeFieldMapping(fieldMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { + targetClassMapping.addFieldMapping(fieldMapping); + return true; + } else { + System.err.println("WARNING: deobf field was already there: " + obfClass + "." + fieldMapping.getDeobfName()); + } + } + return false; + } + + public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { + classMapping.removeMethodMapping(methodMapping); + ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); + if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { + if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { + targetClassMapping.addMethodMapping(methodMapping); + return true; + } else { + System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); + } + } + return false; + } + + public void write(OutputStream out) throws IOException { + // TEMP: just use the object output for now. We can find a more efficient storage format later + GZIPOutputStream gzipout = new GZIPOutputStream(out); + ObjectOutputStream oout = new ObjectOutputStream(gzipout); + oout.writeObject(this); + gzipout.finish(); + } + + private ClassMapping getOrCreateClassMapping(ClassEntry obfClassEntry) { + List mappingChain = getOrCreateClassMappingChain(obfClassEntry); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getOrCreateClassMappingChain(ClassEntry obfClassEntry) { + List classChain = obfClassEntry.getClassChain(); + List mappingChain = mappings.getClassMappingChain(obfClassEntry); + for (int i = 0; i < classChain.size(); i++) { + ClassEntry classEntry = classChain.get(i); + ClassMapping classMapping = mappingChain.get(i); + if (classMapping == null) { + + // create it + classMapping = new ClassMapping(classEntry.getName()); + mappingChain.set(i, classMapping); + + // add it to the right parent + try { + if (i == 0) { + mappings.addClassMapping(classMapping); + } else { + mappingChain.get(i - 1).addInnerClassMapping(classMapping); + } + } catch (MappingConflict mappingConflict) { + mappingConflict.printStackTrace(); + } + } + } + return mappingChain; + } + + public void setClassModifier(ClassEntry obEntry, Mappings.EntryModifier modifier) { + ClassMapping classMapping = getOrCreateClassMapping(obEntry); + classMapping.setModifier(modifier); + } + + public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { + ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); + classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); + } + + public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { + ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); + classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java index a3f0cc8..b0eb826 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java @@ -13,69 +13,67 @@ import java.util.List; */ public class MappingsSRGWriter { - public void write(File file, Mappings mappings) throws IOException { - if(file.exists()){ - file.delete(); - } - file.createNewFile(); + public void write(File file, Mappings mappings) throws IOException { + if (file.exists()) { + file.delete(); + } + file.createNewFile(); - TranslationIndex index = new TranslationIndex(); + TranslationIndex index = new TranslationIndex(); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); - List fieldMappings = new ArrayList<>(); - List methodMappings = new ArrayList<>(); - for (ClassMapping classMapping : sorted(mappings.classes())) { - if(classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null){ - continue; - } - writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); - writer.write(System.lineSeparator()); - for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { - if(innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null){ - continue; - } - String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); - String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); - writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); - writer.write(System.lineSeparator()); - for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { - fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); - } + PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); + List fieldMappings = new ArrayList<>(); + List methodMappings = new ArrayList<>(); + for (ClassMapping classMapping : sorted(mappings.classes())) { + if (classMapping.getDeobfName() == null || classMapping.getObfSimpleName() == null || classMapping.getDeobfName() == null) { + continue; + } + writer.write("CL: " + classMapping.getObfSimpleName() + " " + classMapping.getDeobfName()); + writer.write(System.lineSeparator()); + for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { + if (innerClassMapping.getDeobfName() == null || innerClassMapping.getObfSimpleName() == null || innerClassMapping.getDeobfName() == null) { + continue; + } + String innerClassName = classMapping.getObfSimpleName() + "$" + innerClassMapping.getObfSimpleName(); + String innerDeobfClassName = classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName(); + writer.write("CL: " + innerClassName + " " + classMapping.getDeobfName() + "$" + innerClassMapping.getDeobfName()); + writer.write(System.lineSeparator()); + for (FieldMapping fieldMapping : sorted(innerClassMapping.fields())) { + fieldMappings.add("FD: " + innerClassName + "/" + fieldMapping.getObfName() + " " + innerDeobfClassName + "/" + fieldMapping.getDeobfName()); + } - for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { - methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); - } - } + for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { + methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); + } + } - for (FieldMapping fieldMapping : sorted(classMapping.fields())) { - fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); - } + for (FieldMapping fieldMapping : sorted(classMapping.fields())) { + fieldMappings.add("FD: " + classMapping.getObfFullName() + "/" + fieldMapping.getObfName() + " " + classMapping.getDeobfName() + "/" + fieldMapping.getDeobfName()); + } - for (MethodMapping methodMapping : sorted(classMapping.methods())) { - methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature().toString() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); - } - } - for(String fd : fieldMappings){ - writer.write(fd); - writer.write(System.lineSeparator()); - } + for (MethodMapping methodMapping : sorted(classMapping.methods())) { + methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); + } + } + for (String fd : fieldMappings) { + writer.write(fd); + writer.write(System.lineSeparator()); + } - for(String md : methodMappings){ - writer.write(md); - writer.write(System.lineSeparator()); - } + for (String md : methodMappings) { + writer.write(md); + writer.write(System.lineSeparator()); + } + writer.close(); + } - writer.close(); - } - - - private > List sorted(Iterable classes) { - List out = new ArrayList<>(); - for (T t : classes) { - out.add(t); - } - Collections.sort(out); - return out; - } + private > List sorted(Iterable classes) { + List out = new ArrayList<>(); + for (T t : classes) { + out.add(t); + } + Collections.sort(out); + return out; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java index 90c096f..d4514d4 100644 --- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java @@ -8,11 +8,11 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ -package cuchaz.enigma.mapping; +package cuchaz.enigma.mapping; public interface MemberMapping { - T getObfEntry(ClassEntry classEntry); + T getObfEntry(ClassEntry classEntry); - String getObfName(); + String getObfName(); } diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java index 4d7ed8f..9c3058c 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java @@ -8,82 +8,83 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import cuchaz.enigma.utils.Utils; public class MethodEntry implements BehaviorEntry { - private ClassEntry classEntry; - private String name; - private Signature signature; - - public MethodEntry(ClassEntry classEntry, String name, Signature signature) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - if (name == null) { - throw new IllegalArgumentException("Method name cannot be null!"); - } - if (signature == null) { - throw new IllegalArgumentException("Method signature cannot be null!"); - } - if (name.startsWith("<")) { - throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); - } - - this.classEntry = classEntry; - this.name = name; - this.signature = signature; - } - - public MethodEntry(MethodEntry other, String newClassName) { - this.classEntry = new ClassEntry(newClassName); - this.name = other.name; - this.signature = other.signature; - } - - @Override - public ClassEntry getClassEntry() { - return this.classEntry; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public Signature getSignature() { - return this.signature; - } - - @Override - public String getClassName() { - return this.classEntry.getName(); - } - - @Override - public MethodEntry cloneToNewClass(ClassEntry classEntry) { - return new MethodEntry(this, classEntry.getName()); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); - } - - @Override - public boolean equals(Object other) { - return other instanceof MethodEntry && equals((MethodEntry) other); - } - - public boolean equals(MethodEntry other) { - return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); - } - - @Override - public String toString() { - return this.classEntry.getName() + "." + this.name + this.signature; - } + private ClassEntry classEntry; + private String name; + private Signature signature; + + public MethodEntry(ClassEntry classEntry, String name, Signature signature) { + if (classEntry == null) { + throw new IllegalArgumentException("Class cannot be null!"); + } + if (name == null) { + throw new IllegalArgumentException("Method name cannot be null!"); + } + if (signature == null) { + throw new IllegalArgumentException("Method signature cannot be null!"); + } + if (name.startsWith("<")) { + throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); + } + + this.classEntry = classEntry; + this.name = name; + this.signature = signature; + } + + public MethodEntry(MethodEntry other, String newClassName) { + this.classEntry = new ClassEntry(newClassName); + this.name = other.name; + this.signature = other.signature; + } + + @Override + public ClassEntry getClassEntry() { + return this.classEntry; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public Signature getSignature() { + return this.signature; + } + + @Override + public String getClassName() { + return this.classEntry.getName(); + } + + @Override + public MethodEntry cloneToNewClass(ClassEntry classEntry) { + return new MethodEntry(this, classEntry.getName()); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); + } + + @Override + public boolean equals(Object other) { + return other instanceof MethodEntry && equals((MethodEntry) other); + } + + public boolean equals(MethodEntry other) { + return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); + } + + @Override + public String toString() { + return this.classEntry.getName() + "." + this.name + this.signature; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java index e0aeea2..1524ce6 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java @@ -8,211 +8,206 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Maps; - -import java.util.Map; - import cuchaz.enigma.throwables.IllegalNameException; import cuchaz.enigma.throwables.MappingConflict; +import java.util.Map; + public class MethodMapping implements Comparable, MemberMapping { - private String obfName; - private String deobfName; - private Signature obfSignature; - private Map arguments; - private Mappings.EntryModifier modifier; - - public MethodMapping(String obfName, Signature obfSignature) { - this(obfName, obfSignature, null,Mappings.EntryModifier.UNCHANGED); - } - - public MethodMapping(String obfName, Signature obfSignature, String deobfName) { - this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); - } - - public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { - if (obfName == null) { - throw new IllegalArgumentException("obf name cannot be null!"); - } - if (obfSignature == null) { - throw new IllegalArgumentException("obf signature cannot be null!"); - } - this.obfName = obfName; - this.deobfName = NameValidator.validateMethodName(deobfName); - this.obfSignature = obfSignature; - this.arguments = Maps.newTreeMap(); - this.modifier = modifier; - } - - public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { - this.obfName = other.obfName; - this.deobfName = other.deobfName; - this.modifier = other.modifier; - this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); - this.arguments = Maps.newTreeMap(); - for (Map.Entry entry : other.arguments.entrySet()) { - this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); - } - } - - @Override - public String getObfName() { - return this.obfName; - } - - public String getDeobfName() { - return this.deobfName; - } - - public void setDeobfName(String val) { - this.deobfName = NameValidator.validateMethodName(val); - } - - public Signature getObfSignature() { - return this.obfSignature; - } - - public void setObfName(String name) { - try - { - NameValidator.validateMethodName(name); - } catch (IllegalNameException ex) - { - // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues - if (this.deobfName == null) - { - System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); - setDeobfName(name + "_auto_deob"); - } - } - this.obfName = name; - } - - public void setObfSignature(Signature val) { - this.obfSignature = val; - } - - public Iterable arguments() { - return this.arguments.values(); - } - - public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { - if (this.arguments.containsKey(argumentMapping.getIndex())) { - throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); - } - this.arguments.put(argumentMapping.getIndex(), argumentMapping); - } - - public String getObfArgumentName(int index) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping != null) { - return argumentMapping.getName(); - } - - return null; - } - - public String getDeobfArgumentName(int index) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping != null) { - return argumentMapping.getName(); - } - - return null; - } - - public void setArgumentName(int index, String name) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping == null) { - argumentMapping = new ArgumentMapping(index, name); - boolean wasAdded = this.arguments.put(index, argumentMapping) == null; - assert (wasAdded); - } else { - argumentMapping.setName(name); - } - } - - public void removeArgumentName(int index) { - boolean wasRemoved = this.arguments.remove(index) != null; - assert (wasRemoved); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("\t"); - buf.append(this.obfName); - buf.append(" <-> "); - buf.append(this.deobfName); - buf.append("\n"); - buf.append("\t"); - buf.append(this.obfSignature); - buf.append("\n"); - buf.append("\tArguments:\n"); - for (ArgumentMapping argumentMapping : this.arguments.values()) { - buf.append("\t\t"); - buf.append(argumentMapping.getIndex()); - buf.append(" -> "); - buf.append(argumentMapping.getName()); - buf.append("\n"); - } - return buf.toString(); - } - - @Override - public int compareTo(MethodMapping other) { - return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); - } - - public boolean containsArgument(String name) { - for (ArgumentMapping argumentMapping : this.arguments.values()) { - if (argumentMapping.getName().equals(name)) { - return true; - } - } - return false; - } - - public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { - // rename obf classes in the signature - Signature newSignature = new Signature(this.obfSignature, className -> - { - if (className.equals(oldObfClassName)) { - return newObfClassName; - } - return null; - }); - - if (!newSignature.equals(this.obfSignature)) { - this.obfSignature = newSignature; - return true; - } - return false; - } - - public boolean isConstructor() { - return this.obfName.startsWith("<"); - } - - @Override - public BehaviorEntry getObfEntry(ClassEntry classEntry) { - if (isConstructor()) { - return new ConstructorEntry(classEntry, this.obfSignature); - } else { - return new MethodEntry(classEntry, this.obfName, this.obfSignature); - } - } - - public Mappings.EntryModifier getModifier() - { - return modifier; - } - - public void setModifier(Mappings.EntryModifier modifier) - { - this.modifier = modifier; - } + private String obfName; + private String deobfName; + private Signature obfSignature; + private Map arguments; + private Mappings.EntryModifier modifier; + + public MethodMapping(String obfName, Signature obfSignature) { + this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); + } + + public MethodMapping(String obfName, Signature obfSignature, String deobfName) { + this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); + } + + public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { + if (obfName == null) { + throw new IllegalArgumentException("obf name cannot be null!"); + } + if (obfSignature == null) { + throw new IllegalArgumentException("obf signature cannot be null!"); + } + this.obfName = obfName; + this.deobfName = NameValidator.validateMethodName(deobfName); + this.obfSignature = obfSignature; + this.arguments = Maps.newTreeMap(); + this.modifier = modifier; + } + + public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { + this.obfName = other.obfName; + this.deobfName = other.deobfName; + this.modifier = other.modifier; + this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); + this.arguments = Maps.newTreeMap(); + for (Map.Entry entry : other.arguments.entrySet()) { + this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); + } + } + + @Override + public String getObfName() { + return this.obfName; + } + + public void setObfName(String name) { + try { + NameValidator.validateMethodName(name); + } catch (IllegalNameException ex) { + // Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues + if (this.deobfName == null) { + System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob")); + setDeobfName(name + "_auto_deob"); + } + } + this.obfName = name; + } + + public String getDeobfName() { + return this.deobfName; + } + + public void setDeobfName(String val) { + this.deobfName = NameValidator.validateMethodName(val); + } + + public Signature getObfSignature() { + return this.obfSignature; + } + + public void setObfSignature(Signature val) { + this.obfSignature = val; + } + + public Iterable arguments() { + return this.arguments.values(); + } + + public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { + if (this.arguments.containsKey(argumentMapping.getIndex())) { + throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); + } + this.arguments.put(argumentMapping.getIndex(), argumentMapping); + } + + public String getObfArgumentName(int index) { + ArgumentMapping argumentMapping = this.arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public String getDeobfArgumentName(int index) { + ArgumentMapping argumentMapping = this.arguments.get(index); + if (argumentMapping != null) { + return argumentMapping.getName(); + } + + return null; + } + + public void setArgumentName(int index, String name) { + ArgumentMapping argumentMapping = this.arguments.get(index); + if (argumentMapping == null) { + argumentMapping = new ArgumentMapping(index, name); + boolean wasAdded = this.arguments.put(index, argumentMapping) == null; + assert (wasAdded); + } else { + argumentMapping.setName(name); + } + } + + public void removeArgumentName(int index) { + boolean wasRemoved = this.arguments.remove(index) != null; + assert (wasRemoved); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("\t"); + buf.append(this.obfName); + buf.append(" <-> "); + buf.append(this.deobfName); + buf.append("\n"); + buf.append("\t"); + buf.append(this.obfSignature); + buf.append("\n"); + buf.append("\tArguments:\n"); + for (ArgumentMapping argumentMapping : this.arguments.values()) { + buf.append("\t\t"); + buf.append(argumentMapping.getIndex()); + buf.append(" -> "); + buf.append(argumentMapping.getName()); + buf.append("\n"); + } + return buf.toString(); + } + + @Override + public int compareTo(MethodMapping other) { + return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); + } + + public boolean containsArgument(String name) { + for (ArgumentMapping argumentMapping : this.arguments.values()) { + if (argumentMapping.getName().equals(name)) { + return true; + } + } + return false; + } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + // rename obf classes in the signature + Signature newSignature = new Signature(this.obfSignature, className -> + { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return null; + }); + + if (!newSignature.equals(this.obfSignature)) { + this.obfSignature = newSignature; + return true; + } + return false; + } + + public boolean isConstructor() { + return this.obfName.startsWith("<"); + } + + @Override + public BehaviorEntry getObfEntry(ClassEntry classEntry) { + if (isConstructor()) { + return new ConstructorEntry(classEntry, this.obfSignature); + } else { + return new MethodEntry(classEntry, this.obfName, this.obfSignature); + } + } + + public Mappings.EntryModifier getModifier() { + return modifier; + } + + public void setModifier(Mappings.EntryModifier modifier) { + this.modifier = modifier; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java index 6925b72..aa3dc4d 100644 --- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java +++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java @@ -8,61 +8,62 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; +import cuchaz.enigma.throwables.IllegalNameException; +import javassist.bytecode.Descriptor; + import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; -import cuchaz.enigma.throwables.IllegalNameException; -import javassist.bytecode.Descriptor; - public class NameValidator { - private static final Pattern IdentifierPattern; - private static final Pattern ClassPattern; - private static final List ReservedWords = Arrays.asList( - "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", - "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", - "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", - "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", - "long", "strictfp", "volatile", "const", "float", "native", "super", "while" - ); + private static final Pattern IdentifierPattern; + private static final Pattern ClassPattern; + private static final List ReservedWords = Arrays.asList( + "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", + "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", + "long", "strictfp", "volatile", "const", "float", "native", "super", "while" + ); - static { - String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; - IdentifierPattern = Pattern.compile(identifierRegex); - ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); - } + static { + String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*"; + IdentifierPattern = Pattern.compile(identifierRegex); + ClassPattern = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex)); + } - public static String validateClassName(String name, boolean packageRequired) { - if (name == null) { - return null; - } - if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { - throw new IllegalNameException(name, "This doesn't look like a legal class name"); - } - if (packageRequired && new ClassEntry(name).getPackageName() == null) { - throw new IllegalNameException(name, "Class must be in a package"); - } - return Descriptor.toJvmName(name); - } + public static String validateClassName(String name, boolean packageRequired) { + if (name == null) { + return null; + } + if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal class name"); + } + if (packageRequired && new ClassEntry(name).getPackageName() == null) { + throw new IllegalNameException(name, "Class must be in a package"); + } + return Descriptor.toJvmName(name); + } - public static String validateFieldName(String name) { - if (name == null) { - return null; - } - if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { - throw new IllegalNameException(name, "This doesn't look like a legal identifier"); - } - return name; - } + public static String validateFieldName(String name) { + if (name == null) { + return null; + } + if (!IdentifierPattern.matcher(name).matches() || ReservedWords.contains(name)) { + throw new IllegalNameException(name, "This doesn't look like a legal identifier"); + } + return name; + } - public static String validateMethodName(String name) { - return validateFieldName(name); - } + public static String validateMethodName(String name) { + return validateFieldName(name); + } - public static String validateArgumentName(String name) { - return validateFieldName(name); - } + public static String validateArgumentName(String name) { + return validateFieldName(name); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java index 51fed83..33d930d 100644 --- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.strobel.assembler.metadata.*; @@ -16,57 +17,51 @@ import java.util.List; public class ProcyonEntryFactory { - private static String getErasedSignature(MemberReference def) - { - if (!(def instanceof MethodReference)) - return def.getErasedSignature(); - MethodReference methodReference = (MethodReference) def; - StringBuilder builder = new StringBuilder("("); - for (ParameterDefinition param : methodReference.getParameters()) - { - TypeReference paramType = param.getParameterType(); - if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) - { - List interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces(); - interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); - } - else - builder.append(paramType.getErasedSignature()); - } - builder.append(")"); + private static String getErasedSignature(MemberReference def) { + if (!(def instanceof MethodReference)) + return def.getErasedSignature(); + MethodReference methodReference = (MethodReference) def; + StringBuilder builder = new StringBuilder("("); + for (ParameterDefinition param : methodReference.getParameters()) { + TypeReference paramType = param.getParameterType(); + if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) { + List interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces(); + interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); + } else + builder.append(paramType.getErasedSignature()); + } + builder.append(")"); - TypeReference returnType = methodReference.getReturnType(); - if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) - { - List interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces(); - interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); - } - else - builder.append(returnType.getErasedSignature()); - return builder.toString(); - } + TypeReference returnType = methodReference.getReturnType(); + if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) { + List interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces(); + interfaces.forEach((inter) -> builder.append(inter.getErasedSignature())); + } else + builder.append(returnType.getErasedSignature()); + return builder.toString(); + } - public static FieldEntry getFieldEntry(MemberReference def) { - return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); - } + public static FieldEntry getFieldEntry(MemberReference def) { + return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); + } - public static MethodEntry getMethodEntry(MemberReference def) { - return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); - } + public static MethodEntry getMethodEntry(MemberReference def) { + return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); + } - public static ConstructorEntry getConstructorEntry(MethodReference def) { - if (def.isTypeInitializer()) { - return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); - } else { - return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); - } - } + public static ConstructorEntry getConstructorEntry(MethodReference def) { + if (def.isTypeInitializer()) { + return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); + } else { + return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); + } + } - public static BehaviorEntry getBehaviorEntry(MethodReference def) { - if (def.isConstructor() || def.isTypeInitializer()) { - return getConstructorEntry(def); - } else { - return getMethodEntry(def); - } - } + public static BehaviorEntry getBehaviorEntry(MethodReference def) { + if (def.isConstructor() || def.isTypeInitializer()) { + return getConstructorEntry(def); + } else { + return getMethodEntry(def); + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java index f30b606..78130d6 100644 --- a/src/main/java/cuchaz/enigma/mapping/Signature.java +++ b/src/main/java/cuchaz/enigma/mapping/Signature.java @@ -8,99 +8,99 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; +import cuchaz.enigma.utils.Utils; import java.util.List; -import cuchaz.enigma.utils.Utils; - public class Signature { - private List argumentTypes; - private Type returnType; + private List argumentTypes; + private Type returnType; - public Signature(String signature) { - try { - this.argumentTypes = Lists.newArrayList(); - int i = 0; - while (i < signature.length()) { - char c = signature.charAt(i); - if (c == '(') { - assert (this.argumentTypes.isEmpty()); - assert (this.returnType == null); - i++; - } else if (c == ')') { - i++; - break; - } else { - String type = Type.parseFirst(signature.substring(i)); - this.argumentTypes.add(new Type(type)); - i += type.length(); - } - } - this.returnType = new Type(Type.parseFirst(signature.substring(i))); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); - } - } + public Signature(String signature) { + try { + this.argumentTypes = Lists.newArrayList(); + int i = 0; + while (i < signature.length()) { + char c = signature.charAt(i); + if (c == '(') { + assert (this.argumentTypes.isEmpty()); + assert (this.returnType == null); + i++; + } else if (c == ')') { + i++; + break; + } else { + String type = Type.parseFirst(signature.substring(i)); + this.argumentTypes.add(new Type(type)); + i += type.length(); + } + } + this.returnType = new Type(Type.parseFirst(signature.substring(i))); + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); + } + } - public Signature(Signature other, ClassNameReplacer replacer) { - this.argumentTypes = Lists.newArrayList(other.argumentTypes); - for (int i = 0; i < this.argumentTypes.size(); i++) { - this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); - } - this.returnType = new Type(other.returnType, replacer); - } + public Signature(Signature other, ClassNameReplacer replacer) { + this.argumentTypes = Lists.newArrayList(other.argumentTypes); + for (int i = 0; i < this.argumentTypes.size(); i++) { + this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); + } + this.returnType = new Type(other.returnType, replacer); + } - public List getArgumentTypes() { - return this.argumentTypes; - } + public List getArgumentTypes() { + return this.argumentTypes; + } - public Type getReturnType() { - return this.returnType; - } + public Type getReturnType() { + return this.returnType; + } - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("("); - for (Type type : this.argumentTypes) { - buf.append(type.toString()); - } - buf.append(")"); - buf.append(this.returnType.toString()); - return buf.toString(); - } + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("("); + for (Type type : this.argumentTypes) { + buf.append(type); + } + buf.append(")"); + buf.append(this.returnType); + return buf.toString(); + } - public Iterable types() { - List types = Lists.newArrayList(); - types.addAll(this.argumentTypes); - types.add(this.returnType); - return types; - } + public Iterable types() { + List types = Lists.newArrayList(); + types.addAll(this.argumentTypes); + types.add(this.returnType); + return types; + } - @Override - public boolean equals(Object other) { - return other instanceof Signature && equals((Signature) other); - } + @Override + public boolean equals(Object other) { + return other instanceof Signature && equals((Signature) other); + } - public boolean equals(Signature other) { - return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); - } + public boolean equals(Signature other) { + return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); + } - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); - } + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); + } - public boolean hasClass(ClassEntry classEntry) { - for (Type type : types()) { - if (type.hasClass() && type.getClassEntry().equals(classEntry)) { - return true; - } - } - return false; - } + public boolean hasClass(ClassEntry classEntry) { + for (Type type : types()) { + if (type.hasClass() && type.getClassEntry().equals(classEntry)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java index 9864333..ddc5af4 100644 --- a/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java +++ b/src/main/java/cuchaz/enigma/mapping/SignatureUpdater.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; @@ -18,74 +19,74 @@ import java.util.List; public class SignatureUpdater { - public interface ClassNameUpdater { - String update(String className); - } + public static String update(String signature, ClassNameUpdater updater) { + try { + StringBuilder buf = new StringBuilder(); - public static String update(String signature, ClassNameUpdater updater) { - try { - StringBuilder buf = new StringBuilder(); + // read the signature character-by-character + StringReader reader = new StringReader(signature); + int i; + while ((i = reader.read()) != -1) { + char c = (char) i; - // read the signature character-by-character - StringReader reader = new StringReader(signature); - int i; - while ((i = reader.read()) != -1) { - char c = (char) i; + // does this character start a class name? + if (c == 'L') { + // update the class name and add it to the buffer + buf.append('L'); + String className = readClass(reader); + if (className == null) { + throw new IllegalArgumentException("Malformed signature: " + signature); + } + buf.append(updater.update(className)); + buf.append(';'); + } else { + // copy the character into the buffer + buf.append(c); + } + } - // does this character start a class name? - if (c == 'L') { - // update the class name and add it to the buffer - buf.append('L'); - String className = readClass(reader); - if (className == null) { - throw new IllegalArgumentException("Malformed signature: " + signature); - } - buf.append(updater.update(className)); - buf.append(';'); - } else { - // copy the character into the buffer - buf.append(c); - } - } + return buf.toString(); + } catch (IOException ex) { + // I'm pretty sure a StringReader will never throw one of these + throw new Error(ex); + } + } - return buf.toString(); - } catch (IOException ex) { - // I'm pretty sure a StringReader will never throw one of these - throw new Error(ex); - } - } + private static String readClass(StringReader reader) throws IOException { + // read all the characters in the buffer until we hit a ';' + // remember to treat generics correctly + StringBuilder buf = new StringBuilder(); + int depth = 0; + int i; + while ((i = reader.read()) != -1) { + char c = (char) i; - private static String readClass(StringReader reader) throws IOException { - // read all the characters in the buffer until we hit a ';' - // remember to treat generics correctly - StringBuilder buf = new StringBuilder(); - int depth = 0; - int i; - while ((i = reader.read()) != -1) { - char c = (char) i; + if (c == '<') { + depth++; + } else if (c == '>') { + depth--; + } else if (depth == 0) { + if (c == ';') { + return buf.toString(); + } else { + buf.append(c); + } + } + } - if (c == '<') { - depth++; - } else if (c == '>') { - depth--; - } else if (depth == 0) { - if (c == ';') { - return buf.toString(); - } else { - buf.append(c); - } - } - } + return null; + } - return null; - } + public static List getClasses(String signature) { + final List classNames = Lists.newArrayList(); + update(signature, className -> { + classNames.add(className); + return className; + }); + return classNames; + } - public static List getClasses(String signature) { - final List classNames = Lists.newArrayList(); - update(signature, className -> { - classNames.add(className); - return className; - }); - return classNames; - } + public interface ClassNameUpdater { + String update(String className); + } } diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java index 8329d0d..17e3187 100644 --- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java +++ b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java @@ -8,22 +8,23 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; public enum TranslationDirection { - Deobfuscating { - @Override - public T choose(T deobfChoice, T obfChoice) { - return deobfChoice; - } - }, - Obfuscating { - @Override - public T choose(T deobfChoice, T obfChoice) { - return obfChoice; - } - }; + Deobfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return deobfChoice; + } + }, + Obfuscating { + @Override + public T choose(T deobfChoice, T obfChoice) { + return obfChoice; + } + }; - public abstract T choose(T deobfChoice, T obfChoice); + public abstract T choose(T deobfChoice, T obfChoice); } diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java index e94009e..8d464fc 100644 --- a/src/main/java/cuchaz/enigma/mapping/Translator.java +++ b/src/main/java/cuchaz/enigma/mapping/Translator.java @@ -8,348 +8,335 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import cuchaz.enigma.analysis.TranslationIndex; import java.util.List; import java.util.Map; -import cuchaz.enigma.analysis.TranslationIndex; - public class Translator { - private TranslationDirection direction; - private Map classes; - private TranslationIndex index; - - private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); - - public Translator() { - this.direction = null; - this.classes = Maps.newHashMap(); - this.index = new TranslationIndex(); - } - - public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { - this.direction = direction; - this.classes = classes; - this.index = index; - } - - public TranslationDirection getDirection() { - return direction; - } - - public TranslationIndex getTranslationIndex() { - return index; - } - - @SuppressWarnings("unchecked") - public T translateEntry(T entry) { - if (entry instanceof ClassEntry) { - return (T) translateEntry((ClassEntry) entry); - } else if (entry instanceof FieldEntry) { - return (T) translateEntry((FieldEntry) entry); - } else if (entry instanceof MethodEntry) { - return (T) translateEntry((MethodEntry) entry); - } else if (entry instanceof ConstructorEntry) { - return (T) translateEntry((ConstructorEntry) entry); - } else if (entry instanceof ArgumentEntry) { - return (T) translateEntry((ArgumentEntry) entry); - } else if (entry instanceof LocalVariableEntry) { - return (T) translateEntry((LocalVariableEntry) entry); - } else { - throw new Error("Unknown entry type: " + entry.getClass().getName()); - } - } - - public String translate(T entry) { - if (entry instanceof ClassEntry) { - return translate((ClassEntry) entry); - } else if (entry instanceof FieldEntry) { - return translate((FieldEntry) entry); - } else if (entry instanceof MethodEntry) { - return translate((MethodEntry) entry); - } else if (entry instanceof ConstructorEntry) { - return translate(entry); - } else if (entry instanceof ArgumentEntry) { - return translate((ArgumentEntry) entry); - } else if (entry instanceof LocalVariableEntry) { - return translate((LocalVariableEntry) entry); - } else { - throw new Error("Unknown entry type: " + entry.getClass().getName()); - } - } - - public String translate(LocalVariableEntry in) - { - LocalVariableEntry translated = translateEntry(in); - if (translated.equals(in)) { - return null; - } - return translated.getName(); - } - - public LocalVariableEntry translateEntry(LocalVariableEntry in) - { - // TODO: Implement it - return in; - } - - public String translate(ClassEntry in) { - ClassEntry translated = translateEntry(in); - if (translated.equals(in)) { - return null; - } - return translated.getName(); - } - - public String translateClass(String className) { - return translate(new ClassEntry(className)); - } - - public ClassEntry translateEntry(ClassEntry in) { - - if (in.isInnerClass()) { - - // translate as much of the class chain as we can - List mappingsChain = getClassMappingChain(in); - String[] obfClassNames = in.getName().split("\\$"); - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < obfClassNames.length; i++) { - boolean isFirstClass = buf.length() == 0; - String className = null; - ClassMapping classMapping = mappingsChain.get(i); - if (classMapping != null) { - className = this.direction.choose( - classMapping.getDeobfName(), - isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() - ); - } - if (className == null) { - className = obfClassNames[i]; - } - if (!isFirstClass) { - buf.append("$"); - } - buf.append(className); - } - return new ClassEntry(buf.toString()); - - } else { - - // normal classes are easy - ClassMapping classMapping = this.classes.get(in.getName()); - if (classMapping == null) { - return in; - } - return this.direction.choose( - classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, - new ClassEntry(classMapping.getObfFullName()) - ); - } - } - - public String translate(FieldEntry in) { - - // resolve the class entry - ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); - if (resolvedClassEntry != null) { - - // look for the class - ClassMapping classMapping = findClassMapping(resolvedClassEntry); - if (classMapping != null) { - - // look for the field - String translatedName = this.direction.choose( - classMapping.getDeobfFieldName(in.getName(), in.getType()), - classMapping.getObfFieldName(in.getName(), translateType(in.getType())) - ); - if (translatedName != null) { - return translatedName; - } - } - } - return null; - } - - public FieldEntry translateEntry(FieldEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); - } - - public String translate(MethodEntry in) { - // resolve the class entry - ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); - if (resolvedClassEntry != null) { - - // look for class - ClassMapping classMapping = findClassMapping(resolvedClassEntry); - if (classMapping != null) { - - // look for the method - MethodMapping methodMapping = this.direction.choose( - classMapping.getMethodByObf(in.getName(), in.getSignature()), - classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) - ); - if (methodMapping != null) { - return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); - } - } - } - return null; - } - - public MethodEntry translateEntry(MethodEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); - } - - public ConstructorEntry translateEntry(ConstructorEntry in) { - if (in.isStatic()) { - return new ConstructorEntry(translateEntry(in.getClassEntry())); - } else { - return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); - } - } - - public BehaviorEntry translateEntry(BehaviorEntry in) { - if (in instanceof MethodEntry) { - return translateEntry((MethodEntry) in); - } else if (in instanceof ConstructorEntry) { - return translateEntry((ConstructorEntry) in); - } - throw new Error("Wrong entry type!"); - } - - // TODO: support not identical behavior (specific to constructor) - public String translate(ArgumentEntry in) - { - String classTranslate = translateArgument(in); - - // Not found in this class - if (classTranslate == null) - { - List ancestry = this.index.getAncestry(in.getClassEntry()); - - // Check in mother class for the arg - for (ClassEntry entry : ancestry) - { - ArgumentEntry motherArg = in.cloneToNewClass(entry); - if (this.index.entryExists(motherArg)) - { - String result = translateArgument(motherArg); - if (result != null) - return result; - } - } - } - return classTranslate; - } - - public String translateArgument(ArgumentEntry in) { - // look for identical behavior in superclasses - ClassEntry entry = in.getClassEntry(); - - if (entry != null) - { - // look for the class - ClassMapping classMapping = findClassMapping(entry); - if (classMapping != null) { - - // look for the method - MethodMapping methodMapping = this.direction.choose( - classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), - classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) - ); - if (methodMapping != null) { - return this.direction.choose( - methodMapping.getDeobfArgumentName(in.getIndex()), - methodMapping.getObfArgumentName(in.getIndex()) - ); - } - } - } - return null; - } - - public ArgumentEntry translateEntry(ArgumentEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); - } - - public Type translateType(Type type) { - return new Type(type, this.classNameReplacer); - } - - public Signature translateSignature(Signature signature) { - return new Signature(signature, this.classNameReplacer); - } - - private ClassMapping findClassMapping(ClassEntry in) { - List mappingChain = getClassMappingChain(in); - return mappingChain.get(mappingChain.size() - 1); - } - - private List getClassMappingChain(ClassEntry in) { - - // get a list of all the classes in the hierarchy - String[] parts = in.getName().split("\\$"); - List mappingsChain = Lists.newArrayList(); - - // get mappings for the outer class - ClassMapping outerClassMapping = this.classes.get(parts[0]); - mappingsChain.add(outerClassMapping); - - for (int i = 1; i < parts.length; i++) { - - // get mappings for the inner class - ClassMapping innerClassMapping = null; - if (outerClassMapping != null) { - innerClassMapping = this.direction.choose( - outerClassMapping.getInnerClassByObfSimple(parts[i]), - outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) - ); - } - mappingsChain.add(innerClassMapping); - outerClassMapping = innerClassMapping; - } - - assert (mappingsChain.size() == parts.length); - return mappingsChain; - } - - public Mappings.EntryModifier getModifier(Entry entry) - { - ClassMapping classMapping = findClassMapping(entry.getClassEntry()); - if (classMapping != null && !entry.getName().equals("")) - { - if (entry instanceof ClassEntry) - return classMapping.getModifier(); - else if (entry instanceof FieldEntry) - { - FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); - return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; - } - else if (entry instanceof BehaviorEntry) - { - MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); - return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; - } - else - throw new Error("Unknown entry type: " + entry.getClass().getName()); - } - return Mappings.EntryModifier.UNCHANGED; - } + private TranslationDirection direction; + private Map classes; + private TranslationIndex index; + + private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); + + public Translator() { + this.direction = null; + this.classes = Maps.newHashMap(); + this.index = new TranslationIndex(); + } + + public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { + this.direction = direction; + this.classes = classes; + this.index = index; + } + + public TranslationDirection getDirection() { + return direction; + } + + public TranslationIndex getTranslationIndex() { + return index; + } + + @SuppressWarnings("unchecked") + public T translateEntry(T entry) { + if (entry instanceof ClassEntry) { + return (T) translateEntry((ClassEntry) entry); + } else if (entry instanceof FieldEntry) { + return (T) translateEntry((FieldEntry) entry); + } else if (entry instanceof MethodEntry) { + return (T) translateEntry((MethodEntry) entry); + } else if (entry instanceof ConstructorEntry) { + return (T) translateEntry((ConstructorEntry) entry); + } else if (entry instanceof ArgumentEntry) { + return (T) translateEntry((ArgumentEntry) entry); + } else if (entry instanceof LocalVariableEntry) { + return (T) translateEntry((LocalVariableEntry) entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + + public String translate(T entry) { + if (entry instanceof ClassEntry) { + return translate((ClassEntry) entry); + } else if (entry instanceof FieldEntry) { + return translate((FieldEntry) entry); + } else if (entry instanceof MethodEntry) { + return translate((MethodEntry) entry); + } else if (entry instanceof ConstructorEntry) { + return translate(entry); + } else if (entry instanceof ArgumentEntry) { + return translate((ArgumentEntry) entry); + } else if (entry instanceof LocalVariableEntry) { + return translate((LocalVariableEntry) entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + + public String translate(LocalVariableEntry in) { + LocalVariableEntry translated = translateEntry(in); + if (translated.equals(in)) { + return null; + } + return translated.getName(); + } + + public LocalVariableEntry translateEntry(LocalVariableEntry in) { + // TODO: Implement it + return in; + } + + public String translate(ClassEntry in) { + ClassEntry translated = translateEntry(in); + if (translated.equals(in)) { + return null; + } + return translated.getName(); + } + + public String translateClass(String className) { + return translate(new ClassEntry(className)); + } + + public ClassEntry translateEntry(ClassEntry in) { + + if (in.isInnerClass()) { + + // translate as much of the class chain as we can + List mappingsChain = getClassMappingChain(in); + String[] obfClassNames = in.getName().split("\\$"); + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < obfClassNames.length; i++) { + boolean isFirstClass = buf.length() == 0; + String className = null; + ClassMapping classMapping = mappingsChain.get(i); + if (classMapping != null) { + className = this.direction.choose( + classMapping.getDeobfName(), + isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() + ); + } + if (className == null) { + className = obfClassNames[i]; + } + if (!isFirstClass) { + buf.append("$"); + } + buf.append(className); + } + return new ClassEntry(buf.toString()); + + } else { + + // normal classes are easy + ClassMapping classMapping = this.classes.get(in.getName()); + if (classMapping == null) { + return in; + } + return this.direction.choose( + classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, + new ClassEntry(classMapping.getObfFullName()) + ); + } + } + + public String translate(FieldEntry in) { + + // resolve the class entry + ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); + if (resolvedClassEntry != null) { + + // look for the class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the field + String translatedName = this.direction.choose( + classMapping.getDeobfFieldName(in.getName(), in.getType()), + classMapping.getObfFieldName(in.getName(), translateType(in.getType())) + ); + if (translatedName != null) { + return translatedName; + } + } + } + return null; + } + + public FieldEntry translateEntry(FieldEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); + } + + public String translate(MethodEntry in) { + // resolve the class entry + ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); + if (resolvedClassEntry != null) { + + // look for class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = this.direction.choose( + classMapping.getMethodByObf(in.getName(), in.getSignature()), + classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) + ); + if (methodMapping != null) { + return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); + } + } + } + return null; + } + + public MethodEntry translateEntry(MethodEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); + } + + public ConstructorEntry translateEntry(ConstructorEntry in) { + if (in.isStatic()) { + return new ConstructorEntry(translateEntry(in.getClassEntry())); + } else { + return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); + } + } + + public BehaviorEntry translateEntry(BehaviorEntry in) { + if (in instanceof MethodEntry) { + return translateEntry((MethodEntry) in); + } else if (in instanceof ConstructorEntry) { + return translateEntry((ConstructorEntry) in); + } + throw new Error("Wrong entry type!"); + } + + // TODO: support not identical behavior (specific to constructor) + public String translate(ArgumentEntry in) { + String classTranslate = translateArgument(in); + + // Not found in this class + if (classTranslate == null) { + List ancestry = this.index.getAncestry(in.getClassEntry()); + + // Check in mother class for the arg + for (ClassEntry entry : ancestry) { + ArgumentEntry motherArg = in.cloneToNewClass(entry); + if (this.index.entryExists(motherArg)) { + String result = translateArgument(motherArg); + if (result != null) + return result; + } + } + } + return classTranslate; + } + + public String translateArgument(ArgumentEntry in) { + // look for identical behavior in superclasses + ClassEntry entry = in.getClassEntry(); + + if (entry != null) { + // look for the class + ClassMapping classMapping = findClassMapping(entry); + if (classMapping != null) { + + // look for the method + MethodMapping methodMapping = this.direction.choose( + classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), + classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) + ); + if (methodMapping != null) { + return this.direction.choose( + methodMapping.getDeobfArgumentName(in.getIndex()), + methodMapping.getObfArgumentName(in.getIndex()) + ); + } + } + } + return null; + } + + public ArgumentEntry translateEntry(ArgumentEntry in) { + String name = translate(in); + if (name == null) { + name = in.getName(); + } + return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); + } + + public Type translateType(Type type) { + return new Type(type, this.classNameReplacer); + } + + public Signature translateSignature(Signature signature) { + return new Signature(signature, this.classNameReplacer); + } + + private ClassMapping findClassMapping(ClassEntry in) { + List mappingChain = getClassMappingChain(in); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getClassMappingChain(ClassEntry in) { + + // get a list of all the classes in the hierarchy + String[] parts = in.getName().split("\\$"); + List mappingsChain = Lists.newArrayList(); + + // get mappings for the outer class + ClassMapping outerClassMapping = this.classes.get(parts[0]); + mappingsChain.add(outerClassMapping); + + for (int i = 1; i < parts.length; i++) { + + // get mappings for the inner class + ClassMapping innerClassMapping = null; + if (outerClassMapping != null) { + innerClassMapping = this.direction.choose( + outerClassMapping.getInnerClassByObfSimple(parts[i]), + outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) + ); + } + mappingsChain.add(innerClassMapping); + outerClassMapping = innerClassMapping; + } + + assert (mappingsChain.size() == parts.length); + return mappingsChain; + } + + public Mappings.EntryModifier getModifier(Entry entry) { + ClassMapping classMapping = findClassMapping(entry.getClassEntry()); + if (classMapping != null && !entry.getName().equals("")) { + if (entry instanceof ClassEntry) + return classMapping.getModifier(); + else if (entry instanceof FieldEntry) { + FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); + return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; + } else if (entry instanceof BehaviorEntry) { + MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); + return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; + } else + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + return Mappings.EntryModifier.UNCHANGED; + } } diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/Type.java index 8136e13..609bd64 100644 --- a/src/main/java/cuchaz/enigma/mapping/Type.java +++ b/src/main/java/cuchaz/enigma/mapping/Type.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.mapping; import com.google.common.collect.Maps; @@ -16,219 +17,219 @@ import java.util.Map; public class Type { - public enum Primitive { - Byte('B'), - Character('C'), - Short('S'), - Integer('I'), - Long('J'), - Float('F'), - Double('D'), - Boolean('Z'); - - private static final Map lookup; - - static { - lookup = Maps.newTreeMap(); - for (Primitive val : values()) { - lookup.put(val.getCode(), val); - } - } - - public static Primitive get(char code) { - return lookup.get(code); - } - - private char code; - - Primitive(char code) { - this.code = code; - } - - public char getCode() { - return this.code; - } - } - - public static String parseFirst(String in) { - - if (in == null || in.length() <= 0) { - throw new IllegalArgumentException("No type to parse, input is empty!"); - } - - // read one type from the input - - char c = in.charAt(0); - - // first check for void - if (c == 'V') { - return "V"; - } - - // then check for primitives - Primitive primitive = Primitive.get(c); - if (primitive != null) { - return in.substring(0, 1); - } - - // then check for classes - if (c == 'L') { - return readClass(in); - } - - // then check for templates - if (c == 'T') { - return readClass(in); - } - - // then check for arrays - int dim = countArrayDimension(in); - if (dim > 0) { - String arrayType = Type.parseFirst(in.substring(dim)); - return in.substring(0, dim + arrayType.length()); - } - - throw new IllegalArgumentException("don't know how to parse: " + in); - } - - protected String name; - - public Type(String name) { - - // don't deal with generics - // this is just for raw jvm types - if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { - throw new IllegalArgumentException("don't use with generic types or templates: " + name); - } - - this.name = name; - } - - public Type(Type other, ClassNameReplacer replacer) { - this.name = other.name; - if (other.isClass()) { - String replacedName = replacer.replace(other.getClassEntry().getClassName()); - if (replacedName != null) { - this.name = "L" + replacedName + ";"; - } - } else if (other.isArray() && other.hasClass()) { - String replacedName = replacer.replace(other.getClassEntry().getClassName()); - if (replacedName != null) { - this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; - } - } - } - - @Override - public String toString() { - return this.name; - } - - public boolean isVoid() { - return this.name.length() == 1 && this.name.charAt(0) == 'V'; - } - - public boolean isPrimitive() { - return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; - } - - public Primitive getPrimitive() { - if (!isPrimitive()) { - throw new IllegalStateException("not a primitive"); - } - return Primitive.get(this.name.charAt(0)); - } - - public boolean isClass() { - return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; - } - - public ClassEntry getClassEntry() { - if (isClass()) { - String name = this.name.substring(1, this.name.length() - 1); - - int pos = name.indexOf('<'); - if (pos >= 0) { - // remove the parameters from the class name - name = name.substring(0, pos); - } - - return new ClassEntry(name); - - } else if (isArray() && getArrayType().isClass()) { - return getArrayType().getClassEntry(); - } else { - throw new IllegalStateException("type doesn't have a class"); - } - } - - public boolean isArray() { - return this.name.charAt(0) == '['; - } - - public int getArrayDimension() { - if (!isArray()) { - throw new IllegalStateException("not an array"); - } - return countArrayDimension(this.name); - } - - public Type getArrayType() { - if (!isArray()) { - throw new IllegalStateException("not an array"); - } - return new Type(this.name.substring(getArrayDimension(), this.name.length())); - } - - private static String getArrayPrefix(int dimension) { - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < dimension; i++) { - buf.append("["); - } - return buf.toString(); - } - - public boolean hasClass() { - return isClass() || (isArray() && getArrayType().hasClass()); - } - - @Override - public boolean equals(Object other) { - return other instanceof Type && equals((Type) other); - } - - public boolean equals(Type other) { - return this.name.equals(other.name); - } - - public int hashCode() { - return this.name.hashCode(); - } - - private static int countArrayDimension(String in) { - int i = 0; - while (i < in.length() && in.charAt(i) == '[') - i++; - return i; - } - - private static String readClass(String in) { - // read all the characters in the buffer until we hit a ';' - // include the parameters too - StringBuilder buf = new StringBuilder(); - int depth = 0; - for (int i = 0; i < in.length(); i++) { - char c = in.charAt(i); - buf.append(c); - - if (c == '<') { - depth++; - } else if (c == '>') { - depth--; - } else if (depth == 0 && c == ';') { - return buf.toString(); - } - } - return null; - } + protected String name; + + public Type(String name) { + + // don't deal with generics + // this is just for raw jvm types + if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { + throw new IllegalArgumentException("don't use with generic types or templates: " + name); + } + + this.name = name; + } + + public Type(Type other, ClassNameReplacer replacer) { + this.name = other.name; + if (other.isClass()) { + String replacedName = replacer.replace(other.getClassEntry().getClassName()); + if (replacedName != null) { + this.name = "L" + replacedName + ";"; + } + } else if (other.isArray() && other.hasClass()) { + String replacedName = replacer.replace(other.getClassEntry().getClassName()); + if (replacedName != null) { + this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; + } + } + } + + public static String parseFirst(String in) { + + if (in == null || in.length() <= 0) { + throw new IllegalArgumentException("No type to parse, input is empty!"); + } + + // read one type from the input + + char c = in.charAt(0); + + // first check for void + if (c == 'V') { + return "V"; + } + + // then check for primitives + Primitive primitive = Primitive.get(c); + if (primitive != null) { + return in.substring(0, 1); + } + + // then check for classes + if (c == 'L') { + return readClass(in); + } + + // then check for templates + if (c == 'T') { + return readClass(in); + } + + // then check for arrays + int dim = countArrayDimension(in); + if (dim > 0) { + String arrayType = Type.parseFirst(in.substring(dim)); + return in.substring(0, dim + arrayType.length()); + } + + throw new IllegalArgumentException("don't know how to parse: " + in); + } + + private static String getArrayPrefix(int dimension) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < dimension; i++) { + buf.append("["); + } + return buf.toString(); + } + + private static int countArrayDimension(String in) { + int i = 0; + while (i < in.length() && in.charAt(i) == '[') + i++; + return i; + } + + private static String readClass(String in) { + // read all the characters in the buffer until we hit a ';' + // include the parameters too + StringBuilder buf = new StringBuilder(); + int depth = 0; + for (int i = 0; i < in.length(); i++) { + char c = in.charAt(i); + buf.append(c); + + if (c == '<') { + depth++; + } else if (c == '>') { + depth--; + } else if (depth == 0 && c == ';') { + return buf.toString(); + } + } + return null; + } + + @Override + public String toString() { + return this.name; + } + + public boolean isVoid() { + return this.name.length() == 1 && this.name.charAt(0) == 'V'; + } + + public boolean isPrimitive() { + return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; + } + + public Primitive getPrimitive() { + if (!isPrimitive()) { + throw new IllegalStateException("not a primitive"); + } + return Primitive.get(this.name.charAt(0)); + } + + public boolean isClass() { + return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; + } + + public ClassEntry getClassEntry() { + if (isClass()) { + String name = this.name.substring(1, this.name.length() - 1); + + int pos = name.indexOf('<'); + if (pos >= 0) { + // remove the parameters from the class name + name = name.substring(0, pos); + } + + return new ClassEntry(name); + + } else if (isArray() && getArrayType().isClass()) { + return getArrayType().getClassEntry(); + } else { + throw new IllegalStateException("type doesn't have a class"); + } + } + + public boolean isArray() { + return this.name.charAt(0) == '['; + } + + public int getArrayDimension() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return countArrayDimension(this.name); + } + + public Type getArrayType() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return new Type(this.name.substring(getArrayDimension(), this.name.length())); + } + + public boolean hasClass() { + return isClass() || (isArray() && getArrayType().hasClass()); + } + + @Override + public boolean equals(Object other) { + return other instanceof Type && equals((Type) other); + } + + public boolean equals(Type other) { + return this.name.equals(other.name); + } + + public int hashCode() { + return this.name.hashCode(); + } + + public enum Primitive { + Byte('B'), + Character('C'), + Short('S'), + Integer('I'), + Long('J'), + Float('F'), + Double('D'), + Boolean('Z'); + + private static final Map lookup; + + static { + lookup = Maps.newTreeMap(); + for (Primitive val : values()) { + lookup.put(val.getCode(), val); + } + } + + private char code; + + Primitive(char code) { + this.code = code; + } + + public static Primitive get(char code) { + return lookup.get(code); + } + + public char getCode() { + return this.code; + } + } } -- cgit v1.2.3