diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/convert')
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassForest.java | 79 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassIdentifier.java | 61 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassIdentity.java | 821 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassMatch.java | 106 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassMatches.java | 273 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassMatching.java | 256 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/ClassNamer.java | 71 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/FieldMatches.java | 261 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MappingsConverter.java | 1363 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MatchesReader.java | 157 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MatchesWriter.java | 185 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MemberMatches.java | 315 |
12 files changed, 1968 insertions, 1980 deletions
diff --git a/src/main/java/cuchaz/enigma/convert/ClassForest.java b/src/main/java/cuchaz/enigma/convert/ClassForest.java index b08d48f..4542fb3 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassForest.java +++ b/src/main/java/cuchaz/enigma/convert/ClassForest.java | |||
| @@ -8,53 +8,52 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 14 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 15 | |||
| 16 | import java.util.Collection; | ||
| 17 | |||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 16 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | 17 | ||
| 18 | import java.util.Collection; | ||
| 20 | 19 | ||
| 21 | public class ClassForest { | 20 | public class ClassForest { |
| 22 | 21 | ||
| 23 | private ClassIdentifier identifier; | 22 | private ClassIdentifier identifier; |
| 24 | private Multimap<ClassIdentity, ClassEntry> forest; | 23 | private Multimap<ClassIdentity, ClassEntry> forest; |
| 25 | 24 | ||
| 26 | public ClassForest(ClassIdentifier identifier) { | 25 | public ClassForest(ClassIdentifier identifier) { |
| 27 | this.identifier = identifier; | 26 | this.identifier = identifier; |
| 28 | this.forest = HashMultimap.create(); | 27 | this.forest = HashMultimap.create(); |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | public void addAll(Iterable<ClassEntry> entries) { | 30 | public void addAll(Iterable<ClassEntry> entries) { |
| 32 | for (ClassEntry entry : entries) { | 31 | for (ClassEntry entry : entries) { |
| 33 | add(entry); | 32 | add(entry); |
| 34 | } | 33 | } |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | public void add(ClassEntry entry) { | 36 | public void add(ClassEntry entry) { |
| 38 | try { | 37 | try { |
| 39 | this.forest.put(this.identifier.identify(entry), entry); | 38 | this.forest.put(this.identifier.identify(entry), entry); |
| 40 | } catch (ClassNotFoundException ex) { | 39 | } catch (ClassNotFoundException ex) { |
| 41 | throw new Error("Unable to find class " + entry.getName()); | 40 | throw new Error("Unable to find class " + entry.getName()); |
| 42 | } | 41 | } |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | public Collection<ClassIdentity> identities() { | 44 | public Collection<ClassIdentity> identities() { |
| 46 | return this.forest.keySet(); | 45 | return this.forest.keySet(); |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | public Collection<ClassEntry> classes() { | 48 | public Collection<ClassEntry> classes() { |
| 50 | return this.forest.values(); | 49 | return this.forest.values(); |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public Collection<ClassEntry> getClasses(ClassIdentity identity) { | 52 | public Collection<ClassEntry> getClasses(ClassIdentity identity) { |
| 54 | return this.forest.get(identity); | 53 | return this.forest.get(identity); |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | public boolean containsIdentity(ClassIdentity identity) { | 56 | public boolean containsIdentity(ClassIdentity identity) { |
| 58 | return this.forest.containsKey(identity); | 57 | return this.forest.containsKey(identity); |
| 59 | } | 58 | } |
| 60 | } | 59 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java index 557e608..0a72073 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java +++ b/src/main/java/cuchaz/enigma/convert/ClassIdentifier.java | |||
| @@ -8,13 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Maps; | 14 | import com.google.common.collect.Maps; |
| 14 | |||
| 15 | import java.util.Map; | ||
| 16 | import java.util.jar.JarFile; | ||
| 17 | |||
| 18 | import cuchaz.enigma.TranslatingTypeLoader; | 15 | import cuchaz.enigma.TranslatingTypeLoader; |
| 19 | import cuchaz.enigma.analysis.JarIndex; | 16 | import cuchaz.enigma.analysis.JarIndex; |
| 20 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 17 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| @@ -22,34 +19,36 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 22 | import cuchaz.enigma.mapping.Translator; | 19 | import cuchaz.enigma.mapping.Translator; |
| 23 | import javassist.CtClass; | 20 | import javassist.CtClass; |
| 24 | 21 | ||
| 22 | import java.util.Map; | ||
| 23 | import java.util.jar.JarFile; | ||
| 25 | 24 | ||
| 26 | public class ClassIdentifier { | 25 | public class ClassIdentifier { |
| 27 | 26 | ||
| 28 | private JarIndex index; | 27 | private JarIndex index; |
| 29 | private SidedClassNamer namer; | 28 | private SidedClassNamer namer; |
| 30 | private boolean useReferences; | 29 | private boolean useReferences; |
| 31 | private TranslatingTypeLoader loader; | 30 | private TranslatingTypeLoader loader; |
| 32 | private Map<ClassEntry, ClassIdentity> cache; | 31 | private Map<ClassEntry, ClassIdentity> cache; |
| 33 | 32 | ||
| 34 | public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { | 33 | public ClassIdentifier(JarFile jar, JarIndex index, SidedClassNamer namer, boolean useReferences) { |
| 35 | this.index = index; | 34 | this.index = index; |
| 36 | this.namer = namer; | 35 | this.namer = namer; |
| 37 | this.useReferences = useReferences; | 36 | this.useReferences = useReferences; |
| 38 | this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); | 37 | this.loader = new TranslatingTypeLoader(jar, index, new Translator(), new Translator()); |
| 39 | this.cache = Maps.newHashMap(); | 38 | this.cache = Maps.newHashMap(); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | public ClassIdentity identify(ClassEntry classEntry) | 41 | public ClassIdentity identify(ClassEntry classEntry) |
| 43 | throws ClassNotFoundException { | 42 | throws ClassNotFoundException { |
| 44 | ClassIdentity identity = this.cache.get(classEntry); | 43 | ClassIdentity identity = this.cache.get(classEntry); |
| 45 | if (identity == null) { | 44 | if (identity == null) { |
| 46 | CtClass c = this.loader.loadClass(classEntry.getName()); | 45 | CtClass c = this.loader.loadClass(classEntry.getName()); |
| 47 | if (c == null) { | 46 | if (c == null) { |
| 48 | throw new ClassNotFoundException(classEntry.getName()); | 47 | throw new ClassNotFoundException(classEntry.getName()); |
| 49 | } | 48 | } |
| 50 | identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); | 49 | identity = new ClassIdentity(c, this.namer, this.index, this.useReferences); |
| 51 | this.cache.put(classEntry, identity); | 50 | this.cache.put(classEntry, identity); |
| 52 | } | 51 | } |
| 53 | return identity; | 52 | return identity; |
| 54 | } | 53 | } |
| 55 | } | 54 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java index f72bf70..a395b75 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/main/java/cuchaz/enigma/convert/ClassIdentity.java | |||
| @@ -8,18 +8,10 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| 14 | |||
| 15 | import java.io.UnsupportedEncodingException; | ||
| 16 | import java.security.MessageDigest; | ||
| 17 | import java.security.NoSuchAlgorithmException; | ||
| 18 | import java.util.Enumeration; | ||
| 19 | import java.util.List; | ||
| 20 | import java.util.Map; | ||
| 21 | import java.util.Set; | ||
| 22 | |||
| 23 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; | 15 | import cuchaz.enigma.analysis.ClassImplementationsTreeNode; |
| 24 | import cuchaz.enigma.analysis.EntryReference; | 16 | import cuchaz.enigma.analysis.EntryReference; |
| 25 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -33,408 +25,415 @@ import javassist.*; | |||
| 33 | import javassist.bytecode.*; | 25 | import javassist.bytecode.*; |
| 34 | import javassist.expr.*; | 26 | import javassist.expr.*; |
| 35 | 27 | ||
| 28 | import java.io.UnsupportedEncodingException; | ||
| 29 | import java.security.MessageDigest; | ||
| 30 | import java.security.NoSuchAlgorithmException; | ||
| 31 | import java.util.Enumeration; | ||
| 32 | import java.util.List; | ||
| 33 | import java.util.Map; | ||
| 34 | import java.util.Set; | ||
| 35 | |||
| 36 | public class ClassIdentity { | 36 | public class ClassIdentity { |
| 37 | 37 | ||
| 38 | private ClassEntry classEntry; | 38 | private ClassEntry classEntry; |
| 39 | private SidedClassNamer namer; | 39 | private SidedClassNamer namer; |
| 40 | private Multiset<String> fields; | 40 | private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { |
| 41 | private Multiset<String> methods; | 41 | |
| 42 | private Multiset<String> constructors; | 42 | private Map<String, String> classNames = Maps.newHashMap(); |
| 43 | private String staticInitializer; | 43 | |
| 44 | private String extendz; | 44 | @Override |
| 45 | private Multiset<String> implementz; | 45 | public String replace(String className) { |
| 46 | private Set<String> stringLiterals; | 46 | |
| 47 | private Multiset<String> implementations; | 47 | // classes not in the none package can be passed through |
| 48 | private Multiset<String> references; | 48 | ClassEntry classEntry = new ClassEntry(className); |
| 49 | private String outer; | 49 | if (classEntry.getPackageName() != null) { |
| 50 | 50 | return className; | |
| 51 | private final ClassNameReplacer classNameReplacer = new ClassNameReplacer() { | 51 | } |
| 52 | 52 | ||
| 53 | private Map<String, String> classNames = Maps.newHashMap(); | 53 | // is this class ourself? |
| 54 | 54 | if (className.equals(classEntry.getName())) { | |
| 55 | @Override | 55 | return "CSelf"; |
| 56 | public String replace(String className) { | 56 | } |
| 57 | 57 | ||
| 58 | // classes not in the none package can be passed through | 58 | // try the namer |
| 59 | ClassEntry classEntry = new ClassEntry(className); | 59 | if (namer != null) { |
| 60 | if (classEntry.getPackageName() != null) { | 60 | String newName = namer.getName(className); |
| 61 | return className; | 61 | if (newName != null) { |
| 62 | } | 62 | return newName; |
| 63 | 63 | } | |
| 64 | // is this class ourself? | 64 | } |
| 65 | if (className.equals(classEntry.getName())) { | 65 | |
| 66 | return "CSelf"; | 66 | // otherwise, use local naming |
| 67 | } | 67 | if (!classNames.containsKey(className)) { |
| 68 | 68 | classNames.put(className, getNewClassName()); | |
| 69 | // try the namer | 69 | } |
| 70 | if (namer != null) { | 70 | return classNames.get(className); |
| 71 | String newName = namer.getName(className); | 71 | } |
| 72 | if (newName != null) { | 72 | |
| 73 | return newName; | 73 | private String getNewClassName() { |
| 74 | } | 74 | return String.format("C%03d", classNames.size()); |
| 75 | } | 75 | } |
| 76 | 76 | }; | |
| 77 | // otherwise, use local naming | 77 | private Multiset<String> fields; |
| 78 | if (!classNames.containsKey(className)) { | 78 | private Multiset<String> methods; |
| 79 | classNames.put(className, getNewClassName()); | 79 | private Multiset<String> constructors; |
| 80 | } | 80 | private String staticInitializer; |
| 81 | return classNames.get(className); | 81 | private String extendz; |
| 82 | } | 82 | private Multiset<String> implementz; |
| 83 | 83 | private Set<String> stringLiterals; | |
| 84 | private String getNewClassName() { | 84 | private Multiset<String> implementations; |
| 85 | return String.format("C%03d", classNames.size()); | 85 | private Multiset<String> references; |
| 86 | } | 86 | private String outer; |
| 87 | }; | 87 | |
| 88 | 88 | public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { | |
| 89 | public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { | 89 | this.namer = namer; |
| 90 | this.namer = namer; | 90 | |
| 91 | 91 | // stuff from the bytecode | |
| 92 | // stuff from the bytecode | 92 | |
| 93 | 93 | this.classEntry = EntryFactory.getClassEntry(c); | |
| 94 | this.classEntry = EntryFactory.getClassEntry(c); | 94 | this.fields = HashMultiset.create(); |
| 95 | this.fields = HashMultiset.create(); | 95 | for (CtField field : c.getDeclaredFields()) { |
| 96 | for (CtField field : c.getDeclaredFields()) { | 96 | this.fields.add(scrubType(field.getSignature())); |
| 97 | this.fields.add(scrubType(field.getSignature())); | 97 | } |
| 98 | } | 98 | this.methods = HashMultiset.create(); |
| 99 | this.methods = HashMultiset.create(); | 99 | for (CtMethod method : c.getDeclaredMethods()) { |
| 100 | for (CtMethod method : c.getDeclaredMethods()) { | 100 | this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); |
| 101 | this.methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); | 101 | } |
| 102 | } | 102 | this.constructors = HashMultiset.create(); |
| 103 | this.constructors = HashMultiset.create(); | 103 | for (CtConstructor constructor : c.getDeclaredConstructors()) { |
| 104 | for (CtConstructor constructor : c.getDeclaredConstructors()) { | 104 | this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); |
| 105 | this.constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); | 105 | } |
| 106 | } | 106 | this.staticInitializer = ""; |
| 107 | this.staticInitializer = ""; | 107 | if (c.getClassInitializer() != null) { |
| 108 | if (c.getClassInitializer() != null) { | 108 | this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); |
| 109 | this.staticInitializer = getBehaviorSignature(c.getClassInitializer()); | 109 | } |
| 110 | } | 110 | this.extendz = ""; |
| 111 | this.extendz = ""; | 111 | if (c.getClassFile().getSuperclass() != null) { |
| 112 | if (c.getClassFile().getSuperclass() != null) { | 112 | this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); |
| 113 | this.extendz = scrubClassName(Descriptor.toJvmName(c.getClassFile().getSuperclass())); | 113 | } |
| 114 | } | 114 | this.implementz = HashMultiset.create(); |
| 115 | this.implementz = HashMultiset.create(); | 115 | for (String interfaceName : c.getClassFile().getInterfaces()) { |
| 116 | for (String interfaceName : c.getClassFile().getInterfaces()) { | 116 | this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); |
| 117 | this.implementz.add(scrubClassName(Descriptor.toJvmName(interfaceName))); | 117 | } |
| 118 | } | 118 | |
| 119 | 119 | this.stringLiterals = Sets.newHashSet(); | |
| 120 | this.stringLiterals = Sets.newHashSet(); | 120 | ConstPool constants = c.getClassFile().getConstPool(); |
| 121 | ConstPool constants = c.getClassFile().getConstPool(); | 121 | for (int i = 1; i < constants.getSize(); i++) { |
| 122 | for (int i = 1; i < constants.getSize(); i++) { | 122 | if (constants.getTag(i) == ConstPool.CONST_String) { |
| 123 | if (constants.getTag(i) == ConstPool.CONST_String) { | 123 | this.stringLiterals.add(constants.getStringInfo(i)); |
| 124 | this.stringLiterals.add(constants.getStringInfo(i)); | 124 | } |
| 125 | } | 125 | } |
| 126 | } | 126 | |
| 127 | 127 | // stuff from the jar index | |
| 128 | // stuff from the jar index | 128 | |
| 129 | 129 | this.implementations = HashMultiset.create(); | |
| 130 | this.implementations = HashMultiset.create(); | 130 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); |
| 131 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, this.classEntry); | 131 | if (implementationsNode != null) { |
| 132 | if (implementationsNode != null) { | 132 | @SuppressWarnings("unchecked") |
| 133 | @SuppressWarnings("unchecked") | 133 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); |
| 134 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); | 134 | while (implementations.hasMoreElements()) { |
| 135 | while (implementations.hasMoreElements()) { | 135 | ClassImplementationsTreeNode node = implementations.nextElement(); |
| 136 | ClassImplementationsTreeNode node = implementations.nextElement(); | 136 | this.implementations.add(scrubClassName(node.getClassEntry().getName())); |
| 137 | this.implementations.add(scrubClassName(node.getClassEntry().getName())); | 137 | } |
| 138 | } | 138 | } |
| 139 | } | 139 | |
| 140 | 140 | this.references = HashMultiset.create(); | |
| 141 | this.references = HashMultiset.create(); | 141 | if (useReferences) { |
| 142 | if (useReferences) { | 142 | for (CtField field : c.getDeclaredFields()) { |
| 143 | for (CtField field : c.getDeclaredFields()) { | 143 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); |
| 144 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(field); | 144 | index.getFieldReferences(fieldEntry).forEach(this::addReference); |
| 145 | index.getFieldReferences(fieldEntry).forEach(this::addReference); | 145 | } |
| 146 | } | 146 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { |
| 147 | for (CtBehavior behavior : c.getDeclaredBehaviors()) { | 147 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); |
| 148 | BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); | 148 | index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); |
| 149 | index.getBehaviorReferences(behaviorEntry).forEach(this::addReference); | 149 | } |
| 150 | } | 150 | } |
| 151 | } | 151 | |
| 152 | 152 | this.outer = null; | |
| 153 | this.outer = null; | 153 | if (this.classEntry.isInnerClass()) { |
| 154 | if (this.classEntry.isInnerClass()) { | 154 | this.outer = this.classEntry.getOuterClassName(); |
| 155 | this.outer = this.classEntry.getOuterClassName(); | 155 | } |
| 156 | } | 156 | } |
| 157 | } | 157 | |
| 158 | 158 | private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) { | |
| 159 | private void addReference(EntryReference<? extends Entry, BehaviorEntry> reference) { | 159 | if (reference.context.getSignature() != null) { |
| 160 | if (reference.context.getSignature() != null) { | 160 | this.references.add(String.format("%s_%s", |
| 161 | this.references.add(String.format("%s_%s", | 161 | scrubClassName(reference.context.getClassName()), |
| 162 | scrubClassName(reference.context.getClassName()), | 162 | scrubSignature(reference.context.getSignature()) |
| 163 | scrubSignature(reference.context.getSignature()) | 163 | )); |
| 164 | )); | 164 | } else { |
| 165 | } else { | 165 | this.references.add(String.format("%s_<clinit>", |
| 166 | this.references.add(String.format("%s_<clinit>", | 166 | scrubClassName(reference.context.getClassName()) |
| 167 | scrubClassName(reference.context.getClassName()) | 167 | )); |
| 168 | )); | 168 | } |
| 169 | } | 169 | } |
| 170 | } | 170 | |
| 171 | 171 | public ClassEntry getClassEntry() { | |
| 172 | public ClassEntry getClassEntry() { | 172 | return this.classEntry; |
| 173 | return this.classEntry; | 173 | } |
| 174 | } | 174 | |
| 175 | 175 | @Override | |
| 176 | @Override | 176 | public String toString() { |
| 177 | public String toString() { | 177 | StringBuilder buf = new StringBuilder(); |
| 178 | StringBuilder buf = new StringBuilder(); | 178 | buf.append("class: "); |
| 179 | buf.append("class: "); | 179 | buf.append(this.classEntry.getName()); |
| 180 | buf.append(this.classEntry.getName()); | 180 | buf.append(" "); |
| 181 | buf.append(" "); | 181 | buf.append(hashCode()); |
| 182 | buf.append(hashCode()); | 182 | buf.append("\n"); |
| 183 | buf.append("\n"); | 183 | for (String field : this.fields) { |
| 184 | for (String field : this.fields) { | 184 | buf.append("\tfield "); |
| 185 | buf.append("\tfield "); | 185 | buf.append(field); |
| 186 | buf.append(field); | 186 | buf.append("\n"); |
| 187 | buf.append("\n"); | 187 | } |
| 188 | } | 188 | for (String method : this.methods) { |
| 189 | for (String method : this.methods) { | 189 | buf.append("\tmethod "); |
| 190 | buf.append("\tmethod "); | 190 | buf.append(method); |
| 191 | buf.append(method); | 191 | buf.append("\n"); |
| 192 | buf.append("\n"); | 192 | } |
| 193 | } | 193 | for (String constructor : this.constructors) { |
| 194 | for (String constructor : this.constructors) { | 194 | buf.append("\tconstructor "); |
| 195 | buf.append("\tconstructor "); | 195 | buf.append(constructor); |
| 196 | buf.append(constructor); | 196 | buf.append("\n"); |
| 197 | buf.append("\n"); | 197 | } |
| 198 | } | 198 | if (!this.staticInitializer.isEmpty()) { |
| 199 | if (this.staticInitializer.length() > 0) { | 199 | buf.append("\tinitializer "); |
| 200 | buf.append("\tinitializer "); | 200 | buf.append(this.staticInitializer); |
| 201 | buf.append(this.staticInitializer); | 201 | buf.append("\n"); |
| 202 | buf.append("\n"); | 202 | } |
| 203 | } | 203 | if (!this.extendz.isEmpty()) { |
| 204 | if (this.extendz.length() > 0) { | 204 | buf.append("\textends "); |
| 205 | buf.append("\textends "); | 205 | buf.append(this.extendz); |
| 206 | buf.append(this.extendz); | 206 | buf.append("\n"); |
| 207 | buf.append("\n"); | 207 | } |
| 208 | } | 208 | for (String interfaceName : this.implementz) { |
| 209 | for (String interfaceName : this.implementz) { | 209 | buf.append("\timplements "); |
| 210 | buf.append("\timplements "); | 210 | buf.append(interfaceName); |
| 211 | buf.append(interfaceName); | 211 | buf.append("\n"); |
| 212 | buf.append("\n"); | 212 | } |
| 213 | } | 213 | for (String implementation : this.implementations) { |
| 214 | for (String implementation : this.implementations) { | 214 | buf.append("\timplemented by "); |
| 215 | buf.append("\timplemented by "); | 215 | buf.append(implementation); |
| 216 | buf.append(implementation); | 216 | buf.append("\n"); |
| 217 | buf.append("\n"); | 217 | } |
| 218 | } | 218 | for (String reference : this.references) { |
| 219 | for (String reference : this.references) { | 219 | buf.append("\treference "); |
| 220 | buf.append("\treference "); | 220 | buf.append(reference); |
| 221 | buf.append(reference); | 221 | buf.append("\n"); |
| 222 | buf.append("\n"); | 222 | } |
| 223 | } | 223 | buf.append("\touter "); |
| 224 | buf.append("\touter "); | 224 | buf.append(this.outer); |
| 225 | buf.append(this.outer); | 225 | buf.append("\n"); |
| 226 | buf.append("\n"); | 226 | return buf.toString(); |
| 227 | return buf.toString(); | 227 | } |
| 228 | } | 228 | |
| 229 | 229 | private String scrubClassName(String className) { | |
| 230 | private String scrubClassName(String className) { | 230 | return classNameReplacer.replace(className); |
| 231 | return classNameReplacer.replace(className); | 231 | } |
| 232 | } | 232 | |
| 233 | 233 | private String scrubType(String typeName) { | |
| 234 | private String scrubType(String typeName) { | 234 | return scrubType(new Type(typeName)).toString(); |
| 235 | return scrubType(new Type(typeName)).toString(); | 235 | } |
| 236 | } | 236 | |
| 237 | 237 | private Type scrubType(Type type) { | |
| 238 | private Type scrubType(Type type) { | 238 | if (type.hasClass()) { |
| 239 | if (type.hasClass()) { | 239 | return new Type(type, classNameReplacer); |
| 240 | return new Type(type, classNameReplacer); | 240 | } else { |
| 241 | } else { | 241 | return type; |
| 242 | return type; | 242 | } |
| 243 | } | 243 | } |
| 244 | } | 244 | |
| 245 | 245 | private String scrubSignature(String signature) { | |
| 246 | private String scrubSignature(String signature) { | 246 | return scrubSignature(new Signature(signature)).toString(); |
| 247 | return scrubSignature(new Signature(signature)).toString(); | 247 | } |
| 248 | } | 248 | |
| 249 | 249 | private Signature scrubSignature(Signature signature) { | |
| 250 | private Signature scrubSignature(Signature signature) { | 250 | return new Signature(signature, classNameReplacer); |
| 251 | return new Signature(signature, classNameReplacer); | 251 | } |
| 252 | } | 252 | |
| 253 | 253 | private boolean isClassMatchedUniquely(String className) { | |
| 254 | private boolean isClassMatchedUniquely(String className) { | 254 | return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; |
| 255 | return this.namer != null && this.namer.getName(Descriptor.toJvmName(className)) != null; | 255 | } |
| 256 | } | 256 | |
| 257 | 257 | private String getBehaviorSignature(CtBehavior behavior) { | |
| 258 | private String getBehaviorSignature(CtBehavior behavior) { | 258 | try { |
| 259 | try { | 259 | // does this method have an implementation? |
| 260 | // does this method have an implementation? | 260 | if (behavior.getMethodInfo().getCodeAttribute() == null) { |
| 261 | if (behavior.getMethodInfo().getCodeAttribute() == null) { | 261 | return "(none)"; |
| 262 | return "(none)"; | 262 | } |
| 263 | } | 263 | |
| 264 | 264 | // compute the hash from the opcodes | |
| 265 | // compute the hash from the opcodes | 265 | ConstPool constants = behavior.getMethodInfo().getConstPool(); |
| 266 | ConstPool constants = behavior.getMethodInfo().getConstPool(); | 266 | final MessageDigest digest = MessageDigest.getInstance("MD5"); |
| 267 | final MessageDigest digest = MessageDigest.getInstance("MD5"); | 267 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); |
| 268 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); | 268 | while (iter.hasNext()) { |
| 269 | while (iter.hasNext()) { | 269 | int pos = iter.next(); |
| 270 | int pos = iter.next(); | 270 | |
| 271 | 271 | // update the hash with the opcode | |
| 272 | // update the hash with the opcode | 272 | int opcode = iter.byteAt(pos); |
| 273 | int opcode = iter.byteAt(pos); | 273 | digest.update((byte) opcode); |
| 274 | digest.update((byte) opcode); | 274 | int constIndex; |
| 275 | int constIndex; | 275 | switch (opcode) { |
| 276 | switch (opcode) { | 276 | case Opcode.LDC: |
| 277 | case Opcode.LDC: | 277 | constIndex = iter.byteAt(pos + 1); |
| 278 | constIndex = iter.byteAt(pos + 1); | 278 | updateHashWithConstant(digest, constants, constIndex); |
| 279 | updateHashWithConstant(digest, constants, constIndex); | 279 | break; |
| 280 | break; | 280 | |
| 281 | 281 | case Opcode.LDC_W: | |
| 282 | case Opcode.LDC_W: | 282 | case Opcode.LDC2_W: |
| 283 | case Opcode.LDC2_W: | 283 | constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); |
| 284 | constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); | 284 | updateHashWithConstant(digest, constants, constIndex); |
| 285 | updateHashWithConstant(digest, constants, constIndex); | 285 | break; |
| 286 | break; | 286 | default: |
| 287 | default: | 287 | break; |
| 288 | break; | 288 | } |
| 289 | } | 289 | } |
| 290 | } | 290 | |
| 291 | 291 | // update hash with method and field accesses | |
| 292 | // update hash with method and field accesses | 292 | behavior.instrument(new ExprEditor() { |
| 293 | behavior.instrument(new ExprEditor() { | 293 | @Override |
| 294 | @Override | 294 | public void edit(MethodCall call) { |
| 295 | public void edit(MethodCall call) { | 295 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); |
| 296 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); | 296 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 297 | updateHashWithString(digest, scrubSignature(call.getSignature())); | 297 | if (isClassMatchedUniquely(call.getClassName())) { |
| 298 | if (isClassMatchedUniquely(call.getClassName())) { | 298 | updateHashWithString(digest, call.getMethodName()); |
| 299 | updateHashWithString(digest, call.getMethodName()); | 299 | } |
| 300 | } | 300 | } |
| 301 | } | 301 | |
| 302 | 302 | @Override | |
| 303 | @Override | 303 | public void edit(FieldAccess access) { |
| 304 | public void edit(FieldAccess access) { | 304 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); |
| 305 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(access.getClassName()))); | 305 | updateHashWithString(digest, scrubType(access.getSignature())); |
| 306 | updateHashWithString(digest, scrubType(access.getSignature())); | 306 | if (isClassMatchedUniquely(access.getClassName())) { |
| 307 | if (isClassMatchedUniquely(access.getClassName())) { | 307 | updateHashWithString(digest, access.getFieldName()); |
| 308 | updateHashWithString(digest, access.getFieldName()); | 308 | } |
| 309 | } | 309 | } |
| 310 | } | 310 | |
| 311 | 311 | @Override | |
| 312 | @Override | 312 | public void edit(ConstructorCall call) { |
| 313 | public void edit(ConstructorCall call) { | 313 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); |
| 314 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(call.getClassName()))); | 314 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 315 | updateHashWithString(digest, scrubSignature(call.getSignature())); | 315 | } |
| 316 | } | 316 | |
| 317 | 317 | @Override | |
| 318 | @Override | 318 | public void edit(NewExpr expr) { |
| 319 | public void edit(NewExpr expr) { | 319 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); |
| 320 | updateHashWithString(digest, scrubClassName(Descriptor.toJvmName(expr.getClassName()))); | 320 | } |
| 321 | } | 321 | }); |
| 322 | }); | 322 | |
| 323 | 323 | // convert the hash to a hex string | |
| 324 | // convert the hash to a hex string | 324 | return toHex(digest.digest()); |
| 325 | return toHex(digest.digest()); | 325 | } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { |
| 326 | } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { | 326 | throw new Error(ex); |
| 327 | throw new Error(ex); | 327 | } |
| 328 | } | 328 | } |
| 329 | } | 329 | |
| 330 | 330 | private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { | |
| 331 | private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { | 331 | ConstPoolEditor editor = new ConstPoolEditor(constants); |
| 332 | ConstPoolEditor editor = new ConstPoolEditor(constants); | 332 | ConstInfoAccessor item = editor.getItem(index); |
| 333 | ConstInfoAccessor item = editor.getItem(index); | 333 | if (item.getType() == InfoType.StringInfo) { |
| 334 | if (item.getType() == InfoType.StringInfo) { | 334 | updateHashWithString(digest, constants.getStringInfo(index)); |
| 335 | updateHashWithString(digest, constants.getStringInfo(index)); | 335 | } |
| 336 | } | 336 | // TODO: other constants |
| 337 | // TODO: other constants | 337 | } |
| 338 | } | 338 | |
| 339 | 339 | private void updateHashWithString(MessageDigest digest, String val) { | |
| 340 | private void updateHashWithString(MessageDigest digest, String val) { | 340 | try { |
| 341 | try { | 341 | digest.update(val.getBytes("UTF8")); |
| 342 | digest.update(val.getBytes("UTF8")); | 342 | } catch (UnsupportedEncodingException ex) { |
| 343 | } catch (UnsupportedEncodingException ex) { | 343 | throw new Error(ex); |
| 344 | throw new Error(ex); | 344 | } |
| 345 | } | 345 | } |
| 346 | } | 346 | |
| 347 | 347 | private String toHex(byte[] bytes) { | |
| 348 | private String toHex(byte[] bytes) { | 348 | // function taken from: |
| 349 | // function taken from: | 349 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java |
| 350 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java | 350 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); |
| 351 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); | 351 | char[] hexChars = new char[bytes.length * 2]; |
| 352 | char[] hexChars = new char[bytes.length * 2]; | 352 | for (int j = 0; j < bytes.length; j++) { |
| 353 | for (int j = 0; j < bytes.length; j++) { | 353 | int v = bytes[j] & 0xFF; |
| 354 | int v = bytes[j] & 0xFF; | 354 | hexChars[j * 2] = hexArray[v >>> 4]; |
| 355 | hexChars[j * 2] = hexArray[v >>> 4]; | 355 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; |
| 356 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | 356 | } |
| 357 | } | 357 | return new String(hexChars); |
| 358 | return new String(hexChars); | 358 | } |
| 359 | } | 359 | |
| 360 | 360 | @Override | |
| 361 | @Override | 361 | public boolean equals(Object other) { |
| 362 | public boolean equals(Object other) { | 362 | return other instanceof ClassIdentity && equals((ClassIdentity) other); |
| 363 | return other instanceof ClassIdentity && equals((ClassIdentity) other); | 363 | } |
| 364 | } | 364 | |
| 365 | 365 | public boolean equals(ClassIdentity other) { | |
| 366 | public boolean equals(ClassIdentity other) { | 366 | return this.fields.equals(other.fields) |
| 367 | return this.fields.equals(other.fields) | 367 | && this.methods.equals(other.methods) |
| 368 | && this.methods.equals(other.methods) | 368 | && this.constructors.equals(other.constructors) |
| 369 | && this.constructors.equals(other.constructors) | 369 | && this.staticInitializer.equals(other.staticInitializer) |
| 370 | && this.staticInitializer.equals(other.staticInitializer) | 370 | && this.extendz.equals(other.extendz) |
| 371 | && this.extendz.equals(other.extendz) | 371 | && this.implementz.equals(other.implementz) |
| 372 | && this.implementz.equals(other.implementz) | 372 | && this.implementations.equals(other.implementations) |
| 373 | && this.implementations.equals(other.implementations) | 373 | && this.references.equals(other.references); |
| 374 | && this.references.equals(other.references); | 374 | } |
| 375 | } | 375 | |
| 376 | 376 | @Override | |
| 377 | @Override | 377 | public int hashCode() { |
| 378 | public int hashCode() { | 378 | List<Object> objs = Lists.newArrayList(); |
| 379 | List<Object> objs = Lists.newArrayList(); | 379 | objs.addAll(this.fields); |
| 380 | objs.addAll(this.fields); | 380 | objs.addAll(this.methods); |
| 381 | objs.addAll(this.methods); | 381 | objs.addAll(this.constructors); |
| 382 | objs.addAll(this.constructors); | 382 | objs.add(this.staticInitializer); |
| 383 | objs.add(this.staticInitializer); | 383 | objs.add(this.extendz); |
| 384 | objs.add(this.extendz); | 384 | objs.addAll(this.implementz); |
| 385 | objs.addAll(this.implementz); | 385 | objs.addAll(this.implementations); |
| 386 | objs.addAll(this.implementations); | 386 | objs.addAll(this.references); |
| 387 | objs.addAll(this.references); | 387 | return Utils.combineHashesOrdered(objs); |
| 388 | return Utils.combineHashesOrdered(objs); | 388 | } |
| 389 | } | 389 | |
| 390 | 390 | public int getMatchScore(ClassIdentity other) { | |
| 391 | public int getMatchScore(ClassIdentity other) { | 391 | return 2 * getNumMatches(this.extendz, other.extendz) |
| 392 | return 2 * getNumMatches(this.extendz, other.extendz) | 392 | + 2 * getNumMatches(this.outer, other.outer) |
| 393 | + 2 * getNumMatches(this.outer, other.outer) | 393 | + 2 * getNumMatches(this.implementz, other.implementz) |
| 394 | + 2 * getNumMatches(this.implementz, other.implementz) | 394 | + getNumMatches(this.stringLiterals, other.stringLiterals) |
| 395 | + getNumMatches(this.stringLiterals, other.stringLiterals) | 395 | + getNumMatches(this.fields, other.fields) |
| 396 | + getNumMatches(this.fields, other.fields) | 396 | + getNumMatches(this.methods, other.methods) |
| 397 | + getNumMatches(this.methods, other.methods) | 397 | + getNumMatches(this.constructors, other.constructors); |
| 398 | + getNumMatches(this.constructors, other.constructors); | 398 | } |
| 399 | } | 399 | |
| 400 | 400 | public int getMaxMatchScore() { | |
| 401 | public int getMaxMatchScore() { | 401 | return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); |
| 402 | return 2 + 2 + 2 * this.implementz.size() + this.stringLiterals.size() + this.fields.size() + this.methods.size() + this.constructors.size(); | 402 | } |
| 403 | } | 403 | |
| 404 | 404 | public boolean matches(CtClass c) { | |
| 405 | public boolean matches(CtClass c) { | 405 | // just compare declaration counts |
| 406 | // just compare declaration counts | 406 | return this.fields.size() == c.getDeclaredFields().length |
| 407 | return this.fields.size() == c.getDeclaredFields().length | 407 | && this.methods.size() == c.getDeclaredMethods().length |
| 408 | && this.methods.size() == c.getDeclaredMethods().length | 408 | && this.constructors.size() == c.getDeclaredConstructors().length; |
| 409 | && this.constructors.size() == c.getDeclaredConstructors().length; | 409 | } |
| 410 | } | 410 | |
| 411 | 411 | private int getNumMatches(Set<String> a, Set<String> b) { | |
| 412 | private int getNumMatches(Set<String> a, Set<String> b) { | 412 | int numMatches = 0; |
| 413 | int numMatches = 0; | 413 | for (String val : a) { |
| 414 | for (String val : a) { | 414 | if (b.contains(val)) { |
| 415 | if (b.contains(val)) { | 415 | numMatches++; |
| 416 | numMatches++; | 416 | } |
| 417 | } | 417 | } |
| 418 | } | 418 | return numMatches; |
| 419 | return numMatches; | 419 | } |
| 420 | } | 420 | |
| 421 | 421 | private int getNumMatches(Multiset<String> a, Multiset<String> b) { | |
| 422 | private int getNumMatches(Multiset<String> a, Multiset<String> b) { | 422 | int numMatches = 0; |
| 423 | int numMatches = 0; | 423 | for (String val : a) { |
| 424 | for (String val : a) { | 424 | if (b.contains(val)) { |
| 425 | if (b.contains(val)) { | 425 | numMatches++; |
| 426 | numMatches++; | 426 | } |
| 427 | } | 427 | } |
| 428 | } | 428 | return numMatches; |
| 429 | return numMatches; | 429 | } |
| 430 | } | 430 | |
| 431 | 431 | private int getNumMatches(String a, String b) { | |
| 432 | private int getNumMatches(String a, String b) { | 432 | if (a == null && b == null) { |
| 433 | if (a == null && b == null) { | 433 | return 1; |
| 434 | return 1; | 434 | } else if (a != null && b != null && a.equals(b)) { |
| 435 | } else if (a != null && b != null && a.equals(b)) { | 435 | return 1; |
| 436 | return 1; | 436 | } |
| 437 | } | 437 | return 0; |
| 438 | return 0; | 438 | } |
| 439 | } | ||
| 440 | } | 439 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatch.java b/src/main/java/cuchaz/enigma/convert/ClassMatch.java index 9fa35f0..bb3e4f4 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatch.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatch.java | |||
| @@ -8,76 +8,76 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 16 | import cuchaz.enigma.utils.Utils; | ||
| 14 | 17 | ||
| 15 | import java.util.Collection; | 18 | import java.util.Collection; |
| 16 | import java.util.Set; | 19 | import java.util.Set; |
| 17 | 20 | ||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 19 | import cuchaz.enigma.utils.Utils; | ||
| 20 | |||
| 21 | public class ClassMatch { | 21 | public class ClassMatch { |
| 22 | 22 | ||
| 23 | public Set<ClassEntry> sourceClasses; | 23 | public Set<ClassEntry> sourceClasses; |
| 24 | public Set<ClassEntry> destClasses; | 24 | public Set<ClassEntry> destClasses; |
| 25 | 25 | ||
| 26 | public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) { | 26 | public ClassMatch(Collection<ClassEntry> sourceClasses, Collection<ClassEntry> destClasses) { |
| 27 | this.sourceClasses = Sets.newHashSet(sourceClasses); | 27 | this.sourceClasses = Sets.newHashSet(sourceClasses); |
| 28 | this.destClasses = Sets.newHashSet(destClasses); | 28 | this.destClasses = Sets.newHashSet(destClasses); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { | 31 | public ClassMatch(ClassEntry sourceClass, ClassEntry destClass) { |
| 32 | sourceClasses = Sets.newHashSet(); | 32 | sourceClasses = Sets.newHashSet(); |
| 33 | if (sourceClass != null) { | 33 | if (sourceClass != null) { |
| 34 | sourceClasses.add(sourceClass); | 34 | sourceClasses.add(sourceClass); |
| 35 | } | 35 | } |
| 36 | destClasses = Sets.newHashSet(); | 36 | destClasses = Sets.newHashSet(); |
| 37 | if (destClass != null) { | 37 | if (destClass != null) { |
| 38 | destClasses.add(destClass); | 38 | destClasses.add(destClass); |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | public boolean isMatched() { | 42 | public boolean isMatched() { |
| 43 | return sourceClasses.size() > 0 && destClasses.size() > 0; | 43 | return !sourceClasses.isEmpty() && !destClasses.isEmpty(); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | public boolean isAmbiguous() { | 46 | public boolean isAmbiguous() { |
| 47 | return sourceClasses.size() > 1 || destClasses.size() > 1; | 47 | return sourceClasses.size() > 1 || destClasses.size() > 1; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public ClassEntry getUniqueSource() { | 50 | public ClassEntry getUniqueSource() { |
| 51 | if (sourceClasses.size() != 1) { | 51 | if (sourceClasses.size() != 1) { |
| 52 | throw new IllegalStateException("Match has ambiguous source!"); | 52 | throw new IllegalStateException("Match has ambiguous source!"); |
| 53 | } | 53 | } |
| 54 | return sourceClasses.iterator().next(); | 54 | return sourceClasses.iterator().next(); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | public ClassEntry getUniqueDest() { | 57 | public ClassEntry getUniqueDest() { |
| 58 | if (destClasses.size() != 1) { | 58 | if (destClasses.size() != 1) { |
| 59 | throw new IllegalStateException("Match has ambiguous source!"); | 59 | throw new IllegalStateException("Match has ambiguous source!"); |
| 60 | } | 60 | } |
| 61 | return destClasses.iterator().next(); | 61 | return destClasses.iterator().next(); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) { | 64 | public Set<ClassEntry> intersectSourceClasses(Set<ClassEntry> classes) { |
| 65 | Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses); | 65 | Set<ClassEntry> intersection = Sets.newHashSet(sourceClasses); |
| 66 | intersection.retainAll(classes); | 66 | intersection.retainAll(classes); |
| 67 | return intersection; | 67 | return intersection; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | @Override | 70 | @Override |
| 71 | public int hashCode() { | 71 | public int hashCode() { |
| 72 | return Utils.combineHashesOrdered(sourceClasses, destClasses); | 72 | return Utils.combineHashesOrdered(sourceClasses, destClasses); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | @Override | 75 | @Override |
| 76 | public boolean equals(Object other) { | 76 | public boolean equals(Object other) { |
| 77 | return other instanceof ClassMatch && equals((ClassMatch) other); | 77 | return other instanceof ClassMatch && equals((ClassMatch) other); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | public boolean equals(ClassMatch other) { | 80 | public boolean equals(ClassMatch other) { |
| 81 | return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); | 81 | return this.sourceClasses.equals(other.sourceClasses) && this.destClasses.equals(other.destClasses); |
| 82 | } | 82 | } |
| 83 | } | 83 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatches.java b/src/main/java/cuchaz/enigma/convert/ClassMatches.java index 431c4f2..db2c550 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatches.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatches.java | |||
| @@ -8,152 +8,151 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.HashBiMap; | 15 | import com.google.common.collect.HashBiMap; |
| 15 | import com.google.common.collect.Maps; | 16 | import com.google.common.collect.Maps; |
| 16 | import com.google.common.collect.Sets; | 17 | import com.google.common.collect.Sets; |
| 17 | |||
| 18 | import java.util.*; | ||
| 19 | |||
| 20 | import cuchaz.enigma.mapping.ClassEntry; | 18 | import cuchaz.enigma.mapping.ClassEntry; |
| 21 | 19 | ||
| 20 | import java.util.*; | ||
| 22 | 21 | ||
| 23 | public class ClassMatches implements Iterable<ClassMatch> { | 22 | public class ClassMatches implements Iterable<ClassMatch> { |
| 24 | 23 | ||
| 25 | private Collection<ClassMatch> matches; | 24 | private Collection<ClassMatch> matches; |
| 26 | private Map<ClassEntry, ClassMatch> matchesBySource; | 25 | private Map<ClassEntry, ClassMatch> matchesBySource; |
| 27 | private Map<ClassEntry, ClassMatch> matchesByDest; | 26 | private Map<ClassEntry, ClassMatch> matchesByDest; |
| 28 | private BiMap<ClassEntry, ClassEntry> uniqueMatches; | 27 | private BiMap<ClassEntry, ClassEntry> uniqueMatches; |
| 29 | private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource; | 28 | private Map<ClassEntry, ClassMatch> ambiguousMatchesBySource; |
| 30 | private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest; | 29 | private Map<ClassEntry, ClassMatch> ambiguousMatchesByDest; |
| 31 | private Set<ClassEntry> unmatchedSourceClasses; | 30 | private Set<ClassEntry> unmatchedSourceClasses; |
| 32 | private Set<ClassEntry> unmatchedDestClasses; | 31 | private Set<ClassEntry> unmatchedDestClasses; |
| 33 | 32 | ||
| 34 | public ClassMatches() { | 33 | public ClassMatches() { |
| 35 | this(new ArrayList<>()); | 34 | this(new ArrayList<>()); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | public ClassMatches(Collection<ClassMatch> matches) { | 37 | public ClassMatches(Collection<ClassMatch> matches) { |
| 39 | this.matches = matches; | 38 | this.matches = matches; |
| 40 | matchesBySource = Maps.newHashMap(); | 39 | matchesBySource = Maps.newHashMap(); |
| 41 | matchesByDest = Maps.newHashMap(); | 40 | matchesByDest = Maps.newHashMap(); |
| 42 | uniqueMatches = HashBiMap.create(); | 41 | uniqueMatches = HashBiMap.create(); |
| 43 | ambiguousMatchesBySource = Maps.newHashMap(); | 42 | ambiguousMatchesBySource = Maps.newHashMap(); |
| 44 | ambiguousMatchesByDest = Maps.newHashMap(); | 43 | ambiguousMatchesByDest = Maps.newHashMap(); |
| 45 | unmatchedSourceClasses = Sets.newHashSet(); | 44 | unmatchedSourceClasses = Sets.newHashSet(); |
| 46 | unmatchedDestClasses = Sets.newHashSet(); | 45 | unmatchedDestClasses = Sets.newHashSet(); |
| 47 | 46 | ||
| 48 | for (ClassMatch match : matches) { | 47 | for (ClassMatch match : matches) { |
| 49 | indexMatch(match); | 48 | indexMatch(match); |
| 50 | } | 49 | } |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public void add(ClassMatch match) { | 52 | public void add(ClassMatch match) { |
| 54 | matches.add(match); | 53 | matches.add(match); |
| 55 | indexMatch(match); | 54 | indexMatch(match); |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | public void remove(ClassMatch match) { | 57 | public void remove(ClassMatch match) { |
| 59 | for (ClassEntry sourceClass : match.sourceClasses) { | 58 | for (ClassEntry sourceClass : match.sourceClasses) { |
| 60 | matchesBySource.remove(sourceClass); | 59 | matchesBySource.remove(sourceClass); |
| 61 | uniqueMatches.remove(sourceClass); | 60 | uniqueMatches.remove(sourceClass); |
| 62 | ambiguousMatchesBySource.remove(sourceClass); | 61 | ambiguousMatchesBySource.remove(sourceClass); |
| 63 | unmatchedSourceClasses.remove(sourceClass); | 62 | unmatchedSourceClasses.remove(sourceClass); |
| 64 | } | 63 | } |
| 65 | for (ClassEntry destClass : match.destClasses) { | 64 | for (ClassEntry destClass : match.destClasses) { |
| 66 | matchesByDest.remove(destClass); | 65 | matchesByDest.remove(destClass); |
| 67 | uniqueMatches.inverse().remove(destClass); | 66 | uniqueMatches.inverse().remove(destClass); |
| 68 | ambiguousMatchesByDest.remove(destClass); | 67 | ambiguousMatchesByDest.remove(destClass); |
| 69 | unmatchedDestClasses.remove(destClass); | 68 | unmatchedDestClasses.remove(destClass); |
| 70 | } | 69 | } |
| 71 | matches.remove(match); | 70 | matches.remove(match); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | public int size() { | 73 | public int size() { |
| 75 | return matches.size(); | 74 | return matches.size(); |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | @Override | 77 | @Override |
| 79 | public Iterator<ClassMatch> iterator() { | 78 | public Iterator<ClassMatch> iterator() { |
| 80 | return matches.iterator(); | 79 | return matches.iterator(); |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | private void indexMatch(ClassMatch match) { | 82 | private void indexMatch(ClassMatch match) { |
| 84 | if (!match.isMatched()) { | 83 | if (!match.isMatched()) { |
| 85 | // unmatched | 84 | // unmatched |
| 86 | unmatchedSourceClasses.addAll(match.sourceClasses); | 85 | unmatchedSourceClasses.addAll(match.sourceClasses); |
| 87 | unmatchedDestClasses.addAll(match.destClasses); | 86 | unmatchedDestClasses.addAll(match.destClasses); |
| 88 | } else { | 87 | } else { |
| 89 | if (match.isAmbiguous()) { | 88 | if (match.isAmbiguous()) { |
| 90 | // ambiguously matched | 89 | // ambiguously matched |
| 91 | for (ClassEntry entry : match.sourceClasses) { | 90 | for (ClassEntry entry : match.sourceClasses) { |
| 92 | ambiguousMatchesBySource.put(entry, match); | 91 | ambiguousMatchesBySource.put(entry, match); |
| 93 | } | 92 | } |
| 94 | for (ClassEntry entry : match.destClasses) { | 93 | for (ClassEntry entry : match.destClasses) { |
| 95 | ambiguousMatchesByDest.put(entry, match); | 94 | ambiguousMatchesByDest.put(entry, match); |
| 96 | } | 95 | } |
| 97 | } else { | 96 | } else { |
| 98 | // uniquely matched | 97 | // uniquely matched |
| 99 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); | 98 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); |
| 100 | } | 99 | } |
| 101 | } | 100 | } |
| 102 | for (ClassEntry entry : match.sourceClasses) { | 101 | for (ClassEntry entry : match.sourceClasses) { |
| 103 | matchesBySource.put(entry, match); | 102 | matchesBySource.put(entry, match); |
| 104 | } | 103 | } |
| 105 | for (ClassEntry entry : match.destClasses) { | 104 | for (ClassEntry entry : match.destClasses) { |
| 106 | matchesByDest.put(entry, match); | 105 | matchesByDest.put(entry, match); |
| 107 | } | 106 | } |
| 108 | } | 107 | } |
| 109 | 108 | ||
| 110 | public BiMap<ClassEntry, ClassEntry> getUniqueMatches() { | 109 | public BiMap<ClassEntry, ClassEntry> getUniqueMatches() { |
| 111 | return uniqueMatches; | 110 | return uniqueMatches; |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | public Set<ClassEntry> getUnmatchedSourceClasses() { | 113 | public Set<ClassEntry> getUnmatchedSourceClasses() { |
| 115 | return unmatchedSourceClasses; | 114 | return unmatchedSourceClasses; |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | public Set<ClassEntry> getUnmatchedDestClasses() { | 117 | public Set<ClassEntry> getUnmatchedDestClasses() { |
| 119 | return unmatchedDestClasses; | 118 | return unmatchedDestClasses; |
| 120 | } | 119 | } |
| 121 | 120 | ||
| 122 | public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { | 121 | public Set<ClassEntry> getAmbiguouslyMatchedSourceClasses() { |
| 123 | return ambiguousMatchesBySource.keySet(); | 122 | return ambiguousMatchesBySource.keySet(); |
| 124 | } | 123 | } |
| 125 | 124 | ||
| 126 | public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { | 125 | public ClassMatch getAmbiguousMatchBySource(ClassEntry sourceClass) { |
| 127 | return ambiguousMatchesBySource.get(sourceClass); | 126 | return ambiguousMatchesBySource.get(sourceClass); |
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | public ClassMatch getMatchBySource(ClassEntry sourceClass) { | 129 | public ClassMatch getMatchBySource(ClassEntry sourceClass) { |
| 131 | return matchesBySource.get(sourceClass); | 130 | return matchesBySource.get(sourceClass); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | public ClassMatch getMatchByDest(ClassEntry destClass) { | 133 | public ClassMatch getMatchByDest(ClassEntry destClass) { |
| 135 | return matchesByDest.get(destClass); | 134 | return matchesByDest.get(destClass); |
| 136 | } | 135 | } |
| 137 | 136 | ||
| 138 | public void removeSource(ClassEntry sourceClass) { | 137 | public void removeSource(ClassEntry sourceClass) { |
| 139 | ClassMatch match = matchesBySource.get(sourceClass); | 138 | ClassMatch match = matchesBySource.get(sourceClass); |
| 140 | if (match != null) { | 139 | if (match != null) { |
| 141 | remove(match); | 140 | remove(match); |
| 142 | match.sourceClasses.remove(sourceClass); | 141 | match.sourceClasses.remove(sourceClass); |
| 143 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { | 142 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { |
| 144 | add(match); | 143 | add(match); |
| 145 | } | 144 | } |
| 146 | } | 145 | } |
| 147 | } | 146 | } |
| 148 | 147 | ||
| 149 | public void removeDest(ClassEntry destClass) { | 148 | public void removeDest(ClassEntry destClass) { |
| 150 | ClassMatch match = matchesByDest.get(destClass); | 149 | ClassMatch match = matchesByDest.get(destClass); |
| 151 | if (match != null) { | 150 | if (match != null) { |
| 152 | remove(match); | 151 | remove(match); |
| 153 | match.destClasses.remove(destClass); | 152 | match.destClasses.remove(destClass); |
| 154 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { | 153 | if (!match.sourceClasses.isEmpty() || !match.destClasses.isEmpty()) { |
| 155 | add(match); | 154 | add(match); |
| 156 | } | 155 | } |
| 157 | } | 156 | } |
| 158 | } | 157 | } |
| 159 | } | 158 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassMatching.java b/src/main/java/cuchaz/enigma/convert/ClassMatching.java index b05df87..f302f13 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassMatching.java +++ b/src/main/java/cuchaz/enigma/convert/ClassMatching.java | |||
| @@ -8,12 +8,14 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.HashBiMap; | 15 | import com.google.common.collect.HashBiMap; |
| 15 | import com.google.common.collect.Lists; | 16 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Sets; | 17 | import com.google.common.collect.Sets; |
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 17 | 19 | ||
| 18 | import java.util.ArrayList; | 20 | import java.util.ArrayList; |
| 19 | import java.util.Collection; | 21 | import java.util.Collection; |
| @@ -21,134 +23,132 @@ import java.util.List; | |||
| 21 | import java.util.Map.Entry; | 23 | import java.util.Map.Entry; |
| 22 | import java.util.Set; | 24 | import java.util.Set; |
| 23 | 25 | ||
| 24 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 25 | |||
| 26 | public class ClassMatching { | 26 | public class ClassMatching { |
| 27 | 27 | ||
| 28 | private ClassForest sourceClasses; | 28 | private ClassForest sourceClasses; |
| 29 | private ClassForest destClasses; | 29 | private ClassForest destClasses; |
| 30 | private BiMap<ClassEntry, ClassEntry> knownMatches; | 30 | private BiMap<ClassEntry, ClassEntry> knownMatches; |
| 31 | 31 | ||
| 32 | public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { | 32 | public ClassMatching(ClassIdentifier sourceIdentifier, ClassIdentifier destIdentifier) { |
| 33 | sourceClasses = new ClassForest(sourceIdentifier); | 33 | sourceClasses = new ClassForest(sourceIdentifier); |
| 34 | destClasses = new ClassForest(destIdentifier); | 34 | destClasses = new ClassForest(destIdentifier); |
| 35 | knownMatches = HashBiMap.create(); | 35 | knownMatches = HashBiMap.create(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) { | 38 | public void addKnownMatches(BiMap<ClassEntry, ClassEntry> knownMatches) { |
| 39 | this.knownMatches.putAll(knownMatches); | 39 | this.knownMatches.putAll(knownMatches); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) { | 42 | public void match(Iterable<ClassEntry> sourceClasses, Iterable<ClassEntry> destClasses) { |
| 43 | for (ClassEntry sourceClass : sourceClasses) { | 43 | for (ClassEntry sourceClass : sourceClasses) { |
| 44 | if (!knownMatches.containsKey(sourceClass)) { | 44 | if (!knownMatches.containsKey(sourceClass)) { |
| 45 | this.sourceClasses.add(sourceClass); | 45 | this.sourceClasses.add(sourceClass); |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | for (ClassEntry destClass : destClasses) { | 48 | for (ClassEntry destClass : destClasses) { |
| 49 | if (!knownMatches.containsValue(destClass)) { | 49 | if (!knownMatches.containsValue(destClass)) { |
| 50 | this.destClasses.add(destClass); | 50 | this.destClasses.add(destClass); |
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | public Collection<ClassMatch> matches() { | 55 | public Collection<ClassMatch> matches() { |
| 56 | List<ClassMatch> matches = Lists.newArrayList(); | 56 | List<ClassMatch> matches = Lists.newArrayList(); |
| 57 | for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) { | 57 | for (Entry<ClassEntry, ClassEntry> entry : knownMatches.entrySet()) { |
| 58 | matches.add(new ClassMatch( | 58 | matches.add(new ClassMatch( |
| 59 | entry.getKey(), | 59 | entry.getKey(), |
| 60 | entry.getValue() | 60 | entry.getValue() |
| 61 | )); | 61 | )); |
| 62 | } | 62 | } |
| 63 | for (ClassIdentity identity : sourceClasses.identities()) { | 63 | for (ClassIdentity identity : sourceClasses.identities()) { |
| 64 | matches.add(new ClassMatch( | 64 | matches.add(new ClassMatch( |
| 65 | sourceClasses.getClasses(identity), | 65 | sourceClasses.getClasses(identity), |
| 66 | destClasses.getClasses(identity) | 66 | destClasses.getClasses(identity) |
| 67 | )); | 67 | )); |
| 68 | } | 68 | } |
| 69 | for (ClassIdentity identity : destClasses.identities()) { | 69 | for (ClassIdentity identity : destClasses.identities()) { |
| 70 | if (!sourceClasses.containsIdentity(identity)) { | 70 | if (!sourceClasses.containsIdentity(identity)) { |
| 71 | matches.add(new ClassMatch( | 71 | matches.add(new ClassMatch( |
| 72 | new ArrayList<>(), | 72 | new ArrayList<>(), |
| 73 | destClasses.getClasses(identity) | 73 | destClasses.getClasses(identity) |
| 74 | )); | 74 | )); |
| 75 | } | 75 | } |
| 76 | } | 76 | } |
| 77 | return matches; | 77 | return matches; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | public Collection<ClassEntry> sourceClasses() { | 80 | public Collection<ClassEntry> sourceClasses() { |
| 81 | Set<ClassEntry> classes = Sets.newHashSet(); | 81 | Set<ClassEntry> classes = Sets.newHashSet(); |
| 82 | for (ClassMatch match : matches()) { | 82 | for (ClassMatch match : matches()) { |
| 83 | classes.addAll(match.sourceClasses); | 83 | classes.addAll(match.sourceClasses); |
| 84 | } | 84 | } |
| 85 | return classes; | 85 | return classes; |
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | public Collection<ClassEntry> destClasses() { | 88 | public Collection<ClassEntry> destClasses() { |
| 89 | Set<ClassEntry> classes = Sets.newHashSet(); | 89 | Set<ClassEntry> classes = Sets.newHashSet(); |
| 90 | for (ClassMatch match : matches()) { | 90 | for (ClassMatch match : matches()) { |
| 91 | classes.addAll(match.destClasses); | 91 | classes.addAll(match.destClasses); |
| 92 | } | 92 | } |
| 93 | return classes; | 93 | return classes; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | public BiMap<ClassEntry, ClassEntry> uniqueMatches() { | 96 | public BiMap<ClassEntry, ClassEntry> uniqueMatches() { |
| 97 | BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create(); | 97 | BiMap<ClassEntry, ClassEntry> uniqueMatches = HashBiMap.create(); |
| 98 | for (ClassMatch match : matches()) { | 98 | for (ClassMatch match : matches()) { |
| 99 | if (match.isMatched() && !match.isAmbiguous()) { | 99 | if (match.isMatched() && !match.isAmbiguous()) { |
| 100 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); | 100 | uniqueMatches.put(match.getUniqueSource(), match.getUniqueDest()); |
| 101 | } | 101 | } |
| 102 | } | 102 | } |
| 103 | return uniqueMatches; | 103 | return uniqueMatches; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | public Collection<ClassMatch> ambiguousMatches() { | 106 | public Collection<ClassMatch> ambiguousMatches() { |
| 107 | List<ClassMatch> ambiguousMatches = Lists.newArrayList(); | 107 | List<ClassMatch> ambiguousMatches = Lists.newArrayList(); |
| 108 | for (ClassMatch match : matches()) { | 108 | for (ClassMatch match : matches()) { |
| 109 | if (match.isMatched() && match.isAmbiguous()) { | 109 | if (match.isMatched() && match.isAmbiguous()) { |
| 110 | ambiguousMatches.add(match); | 110 | ambiguousMatches.add(match); |
| 111 | } | 111 | } |
| 112 | } | 112 | } |
| 113 | return ambiguousMatches; | 113 | return ambiguousMatches; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | public Collection<ClassEntry> unmatchedSourceClasses() { | 116 | public Collection<ClassEntry> unmatchedSourceClasses() { |
| 117 | List<ClassEntry> classes = Lists.newArrayList(); | 117 | List<ClassEntry> classes = Lists.newArrayList(); |
| 118 | for (ClassMatch match : matches()) { | 118 | for (ClassMatch match : matches()) { |
| 119 | if (!match.isMatched() && !match.sourceClasses.isEmpty()) { | 119 | if (!match.isMatched() && !match.sourceClasses.isEmpty()) { |
| 120 | classes.addAll(match.sourceClasses); | 120 | classes.addAll(match.sourceClasses); |
| 121 | } | 121 | } |
| 122 | } | 122 | } |
| 123 | return classes; | 123 | return classes; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | public Collection<ClassEntry> unmatchedDestClasses() { | 126 | public Collection<ClassEntry> unmatchedDestClasses() { |
| 127 | List<ClassEntry> classes = Lists.newArrayList(); | 127 | List<ClassEntry> classes = Lists.newArrayList(); |
| 128 | for (ClassMatch match : matches()) { | 128 | for (ClassMatch match : matches()) { |
| 129 | if (!match.isMatched() && !match.destClasses.isEmpty()) { | 129 | if (!match.isMatched() && !match.destClasses.isEmpty()) { |
| 130 | classes.addAll(match.destClasses); | 130 | classes.addAll(match.destClasses); |
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | return classes; | 133 | return classes; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | @Override | 136 | @Override |
| 137 | public String toString() { | 137 | public String toString() { |
| 138 | 138 | ||
| 139 | // count the ambiguous classes | 139 | // count the ambiguous classes |
| 140 | int numAmbiguousSource = 0; | 140 | int numAmbiguousSource = 0; |
| 141 | int numAmbiguousDest = 0; | 141 | int numAmbiguousDest = 0; |
| 142 | for (ClassMatch match : ambiguousMatches()) { | 142 | for (ClassMatch match : ambiguousMatches()) { |
| 143 | numAmbiguousSource += match.sourceClasses.size(); | 143 | numAmbiguousSource += match.sourceClasses.size(); |
| 144 | numAmbiguousDest += match.destClasses.size(); | 144 | numAmbiguousDest += match.destClasses.size(); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String | 147 | String buf = String.format("%20s%8s%8s\n", "", "Source", "Dest") + String |
| 148 | .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String | 148 | .format("%20s%8d%8d\n", "Classes", sourceClasses().size(), destClasses().size()) + String |
| 149 | .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String | 149 | .format("%20s%8d%8d\n", "Uniquely matched", uniqueMatches().size(), uniqueMatches().size()) + String |
| 150 | .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String | 150 | .format("%20s%8d%8d\n", "Ambiguously matched", numAmbiguousSource, numAmbiguousDest) + String |
| 151 | .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); | 151 | .format("%20s%8d%8d\n", "Unmatched", unmatchedSourceClasses().size(), unmatchedDestClasses().size()); |
| 152 | return buf; | 152 | return buf; |
| 153 | } | 153 | } |
| 154 | } | 154 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/ClassNamer.java b/src/main/java/cuchaz/enigma/convert/ClassNamer.java index e471c7d..3969910 100644 --- a/src/main/java/cuchaz/enigma/convert/ClassNamer.java +++ b/src/main/java/cuchaz/enigma/convert/ClassNamer.java | |||
| @@ -8,49 +8,48 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.BiMap; | 14 | import com.google.common.collect.BiMap; |
| 14 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 15 | 17 | ||
| 16 | import java.util.Map; | 18 | import java.util.Map; |
| 17 | 19 | ||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 19 | |||
| 20 | public class ClassNamer { | 20 | public class ClassNamer { |
| 21 | 21 | ||
| 22 | public interface SidedClassNamer { | 22 | private Map<String, String> sourceNames; |
| 23 | String getName(String name); | 23 | private Map<String, String> destNames; |
| 24 | } | 24 | public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) { |
| 25 | 25 | // convert the identity mappings to name maps | |
| 26 | private Map<String, String> sourceNames; | 26 | this.sourceNames = Maps.newHashMap(); |
| 27 | private Map<String, String> destNames; | 27 | this.destNames = Maps.newHashMap(); |
| 28 | 28 | int i = 0; | |
| 29 | public ClassNamer(BiMap<ClassEntry, ClassEntry> mappings) { | 29 | for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) { |
| 30 | // convert the identity mappings to name maps | 30 | String name = String.format("M%04d", i++); |
| 31 | this.sourceNames = Maps.newHashMap(); | 31 | this.sourceNames.put(entry.getKey().getName(), name); |
| 32 | this.destNames = Maps.newHashMap(); | 32 | this.destNames.put(entry.getValue().getName(), name); |
| 33 | int i = 0; | 33 | } |
| 34 | for (Map.Entry<ClassEntry, ClassEntry> entry : mappings.entrySet()) { | 34 | } |
| 35 | String name = String.format("M%04d", i++); | 35 | |
| 36 | this.sourceNames.put(entry.getKey().getName(), name); | 36 | public String getSourceName(String name) { |
| 37 | this.destNames.put(entry.getValue().getName(), name); | 37 | return this.sourceNames.get(name); |
| 38 | } | 38 | } |
| 39 | } | 39 | |
| 40 | 40 | public String getDestName(String name) { | |
| 41 | public String getSourceName(String name) { | 41 | return this.destNames.get(name); |
| 42 | return this.sourceNames.get(name); | 42 | } |
| 43 | } | 43 | |
| 44 | 44 | public SidedClassNamer getSourceNamer() { | |
| 45 | public String getDestName(String name) { | 45 | return this::getSourceName; |
| 46 | return this.destNames.get(name); | 46 | } |
| 47 | } | 47 | |
| 48 | 48 | public SidedClassNamer getDestNamer() { | |
| 49 | public SidedClassNamer getSourceNamer() { | 49 | return this::getDestName; |
| 50 | return this::getSourceName; | 50 | } |
| 51 | } | 51 | |
| 52 | 52 | public interface SidedClassNamer { | |
| 53 | public SidedClassNamer getDestNamer() { | 53 | String getName(String name); |
| 54 | return this::getDestName; | 54 | } |
| 55 | } | ||
| 56 | } | 55 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/FieldMatches.java b/src/main/java/cuchaz/enigma/convert/FieldMatches.java index 236cd4d..a528b27 100644 --- a/src/main/java/cuchaz/enigma/convert/FieldMatches.java +++ b/src/main/java/cuchaz/enigma/convert/FieldMatches.java | |||
| @@ -8,144 +8,143 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| 14 | |||
| 15 | import java.util.Collection; | ||
| 16 | import java.util.Set; | ||
| 17 | |||
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.FieldEntry; | 16 | import cuchaz.enigma.mapping.FieldEntry; |
| 20 | 17 | ||
| 18 | import java.util.Collection; | ||
| 19 | import java.util.Set; | ||
| 21 | 20 | ||
| 22 | public class FieldMatches { | 21 | public class FieldMatches { |
| 23 | 22 | ||
| 24 | private BiMap<FieldEntry, FieldEntry> matches; | 23 | private BiMap<FieldEntry, FieldEntry> matches; |
| 25 | private Multimap<ClassEntry, FieldEntry> matchedSourceFields; | 24 | private Multimap<ClassEntry, FieldEntry> matchedSourceFields; |
| 26 | private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields; | 25 | private Multimap<ClassEntry, FieldEntry> unmatchedSourceFields; |
| 27 | private Multimap<ClassEntry, FieldEntry> unmatchedDestFields; | 26 | private Multimap<ClassEntry, FieldEntry> unmatchedDestFields; |
| 28 | private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields; | 27 | private Multimap<ClassEntry, FieldEntry> unmatchableSourceFields; |
| 29 | 28 | ||
| 30 | public FieldMatches() { | 29 | public FieldMatches() { |
| 31 | matches = HashBiMap.create(); | 30 | matches = HashBiMap.create(); |
| 32 | matchedSourceFields = HashMultimap.create(); | 31 | matchedSourceFields = HashMultimap.create(); |
| 33 | unmatchedSourceFields = HashMultimap.create(); | 32 | unmatchedSourceFields = HashMultimap.create(); |
| 34 | unmatchedDestFields = HashMultimap.create(); | 33 | unmatchedDestFields = HashMultimap.create(); |
| 35 | unmatchableSourceFields = HashMultimap.create(); | 34 | unmatchableSourceFields = HashMultimap.create(); |
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | public void addMatch(FieldEntry srcField, FieldEntry destField) { | 37 | public void addMatch(FieldEntry srcField, FieldEntry destField) { |
| 39 | boolean wasAdded = matches.put(srcField, destField) == null; | 38 | boolean wasAdded = matches.put(srcField, destField) == null; |
| 40 | assert (wasAdded); | 39 | assert (wasAdded); |
| 41 | wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); | 40 | wasAdded = matchedSourceFields.put(srcField.getClassEntry(), srcField); |
| 42 | assert (wasAdded); | 41 | assert (wasAdded); |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | public void addUnmatchedSourceField(FieldEntry fieldEntry) { | 44 | public void addUnmatchedSourceField(FieldEntry fieldEntry) { |
| 46 | boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); | 45 | boolean wasAdded = unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); |
| 47 | assert (wasAdded); | 46 | assert (wasAdded); |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { | 49 | public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { |
| 51 | for (FieldEntry fieldEntry : fieldEntries) { | 50 | for (FieldEntry fieldEntry : fieldEntries) { |
| 52 | addUnmatchedSourceField(fieldEntry); | 51 | addUnmatchedSourceField(fieldEntry); |
| 53 | } | 52 | } |
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | public void addUnmatchedDestField(FieldEntry fieldEntry) { | 55 | public void addUnmatchedDestField(FieldEntry fieldEntry) { |
| 57 | boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); | 56 | boolean wasAdded = unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); |
| 58 | assert (wasAdded); | 57 | assert (wasAdded); |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { | 60 | public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { |
| 62 | for (FieldEntry fieldEntry : fieldEntries) { | 61 | for (FieldEntry fieldEntry : fieldEntries) { |
| 63 | addUnmatchedDestField(fieldEntry); | 62 | addUnmatchedDestField(fieldEntry); |
| 64 | } | 63 | } |
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | public void addUnmatchableSourceField(FieldEntry sourceField) { | 66 | public void addUnmatchableSourceField(FieldEntry sourceField) { |
| 68 | boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); | 67 | boolean wasAdded = unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField); |
| 69 | assert (wasAdded); | 68 | assert (wasAdded); |
| 70 | } | 69 | } |
| 71 | 70 | ||
| 72 | public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { | 71 | public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { |
| 73 | return unmatchedSourceFields.keySet(); | 72 | return unmatchedSourceFields.keySet(); |
| 74 | } | 73 | } |
| 75 | 74 | ||
| 76 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() { | 75 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() { |
| 77 | Set<ClassEntry> out = Sets.newHashSet(); | 76 | Set<ClassEntry> out = Sets.newHashSet(); |
| 78 | out.addAll(matchedSourceFields.keySet()); | 77 | out.addAll(matchedSourceFields.keySet()); |
| 79 | out.removeAll(unmatchedSourceFields.keySet()); | 78 | out.removeAll(unmatchedSourceFields.keySet()); |
| 80 | return out; | 79 | return out; |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | public Collection<FieldEntry> getUnmatchedSourceFields() { | 82 | public Collection<FieldEntry> getUnmatchedSourceFields() { |
| 84 | return unmatchedSourceFields.values(); | 83 | return unmatchedSourceFields.values(); |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) { | 86 | public Collection<FieldEntry> getUnmatchedSourceFields(ClassEntry sourceClass) { |
| 88 | return unmatchedSourceFields.get(sourceClass); | 87 | return unmatchedSourceFields.get(sourceClass); |
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | public Collection<FieldEntry> getUnmatchedDestFields() { | 90 | public Collection<FieldEntry> getUnmatchedDestFields() { |
| 92 | return unmatchedDestFields.values(); | 91 | return unmatchedDestFields.values(); |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) { | 94 | public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) { |
| 96 | return unmatchedDestFields.get(destClass); | 95 | return unmatchedDestFields.get(destClass); |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | public Collection<FieldEntry> getUnmatchableSourceFields() { | 98 | public Collection<FieldEntry> getUnmatchableSourceFields() { |
| 100 | return unmatchableSourceFields.values(); | 99 | return unmatchableSourceFields.values(); |
| 101 | } | 100 | } |
| 102 | 101 | ||
| 103 | public boolean hasSource(FieldEntry fieldEntry) { | 102 | public boolean hasSource(FieldEntry fieldEntry) { |
| 104 | return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); | 103 | return matches.containsKey(fieldEntry) || unmatchedSourceFields.containsValue(fieldEntry); |
| 105 | } | 104 | } |
| 106 | 105 | ||
| 107 | public boolean hasDest(FieldEntry fieldEntry) { | 106 | public boolean hasDest(FieldEntry fieldEntry) { |
| 108 | return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); | 107 | return matches.containsValue(fieldEntry) || unmatchedDestFields.containsValue(fieldEntry); |
| 109 | } | 108 | } |
| 110 | 109 | ||
| 111 | public BiMap<FieldEntry, FieldEntry> matches() { | 110 | public BiMap<FieldEntry, FieldEntry> matches() { |
| 112 | return matches; | 111 | return matches; |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 115 | public boolean isMatchedSourceField(FieldEntry sourceField) { | 114 | public boolean isMatchedSourceField(FieldEntry sourceField) { |
| 116 | return matches.containsKey(sourceField); | 115 | return matches.containsKey(sourceField); |
| 117 | } | 116 | } |
| 118 | 117 | ||
| 119 | public boolean isMatchedDestField(FieldEntry destField) { | 118 | public boolean isMatchedDestField(FieldEntry destField) { |
| 120 | return matches.containsValue(destField); | 119 | return matches.containsValue(destField); |
| 121 | } | 120 | } |
| 122 | 121 | ||
| 123 | public void makeMatch(FieldEntry sourceField, FieldEntry destField) { | 122 | public void makeMatch(FieldEntry sourceField, FieldEntry destField) { |
| 124 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 123 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 125 | assert (wasRemoved); | 124 | assert (wasRemoved); |
| 126 | wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); | 125 | wasRemoved = unmatchedDestFields.remove(destField.getClassEntry(), destField); |
| 127 | assert (wasRemoved); | 126 | assert (wasRemoved); |
| 128 | addMatch(sourceField, destField); | 127 | addMatch(sourceField, destField); |
| 129 | } | 128 | } |
| 130 | 129 | ||
| 131 | public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { | 130 | public boolean isMatched(FieldEntry sourceField, FieldEntry destField) { |
| 132 | FieldEntry match = matches.get(sourceField); | 131 | FieldEntry match = matches.get(sourceField); |
| 133 | return match != null && match.equals(destField); | 132 | return match != null && match.equals(destField); |
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { | 135 | public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) { |
| 137 | boolean wasRemoved = matches.remove(sourceField) != null; | 136 | boolean wasRemoved = matches.remove(sourceField) != null; |
| 138 | assert (wasRemoved); | 137 | assert (wasRemoved); |
| 139 | wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 138 | wasRemoved = matchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 140 | assert (wasRemoved); | 139 | assert (wasRemoved); |
| 141 | addUnmatchedSourceField(sourceField); | 140 | addUnmatchedSourceField(sourceField); |
| 142 | addUnmatchedDestField(destField); | 141 | addUnmatchedDestField(destField); |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | public void makeSourceUnmatchable(FieldEntry sourceField) { | 144 | public void makeSourceUnmatchable(FieldEntry sourceField) { |
| 146 | assert (!isMatchedSourceField(sourceField)); | 145 | assert (!isMatchedSourceField(sourceField)); |
| 147 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); | 146 | boolean wasRemoved = unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); |
| 148 | assert (wasRemoved); | 147 | assert (wasRemoved); |
| 149 | addUnmatchableSourceField(sourceField); | 148 | addUnmatchableSourceField(sourceField); |
| 150 | } | 149 | } |
| 151 | } | 150 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java index a5ded67..fa3e936 100644 --- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| @@ -29,688 +30,682 @@ import java.util.jar.JarFile; | |||
| 29 | 30 | ||
| 30 | public class MappingsConverter { | 31 | public class MappingsConverter { |
| 31 | 32 | ||
| 32 | public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { | 33 | public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { |
| 33 | 34 | ||
| 34 | // index jars | 35 | // index jars |
| 35 | System.out.println("Indexing source jar..."); | 36 | System.out.println("Indexing source jar..."); |
| 36 | JarIndex sourceIndex = new JarIndex(); | 37 | JarIndex sourceIndex = new JarIndex(); |
| 37 | sourceIndex.indexJar(sourceJar, false); | 38 | sourceIndex.indexJar(sourceJar, false); |
| 38 | System.out.println("Indexing dest jar..."); | 39 | System.out.println("Indexing dest jar..."); |
| 39 | JarIndex destIndex = new JarIndex(); | 40 | JarIndex destIndex = new JarIndex(); |
| 40 | destIndex.indexJar(destJar, false); | 41 | destIndex.indexJar(destJar, false); |
| 41 | 42 | ||
| 42 | // compute the matching | 43 | // compute the matching |
| 43 | ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); | 44 | ClassMatching matching = computeMatching(sourceJar, sourceIndex, destJar, destIndex, null); |
| 44 | return new ClassMatches(matching.matches()); | 45 | return new ClassMatches(matching.matches()); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { | 48 | public static ClassMatching computeMatching(JarFile sourceJar, JarIndex sourceIndex, JarFile destJar, JarIndex destIndex, BiMap<ClassEntry, ClassEntry> knownMatches) { |
| 48 | 49 | ||
| 49 | System.out.println("Iteratively matching classes"); | 50 | System.out.println("Iteratively matching classes"); |
| 50 | 51 | ||
| 51 | ClassMatching lastMatching = null; | 52 | ClassMatching lastMatching = null; |
| 52 | int round = 0; | 53 | int round = 0; |
| 53 | SidedClassNamer sourceNamer = null; | 54 | SidedClassNamer sourceNamer = null; |
| 54 | SidedClassNamer destNamer = null; | 55 | SidedClassNamer destNamer = null; |
| 55 | for (boolean useReferences : Arrays.asList(false, true)) { | 56 | for (boolean useReferences : Arrays.asList(false, true)) { |
| 56 | 57 | ||
| 57 | int numUniqueMatchesLastTime = 0; | 58 | int numUniqueMatchesLastTime = 0; |
| 58 | if (lastMatching != null) { | 59 | if (lastMatching != null) { |
| 59 | numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); | 60 | numUniqueMatchesLastTime = lastMatching.uniqueMatches().size(); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | while (true) { | 63 | while (true) { |
| 63 | 64 | ||
| 64 | System.out.println("Round " + (++round) + "..."); | 65 | System.out.println("Round " + (++round) + "..."); |
| 65 | 66 | ||
| 66 | // init the matching with identity settings | 67 | // init the matching with identity settings |
| 67 | ClassMatching matching = new ClassMatching( | 68 | ClassMatching matching = new ClassMatching( |
| 68 | new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), | 69 | new ClassIdentifier(sourceJar, sourceIndex, sourceNamer, useReferences), |
| 69 | new ClassIdentifier(destJar, destIndex, destNamer, useReferences) | 70 | new ClassIdentifier(destJar, destIndex, destNamer, useReferences) |
| 70 | ); | 71 | ); |
| 71 | 72 | ||
| 72 | if (knownMatches != null) { | 73 | if (knownMatches != null) { |
| 73 | matching.addKnownMatches(knownMatches); | 74 | matching.addKnownMatches(knownMatches); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | if (lastMatching == null) { | 77 | if (lastMatching == null) { |
| 77 | // search all classes | 78 | // search all classes |
| 78 | matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); | 79 | matching.match(sourceIndex.getObfClassEntries(), destIndex.getObfClassEntries()); |
| 79 | } else { | 80 | } else { |
| 80 | // we already know about these matches from last time | 81 | // we already know about these matches from last time |
| 81 | matching.addKnownMatches(lastMatching.uniqueMatches()); | 82 | matching.addKnownMatches(lastMatching.uniqueMatches()); |
| 82 | 83 | ||
| 83 | // search unmatched and ambiguously-matched classes | 84 | // search unmatched and ambiguously-matched classes |
| 84 | matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); | 85 | matching.match(lastMatching.unmatchedSourceClasses(), lastMatching.unmatchedDestClasses()); |
| 85 | for (ClassMatch match : lastMatching.ambiguousMatches()) { | 86 | for (ClassMatch match : lastMatching.ambiguousMatches()) { |
| 86 | matching.match(match.sourceClasses, match.destClasses); | 87 | matching.match(match.sourceClasses, match.destClasses); |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 89 | System.out.println(matching); | 90 | System.out.println(matching); |
| 90 | BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); | 91 | BiMap<ClassEntry, ClassEntry> uniqueMatches = matching.uniqueMatches(); |
| 91 | 92 | ||
| 92 | // did we match anything new this time? | 93 | // did we match anything new this time? |
| 93 | if (uniqueMatches.size() > numUniqueMatchesLastTime) { | 94 | if (uniqueMatches.size() > numUniqueMatchesLastTime) { |
| 94 | numUniqueMatchesLastTime = uniqueMatches.size(); | 95 | numUniqueMatchesLastTime = uniqueMatches.size(); |
| 95 | lastMatching = matching; | 96 | lastMatching = matching; |
| 96 | } else { | 97 | } else { |
| 97 | break; | 98 | break; |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | // update the namers | 101 | // update the namers |
| 101 | ClassNamer namer = new ClassNamer(uniqueMatches); | 102 | ClassNamer namer = new ClassNamer(uniqueMatches); |
| 102 | sourceNamer = namer.getSourceNamer(); | 103 | sourceNamer = namer.getSourceNamer(); |
| 103 | destNamer = namer.getDestNamer(); | 104 | destNamer = namer.getDestNamer(); |
| 104 | } | 105 | } |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | return lastMatching; | 108 | return lastMatching; |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) | 111 | public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) |
| 111 | throws MappingConflict { | 112 | throws MappingConflict { |
| 112 | // sort the unique matches by size of inner class chain | 113 | // sort the unique matches by size of inner class chain |
| 113 | Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); | 114 | Multimap<Integer, java.util.Map.Entry<ClassEntry, ClassEntry>> matchesByDestChainSize = HashMultimap.create(); |
| 114 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { | 115 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matches.getUniqueMatches().entrySet()) { |
| 115 | int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); | 116 | int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); |
| 116 | matchesByDestChainSize.put(chainSize, match); | 117 | matchesByDestChainSize.put(chainSize, match); |
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | // build the mappings (in order of small-to-large inner chains) | 120 | // build the mappings (in order of small-to-large inner chains) |
| 120 | Mappings newMappings = new Mappings(); | 121 | Mappings newMappings = new Mappings(); |
| 121 | List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); | 122 | List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); |
| 122 | Collections.sort(chainSizes); | 123 | Collections.sort(chainSizes); |
| 123 | for (int chainSize : chainSizes) { | 124 | for (int chainSize : chainSizes) { |
| 124 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { | 125 | for (java.util.Map.Entry<ClassEntry, ClassEntry> match : matchesByDestChainSize.get(chainSize)) { |
| 125 | // get class info | 126 | // get class info |
| 126 | ClassEntry obfSourceClassEntry = match.getKey(); | 127 | ClassEntry obfSourceClassEntry = match.getKey(); |
| 127 | ClassEntry obfDestClassEntry = match.getValue(); | 128 | ClassEntry obfDestClassEntry = match.getValue(); |
| 128 | List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); | 129 | List<ClassEntry> destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); |
| 129 | 130 | ||
| 130 | ClassMapping sourceMapping; | 131 | ClassMapping sourceMapping; |
| 131 | if (obfSourceClassEntry.isInnerClass()) { | 132 | if (obfSourceClassEntry.isInnerClass()) { |
| 132 | List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); | 133 | List<ClassMapping> srcClassChain = sourceDeobfuscator.getMappings().getClassMappingChain(obfSourceClassEntry); |
| 133 | sourceMapping = srcClassChain.get(srcClassChain.size() - 1); | 134 | sourceMapping = srcClassChain.get(srcClassChain.size() - 1); |
| 134 | } else { | 135 | } else { |
| 135 | sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); | 136 | sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | if (sourceMapping == null) { | 139 | if (sourceMapping == null) { |
| 139 | // if this class was never deobfuscated, don't try to match it | 140 | // if this class was never deobfuscated, don't try to match it |
| 140 | continue; | 141 | continue; |
| 141 | } | 142 | } |
| 142 | 143 | ||
| 143 | // find out where to make the dest class mapping | 144 | // find out where to make the dest class mapping |
| 144 | if (destClassChain.size() == 1) { | 145 | if (destClassChain.size() == 1) { |
| 145 | // not an inner class, add directly to mappings | 146 | // not an inner class, add directly to mappings |
| 146 | newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); | 147 | newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); |
| 147 | } else { | 148 | } else { |
| 148 | // inner class, find the outer class mapping | 149 | // inner class, find the outer class mapping |
| 149 | ClassMapping destMapping = null; | 150 | ClassMapping destMapping = null; |
| 150 | for (int i = 0; i < destClassChain.size() - 1; i++) { | 151 | for (int i = 0; i < destClassChain.size() - 1; i++) { |
| 151 | ClassEntry destChainClassEntry = destClassChain.get(i); | 152 | ClassEntry destChainClassEntry = destClassChain.get(i); |
| 152 | if (destMapping == null) { | 153 | if (destMapping == null) { |
| 153 | destMapping = newMappings.getClassByObf(destChainClassEntry); | 154 | destMapping = newMappings.getClassByObf(destChainClassEntry); |
| 154 | if (destMapping == null) { | 155 | if (destMapping == null) { |
| 155 | destMapping = new ClassMapping(destChainClassEntry.getName()); | 156 | destMapping = new ClassMapping(destChainClassEntry.getName()); |
| 156 | newMappings.addClassMapping(destMapping); | 157 | newMappings.addClassMapping(destMapping); |
| 157 | } | 158 | } |
| 158 | } else { | 159 | } else { |
| 159 | destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); | 160 | destMapping = destMapping.getInnerClassByObfSimple(destChainClassEntry.getInnermostClassName()); |
| 160 | if (destMapping == null) { | 161 | if (destMapping == null) { |
| 161 | destMapping = new ClassMapping(destChainClassEntry.getName()); | 162 | destMapping = new ClassMapping(destChainClassEntry.getName()); |
| 162 | destMapping.addInnerClassMapping(destMapping); | 163 | destMapping.addInnerClassMapping(destMapping); |
| 163 | } | 164 | } |
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); | 167 | destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); |
| 167 | } | 168 | } |
| 168 | } | 169 | } |
| 169 | } | 170 | } |
| 170 | return newMappings; | 171 | return newMappings; |
| 171 | } | 172 | } |
| 172 | 173 | ||
| 173 | private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { | 174 | private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping oldClassMapping, final ClassMatches matches, boolean useSimpleName) { |
| 174 | 175 | ||
| 175 | ClassNameReplacer replacer = className -> | 176 | ClassNameReplacer replacer = className -> |
| 176 | { | 177 | { |
| 177 | ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); | 178 | ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); |
| 178 | if (newClassEntry != null) { | 179 | if (newClassEntry != null) { |
| 179 | return newClassEntry.getName(); | 180 | return newClassEntry.getName(); |
| 180 | } | 181 | } |
| 181 | return null; | 182 | return null; |
| 182 | }; | 183 | }; |
| 183 | 184 | ||
| 184 | ClassMapping newClassMapping; | 185 | ClassMapping newClassMapping; |
| 185 | String deobfName = oldClassMapping.getDeobfName(); | 186 | String deobfName = oldClassMapping.getDeobfName(); |
| 186 | if (deobfName != null) { | 187 | if (deobfName != null) { |
| 187 | if (useSimpleName) { | 188 | if (useSimpleName) { |
| 188 | deobfName = new ClassEntry(deobfName).getSimpleName(); | 189 | deobfName = new ClassEntry(deobfName).getSimpleName(); |
| 189 | } | 190 | } |
| 190 | newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); | 191 | newClassMapping = new ClassMapping(newObfClass.getName(), deobfName); |
| 191 | } else { | 192 | } else { |
| 192 | newClassMapping = new ClassMapping(newObfClass.getName()); | 193 | newClassMapping = new ClassMapping(newObfClass.getName()); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | // migrate fields | 196 | // migrate fields |
| 196 | for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { | 197 | for (FieldMapping oldFieldMapping : oldClassMapping.fields()) { |
| 197 | if (canMigrate(oldFieldMapping.getObfType(), matches)) { | 198 | if (canMigrate(oldFieldMapping.getObfType(), matches)) { |
| 198 | newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); | 199 | newClassMapping.addFieldMapping(new FieldMapping(oldFieldMapping, replacer)); |
| 199 | } else { | 200 | } else { |
| 200 | System.out.println(String.format("Can't map field, dropping: %s.%s %s", | 201 | System.out.println(String.format("Can't map field, dropping: %s.%s %s", |
| 201 | oldClassMapping.getDeobfName(), | 202 | oldClassMapping.getDeobfName(), |
| 202 | oldFieldMapping.getDeobfName(), | 203 | oldFieldMapping.getDeobfName(), |
| 203 | oldFieldMapping.getObfType() | 204 | oldFieldMapping.getObfType() |
| 204 | )); | 205 | )); |
| 205 | } | 206 | } |
| 206 | } | 207 | } |
| 207 | 208 | ||
| 208 | // migrate methods | 209 | // migrate methods |
| 209 | for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { | 210 | for (MethodMapping oldMethodMapping : oldClassMapping.methods()) { |
| 210 | if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { | 211 | if (canMigrate(oldMethodMapping.getObfSignature(), matches)) { |
| 211 | newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); | 212 | newClassMapping.addMethodMapping(new MethodMapping(oldMethodMapping, replacer)); |
| 212 | } else { | 213 | } else { |
| 213 | System.out.println(String.format("Can't map method, dropping: %s.%s %s", | 214 | System.out.println(String.format("Can't map method, dropping: %s.%s %s", |
| 214 | oldClassMapping.getDeobfName(), | 215 | oldClassMapping.getDeobfName(), |
| 215 | oldMethodMapping.getDeobfName(), | 216 | oldMethodMapping.getDeobfName(), |
| 216 | oldMethodMapping.getObfSignature() | 217 | oldMethodMapping.getObfSignature() |
| 217 | )); | 218 | )); |
| 218 | } | 219 | } |
| 219 | } | 220 | } |
| 220 | 221 | ||
| 221 | return newClassMapping; | 222 | return newClassMapping; |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { | 225 | private static boolean canMigrate(Signature oldObfSignature, ClassMatches classMatches) { |
| 225 | for (Type oldObfType : oldObfSignature.types()) { | 226 | for (Type oldObfType : oldObfSignature.types()) { |
| 226 | if (!canMigrate(oldObfType, classMatches)) { | 227 | if (!canMigrate(oldObfType, classMatches)) { |
| 227 | return false; | 228 | return false; |
| 228 | } | 229 | } |
| 229 | } | 230 | } |
| 230 | return true; | 231 | return true; |
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { | 234 | private static boolean canMigrate(Type oldObfType, ClassMatches classMatches) { |
| 234 | 235 | ||
| 235 | // non classes can be migrated | 236 | // non classes can be migrated |
| 236 | if (!oldObfType.hasClass()) { | 237 | if (!oldObfType.hasClass()) { |
| 237 | return true; | 238 | return true; |
| 238 | } | 239 | } |
| 239 | 240 | ||
| 240 | // non obfuscated classes can be migrated | 241 | // non obfuscated classes can be migrated |
| 241 | ClassEntry classEntry = oldObfType.getClassEntry(); | 242 | ClassEntry classEntry = oldObfType.getClassEntry(); |
| 242 | if (classEntry.getPackageName() != null) { | 243 | if (classEntry.getPackageName() != null) { |
| 243 | return true; | 244 | return true; |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 246 | // obfuscated classes with mappings can be migrated | 247 | // obfuscated classes with mappings can be migrated |
| 247 | return classMatches.getUniqueMatches().containsKey(classEntry); | 248 | return classMatches.getUniqueMatches().containsKey(classEntry); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { | 251 | public static void convertMappings(Mappings mappings, BiMap<ClassEntry, ClassEntry> changes) { |
| 251 | 252 | ||
| 252 | // sort the changes so classes are renamed in the correct order | 253 | // sort the changes so classes are renamed in the correct order |
| 253 | // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b | 254 | // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b |
| 254 | LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); | 255 | LinkedHashMap<ClassEntry, ClassEntry> sortedChanges = Maps.newLinkedHashMap(); |
| 255 | int numChangesLeft = changes.size(); | 256 | int numChangesLeft = changes.size(); |
| 256 | while (!changes.isEmpty()) { | 257 | while (!changes.isEmpty()) { |
| 257 | Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); | 258 | Iterator<Map.Entry<ClassEntry, ClassEntry>> iter = changes.entrySet().iterator(); |
| 258 | while (iter.hasNext()) { | 259 | while (iter.hasNext()) { |
| 259 | Map.Entry<ClassEntry, ClassEntry> change = iter.next(); | 260 | Map.Entry<ClassEntry, ClassEntry> change = iter.next(); |
| 260 | if (changes.containsKey(change.getValue())) { | 261 | if (changes.containsKey(change.getValue())) { |
| 261 | sortedChanges.put(change.getKey(), change.getValue()); | 262 | sortedChanges.put(change.getKey(), change.getValue()); |
| 262 | iter.remove(); | 263 | iter.remove(); |
| 263 | } | 264 | } |
| 264 | } | 265 | } |
| 265 | 266 | ||
| 266 | // did we remove any changes? | 267 | // did we remove any changes? |
| 267 | if (numChangesLeft - changes.size() > 0) { | 268 | if (numChangesLeft - changes.size() > 0) { |
| 268 | // keep going | 269 | // keep going |
| 269 | numChangesLeft = changes.size(); | 270 | numChangesLeft = changes.size(); |
| 270 | } else { | 271 | } else { |
| 271 | // can't sort anymore. There must be a loop | 272 | // can't sort anymore. There must be a loop |
| 272 | break; | 273 | break; |
| 273 | } | 274 | } |
| 274 | } | 275 | } |
| 275 | if (!changes.isEmpty()) { | 276 | if (!changes.isEmpty()) { |
| 276 | throw new Error("Unable to sort class changes! There must be a cycle."); | 277 | throw new Error("Unable to sort class changes! There must be a cycle."); |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | // convert the mappings in the correct class order | 280 | // convert the mappings in the correct class order |
| 280 | for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { | 281 | for (Map.Entry<ClassEntry, ClassEntry> entry : sortedChanges.entrySet()) { |
| 281 | mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); | 282 | mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); |
| 282 | } | 283 | } |
| 283 | } | 284 | } |
| 284 | 285 | ||
| 285 | public interface Doer<T extends Entry> { | 286 | public static Doer<FieldEntry> getFieldDoer() { |
| 286 | Collection<T> getDroppedEntries(MappingsChecker checker); | 287 | return new Doer<FieldEntry>() { |
| 287 | 288 | ||
| 288 | Collection<T> getObfEntries(JarIndex jarIndex); | 289 | @Override |
| 289 | 290 | public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { | |
| 290 | Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); | 291 | return checker.getDroppedFieldMappings().keySet(); |
| 291 | 292 | } | |
| 292 | Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); | 293 | |
| 293 | 294 | @Override | |
| 294 | void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); | 295 | public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { |
| 295 | 296 | return jarIndex.getObfFieldEntries(); | |
| 296 | boolean hasObfMember(ClassMapping classMapping, T obfEntry); | 297 | } |
| 297 | 298 | ||
| 298 | void removeMemberByObf(ClassMapping classMapping, T obfEntry); | 299 | @Override |
| 299 | } | 300 | public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { |
| 300 | 301 | return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); | |
| 301 | public static Doer<FieldEntry> getFieldDoer() { | 302 | } |
| 302 | return new Doer<FieldEntry>() { | 303 | |
| 303 | 304 | @Override | |
| 304 | @Override | 305 | public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { |
| 305 | public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) { | 306 | Set<FieldEntry> out = Sets.newHashSet(); |
| 306 | return checker.getDroppedFieldMappings().keySet(); | 307 | for (FieldEntry obfDestField : obfDestFields) { |
| 307 | } | 308 | Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); |
| 308 | 309 | if (translatedDestType.equals(obfSourceField.getType())) { | |
| 309 | @Override | 310 | out.add(obfDestField); |
| 310 | public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) { | 311 | } |
| 311 | return jarIndex.getObfFieldEntries(); | 312 | } |
| 312 | } | 313 | return out; |
| 313 | 314 | } | |
| 314 | @Override | 315 | |
| 315 | public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) { | 316 | @Override |
| 316 | return (Collection<? extends MemberMapping<FieldEntry>>) destClassMapping.fields(); | 317 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { |
| 317 | } | 318 | FieldMapping fieldMapping = (FieldMapping) memberMapping; |
| 318 | 319 | classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); | |
| 319 | @Override | 320 | } |
| 320 | public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) { | 321 | |
| 321 | Set<FieldEntry> out = Sets.newHashSet(); | 322 | @Override |
| 322 | for (FieldEntry obfDestField : obfDestFields) { | 323 | public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { |
| 323 | Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse()); | 324 | return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; |
| 324 | if (translatedDestType.equals(obfSourceField.getType())) { | 325 | } |
| 325 | out.add(obfDestField); | 326 | |
| 326 | } | 327 | @Override |
| 327 | } | 328 | public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { |
| 328 | return out; | 329 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); |
| 329 | } | 330 | } |
| 330 | 331 | }; | |
| 331 | @Override | 332 | } |
| 332 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<FieldEntry> memberMapping, FieldEntry newField) { | 333 | |
| 333 | FieldMapping fieldMapping = (FieldMapping) memberMapping; | 334 | public static Doer<BehaviorEntry> getMethodDoer() { |
| 334 | classMapping.setFieldObfNameAndType(fieldMapping.getObfName(), fieldMapping.getObfType(), newField.getName(), newField.getType()); | 335 | return new Doer<BehaviorEntry>() { |
| 335 | } | 336 | |
| 336 | 337 | @Override | |
| 337 | @Override | 338 | public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { |
| 338 | public boolean hasObfMember(ClassMapping classMapping, FieldEntry obfField) { | 339 | return checker.getDroppedMethodMappings().keySet(); |
| 339 | return classMapping.getFieldByObf(obfField.getName(), obfField.getType()) != null; | 340 | } |
| 340 | } | 341 | |
| 341 | 342 | @Override | |
| 342 | @Override | 343 | public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { |
| 343 | public void removeMemberByObf(ClassMapping classMapping, FieldEntry obfField) { | 344 | return jarIndex.getObfBehaviorEntries(); |
| 344 | classMapping.removeFieldMapping(classMapping.getFieldByObf(obfField.getName(), obfField.getType())); | 345 | } |
| 345 | } | 346 | |
| 346 | }; | 347 | @Override |
| 347 | } | 348 | public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { |
| 348 | 349 | return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); | |
| 349 | public static Doer<BehaviorEntry> getMethodDoer() { | 350 | } |
| 350 | return new Doer<BehaviorEntry>() { | 351 | |
| 351 | 352 | @Override | |
| 352 | @Override | 353 | public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { |
| 353 | public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) { | 354 | Set<BehaviorEntry> out = Sets.newHashSet(); |
| 354 | return checker.getDroppedMethodMappings().keySet(); | 355 | for (BehaviorEntry obfDestField : obfDestFields) { |
| 355 | } | 356 | // Try to translate the signature |
| 356 | 357 | Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); | |
| 357 | @Override | 358 | if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) |
| 358 | public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) { | 359 | out.add(obfDestField); |
| 359 | return jarIndex.getObfBehaviorEntries(); | 360 | } |
| 360 | } | 361 | return out; |
| 361 | 362 | } | |
| 362 | @Override | 363 | |
| 363 | public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) { | 364 | @Override |
| 364 | return (Collection<? extends MemberMapping<BehaviorEntry>>) destClassMapping.methods(); | 365 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { |
| 365 | } | 366 | MethodMapping methodMapping = (MethodMapping) memberMapping; |
| 366 | 367 | classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); | |
| 367 | @Override | 368 | } |
| 368 | public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) { | 369 | |
| 369 | Set<BehaviorEntry> out = Sets.newHashSet(); | 370 | @Override |
| 370 | for (BehaviorEntry obfDestField : obfDestFields) { | 371 | public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { |
| 371 | // Try to translate the signature | 372 | return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; |
| 372 | Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse()); | 373 | } |
| 373 | if (translatedDestSignature != null && obfSourceField.getSignature() != null && translatedDestSignature.equals(obfSourceField.getSignature())) | 374 | |
| 374 | out.add(obfDestField); | 375 | @Override |
| 375 | } | 376 | public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { |
| 376 | return out; | 377 | classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); |
| 377 | } | 378 | } |
| 378 | 379 | }; | |
| 379 | @Override | 380 | } |
| 380 | public void setUpdateObfMember(ClassMapping classMapping, MemberMapping<BehaviorEntry> memberMapping, BehaviorEntry newBehavior) { | 381 | |
| 381 | MethodMapping methodMapping = (MethodMapping) memberMapping; | 382 | public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) { |
| 382 | classMapping.setMethodObfNameAndSignature(methodMapping.getObfName(), methodMapping.getObfSignature(), newBehavior.getName(), newBehavior.getSignature()); | 383 | int sourcePos = 0; |
| 383 | } | 384 | int destPos = 0; |
| 384 | 385 | while (sourceIt.hasNext() && destIt.hasNext()) { | |
| 385 | @Override | 386 | try { |
| 386 | public boolean hasObfMember(ClassMapping classMapping, BehaviorEntry obfBehavior) { | 387 | sourcePos = sourceIt.next(); |
| 387 | return classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature()) != null; | 388 | destPos = destIt.next(); |
| 388 | } | 389 | if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) |
| 389 | 390 | return sourcePos; | |
| 390 | @Override | 391 | } catch (BadBytecode badBytecode) { |
| 391 | public void removeMemberByObf(ClassMapping classMapping, BehaviorEntry obfBehavior) { | 392 | // Ignore bad bytecode (it might be a little bit dangerous...) |
| 392 | classMapping.removeMethodMapping(classMapping.getMethodByObf(obfBehavior.getName(), obfBehavior.getSignature())); | 393 | } |
| 393 | } | 394 | } |
| 394 | }; | 395 | if (sourcePos < destPos) |
| 395 | } | 396 | return sourcePos; |
| 396 | 397 | else if (destPos < sourcePos) | |
| 397 | public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt) | 398 | return destPos; |
| 398 | { | 399 | return sourcePos; |
| 399 | int sourcePos = 0; | 400 | } |
| 400 | int destPos = 0; | 401 | |
| 401 | while (sourceIt.hasNext() && destIt.hasNext()) | 402 | public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, |
| 402 | { | 403 | Set<BehaviorEntry> obfDestEntries) { |
| 403 | try | 404 | try { |
| 404 | { | 405 | // Get the source method with Javassist |
| 405 | sourcePos = sourceIt.next(); | 406 | CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); |
| 406 | destPos = destIt.next(); | 407 | CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); |
| 407 | if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos)) | 408 | |
| 408 | return sourcePos; | 409 | // Empty method body, ignore! |
| 409 | } catch (BadBytecode badBytecode) | 410 | if (sourceAttribute == null) |
| 410 | { | 411 | return null; |
| 411 | // Ignore bad bytecode (it might be a little bit dangerous...) | 412 | for (BehaviorEntry desEntry : obfDestEntries) { |
| 412 | } | 413 | try { |
| 413 | } | 414 | CtMethod destCtClassMethod = destCtClass |
| 414 | if (sourcePos < destPos) | 415 | .getMethod(desEntry.getName(), desEntry.getSignature().toString()); |
| 415 | return sourcePos; | 416 | CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); |
| 416 | else if (destPos < sourcePos) | 417 | |
| 417 | return destPos; | 418 | // Ignore empty body methods |
| 418 | return sourcePos; | 419 | if (destAttribute == null) |
| 419 | } | 420 | continue; |
| 420 | 421 | CodeIterator destIterator = destAttribute.iterator(); | |
| 421 | public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry, | 422 | int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); |
| 422 | Set<BehaviorEntry> obfDestEntries) | 423 | |
| 423 | { | 424 | // The bytecode is identical to the original method, assuming that the method is correct! |
| 424 | try | 425 | if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) |
| 425 | { | 426 | return desEntry; |
| 426 | // Get the source method with Javassist | 427 | } catch (NotFoundException e) { |
| 427 | CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString()); | 428 | e.printStackTrace(); |
| 428 | CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute(); | 429 | } |
| 429 | 430 | } | |
| 430 | // Empty method body, ignore! | 431 | } catch (NotFoundException e) { |
| 431 | if (sourceAttribute == null) | 432 | e.printStackTrace(); |
| 432 | return null; | 433 | return null; |
| 433 | for (BehaviorEntry desEntry : obfDestEntries) | 434 | } |
| 434 | { | 435 | return null; |
| 435 | try | 436 | } |
| 436 | { | 437 | |
| 437 | CtMethod destCtClassMethod = destCtClass | 438 | public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, |
| 438 | .getMethod(desEntry.getName(), desEntry.getSignature().toString()); | 439 | Mappings destMappings, |
| 439 | CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute(); | 440 | Deobfuscator sourceDeobfuscator, |
| 440 | 441 | Mappings sourceMappings, | |
| 441 | // Ignore empty body methods | 442 | ClassMatches classMatches, |
| 442 | if (destAttribute == null) | 443 | Doer<BehaviorEntry> doer) { |
| 443 | continue; | 444 | |
| 444 | CodeIterator destIterator = destAttribute.iterator(); | 445 | MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); |
| 445 | int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator); | 446 | |
| 446 | 447 | // unmatched source fields are easy | |
| 447 | // The bytecode is identical to the original method, assuming that the method is correct! | 448 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); |
| 448 | if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1) | 449 | checker.dropBrokenMappings(destMappings); |
| 449 | return desEntry; | 450 | for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { |
| 450 | } catch (NotFoundException e) | 451 | BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); |
| 451 | { | 452 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); |
| 452 | e.printStackTrace(); | 453 | } |
| 453 | } | 454 | |
| 454 | } | 455 | // get matched fields (anything that's left after the checks/drops is matched( |
| 455 | } catch (NotFoundException e) | 456 | for (ClassMapping classMapping : destMappings.classes()) |
| 456 | { | 457 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); |
| 457 | e.printStackTrace(); | 458 | |
| 458 | return null; | 459 | // get unmatched dest fields |
| 459 | } | 460 | doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() |
| 460 | return null; | 461 | .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) |
| 461 | } | 462 | .forEach(memberMatches::addUnmatchedDestEntry); |
| 462 | 463 | ||
| 463 | public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, Mappings destMappings, Deobfuscator sourceDeobfuscator, Mappings sourceMappings, ClassMatches classMatches, Doer<BehaviorEntry> doer) { | 464 | // Apply mappings to deobfuscator |
| 464 | 465 | ||
| 465 | MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>(); | 466 | // Create type loader |
| 466 | 467 | TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); | |
| 467 | // unmatched source fields are easy | 468 | TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); |
| 468 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 469 | |
| 469 | checker.dropBrokenMappings(destMappings); | 470 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); |
| 470 | for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) { | 471 | |
| 471 | BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); | 472 | // go through the unmatched source fields and try to pick out the easy matches |
| 472 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); | 473 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { |
| 473 | } | 474 | for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { |
| 474 | 475 | ||
| 475 | // get matched fields (anything that's left after the checks/drops is matched( | 476 | // get the possible dest matches |
| 476 | for (ClassMapping classMapping : destMappings.classes()) | 477 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); |
| 477 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); | 478 | |
| 478 | 479 | // filter by type/signature | |
| 479 | // get unmatched dest fields | 480 | Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); |
| 480 | doer.getObfEntries(destDeobfuscator.getJarIndex()).stream() | 481 | |
| 481 | .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry)) | 482 | if (obfDestEntries.size() == 1) { |
| 482 | .forEach(memberMatches::addUnmatchedDestEntry); | 483 | // make the easy match |
| 483 | 484 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | |
| 484 | // Apply mappings to deobfuscator | 485 | } else if (obfDestEntries.isEmpty()) { |
| 485 | 486 | // no match is possible =( | |
| 486 | // Create type loader | 487 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); |
| 487 | TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader(); | 488 | } else { |
| 488 | TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader(); | 489 | // Multiple matches! Scan methods instructions |
| 489 | 490 | CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); | |
| 490 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); | 491 | CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); |
| 491 | 492 | BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); | |
| 492 | // go through the unmatched source fields and try to pick out the easy matches | 493 | // the method match correctly, match it on the member mapping! |
| 493 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { | 494 | if (match != null) |
| 494 | for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { | 495 | memberMatches.makeMatch(obfSourceEntry, match); |
| 495 | 496 | } | |
| 496 | // get the possible dest matches | 497 | } |
| 497 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 498 | } |
| 498 | 499 | ||
| 499 | // filter by type/signature | 500 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", |
| 500 | Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); | 501 | memberMatches.getUnmatchedSourceEntries().size(), |
| 501 | 502 | memberMatches.getUnmatchableSourceEntries().size() | |
| 502 | if (obfDestEntries.size() == 1) { | 503 | )); |
| 503 | // make the easy match | 504 | |
| 504 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | 505 | return memberMatches; |
| 505 | } else if (obfDestEntries.isEmpty()) { | 506 | } |
| 506 | // no match is possible =( | 507 | |
| 507 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); | 508 | public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { |
| 508 | } else | 509 | |
| 509 | { | 510 | MemberMatches<T> memberMatches = new MemberMatches<>(); |
| 510 | // Multiple matches! Scan methods instructions | 511 | |
| 511 | CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName()); | 512 | // unmatched source fields are easy |
| 512 | CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName()); | 513 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); |
| 513 | BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries); | 514 | checker.dropBrokenMappings(destMappings); |
| 514 | // the method match correctly, match it on the member mapping! | 515 | for (T destObfEntry : doer.getDroppedEntries(checker)) { |
| 515 | if (match != null) | 516 | T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); |
| 516 | memberMatches.makeMatch(obfSourceEntry, match); | 517 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); |
| 517 | } | 518 | } |
| 518 | } | 519 | |
| 519 | } | 520 | // get matched fields (anything that's left after the checks/drops is matched( |
| 520 | 521 | for (ClassMapping classMapping : destMappings.classes()) { | |
| 521 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", | 522 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); |
| 522 | memberMatches.getUnmatchedSourceEntries().size(), | 523 | } |
| 523 | memberMatches.getUnmatchableSourceEntries().size() | 524 | |
| 524 | )); | 525 | // get unmatched dest fields |
| 525 | 526 | for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { | |
| 526 | return memberMatches; | 527 | if (!memberMatches.isMatchedDestEntry(destEntry)) { |
| 527 | } | 528 | memberMatches.addUnmatchedDestEntry(destEntry); |
| 528 | 529 | } | |
| 529 | public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { | 530 | } |
| 530 | 531 | ||
| 531 | MemberMatches<T> memberMatches = new MemberMatches<>(); | 532 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); |
| 532 | 533 | ||
| 533 | // unmatched source fields are easy | 534 | // go through the unmatched source fields and try to pick out the easy matches |
| 534 | MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); | 535 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { |
| 535 | checker.dropBrokenMappings(destMappings); | 536 | for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { |
| 536 | for (T destObfEntry : doer.getDroppedEntries(checker)) { | 537 | |
| 537 | T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse()); | 538 | // get the possible dest matches |
| 538 | memberMatches.addUnmatchedSourceEntry(srcObfEntry); | 539 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); |
| 539 | } | 540 | |
| 540 | 541 | // filter by type/signature | |
| 541 | // get matched fields (anything that's left after the checks/drops is matched( | 542 | Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); |
| 542 | for (ClassMapping classMapping : destMappings.classes()) { | 543 | |
| 543 | collectMatchedFields(memberMatches, classMapping, classMatches, doer); | 544 | if (obfDestEntries.size() == 1) { |
| 544 | } | 545 | // make the easy match |
| 545 | 546 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | |
| 546 | // get unmatched dest fields | 547 | } else if (obfDestEntries.isEmpty()) { |
| 547 | for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) { | 548 | // no match is possible =( |
| 548 | if (!memberMatches.isMatchedDestEntry(destEntry)) { | 549 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); |
| 549 | memberMatches.addUnmatchedDestEntry(destEntry); | 550 | } |
| 550 | } | 551 | } |
| 551 | } | 552 | } |
| 552 | 553 | ||
| 553 | System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries..."); | 554 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", |
| 554 | 555 | memberMatches.getUnmatchedSourceEntries().size(), | |
| 555 | // go through the unmatched source fields and try to pick out the easy matches | 556 | memberMatches.getUnmatchableSourceEntries().size() |
| 556 | for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) { | 557 | )); |
| 557 | for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) { | 558 | |
| 558 | 559 | return memberMatches; | |
| 559 | // get the possible dest matches | 560 | } |
| 560 | ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); | 561 | |
| 561 | 562 | private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { | |
| 562 | // filter by type/signature | 563 | |
| 563 | Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches); | 564 | // get the fields for this class |
| 564 | 565 | for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { | |
| 565 | if (obfDestEntries.size() == 1) { | 566 | T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); |
| 566 | // make the easy match | 567 | T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); |
| 567 | memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next()); | 568 | memberMatches.addMatch(srcObfField, destObfField); |
| 568 | } else if (obfDestEntries.isEmpty()) { | 569 | } |
| 569 | // no match is possible =( | 570 | |
| 570 | memberMatches.makeSourceUnmatchable(obfSourceEntry, null); | 571 | // recurse |
| 571 | } | 572 | for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { |
| 572 | } | 573 | collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); |
| 573 | } | 574 | } |
| 574 | 575 | } | |
| 575 | System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries", | 576 | |
| 576 | memberMatches.getUnmatchedSourceEntries().size(), | 577 | @SuppressWarnings("unchecked") |
| 577 | memberMatches.getUnmatchableSourceEntries().size() | 578 | private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { |
| 578 | )); | 579 | if (in instanceof FieldEntry) { |
| 579 | 580 | return (T) new FieldEntry( | |
| 580 | return memberMatches; | 581 | map.get(in.getClassEntry()), |
| 581 | } | 582 | in.getName(), |
| 582 | 583 | translate(((FieldEntry) in).getType(), map) | |
| 583 | private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) { | 584 | ); |
| 584 | 585 | } else if (in instanceof MethodEntry) { | |
| 585 | // get the fields for this class | 586 | return (T) new MethodEntry( |
| 586 | for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) { | 587 | map.get(in.getClassEntry()), |
| 587 | T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry()); | 588 | in.getName(), |
| 588 | T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); | 589 | translate(((MethodEntry) in).getSignature(), map) |
| 589 | memberMatches.addMatch(srcObfField, destObfField); | 590 | ); |
| 590 | } | 591 | } else if (in instanceof ConstructorEntry) { |
| 591 | 592 | return (T) new ConstructorEntry( | |
| 592 | // recurse | 593 | map.get(in.getClassEntry()), |
| 593 | for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { | 594 | translate(((ConstructorEntry) in).getSignature(), map) |
| 594 | collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer); | 595 | ); |
| 595 | } | 596 | } |
| 596 | } | 597 | throw new Error("Unhandled entry type: " + in.getClass()); |
| 597 | 598 | } | |
| 598 | @SuppressWarnings("unchecked") | 599 | |
| 599 | private static <T extends Entry> T translate(T in, BiMap<ClassEntry, ClassEntry> map) { | 600 | private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { |
| 600 | if (in instanceof FieldEntry) { | 601 | return new Type(type, inClassName -> |
| 601 | return (T) new FieldEntry( | 602 | { |
| 602 | map.get(in.getClassEntry()), | 603 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); |
| 603 | in.getName(), | 604 | if (outClassEntry == null) { |
| 604 | translate(((FieldEntry) in).getType(), map) | 605 | return null; |
| 605 | ); | 606 | } |
| 606 | } else if (in instanceof MethodEntry) { | 607 | return outClassEntry.getName(); |
| 607 | return (T) new MethodEntry( | 608 | }); |
| 608 | map.get(in.getClassEntry()), | 609 | } |
| 609 | in.getName(), | 610 | |
| 610 | translate(((MethodEntry) in).getSignature(), map) | 611 | private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { |
| 611 | ); | 612 | if (signature == null) { |
| 612 | } else if (in instanceof ConstructorEntry) { | 613 | return null; |
| 613 | return (T) new ConstructorEntry( | 614 | } |
| 614 | map.get(in.getClassEntry()), | 615 | return new Signature(signature, inClassName -> |
| 615 | translate(((ConstructorEntry) in).getSignature(), map) | 616 | { |
| 616 | ); | 617 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); |
| 617 | } | 618 | if (outClassEntry == null) { |
| 618 | throw new Error("Unhandled entry type: " + in.getClass()); | 619 | return null; |
| 619 | } | 620 | } |
| 620 | 621 | return outClassEntry.getName(); | |
| 621 | private static Type translate(Type type, final BiMap<ClassEntry, ClassEntry> map) { | 622 | }); |
| 622 | return new Type(type, inClassName -> | 623 | } |
| 623 | { | 624 | |
| 624 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); | 625 | public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { |
| 625 | if (outClassEntry == null) { | 626 | for (ClassMapping classMapping : mappings.classes()) { |
| 626 | return null; | 627 | applyMemberMatches(classMapping, classMatches, memberMatches, doer); |
| 627 | } | 628 | } |
| 628 | return outClassEntry.getName(); | 629 | } |
| 629 | }); | 630 | |
| 630 | } | 631 | private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { |
| 631 | 632 | ||
| 632 | private static Signature translate(Signature signature, final BiMap<ClassEntry, ClassEntry> map) { | 633 | // get the classes |
| 633 | if (signature == null) { | 634 | ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); |
| 634 | return null; | 635 | |
| 635 | } | 636 | // make a map of all the renames we need to make |
| 636 | return new Signature(signature, inClassName -> | 637 | Map<T, T> renames = Maps.newHashMap(); |
| 637 | { | 638 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { |
| 638 | ClassEntry outClassEntry = map.get(new ClassEntry(inClassName)); | 639 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); |
| 639 | if (outClassEntry == null) { | 640 | T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); |
| 640 | return null; | 641 | |
| 641 | } | 642 | // but drop the unmatchable things |
| 642 | return outClassEntry.getName(); | 643 | if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { |
| 643 | }); | 644 | doer.removeMemberByObf(classMapping, obfOldDestEntry); |
| 644 | } | 645 | continue; |
| 645 | 646 | } | |
| 646 | public static <T extends Entry> void applyMemberMatches(Mappings mappings, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { | 647 | |
| 647 | for (ClassMapping classMapping : mappings.classes()) { | 648 | T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); |
| 648 | applyMemberMatches(classMapping, classMatches, memberMatches, doer); | 649 | if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { |
| 649 | } | 650 | renames.put(obfOldDestEntry, obfNewDestEntry); |
| 650 | } | 651 | } |
| 651 | 652 | } | |
| 652 | private static <T extends Entry> void applyMemberMatches(ClassMapping classMapping, ClassMatches classMatches, MemberMatches<T> memberMatches, Doer<T> doer) { | 653 | |
| 653 | 654 | if (!renames.isEmpty()) { | |
| 654 | // get the classes | 655 | |
| 655 | ClassEntry obfDestClass = new ClassEntry(classMapping.getObfFullName()); | 656 | // apply to this class (should never need more than n passes) |
| 656 | 657 | int numRenamesAppliedThisRound; | |
| 657 | // make a map of all the renames we need to make | 658 | do { |
| 658 | Map<T, T> renames = Maps.newHashMap(); | 659 | numRenamesAppliedThisRound = 0; |
| 659 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { | 660 | |
| 660 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); | 661 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { |
| 661 | T obfSourceEntry = getSourceEntryFromDestMapping(memberMapping, obfDestClass, classMatches); | 662 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); |
| 662 | 663 | T obfNewDestEntry = renames.get(obfOldDestEntry); | |
| 663 | // but drop the unmatchable things | 664 | if (obfNewDestEntry != null) { |
| 664 | if (memberMatches.isUnmatchableSourceEntry(obfSourceEntry)) { | 665 | // make sure this rename won't cause a collision |
| 665 | doer.removeMemberByObf(classMapping, obfOldDestEntry); | 666 | // otherwise, save it for the next round and try again next time |
| 666 | continue; | 667 | if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { |
| 667 | } | 668 | doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); |
| 668 | 669 | renames.remove(obfOldDestEntry); | |
| 669 | T obfNewDestEntry = memberMatches.matches().get(obfSourceEntry); | 670 | numRenamesAppliedThisRound++; |
| 670 | if (obfNewDestEntry != null && !obfOldDestEntry.getName().equals(obfNewDestEntry.getName())) { | 671 | } |
| 671 | renames.put(obfOldDestEntry, obfNewDestEntry); | 672 | } |
| 672 | } | 673 | } |
| 673 | } | 674 | } while (numRenamesAppliedThisRound > 0); |
| 674 | 675 | ||
| 675 | if (!renames.isEmpty()) { | 676 | if (!renames.isEmpty()) { |
| 676 | 677 | System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", | |
| 677 | // apply to this class (should never need more than n passes) | 678 | classMapping.getObfFullName(), renames.size() |
| 678 | int numRenamesAppliedThisRound; | 679 | )); |
| 679 | do { | 680 | for (Map.Entry<T, T> entry : renames.entrySet()) { |
| 680 | numRenamesAppliedThisRound = 0; | 681 | System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); |
| 681 | 682 | } | |
| 682 | for (MemberMapping<T> memberMapping : Lists.newArrayList(doer.getMappings(classMapping))) { | 683 | } |
| 683 | T obfOldDestEntry = memberMapping.getObfEntry(obfDestClass); | 684 | } |
| 684 | T obfNewDestEntry = renames.get(obfOldDestEntry); | 685 | |
| 685 | if (obfNewDestEntry != null) { | 686 | // recurse |
| 686 | // make sure this rename won't cause a collision | 687 | for (ClassMapping innerClassMapping : classMapping.innerClasses()) { |
| 687 | // otherwise, save it for the next round and try again next time | 688 | applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); |
| 688 | if (!doer.hasObfMember(classMapping, obfNewDestEntry)) { | 689 | } |
| 689 | doer.setUpdateObfMember(classMapping, memberMapping, obfNewDestEntry); | 690 | } |
| 690 | renames.remove(obfOldDestEntry); | 691 | |
| 691 | numRenamesAppliedThisRound++; | 692 | private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { |
| 692 | } | 693 | return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); |
| 693 | } | 694 | } |
| 694 | } | 695 | |
| 695 | } while (numRenamesAppliedThisRound > 0); | 696 | public interface Doer<T extends Entry> { |
| 696 | 697 | Collection<T> getDroppedEntries(MappingsChecker checker); | |
| 697 | if (!renames.isEmpty()) { | 698 | |
| 698 | System.err.println(String.format("WARNING: Couldn't apply all the renames for class %s. %d renames left.", | 699 | Collection<T> getObfEntries(JarIndex jarIndex); |
| 699 | classMapping.getObfFullName(), renames.size() | 700 | |
| 700 | )); | 701 | Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping); |
| 701 | for (Map.Entry<T, T> entry : renames.entrySet()) { | 702 | |
| 702 | System.err.println(String.format("\t%s -> %s", entry.getKey().getName(), entry.getValue().getName())); | 703 | Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches); |
| 703 | } | 704 | |
| 704 | } | 705 | void setUpdateObfMember(ClassMapping classMapping, MemberMapping<T> memberMapping, T newEntry); |
| 705 | } | 706 | |
| 706 | 707 | boolean hasObfMember(ClassMapping classMapping, T obfEntry); | |
| 707 | // recurse | 708 | |
| 708 | for (ClassMapping innerClassMapping : classMapping.innerClasses()) { | 709 | void removeMemberByObf(ClassMapping classMapping, T obfEntry); |
| 709 | applyMemberMatches(innerClassMapping, classMatches, memberMatches, doer); | 710 | } |
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | private static <T extends Entry> T getSourceEntryFromDestMapping(MemberMapping<T> destMemberMapping, ClassEntry obfDestClass, ClassMatches classMatches) { | ||
| 714 | return translate(destMemberMapping.getObfEntry(obfDestClass), classMatches.getUniqueMatches().inverse()); | ||
| 715 | } | ||
| 716 | } | 711 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesReader.java b/src/main/java/cuchaz/enigma/convert/MatchesReader.java index d86d6c2..1cf50fa 100644 --- a/src/main/java/cuchaz/enigma/convert/MatchesReader.java +++ b/src/main/java/cuchaz/enigma/convert/MatchesReader.java | |||
| @@ -8,99 +8,98 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import cuchaz.enigma.mapping.*; | ||
| 14 | 16 | ||
| 15 | import java.io.*; | 17 | import java.io.*; |
| 16 | import java.nio.charset.Charset; | 18 | import java.nio.charset.Charset; |
| 17 | import java.util.Collection; | 19 | import java.util.Collection; |
| 18 | import java.util.List; | 20 | import java.util.List; |
| 19 | 21 | ||
| 20 | import cuchaz.enigma.mapping.*; | ||
| 21 | |||
| 22 | |||
| 23 | public class MatchesReader { | 22 | public class MatchesReader { |
| 24 | 23 | ||
| 25 | public static ClassMatches readClasses(File file) | 24 | public static ClassMatches readClasses(File file) |
| 26 | throws IOException { | 25 | throws IOException { |
| 27 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { | 26 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { |
| 28 | ClassMatches matches = new ClassMatches(); | 27 | ClassMatches matches = new ClassMatches(); |
| 29 | String line; | 28 | String line; |
| 30 | while ((line = in.readLine()) != null) { | 29 | while ((line = in.readLine()) != null) { |
| 31 | matches.add(readClassMatch(line)); | 30 | matches.add(readClassMatch(line)); |
| 32 | } | 31 | } |
| 33 | return matches; | 32 | return matches; |
| 34 | } | 33 | } |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | private static ClassMatch readClassMatch(String line) { | 36 | private static ClassMatch readClassMatch(String line) { |
| 38 | String[] sides = line.split(":", 2); | 37 | String[] sides = line.split(":", 2); |
| 39 | return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); | 38 | return new ClassMatch(readClasses(sides[0]), readClasses(sides[1])); |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | private static Collection<ClassEntry> readClasses(String in) { | 41 | private static Collection<ClassEntry> readClasses(String in) { |
| 43 | List<ClassEntry> entries = Lists.newArrayList(); | 42 | List<ClassEntry> entries = Lists.newArrayList(); |
| 44 | for (String className : in.split(",")) { | 43 | for (String className : in.split(",")) { |
| 45 | className = className.trim(); | 44 | className = className.trim(); |
| 46 | if (className.length() > 0) { | 45 | if (!className.isEmpty()) { |
| 47 | entries.add(new ClassEntry(className)); | 46 | entries.add(new ClassEntry(className)); |
| 48 | } | 47 | } |
| 49 | } | 48 | } |
| 50 | return entries; | 49 | return entries; |
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | public static <T extends Entry> MemberMatches<T> readMembers(File file) | 52 | public static <T extends Entry> MemberMatches<T> readMembers(File file) |
| 54 | throws IOException { | 53 | throws IOException { |
| 55 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { | 54 | try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")))) { |
| 56 | MemberMatches<T> matches = new MemberMatches<>(); | 55 | MemberMatches<T> matches = new MemberMatches<>(); |
| 57 | String line; | 56 | String line; |
| 58 | while ((line = in.readLine()) != null) { | 57 | while ((line = in.readLine()) != null) { |
| 59 | readMemberMatch(matches, line); | 58 | readMemberMatch(matches, line); |
| 60 | } | 59 | } |
| 61 | return matches; | 60 | return matches; |
| 62 | } | 61 | } |
| 63 | } | 62 | } |
| 64 | 63 | ||
| 65 | private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) { | 64 | private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) { |
| 66 | if (line.startsWith("!")) { | 65 | if (line.startsWith("!")) { |
| 67 | T source = readEntry(line.substring(1)); | 66 | T source = readEntry(line.substring(1)); |
| 68 | matches.addUnmatchableSourceEntry(source); | 67 | matches.addUnmatchableSourceEntry(source); |
| 69 | } else { | 68 | } else { |
| 70 | String[] parts = line.split(":", 2); | 69 | String[] parts = line.split(":", 2); |
| 71 | T source = readEntry(parts[0]); | 70 | T source = readEntry(parts[0]); |
| 72 | T dest = readEntry(parts[1]); | 71 | T dest = readEntry(parts[1]); |
| 73 | if (source != null && dest != null) { | 72 | if (source != null && dest != null) { |
| 74 | matches.addMatch(source, dest); | 73 | matches.addMatch(source, dest); |
| 75 | } else if (source != null) { | 74 | } else if (source != null) { |
| 76 | matches.addUnmatchedSourceEntry(source); | 75 | matches.addUnmatchedSourceEntry(source); |
| 77 | } else if (dest != null) { | 76 | } else if (dest != null) { |
| 78 | matches.addUnmatchedDestEntry(dest); | 77 | matches.addUnmatchedDestEntry(dest); |
| 79 | } | 78 | } |
| 80 | } | 79 | } |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | @SuppressWarnings("unchecked") | 82 | @SuppressWarnings("unchecked") |
| 84 | private static <T extends Entry> T readEntry(String in) { | 83 | private static <T extends Entry> T readEntry(String in) { |
| 85 | if (in.length() <= 0) { | 84 | if (in.length() <= 0) { |
| 86 | return null; | 85 | return null; |
| 87 | } | 86 | } |
| 88 | String[] parts = in.split(" "); | 87 | String[] parts = in.split(" "); |
| 89 | if (parts.length == 3 && parts[2].indexOf('(') < 0) { | 88 | if (parts.length == 3 && parts[2].indexOf('(') < 0) { |
| 90 | return (T) new FieldEntry( | 89 | return (T) new FieldEntry( |
| 91 | new ClassEntry(parts[0]), | 90 | new ClassEntry(parts[0]), |
| 92 | parts[1], | 91 | parts[1], |
| 93 | new Type(parts[2]) | 92 | new Type(parts[2]) |
| 94 | ); | 93 | ); |
| 95 | } else { | 94 | } else { |
| 96 | assert (parts.length == 2 || parts.length == 3); | 95 | assert (parts.length == 2 || parts.length == 3); |
| 97 | if (parts.length == 2) { | 96 | if (parts.length == 2) { |
| 98 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); | 97 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1]); |
| 99 | } else if (parts.length == 3) { | 98 | } else if (parts.length == 3) { |
| 100 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); | 99 | return (T) EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]); |
| 101 | } else { | 100 | } else { |
| 102 | throw new Error("Malformed behavior entry: " + in); | 101 | throw new Error("Malformed behavior entry: " + in); |
| 103 | } | 102 | } |
| 104 | } | 103 | } |
| 105 | } | 104 | } |
| 106 | } | 105 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java index dccbf6f..8fe7326 100644 --- a/src/main/java/cuchaz/enigma/convert/MatchesWriter.java +++ b/src/main/java/cuchaz/enigma/convert/MatchesWriter.java | |||
| @@ -8,113 +8,116 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.convert; | ||
| 12 | 11 | ||
| 13 | import java.io.*; | 12 | package cuchaz.enigma.convert; |
| 14 | import java.nio.charset.Charset; | ||
| 15 | import java.util.Map; | ||
| 16 | 13 | ||
| 17 | import cuchaz.enigma.mapping.BehaviorEntry; | 14 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 18 | import cuchaz.enigma.mapping.ClassEntry; | 15 | import cuchaz.enigma.mapping.ClassEntry; |
| 19 | import cuchaz.enigma.mapping.Entry; | 16 | import cuchaz.enigma.mapping.Entry; |
| 20 | import cuchaz.enigma.mapping.FieldEntry; | 17 | import cuchaz.enigma.mapping.FieldEntry; |
| 21 | 18 | ||
| 19 | import java.io.File; | ||
| 20 | import java.io.FileOutputStream; | ||
| 21 | import java.io.IOException; | ||
| 22 | import java.io.OutputStreamWriter; | ||
| 23 | import java.nio.charset.Charset; | ||
| 24 | import java.util.Map; | ||
| 22 | 25 | ||
| 23 | public class MatchesWriter { | 26 | public class MatchesWriter { |
| 24 | 27 | ||
| 25 | public static void writeClasses(ClassMatches matches, File file) | 28 | public static void writeClasses(ClassMatches matches, File file) |
| 26 | throws IOException { | 29 | throws IOException { |
| 27 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { | 30 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { |
| 28 | for (ClassMatch match : matches) { | 31 | for (ClassMatch match : matches) { |
| 29 | writeClassMatch(out, match); | 32 | writeClassMatch(out, match); |
| 30 | } | 33 | } |
| 31 | } | 34 | } |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) | 37 | private static void writeClassMatch(OutputStreamWriter out, ClassMatch match) |
| 35 | throws IOException { | 38 | throws IOException { |
| 36 | writeClasses(out, match.sourceClasses); | 39 | writeClasses(out, match.sourceClasses); |
| 37 | out.write(":"); | 40 | out.write(":"); |
| 38 | writeClasses(out, match.destClasses); | 41 | writeClasses(out, match.destClasses); |
| 39 | out.write("\n"); | 42 | out.write("\n"); |
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes) | 45 | private static void writeClasses(OutputStreamWriter out, Iterable<ClassEntry> classes) |
| 43 | throws IOException { | 46 | throws IOException { |
| 44 | boolean isFirst = true; | 47 | boolean isFirst = true; |
| 45 | for (ClassEntry entry : classes) { | 48 | for (ClassEntry entry : classes) { |
| 46 | if (isFirst) { | 49 | if (isFirst) { |
| 47 | isFirst = false; | 50 | isFirst = false; |
| 48 | } else { | 51 | } else { |
| 49 | out.write(","); | 52 | out.write(","); |
| 50 | } | 53 | } |
| 51 | out.write(entry.toString()); | 54 | out.write(entry.toString()); |
| 52 | } | 55 | } |
| 53 | } | 56 | } |
| 54 | 57 | ||
| 55 | public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file) | 58 | public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file) |
| 56 | throws IOException { | 59 | throws IOException { |
| 57 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { | 60 | try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"))) { |
| 58 | for (Map.Entry<T, T> match : matches.matches().entrySet()) { | 61 | for (Map.Entry<T, T> match : matches.matches().entrySet()) { |
| 59 | writeMemberMatch(out, match.getKey(), match.getValue()); | 62 | writeMemberMatch(out, match.getKey(), match.getValue()); |
| 60 | } | 63 | } |
| 61 | for (T entry : matches.getUnmatchedSourceEntries()) { | 64 | for (T entry : matches.getUnmatchedSourceEntries()) { |
| 62 | writeMemberMatch(out, entry, null); | 65 | writeMemberMatch(out, entry, null); |
| 63 | } | 66 | } |
| 64 | for (T entry : matches.getUnmatchedDestEntries()) { | 67 | for (T entry : matches.getUnmatchedDestEntries()) { |
| 65 | writeMemberMatch(out, null, entry); | 68 | writeMemberMatch(out, null, entry); |
| 66 | } | 69 | } |
| 67 | for (T entry : matches.getUnmatchableSourceEntries()) { | 70 | for (T entry : matches.getUnmatchableSourceEntries()) { |
| 68 | writeUnmatchableEntry(out, entry); | 71 | writeUnmatchableEntry(out, entry); |
| 69 | } | 72 | } |
| 70 | } | 73 | } |
| 71 | } | 74 | } |
| 72 | 75 | ||
| 73 | private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest) | 76 | private static <T extends Entry> void writeMemberMatch(OutputStreamWriter out, T source, T dest) |
| 74 | throws IOException { | 77 | throws IOException { |
| 75 | if (source != null) { | 78 | if (source != null) { |
| 76 | writeEntry(out, source); | 79 | writeEntry(out, source); |
| 77 | } | 80 | } |
| 78 | out.write(":"); | 81 | out.write(":"); |
| 79 | if (dest != null) { | 82 | if (dest != null) { |
| 80 | writeEntry(out, dest); | 83 | writeEntry(out, dest); |
| 81 | } | 84 | } |
| 82 | out.write("\n"); | 85 | out.write("\n"); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry) | 88 | private static <T extends Entry> void writeUnmatchableEntry(OutputStreamWriter out, T entry) |
| 86 | throws IOException { | 89 | throws IOException { |
| 87 | out.write("!"); | 90 | out.write("!"); |
| 88 | writeEntry(out, entry); | 91 | writeEntry(out, entry); |
| 89 | out.write("\n"); | 92 | out.write("\n"); |
| 90 | } | 93 | } |
| 91 | 94 | ||
| 92 | private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry) | 95 | private static <T extends Entry> void writeEntry(OutputStreamWriter out, T entry) |
| 93 | throws IOException { | 96 | throws IOException { |
| 94 | if (entry instanceof FieldEntry) { | 97 | if (entry instanceof FieldEntry) { |
| 95 | writeField(out, (FieldEntry) entry); | 98 | writeField(out, (FieldEntry) entry); |
| 96 | } else if (entry instanceof BehaviorEntry) { | 99 | } else if (entry instanceof BehaviorEntry) { |
| 97 | writeBehavior(out, (BehaviorEntry) entry); | 100 | writeBehavior(out, (BehaviorEntry) entry); |
| 98 | } | 101 | } |
| 99 | } | 102 | } |
| 100 | 103 | ||
| 101 | private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) | 104 | private static void writeField(OutputStreamWriter out, FieldEntry fieldEntry) |
| 102 | throws IOException { | 105 | throws IOException { |
| 103 | out.write(fieldEntry.getClassName()); | 106 | out.write(fieldEntry.getClassName()); |
| 104 | out.write(" "); | 107 | out.write(" "); |
| 105 | out.write(fieldEntry.getName()); | 108 | out.write(fieldEntry.getName()); |
| 106 | out.write(" "); | 109 | out.write(" "); |
| 107 | out.write(fieldEntry.getType().toString()); | 110 | out.write(fieldEntry.getType().toString()); |
| 108 | } | 111 | } |
| 109 | 112 | ||
| 110 | private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) | 113 | private static void writeBehavior(OutputStreamWriter out, BehaviorEntry behaviorEntry) |
| 111 | throws IOException { | 114 | throws IOException { |
| 112 | out.write(behaviorEntry.getClassName()); | 115 | out.write(behaviorEntry.getClassName()); |
| 113 | out.write(" "); | 116 | out.write(" "); |
| 114 | out.write(behaviorEntry.getName()); | 117 | out.write(behaviorEntry.getName()); |
| 115 | out.write(" "); | 118 | out.write(" "); |
| 116 | if (behaviorEntry.getSignature() != null) { | 119 | if (behaviorEntry.getSignature() != null) { |
| 117 | out.write(behaviorEntry.getSignature().toString()); | 120 | out.write(behaviorEntry.getSignature().toString()); |
| 118 | } | 121 | } |
| 119 | } | 122 | } |
| 120 | } | 123 | } |
diff --git a/src/main/java/cuchaz/enigma/convert/MemberMatches.java b/src/main/java/cuchaz/enigma/convert/MemberMatches.java index 51cee85..bd74311 100644 --- a/src/main/java/cuchaz/enigma/convert/MemberMatches.java +++ b/src/main/java/cuchaz/enigma/convert/MemberMatches.java | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Contributors: | 8 | * Contributors: |
| 9 | * Jeff Martin - initial API and implementation | 9 | * Jeff Martin - initial API and implementation |
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | |||
| 11 | package cuchaz.enigma.convert; | 12 | package cuchaz.enigma.convert; |
| 12 | 13 | ||
| 13 | import com.google.common.collect.*; | 14 | import com.google.common.collect.*; |
| @@ -18,165 +19,161 @@ import cuchaz.enigma.mapping.Entry; | |||
| 18 | import java.util.Collection; | 19 | import java.util.Collection; |
| 19 | import java.util.Set; | 20 | import java.util.Set; |
| 20 | 21 | ||
| 21 | |||
| 22 | public class MemberMatches<T extends Entry> { | 22 | public class MemberMatches<T extends Entry> { |
| 23 | 23 | ||
| 24 | private BiMap<T, T> matches; | 24 | private BiMap<T, T> matches; |
| 25 | private Multimap<ClassEntry, T> matchedSourceEntries; | 25 | private Multimap<ClassEntry, T> matchedSourceEntries; |
| 26 | private Multimap<ClassEntry, T> unmatchedSourceEntries; | 26 | private Multimap<ClassEntry, T> unmatchedSourceEntries; |
| 27 | private Multimap<ClassEntry, T> unmatchedDestEntries; | 27 | private Multimap<ClassEntry, T> unmatchedDestEntries; |
| 28 | private Multimap<ClassEntry, T> unmatchableSourceEntries; | 28 | private Multimap<ClassEntry, T> unmatchableSourceEntries; |
| 29 | 29 | ||
| 30 | public MemberMatches() { | 30 | public MemberMatches() { |
| 31 | matches = HashBiMap.create(); | 31 | matches = HashBiMap.create(); |
| 32 | matchedSourceEntries = HashMultimap.create(); | 32 | matchedSourceEntries = HashMultimap.create(); |
| 33 | unmatchedSourceEntries = HashMultimap.create(); | 33 | unmatchedSourceEntries = HashMultimap.create(); |
| 34 | unmatchedDestEntries = HashMultimap.create(); | 34 | unmatchedDestEntries = HashMultimap.create(); |
| 35 | unmatchableSourceEntries = HashMultimap.create(); | 35 | unmatchableSourceEntries = HashMultimap.create(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | public void addMatch(T srcEntry, T destEntry) { | 38 | public void addMatch(T srcEntry, T destEntry) { |
| 39 | boolean wasAdded = matches.put(srcEntry, destEntry) == null; | 39 | boolean wasAdded = matches.put(srcEntry, destEntry) == null; |
| 40 | assert (wasAdded); | 40 | assert (wasAdded); |
| 41 | wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); | 41 | wasAdded = matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry); |
| 42 | assert (wasAdded); | 42 | assert (wasAdded); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | public void addUnmatchedSourceEntry(T sourceEntry) { | 45 | public void addUnmatchedSourceEntry(T sourceEntry) { |
| 46 | boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); | 46 | boolean wasAdded = unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); |
| 47 | assert (wasAdded); | 47 | assert (wasAdded); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) { | 50 | public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) { |
| 51 | for (T sourceEntry : sourceEntries) { | 51 | for (T sourceEntry : sourceEntries) { |
| 52 | addUnmatchedSourceEntry(sourceEntry); | 52 | addUnmatchedSourceEntry(sourceEntry); |
| 53 | } | 53 | } |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | public void addUnmatchedDestEntry(T destEntry) { | 56 | public void addUnmatchedDestEntry(T destEntry) { |
| 57 | if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>")) | 57 | if (destEntry.getName().equals("<clinit>") || destEntry.getName().equals("<init>")) |
| 58 | return; | 58 | return; |
| 59 | boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); | 59 | boolean wasAdded = unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry); |
| 60 | assert (wasAdded); | 60 | assert (wasAdded); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) { | 63 | public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) { |
| 64 | for (T entry : destEntriesntries) { | 64 | for (T entry : destEntriesntries) { |
| 65 | addUnmatchedDestEntry(entry); | 65 | addUnmatchedDestEntry(entry); |
| 66 | } | 66 | } |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | public void addUnmatchableSourceEntry(T sourceEntry) { | 69 | public void addUnmatchableSourceEntry(T sourceEntry) { |
| 70 | boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); | 70 | boolean wasAdded = unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry); |
| 71 | assert (wasAdded); | 71 | assert (wasAdded); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() { | 74 | public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() { |
| 75 | return unmatchedSourceEntries.keySet(); | 75 | return unmatchedSourceEntries.keySet(); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() { | 78 | public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() { |
| 79 | Set<ClassEntry> out = Sets.newHashSet(); | 79 | Set<ClassEntry> out = Sets.newHashSet(); |
| 80 | out.addAll(matchedSourceEntries.keySet()); | 80 | out.addAll(matchedSourceEntries.keySet()); |
| 81 | out.removeAll(unmatchedSourceEntries.keySet()); | 81 | out.removeAll(unmatchedSourceEntries.keySet()); |
| 82 | return out; | 82 | return out; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | public Collection<T> getUnmatchedSourceEntries() { | 85 | public Collection<T> getUnmatchedSourceEntries() { |
| 86 | return unmatchedSourceEntries.values(); | 86 | return unmatchedSourceEntries.values(); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) { | 89 | public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) { |
| 90 | return unmatchedSourceEntries.get(sourceClass); | 90 | return unmatchedSourceEntries.get(sourceClass); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | public Collection<T> getUnmatchedDestEntries() { | 93 | public Collection<T> getUnmatchedDestEntries() { |
| 94 | return unmatchedDestEntries.values(); | 94 | return unmatchedDestEntries.values(); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) { | 97 | public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) { |
| 98 | return unmatchedDestEntries.get(destClass); | 98 | return unmatchedDestEntries.get(destClass); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | public Collection<T> getUnmatchableSourceEntries() { | 101 | public Collection<T> getUnmatchableSourceEntries() { |
| 102 | return unmatchableSourceEntries.values(); | 102 | return unmatchableSourceEntries.values(); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | public boolean hasSource(T sourceEntry) { | 105 | public boolean hasSource(T sourceEntry) { |
| 106 | return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); | 106 | return matches.containsKey(sourceEntry) || unmatchedSourceEntries.containsValue(sourceEntry); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | public boolean hasDest(T destEntry) { | 109 | public boolean hasDest(T destEntry) { |
| 110 | return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); | 110 | return matches.containsValue(destEntry) || unmatchedDestEntries.containsValue(destEntry); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | public BiMap<T, T> matches() { | 113 | public BiMap<T, T> matches() { |
| 114 | return matches; | 114 | return matches; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | public boolean isMatchedSourceEntry(T sourceEntry) { | 117 | public boolean isMatchedSourceEntry(T sourceEntry) { |
| 118 | return matches.containsKey(sourceEntry); | 118 | return matches.containsKey(sourceEntry); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | public boolean isMatchedDestEntry(T destEntry) { | 121 | public boolean isMatchedDestEntry(T destEntry) { |
| 122 | return matches.containsValue(destEntry); | 122 | return matches.containsValue(destEntry); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | public boolean isUnmatchableSourceEntry(T sourceEntry) { | 125 | public boolean isUnmatchableSourceEntry(T sourceEntry) { |
| 126 | return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); | 126 | return unmatchableSourceEntries.containsEntry(sourceEntry.getClassEntry(), sourceEntry); |
| 127 | } | 127 | } |
| 128 | public void makeMatch(T sourceEntry, T destEntry) { | 128 | |
| 129 | makeMatch(sourceEntry, destEntry, null, null); | 129 | public void makeMatch(T sourceEntry, T destEntry) { |
| 130 | } | 130 | makeMatch(sourceEntry, destEntry, null, null); |
| 131 | 131 | } | |
| 132 | public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { | 132 | |
| 133 | if (sourceDeobfuscator != null && destDeobfuscator != null) | 133 | public void makeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 134 | { | 134 | if (sourceDeobfuscator != null && destDeobfuscator != null) { |
| 135 | makeMatch(sourceEntry, destEntry); | 135 | makeMatch(sourceEntry, destEntry); |
| 136 | sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 136 | sourceEntry = (T) sourceEntry.cloneToNewClass(sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 137 | destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); | 137 | destEntry = (T) destEntry.cloneToNewClass(destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); |
| 138 | } | 138 | } |
| 139 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 139 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 140 | assert (wasRemoved); | 140 | assert (wasRemoved); |
| 141 | wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); | 141 | wasRemoved = unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry); |
| 142 | assert (wasRemoved); | 142 | assert (wasRemoved); |
| 143 | addMatch(sourceEntry, destEntry); | 143 | addMatch(sourceEntry, destEntry); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | public boolean isMatched(T sourceEntry, T destEntry) { | 146 | public boolean isMatched(T sourceEntry, T destEntry) { |
| 147 | T match = matches.get(sourceEntry); | 147 | T match = matches.get(sourceEntry); |
| 148 | return match != null && match.equals(destEntry); | 148 | return match != null && match.equals(destEntry); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) | 151 | public void unmakeMatch(T sourceEntry, T destEntry, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { |
| 152 | { | 152 | if (sourceDeobfuscator != null && destDeobfuscator != null) { |
| 153 | if (sourceDeobfuscator != null && destDeobfuscator != null) | 153 | unmakeMatch(sourceEntry, destEntry, null, null); |
| 154 | { | 154 | sourceEntry = (T) sourceEntry.cloneToNewClass( |
| 155 | unmakeMatch(sourceEntry, destEntry, null, null); | 155 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 156 | sourceEntry = (T) sourceEntry.cloneToNewClass( | 156 | destEntry = (T) destEntry.cloneToNewClass( |
| 157 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 157 | destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); |
| 158 | destEntry = (T) destEntry.cloneToNewClass( | 158 | } |
| 159 | destDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(destEntry, true)); | 159 | |
| 160 | } | 160 | boolean wasRemoved = matches.remove(sourceEntry) != null; |
| 161 | 161 | assert (wasRemoved); | |
| 162 | boolean wasRemoved = matches.remove(sourceEntry) != null; | 162 | wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 163 | assert (wasRemoved); | 163 | assert (wasRemoved); |
| 164 | wasRemoved = matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 164 | addUnmatchedSourceEntry(sourceEntry); |
| 165 | assert (wasRemoved); | 165 | addUnmatchedDestEntry(destEntry); |
| 166 | addUnmatchedSourceEntry(sourceEntry); | 166 | } |
| 167 | addUnmatchedDestEntry(destEntry); | 167 | |
| 168 | } | 168 | public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { |
| 169 | 169 | if (sourceDeobfuscator != null) { | |
| 170 | public void makeSourceUnmatchable(T sourceEntry, Deobfuscator sourceDeobfuscator) { | 170 | makeSourceUnmatchable(sourceEntry, null); |
| 171 | if (sourceDeobfuscator != null) | 171 | sourceEntry = (T) sourceEntry.cloneToNewClass( |
| 172 | { | 172 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); |
| 173 | makeSourceUnmatchable(sourceEntry, null); | 173 | } |
| 174 | sourceEntry = (T) sourceEntry.cloneToNewClass( | 174 | assert (!isMatchedSourceEntry(sourceEntry)); |
| 175 | sourceDeobfuscator.getJarIndex().getTranslationIndex().resolveEntryClass(sourceEntry, true)); | 175 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); |
| 176 | } | 176 | assert (wasRemoved); |
| 177 | assert (!isMatchedSourceEntry(sourceEntry)); | 177 | addUnmatchableSourceEntry(sourceEntry); |
| 178 | boolean wasRemoved = unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry); | 178 | } |
| 179 | assert (wasRemoved); | ||
| 180 | addUnmatchableSourceEntry(sourceEntry); | ||
| 181 | } | ||
| 182 | } | 179 | } |