summaryrefslogtreecommitdiff
path: root/src/cuchaz
diff options
context:
space:
mode:
authorGravatar jeff2015-03-10 00:55:03 -0400
committerGravatar jeff2015-03-10 00:55:03 -0400
commit430df87ba5d855ca29bc53a5765a2862d2209098 (patch)
treeee427f009da8b29e7a66a4b4ce882120f9bb2cbf /src/cuchaz
parentfield matcher is starting to be useful (diff)
downloadenigma-fork-430df87ba5d855ca29bc53a5765a2862d2209098.tar.gz
enigma-fork-430df87ba5d855ca29bc53a5765a2862d2209098.tar.xz
enigma-fork-430df87ba5d855ca29bc53a5765a2862d2209098.zip
tweaks and improvements to field matching gui
Diffstat (limited to 'src/cuchaz')
-rw-r--r--src/cuchaz/enigma/ConvertMain.java73
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java12
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndex.java8
-rw-r--r--src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java10
-rw-r--r--src/cuchaz/enigma/convert/FieldMatches.java81
-rw-r--r--src/cuchaz/enigma/convert/MappingsConverter.java130
-rw-r--r--src/cuchaz/enigma/convert/MatchesReader.java22
-rw-r--r--src/cuchaz/enigma/convert/MatchesWriter.java10
-rw-r--r--src/cuchaz/enigma/gui/ClassMatchingGui.java24
-rw-r--r--src/cuchaz/enigma/gui/CodeReader.java8
-rw-r--r--src/cuchaz/enigma/gui/FieldMatchingGui.java317
-rw-r--r--src/cuchaz/enigma/gui/GuiTricks.java20
12 files changed, 495 insertions, 220 deletions
diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java
index a5a00e8..a45fb35 100644
--- a/src/cuchaz/enigma/ConvertMain.java
+++ b/src/cuchaz/enigma/ConvertMain.java
@@ -4,12 +4,8 @@ import java.io.File;
4import java.io.FileReader; 4import java.io.FileReader;
5import java.io.FileWriter; 5import java.io.FileWriter;
6import java.io.IOException; 6import java.io.IOException;
7import java.util.Set;
8import java.util.jar.JarFile; 7import java.util.jar.JarFile;
9 8
10import com.google.common.collect.BiMap;
11import com.google.common.collect.Sets;
12
13import cuchaz.enigma.convert.ClassMatches; 9import cuchaz.enigma.convert.ClassMatches;
14import cuchaz.enigma.convert.FieldMatches; 10import cuchaz.enigma.convert.FieldMatches;
15import cuchaz.enigma.convert.MappingsConverter; 11import cuchaz.enigma.convert.MappingsConverter;
@@ -17,18 +13,11 @@ import cuchaz.enigma.convert.MatchesReader;
17import cuchaz.enigma.convert.MatchesWriter; 13import cuchaz.enigma.convert.MatchesWriter;
18import cuchaz.enigma.gui.ClassMatchingGui; 14import cuchaz.enigma.gui.ClassMatchingGui;
19import cuchaz.enigma.gui.FieldMatchingGui; 15import cuchaz.enigma.gui.FieldMatchingGui;
20import cuchaz.enigma.mapping.ClassEntry;
21import cuchaz.enigma.mapping.ClassMapping;
22import cuchaz.enigma.mapping.ClassNameReplacer;
23import cuchaz.enigma.mapping.EntryFactory;
24import cuchaz.enigma.mapping.FieldEntry;
25import cuchaz.enigma.mapping.FieldMapping;
26import cuchaz.enigma.mapping.MappingParseException; 16import cuchaz.enigma.mapping.MappingParseException;
27import cuchaz.enigma.mapping.Mappings; 17import cuchaz.enigma.mapping.Mappings;
28import cuchaz.enigma.mapping.MappingsChecker; 18import cuchaz.enigma.mapping.MappingsChecker;
29import cuchaz.enigma.mapping.MappingsReader; 19import cuchaz.enigma.mapping.MappingsReader;
30import cuchaz.enigma.mapping.MappingsWriter; 20import cuchaz.enigma.mapping.MappingsWriter;
31import cuchaz.enigma.mapping.Type;
32 21
33 22
34public class ConvertMain { 23public class ConvertMain {
@@ -63,7 +52,7 @@ public class ConvertMain {
63 52
64 private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings) 53 private static void computeClassMatches(File classMatchesFile, JarFile sourceJar, JarFile destJar, Mappings mappings)
65 throws IOException { 54 throws IOException {
66 ClassMatches classMatches = MappingsConverter.computeMatches(sourceJar, destJar, mappings); 55 ClassMatches classMatches = MappingsConverter.computeClassMatches(sourceJar, destJar, mappings);
67 MatchesWriter.writeClasses(classMatches, classMatchesFile); 56 MatchesWriter.writeClasses(classMatches, classMatchesFile);
68 System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath()); 57 System.out.println("Wrote:\n\t" + classMatchesFile.getAbsolutePath());
69 } 58 }
@@ -115,70 +104,12 @@ public class ConvertMain {
115 System.out.println("Writing field matches..."); 104 System.out.println("Writing field matches...");
116 105
117 // get the matched and unmatched field mappings 106 // get the matched and unmatched field mappings
118 FieldMatches fieldMatches = new FieldMatches(); 107 FieldMatches fieldMatches = MappingsConverter.computeFieldMatches(destDeobfuscator, destMappings, classMatches);
119
120 // unmatched source fields are easy
121 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
122 checker.dropBrokenMappings(destMappings);
123 for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) {
124 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
125 fieldMatches.addUnmatchedSourceField(srcObfField);
126 }
127
128 // get matched fields (anything that's left after the checks/drops is matched(
129 for (ClassMapping classMapping : destMappings.classes()) {
130 collectMatchedFields(fieldMatches, classMapping, classMatches);
131 }
132
133 // get unmatched dest fields
134 Set<FieldEntry> unmatchedDestFields = Sets.newHashSet();
135 for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) {
136 if (!fieldMatches.isDestMatched(destFieldEntry)) {
137 unmatchedDestFields.add(destFieldEntry);
138 }
139 }
140 fieldMatches.addUnmatchedDestFields(unmatchedDestFields);
141 108
142 MatchesWriter.writeFields(fieldMatches, fieldMatchesFile); 109 MatchesWriter.writeFields(fieldMatches, fieldMatchesFile);
143 System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath()); 110 System.out.println("Wrote:\n\t" + fieldMatchesFile.getAbsolutePath());
144 } 111 }
145 112
146 private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) {
147
148 // get the fields for this class
149 for (FieldMapping destFieldMapping : destClassMapping.fields()) {
150 FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping);
151 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
152 fieldMatches.addMatch(srcObfField, destObfField);
153 }
154
155 // recurse
156 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
157 collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches);
158 }
159 }
160
161 private static FieldEntry translate(FieldEntry in, BiMap<ClassEntry,ClassEntry> map) {
162 return new FieldEntry(
163 map.get(in.getClassEntry()),
164 in.getName(),
165 translate(in.getType(), map)
166 );
167 }
168
169 private static Type translate(Type type, final BiMap<ClassEntry,ClassEntry> map) {
170 return new Type(type, new ClassNameReplacer() {
171 @Override
172 public String replace(String inClassName) {
173 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
174 if (outClassEntry == null) {
175 return null;
176 }
177 return outClassEntry.getName();
178 }
179 });
180 }
181
182 private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile) 113 private static void editFieldMatches(JarFile sourceJar, JarFile destJar, File destMappingsFile, Mappings sourceMappings, File classMatchesFile, final File fieldMatchesFile)
183 throws IOException, MappingParseException { 114 throws IOException, MappingParseException {
184 115
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 35cfd0b..f5012bd 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -225,8 +225,18 @@ public class Deobfuscator {
225 } 225 }
226 226
227 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 227 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
228 return getSourceIndex(sourceTree, source, null);
229 }
230
231 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) {
232
228 // build the source index 233 // build the source index
229 SourceIndex index = new SourceIndex(source); 234 SourceIndex index;
235 if (ignoreBadTokens != null) {
236 index = new SourceIndex(source, ignoreBadTokens);
237 } else {
238 index = new SourceIndex(source);
239 }
230 sourceTree.acceptVisitor(new SourceIndexVisitor(), index); 240 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
231 241
232 // DEBUG 242 // DEBUG
diff --git a/src/cuchaz/enigma/analysis/SourceIndex.java b/src/cuchaz/enigma/analysis/SourceIndex.java
index b3fb751..8f751ef 100644
--- a/src/cuchaz/enigma/analysis/SourceIndex.java
+++ b/src/cuchaz/enigma/analysis/SourceIndex.java
@@ -32,9 +32,15 @@ public class SourceIndex {
32 private Multimap<EntryReference<Entry,Entry>,Token> m_referenceToTokens; 32 private Multimap<EntryReference<Entry,Entry>,Token> m_referenceToTokens;
33 private Map<Entry,Token> m_declarationToToken; 33 private Map<Entry,Token> m_declarationToToken;
34 private List<Integer> m_lineOffsets; 34 private List<Integer> m_lineOffsets;
35 private boolean m_ignoreBadTokens;
35 36
36 public SourceIndex(String source) { 37 public SourceIndex(String source) {
38 this(source, true);
39 }
40
41 public SourceIndex(String source, boolean ignoreBadTokens) {
37 m_source = source; 42 m_source = source;
43 m_ignoreBadTokens = ignoreBadTokens;
38 m_tokenToReference = Maps.newTreeMap(); 44 m_tokenToReference = Maps.newTreeMap();
39 m_referenceToTokens = HashMultimap.create(); 45 m_referenceToTokens = HashMultimap.create();
40 m_declarationToToken = Maps.newHashMap(); 46 m_declarationToToken = Maps.newHashMap();
@@ -83,7 +89,7 @@ public class SourceIndex {
83 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); 89 // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) );
84 90
85 // if the token has a $ in it, something's wrong. Ignore this token 91 // if the token has a $ in it, something's wrong. Ignore this token
86 if (name.lastIndexOf('$') >= 0) { 92 if (name.lastIndexOf('$') >= 0 && m_ignoreBadTokens) {
87 // DEBUG 93 // DEBUG
88 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name)); 94 System.err.println(String.format("WARNING: %s \"%s\" is probably a bad token. It was ignored", node.getNodeType(), name));
89 return null; 95 return null;
diff --git a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
index a9a055b..eb120b6 100644
--- a/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
+++ b/src/cuchaz/enigma/analysis/SourceIndexBehaviorVisitor.java
@@ -111,10 +111,12 @@ public class SourceIndexBehaviorVisitor extends SourceIndexVisitor {
111 @Override 111 @Override
112 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) { 112 public Void visitParameterDeclaration(ParameterDeclaration node, SourceIndex index) {
113 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION); 113 ParameterDefinition def = node.getUserData(Keys.PARAMETER_DEFINITION);
114 MethodDefinition methodDef = (MethodDefinition)def.getMethod(); 114 if (def.getMethod() instanceof MethodDefinition) {
115 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef); 115 MethodDefinition methodDef = (MethodDefinition)def.getMethod();
116 ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName()); 116 BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(methodDef);
117 index.addDeclaration(node.getNameToken(), argumentEntry); 117 ArgumentEntry argumentEntry = new ArgumentEntry(behaviorEntry, def.getPosition(), node.getName());
118 index.addDeclaration(node.getNameToken(), argumentEntry);
119 }
118 120
119 return recurse(node, index); 121 return recurse(node, index);
120 } 122 }
diff --git a/src/cuchaz/enigma/convert/FieldMatches.java b/src/cuchaz/enigma/convert/FieldMatches.java
index 6335974..2973356 100644
--- a/src/cuchaz/enigma/convert/FieldMatches.java
+++ b/src/cuchaz/enigma/convert/FieldMatches.java
@@ -7,6 +7,7 @@ import com.google.common.collect.BiMap;
7import com.google.common.collect.HashBiMap; 7import com.google.common.collect.HashBiMap;
8import com.google.common.collect.HashMultimap; 8import com.google.common.collect.HashMultimap;
9import com.google.common.collect.Multimap; 9import com.google.common.collect.Multimap;
10import com.google.common.collect.Sets;
10 11
11import cuchaz.enigma.mapping.ClassEntry; 12import cuchaz.enigma.mapping.ClassEntry;
12import cuchaz.enigma.mapping.FieldEntry; 13import cuchaz.enigma.mapping.FieldEntry;
@@ -15,21 +16,29 @@ import cuchaz.enigma.mapping.FieldEntry;
15public class FieldMatches { 16public class FieldMatches {
16 17
17 private BiMap<FieldEntry,FieldEntry> m_matches; 18 private BiMap<FieldEntry,FieldEntry> m_matches;
19 private Multimap<ClassEntry,FieldEntry> m_matchedSourceFields;
18 private Multimap<ClassEntry,FieldEntry> m_unmatchedSourceFields; 20 private Multimap<ClassEntry,FieldEntry> m_unmatchedSourceFields;
19 private Multimap<ClassEntry,FieldEntry> m_unmatchedDestFields; 21 private Multimap<ClassEntry,FieldEntry> m_unmatchedDestFields;
22 private Multimap<ClassEntry,FieldEntry> m_unmatchableSourceFields;
20 23
21 public FieldMatches() { 24 public FieldMatches() {
22 m_matches = HashBiMap.create(); 25 m_matches = HashBiMap.create();
26 m_matchedSourceFields = HashMultimap.create();
23 m_unmatchedSourceFields = HashMultimap.create(); 27 m_unmatchedSourceFields = HashMultimap.create();
24 m_unmatchedDestFields = HashMultimap.create(); 28 m_unmatchedDestFields = HashMultimap.create();
29 m_unmatchableSourceFields = HashMultimap.create();
25 } 30 }
26 31
27 public void addMatch(FieldEntry srcField, FieldEntry destField) { 32 public void addMatch(FieldEntry srcField, FieldEntry destField) {
28 m_matches.put(srcField, destField); 33 boolean wasAdded = m_matches.put(srcField, destField) == null;
34 assert (wasAdded);
35 wasAdded = m_matchedSourceFields.put(srcField.getClassEntry(), srcField);
36 assert (wasAdded);
29 } 37 }
30 38
31 public void addUnmatchedSourceField(FieldEntry fieldEntry) { 39 public void addUnmatchedSourceField(FieldEntry fieldEntry) {
32 m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry); 40 boolean wasAdded = m_unmatchedSourceFields.put(fieldEntry.getClassEntry(), fieldEntry);
41 assert (wasAdded);
33 } 42 }
34 43
35 public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) { 44 public void addUnmatchedSourceFields(Iterable<FieldEntry> fieldEntries) {
@@ -39,7 +48,8 @@ public class FieldMatches {
39 } 48 }
40 49
41 public void addUnmatchedDestField(FieldEntry fieldEntry) { 50 public void addUnmatchedDestField(FieldEntry fieldEntry) {
42 m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry); 51 boolean wasAdded = m_unmatchedDestFields.put(fieldEntry.getClassEntry(), fieldEntry);
52 assert (wasAdded);
43 } 53 }
44 54
45 public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) { 55 public void addUnmatchedDestFields(Iterable<FieldEntry> fieldEntries) {
@@ -48,9 +58,21 @@ public class FieldMatches {
48 } 58 }
49 } 59 }
50 60
61 public void addUnmatchableSourceField(FieldEntry sourceField) {
62 boolean wasAdded = m_unmatchableSourceFields.put(sourceField.getClassEntry(), sourceField);
63 assert (wasAdded);
64 }
65
51 public Set<ClassEntry> getSourceClassesWithUnmatchedFields() { 66 public Set<ClassEntry> getSourceClassesWithUnmatchedFields() {
52 return m_unmatchedSourceFields.keySet(); 67 return m_unmatchedSourceFields.keySet();
53 } 68 }
69
70 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedFields() {
71 Set<ClassEntry> out = Sets.newHashSet();
72 out.addAll(m_matchedSourceFields.keySet());
73 out.removeAll(m_unmatchedSourceFields.keySet());
74 return out;
75 }
54 76
55 public Collection<FieldEntry> getUnmatchedSourceFields() { 77 public Collection<FieldEntry> getUnmatchedSourceFields() {
56 return m_unmatchedSourceFields.values(); 78 return m_unmatchedSourceFields.values();
@@ -64,21 +86,60 @@ public class FieldMatches {
64 return m_unmatchedDestFields.values(); 86 return m_unmatchedDestFields.values();
65 } 87 }
66 88
67 public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry sourceClass) { 89 public Collection<FieldEntry> getUnmatchedDestFields(ClassEntry destClass) {
68 return m_unmatchedDestFields.get(sourceClass); 90 return m_unmatchedDestFields.get(destClass);
91 }
92
93 public Collection<FieldEntry> getUnmatchableSourceFields() {
94 return m_unmatchableSourceFields.values();
95 }
96
97 public boolean hasSource(FieldEntry fieldEntry) {
98 return m_matches.containsKey(fieldEntry) || m_unmatchedSourceFields.containsValue(fieldEntry);
99 }
100
101 public boolean hasDest(FieldEntry fieldEntry) {
102 return m_matches.containsValue(fieldEntry) || m_unmatchedDestFields.containsValue(fieldEntry);
69 } 103 }
70 104
71 public BiMap<FieldEntry,FieldEntry> matches() { 105 public BiMap<FieldEntry,FieldEntry> matches() {
72 return m_matches; 106 return m_matches;
73 } 107 }
108
109 public boolean isMatchedSourceField(FieldEntry sourceField) {
110 return m_matches.containsKey(sourceField);
111 }
74 112
75 public boolean isDestMatched(FieldEntry destFieldEntry) { 113 public boolean isMatchedDestField(FieldEntry destField) {
76 return m_matches.containsValue(destFieldEntry); 114 return m_matches.containsValue(destField);
77 } 115 }
78 116
79 public void makeMatch(FieldEntry sourceField, FieldEntry destField) { 117 public void makeMatch(FieldEntry sourceField, FieldEntry destField) {
80 m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField); 118 boolean wasRemoved = m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
81 m_unmatchedDestFields.remove(destField.getClassEntry(), destField); 119 assert (wasRemoved);
82 m_matches.put(sourceField, destField); 120 wasRemoved = m_unmatchedDestFields.remove(destField.getClassEntry(), destField);
121 assert (wasRemoved);
122 addMatch(sourceField, destField);
123 }
124
125 public boolean isMatched(FieldEntry sourceField, FieldEntry destField) {
126 FieldEntry match = m_matches.get(sourceField);
127 return match != null && match.equals(destField);
128 }
129
130 public void unmakeMatch(FieldEntry sourceField, FieldEntry destField) {
131 boolean wasRemoved = m_matches.remove(sourceField) != null;
132 assert (wasRemoved);
133 wasRemoved = m_matchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
134 assert (wasRemoved);
135 addUnmatchedSourceField(sourceField);
136 addUnmatchedDestField(destField);
137 }
138
139 public void makeSourceUnmatchable(FieldEntry sourceField) {
140 assert(!isMatchedSourceField(sourceField));
141 boolean wasRemoved = m_unmatchedSourceFields.remove(sourceField.getClassEntry(), sourceField);
142 assert (wasRemoved);
143 addUnmatchableSourceField(sourceField);
83 } 144 }
84} 145}
diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java
index 667ee9d..9ab1baa 100644
--- a/src/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/cuchaz/enigma/convert/MappingsConverter.java
@@ -17,6 +17,7 @@ import java.util.LinkedHashMap;
17import java.util.List; 17import java.util.List;
18import java.util.Map; 18import java.util.Map;
19import java.util.Map.Entry; 19import java.util.Map.Entry;
20import java.util.Set;
20import java.util.jar.JarFile; 21import java.util.jar.JarFile;
21 22
22import com.beust.jcommander.internal.Lists; 23import com.beust.jcommander.internal.Lists;
@@ -24,6 +25,7 @@ import com.google.common.collect.BiMap;
24import com.google.common.collect.HashMultimap; 25import com.google.common.collect.HashMultimap;
25import com.google.common.collect.Maps; 26import com.google.common.collect.Maps;
26import com.google.common.collect.Multimap; 27import com.google.common.collect.Multimap;
28import com.google.common.collect.Sets;
27 29
28import cuchaz.enigma.Deobfuscator; 30import cuchaz.enigma.Deobfuscator;
29import cuchaz.enigma.analysis.JarIndex; 31import cuchaz.enigma.analysis.JarIndex;
@@ -31,13 +33,17 @@ import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
31import cuchaz.enigma.mapping.ClassEntry; 33import cuchaz.enigma.mapping.ClassEntry;
32import cuchaz.enigma.mapping.ClassMapping; 34import cuchaz.enigma.mapping.ClassMapping;
33import cuchaz.enigma.mapping.ClassNameReplacer; 35import cuchaz.enigma.mapping.ClassNameReplacer;
36import cuchaz.enigma.mapping.EntryFactory;
37import cuchaz.enigma.mapping.FieldEntry;
34import cuchaz.enigma.mapping.FieldMapping; 38import cuchaz.enigma.mapping.FieldMapping;
35import cuchaz.enigma.mapping.Mappings; 39import cuchaz.enigma.mapping.Mappings;
40import cuchaz.enigma.mapping.MappingsChecker;
36import cuchaz.enigma.mapping.MethodMapping; 41import cuchaz.enigma.mapping.MethodMapping;
42import cuchaz.enigma.mapping.Type;
37 43
38public class MappingsConverter { 44public class MappingsConverter {
39 45
40 public static ClassMatches computeMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) { 46 public static ClassMatches computeClassMatches(JarFile sourceJar, JarFile destJar, Mappings mappings) {
41 47
42 // index jars 48 // index jars
43 System.out.println("Indexing source jar..."); 49 System.out.println("Indexing source jar...");
@@ -245,47 +251,101 @@ public class MappingsConverter {
245 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); 251 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName());
246 } 252 }
247 } 253 }
248 254
249 /* TODO: after we get a mapping, check to see that the other entries match 255 public static FieldMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) {
250 public static void checkMethods() { 256
257 FieldMatches fieldMatches = new FieldMatches();
258
259 // unmatched source fields are easy
260 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
261 checker.dropBrokenMappings(destMappings);
262 for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) {
263 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
264 fieldMatches.addUnmatchedSourceField(srcObfField);
265 }
251 266
252 // check the method matches 267 // get matched fields (anything that's left after the checks/drops is matched(
253 System.out.println("Checking methods..."); 268 for (ClassMapping classMapping : destMappings.classes()) {
254 for (ClassMapping classMapping : mappings.classes()) { 269 collectMatchedFields(fieldMatches, classMapping, classMatches);
255 ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); 270 }
256 for (MethodMapping methodMapping : classMapping.methods()) { 271
272 // get unmatched dest fields
273 for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) {
274 if (!fieldMatches.isMatchedDestField(destFieldEntry)) {
275 fieldMatches.addUnmatchedDestField(destFieldEntry);
276 }
277 }
278
279 System.out.println("Automatching " + fieldMatches.getUnmatchedSourceFields().size() + " unmatched source fields...");
280
281 // go through the unmatched source fields and try to pick out the easy matches
282 for (ClassEntry obfSourceClass : Lists.newArrayList(fieldMatches.getSourceClassesWithUnmatchedFields())) {
283 for (FieldEntry obfSourceField : Lists.newArrayList(fieldMatches.getUnmatchedSourceFields(obfSourceClass))) {
257 284
258 // skip constructors 285 // get the possible dest matches
259 if (methodMapping.getObfName().equals("<init>")) { 286 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
260 continue;
261 }
262 287
263 MethodEntry methodEntry = new MethodEntry( 288 // filter by type
264 classEntry, 289 Set<FieldEntry> obfDestFields = Sets.newHashSet();
265 methodMapping.getObfName(), 290 for (FieldEntry obfDestField : fieldMatches.getUnmatchedDestFields(obfDestClass)) {
266 methodMapping.getObfSignature() 291 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
267 ); 292 if (translatedDestType.equals(obfSourceField.getType())) {
268 if (!destIndex.containsObfBehavior(methodEntry)) { 293 obfDestFields.add(obfDestField);
269 System.err.println("WARNING: method doesn't match: " + methodEntry);
270
271 // TODO: show methods if needed
272 // show the available methods
273 System.err.println("\tAvailable dest methods:");
274 CtClass c = destLoader.loadClass(classMapping.getObfFullName());
275 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
276 System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior));
277 }
278
279 System.err.println("\tAvailable source methods:");
280 c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName()));
281 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
282 System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior));
283 } 294 }
284 } 295 }
296
297 if (obfDestFields.size() == 1) {
298 // make the easy match
299 FieldEntry obfDestField = obfDestFields.iterator().next();
300 fieldMatches.makeMatch(obfSourceField, obfDestField);
301 } else if (obfDestFields.isEmpty()) {
302 // no match is possible =(
303 fieldMatches.makeSourceUnmatchable(obfSourceField);
304 }
285 } 305 }
286 } 306 }
287 307
288 System.out.println("Done!"); 308 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source fields",
309 fieldMatches.getUnmatchedSourceFields().size(),
310 fieldMatches.getUnmatchableSourceFields().size()
311 ));
312
313 return fieldMatches;
314 }
315
316 private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) {
317
318 // get the fields for this class
319 for (FieldMapping destFieldMapping : destClassMapping.fields()) {
320 FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping);
321 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
322 fieldMatches.addMatch(srcObfField, destObfField);
323 }
324
325 // recurse
326 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
327 collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches);
328 }
329 }
330
331 private static FieldEntry translate(FieldEntry in, BiMap<ClassEntry,ClassEntry> map) {
332 return new FieldEntry(
333 map.get(in.getClassEntry()),
334 in.getName(),
335 translate(in.getType(), map)
336 );
337 }
338
339 private static Type translate(Type type, final BiMap<ClassEntry,ClassEntry> map) {
340 return new Type(type, new ClassNameReplacer() {
341 @Override
342 public String replace(String inClassName) {
343 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
344 if (outClassEntry == null) {
345 return null;
346 }
347 return outClassEntry.getName();
348 }
349 });
289 } 350 }
290 */
291} 351}
diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java
index 1dd042d..921ab1d 100644
--- a/src/cuchaz/enigma/convert/MatchesReader.java
+++ b/src/cuchaz/enigma/convert/MatchesReader.java
@@ -58,15 +58,19 @@ public class MatchesReader {
58 } 58 }
59 59
60 private static void readFieldMatch(FieldMatches matches, String line) { 60 private static void readFieldMatch(FieldMatches matches, String line) {
61 String[] parts = line.split(":", 2); 61 if (line.startsWith("!")) {
62 FieldEntry source = readField(parts[0]); 62 matches.addUnmatchableSourceField(readField(line.substring(1)));
63 FieldEntry dest = readField(parts[1]); 63 } else {
64 if (source != null && dest != null) { 64 String[] parts = line.split(":", 2);
65 matches.addMatch(source, dest); 65 FieldEntry source = readField(parts[0]);
66 } else if (source != null) { 66 FieldEntry dest = readField(parts[1]);
67 matches.addUnmatchedSourceField(source); 67 if (source != null && dest != null) {
68 } else if (dest != null) { 68 matches.addMatch(source, dest);
69 matches.addUnmatchedDestField(dest); 69 } else if (source != null) {
70 matches.addUnmatchedSourceField(source);
71 } else if (dest != null) {
72 matches.addUnmatchedDestField(dest);
73 }
70 } 74 }
71 } 75 }
72 76
diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java
index 6e371bc..2118dd0 100644
--- a/src/cuchaz/enigma/convert/MatchesWriter.java
+++ b/src/cuchaz/enigma/convert/MatchesWriter.java
@@ -53,6 +53,9 @@ public class MatchesWriter {
53 for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { 53 for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) {
54 writeFieldMatch(out, null, fieldEntry); 54 writeFieldMatch(out, null, fieldEntry);
55 } 55 }
56 for (FieldEntry fieldEntry : fieldMatches.getUnmatchableSourceFields()) {
57 writeUnmatchableField(out, fieldEntry);
58 }
56 } 59 }
57 } 60 }
58 61
@@ -68,6 +71,13 @@ public class MatchesWriter {
68 out.write("\n"); 71 out.write("\n");
69 } 72 }
70 73
74 private static void writeUnmatchableField(FileWriter out, FieldEntry fieldEntry)
75 throws IOException {
76 out.write("!");
77 writeField(out, fieldEntry);
78 out.write("\n");
79 }
80
71 private static void writeField(FileWriter out, FieldEntry fieldEntry) 81 private static void writeField(FileWriter out, FieldEntry fieldEntry)
72 throws IOException { 82 throws IOException {
73 out.write(fieldEntry.getClassName()); 83 out.write(fieldEntry.getClassName());
diff --git a/src/cuchaz/enigma/gui/ClassMatchingGui.java b/src/cuchaz/enigma/gui/ClassMatchingGui.java
index 6943c3e..9e210ec 100644
--- a/src/cuchaz/enigma/gui/ClassMatchingGui.java
+++ b/src/cuchaz/enigma/gui/ClassMatchingGui.java
@@ -6,7 +6,6 @@ import java.awt.Dimension;
6import java.awt.FlowLayout; 6import java.awt.FlowLayout;
7import java.awt.event.ActionEvent; 7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener; 8import java.awt.event.ActionListener;
9import java.util.Arrays;
10import java.util.Collection; 9import java.util.Collection;
11import java.util.List; 10import java.util.List;
12import java.util.Map; 11import java.util.Map;
@@ -421,17 +420,17 @@ public class ClassMatchingGui {
421 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest); 420 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest);
422 boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest); 421 boolean canMatch = !uniqueMatches.containsKey(obfSource) && ! uniqueMatches.containsValue(obfDest);
423 422
424 deactivateButton(m_matchButton); 423 GuiTricks.deactivateButton(m_matchButton);
425 if (twoSelected) { 424 if (twoSelected) {
426 if (isMatched) { 425 if (isMatched) {
427 activateButton(m_matchButton, "Unmatch", new ActionListener() { 426 GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() {
428 @Override 427 @Override
429 public void actionPerformed(ActionEvent event) { 428 public void actionPerformed(ActionEvent event) {
430 onUnmatchClick(); 429 onUnmatchClick();
431 } 430 }
432 }); 431 });
433 } else if (canMatch) { 432 } else if (canMatch) {
434 activateButton(m_matchButton, "Match", new ActionListener() { 433 GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() {
435 @Override 434 @Override
436 public void actionPerformed(ActionEvent event) { 435 public void actionPerformed(ActionEvent event) {
437 onMatchClick(); 436 onMatchClick();
@@ -440,23 +439,6 @@ public class ClassMatchingGui {
440 } 439 }
441 } 440 }
442 } 441 }
443
444 private void deactivateButton(JButton button) {
445 button.setEnabled(false);
446 button.setText("");
447 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
448 button.removeActionListener(listener);
449 }
450 }
451
452 private void activateButton(JButton button, String text, ActionListener newListener) {
453 button.setText(text);
454 button.setEnabled(true);
455 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
456 button.removeActionListener(listener);
457 }
458 button.addActionListener(newListener);
459 }
460 442
461 private void onMatchClick() { 443 private void onMatchClick() {
462 // precondition: source and dest classes are set correctly 444 // precondition: source and dest classes are set correctly
diff --git a/src/cuchaz/enigma/gui/CodeReader.java b/src/cuchaz/enigma/gui/CodeReader.java
index aa7e2db..743ef2e 100644
--- a/src/cuchaz/enigma/gui/CodeReader.java
+++ b/src/cuchaz/enigma/gui/CodeReader.java
@@ -85,7 +85,11 @@ public class CodeReader extends JEditorPane {
85 decompileClass(classEntry, deobfuscator, null); 85 decompileClass(classEntry, deobfuscator, null);
86 } 86 }
87 87
88 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Runnable callback) { 88 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) {
89 decompileClass(classEntry, deobfuscator, null, callback);
90 }
91
92 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) {
89 93
90 if (classEntry == null) { 94 if (classEntry == null) {
91 setCode(null); 95 setCode(null);
@@ -109,7 +113,7 @@ public class CodeReader extends JEditorPane {
109 CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName()); 113 CompilationUnit sourceTree = deobfuscator.getSourceTree(outermostClassEntry.getName());
110 String source = deobfuscator.getSource(sourceTree); 114 String source = deobfuscator.getSource(sourceTree);
111 setCode(source); 115 setCode(source);
112 m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source); 116 m_sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
113 117
114 if (callback != null) { 118 if (callback != null) {
115 callback.run(); 119 callback.run();
diff --git a/src/cuchaz/enigma/gui/FieldMatchingGui.java b/src/cuchaz/enigma/gui/FieldMatchingGui.java
index ef374c8..3f4a378 100644
--- a/src/cuchaz/enigma/gui/FieldMatchingGui.java
+++ b/src/cuchaz/enigma/gui/FieldMatchingGui.java
@@ -6,20 +6,26 @@ import java.awt.Dimension;
6import java.awt.FlowLayout; 6import java.awt.FlowLayout;
7import java.awt.event.ActionEvent; 7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener; 8import java.awt.event.ActionListener;
9import java.awt.event.KeyAdapter;
10import java.awt.event.KeyEvent;
9import java.util.Collection; 11import java.util.Collection;
10import java.util.Set; 12import java.util.List;
13import java.util.Map;
11 14
12import javax.swing.BoxLayout; 15import javax.swing.BoxLayout;
16import javax.swing.ButtonGroup;
13import javax.swing.JButton; 17import javax.swing.JButton;
14import javax.swing.JFrame; 18import javax.swing.JFrame;
15import javax.swing.JLabel; 19import javax.swing.JLabel;
16import javax.swing.JPanel; 20import javax.swing.JPanel;
21import javax.swing.JRadioButton;
17import javax.swing.JScrollPane; 22import javax.swing.JScrollPane;
18import javax.swing.JSplitPane; 23import javax.swing.JSplitPane;
19import javax.swing.WindowConstants; 24import javax.swing.WindowConstants;
20import javax.swing.text.Highlighter.HighlightPainter; 25import javax.swing.text.Highlighter.HighlightPainter;
21 26
22import com.google.common.collect.Sets; 27import com.beust.jcommander.internal.Lists;
28import com.beust.jcommander.internal.Maps;
23 29
24import cuchaz.enigma.Constants; 30import cuchaz.enigma.Constants;
25import cuchaz.enigma.Deobfuscator; 31import cuchaz.enigma.Deobfuscator;
@@ -37,16 +43,49 @@ import de.sciss.syntaxpane.DefaultSyntaxKit;
37 43
38public class FieldMatchingGui { 44public class FieldMatchingGui {
39 45
46 private static enum SourceType {
47 Matched {
48
49 @Override
50 public Collection<ClassEntry> getObfSourceClasses(FieldMatches matches) {
51 return matches.getSourceClassesWithoutUnmatchedFields();
52 }
53 },
54 Unmatched {
55
56 @Override
57 public Collection<ClassEntry> getObfSourceClasses(FieldMatches matches) {
58 return matches.getSourceClassesWithUnmatchedFields();
59 }
60 };
61
62 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
63 JRadioButton button = new JRadioButton(name(), this == getDefault());
64 button.setActionCommand(name());
65 button.addActionListener(listener);
66 group.add(button);
67 return button;
68 }
69
70 public abstract Collection<ClassEntry> getObfSourceClasses(FieldMatches matches);
71
72 public static SourceType getDefault() {
73 return values()[0];
74 }
75 }
76
40 public static interface SaveListener { 77 public static interface SaveListener {
41 public void save(FieldMatches matches); 78 public void save(FieldMatches matches);
42 } 79 }
43 80
44 // controls 81 // controls
45 private JFrame m_frame; 82 private JFrame m_frame;
83 private Map<SourceType,JRadioButton> m_sourceTypeButtons;
46 private ClassSelector m_sourceClasses; 84 private ClassSelector m_sourceClasses;
47 private CodeReader m_sourceReader; 85 private CodeReader m_sourceReader;
48 private CodeReader m_destReader; 86 private CodeReader m_destReader;
49 private JButton m_matchButton; 87 private JButton m_matchButton;
88 private JButton m_unmatchableButton;
50 private JLabel m_sourceLabel; 89 private JLabel m_sourceLabel;
51 private JLabel m_destLabel; 90 private JLabel m_destLabel;
52 private HighlightPainter m_unmatchedHighlightPainter; 91 private HighlightPainter m_unmatchedHighlightPainter;
@@ -57,12 +96,11 @@ public class FieldMatchingGui {
57 private Deobfuscator m_sourceDeobfuscator; 96 private Deobfuscator m_sourceDeobfuscator;
58 private Deobfuscator m_destDeobfuscator; 97 private Deobfuscator m_destDeobfuscator;
59 private SaveListener m_saveListener; 98 private SaveListener m_saveListener;
99 private SourceType m_sourceType;
60 private ClassEntry m_obfSourceClass; 100 private ClassEntry m_obfSourceClass;
61 private ClassEntry m_obfDestClass; 101 private ClassEntry m_obfDestClass;
62 private FieldEntry m_obfSourceField; 102 private FieldEntry m_obfSourceField;
63 private FieldEntry m_obfDestField; 103 private FieldEntry m_obfDestField;
64 private Set<FieldEntry> m_obfUnmatchedSourceFields;
65 private Set<FieldEntry> m_obfUnmatchedDestFields;
66 104
67 public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { 105 public FieldMatchingGui(ClassMatches classMatches, FieldMatches fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
68 106
@@ -83,6 +121,24 @@ public class FieldMatchingGui {
83 pane.add(classesPanel, BorderLayout.WEST); 121 pane.add(classesPanel, BorderLayout.WEST);
84 classesPanel.add(new JLabel("Classes")); 122 classesPanel.add(new JLabel("Classes"));
85 123
124 // init source type radios
125 JPanel sourceTypePanel = new JPanel();
126 classesPanel.add(sourceTypePanel);
127 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
128 ActionListener sourceTypeListener = new ActionListener() {
129 @Override
130 public void actionPerformed(ActionEvent event) {
131 setSourceType(SourceType.valueOf(event.getActionCommand()));
132 }
133 };
134 ButtonGroup sourceTypeButtons = new ButtonGroup();
135 m_sourceTypeButtons = Maps.newHashMap();
136 for (SourceType sourceType : SourceType.values()) {
137 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
138 m_sourceTypeButtons.put(sourceType, button);
139 sourceTypePanel.add(button);
140 }
141
86 m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator); 142 m_sourceClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator);
87 m_sourceClasses.setListener(new ClassSelectionListener() { 143 m_sourceClasses.setListener(new ClassSelectionListener() {
88 @Override 144 @Override
@@ -117,6 +173,20 @@ public class FieldMatchingGui {
117 } 173 }
118 } 174 }
119 }); 175 });
176
177 // add key bindings
178 KeyAdapter keyListener = new KeyAdapter() {
179 @Override
180 public void keyPressed(KeyEvent event) {
181 switch (event.getKeyCode()) {
182 case KeyEvent.VK_M:
183 m_matchButton.doClick();
184 break;
185 }
186 }
187 };
188 m_sourceReader.addKeyListener(keyListener);
189 m_destReader.addKeyListener(keyListener);
120 190
121 // init all the splits 191 // init all the splits
122 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader)); 192 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader));
@@ -131,17 +201,13 @@ public class FieldMatchingGui {
131 bottomPanel.setLayout(new FlowLayout()); 201 bottomPanel.setLayout(new FlowLayout());
132 pane.add(bottomPanel, BorderLayout.SOUTH); 202 pane.add(bottomPanel, BorderLayout.SOUTH);
133 203
134 m_matchButton = new JButton("Match"); 204 m_matchButton = new JButton();
135 m_matchButton.addActionListener(new ActionListener() { 205 m_unmatchableButton = new JButton();
136 @Override
137 public void actionPerformed(ActionEvent event) {
138 match();
139 }
140 });
141 206
142 m_sourceLabel = new JLabel(); 207 m_sourceLabel = new JLabel();
143 bottomPanel.add(m_sourceLabel); 208 bottomPanel.add(m_sourceLabel);
144 bottomPanel.add(m_matchButton); 209 bottomPanel.add(m_matchButton);
210 bottomPanel.add(m_unmatchableButton);
145 m_destLabel = new JLabel(); 211 m_destLabel = new JLabel();
146 bottomPanel.add(m_destLabel); 212 bottomPanel.add(m_destLabel);
147 213
@@ -161,10 +227,13 @@ public class FieldMatchingGui {
161 m_obfDestClass = null; 227 m_obfDestClass = null;
162 m_obfSourceField = null; 228 m_obfSourceField = null;
163 m_obfDestField = null; 229 m_obfDestField = null;
164 m_obfUnmatchedSourceFields = null; 230 setSourceType(SourceType.getDefault());
165 m_obfUnmatchedDestFields = null; 231 updateButtons();
232 }
233
234 protected void setSourceType(SourceType val) {
235 m_sourceType = val;
166 updateSourceClasses(); 236 updateSourceClasses();
167 updateMatchButton();
168 } 237 }
169 238
170 public void setSaveListener(SaveListener val) { 239 public void setSaveListener(SaveListener val) {
@@ -172,26 +241,41 @@ public class FieldMatchingGui {
172 } 241 }
173 242
174 private void updateSourceClasses() { 243 private void updateSourceClasses() {
175 m_sourceClasses.setClasses(m_fieldMatches.getSourceClassesWithUnmatchedFields()); 244
176 m_sourceClasses.expandAll(); 245 String selectedPackage = m_sourceClasses.getSelectedPackage();
246
247 List<ClassEntry> deobfClassEntries = Lists.newArrayList();
248 for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_fieldMatches)) {
249 deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry));
250 }
251 m_sourceClasses.setClasses(deobfClassEntries);
252
253 if (selectedPackage != null) {
254 m_sourceClasses.expandPackage(selectedPackage);
255 }
256
257 for (SourceType sourceType : SourceType.values()) {
258 m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
259 sourceType.name(), sourceType.getObfSourceClasses(m_fieldMatches).size()
260 ));
261 }
177 } 262 }
178 263
179 protected void setSourceClass(ClassEntry obfSourceClass) { 264 protected void setSourceClass(ClassEntry sourceClass) {
180 265
181 m_obfSourceClass = obfSourceClass; 266 m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass);
182 m_obfDestClass = m_classMatches.getUniqueMatches().get(obfSourceClass); 267 m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass);
183 if (m_obfDestClass == null) { 268 if (m_obfDestClass == null) {
184 throw new Error("No matching dest class for source class: " + m_obfSourceClass); 269 throw new Error("No matching dest class for source class: " + m_obfSourceClass);
185 } 270 }
186 271
187 updateUnmatchedFields(); 272 m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, new Runnable() {
188 m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, new Runnable() {
189 @Override 273 @Override
190 public void run() { 274 public void run() {
191 updateSourceHighlights(); 275 updateSourceHighlights();
192 } 276 }
193 }); 277 });
194 m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, new Runnable() { 278 m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, new Runnable() {
195 @Override 279 @Override
196 public void run() { 280 public void run() {
197 updateDestHighlights(); 281 updateDestHighlights();
@@ -199,71 +283,145 @@ public class FieldMatchingGui {
199 }); 283 });
200 } 284 }
201 285
202 private void updateUnmatchedFields() {
203 m_obfUnmatchedSourceFields = Sets.newHashSet(m_fieldMatches.getUnmatchedSourceFields(m_obfSourceClass));
204 m_obfUnmatchedDestFields = Sets.newHashSet();
205 for (FieldEntry destFieldEntry : m_destDeobfuscator.getJarIndex().getObfFieldEntries(m_obfDestClass)) {
206 if (!m_fieldMatches.isDestMatched(destFieldEntry)) {
207 m_obfUnmatchedDestFields.add(destFieldEntry);
208 }
209 }
210 }
211
212 protected void updateSourceHighlights() { 286 protected void updateSourceHighlights() {
213 highlightFields(m_sourceReader, m_sourceDeobfuscator, m_obfUnmatchedSourceFields, m_fieldMatches.matches().keySet()); 287 highlightFields(m_sourceReader, m_sourceDeobfuscator, m_fieldMatches.matches().keySet(), m_fieldMatches.getUnmatchedSourceFields());
214 } 288 }
215 289
216 protected void updateDestHighlights() { 290 protected void updateDestHighlights() {
217 highlightFields(m_destReader, m_destDeobfuscator, m_obfUnmatchedDestFields, m_fieldMatches.matches().values()); 291 highlightFields(m_destReader, m_destDeobfuscator, m_fieldMatches.matches().values(), m_fieldMatches.getUnmatchedDestFields());
218 } 292 }
219 293
220 private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection<FieldEntry> obfFieldEntries, Collection<FieldEntry> obfMatchedFieldEntries) { 294 private void highlightFields(CodeReader reader, Deobfuscator deobfuscator, Collection<FieldEntry> obfMatchedFields, Collection<FieldEntry> obfUnmatchedFields) {
221 reader.clearHighlights(); 295 reader.clearHighlights();
222 SourceIndex index = reader.getSourceIndex(); 296 SourceIndex index = reader.getSourceIndex();
223 for (FieldEntry obfFieldEntry : obfFieldEntries) { 297
298 // matched fields
299 for (FieldEntry obfFieldEntry : obfMatchedFields) {
224 FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry); 300 FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry);
225 Token token = index.getDeclarationToken(deobfFieldEntry); 301 Token token = index.getDeclarationToken(deobfFieldEntry);
226 if (token == null) { 302 if (token != null) {
227 System.err.println("WARNING: Can't find declaration token for " + deobfFieldEntry); 303 reader.setHighlightedToken(token, m_matchedHighlightPainter);
228 } else { 304 }
229 reader.setHighlightedToken( 305 }
230 token, 306
231 obfMatchedFieldEntries.contains(obfFieldEntry) ? m_matchedHighlightPainter : m_unmatchedHighlightPainter 307 // unmatched fields
232 ); 308 for (FieldEntry obfFieldEntry : obfUnmatchedFields) {
309 FieldEntry deobfFieldEntry = deobfuscator.deobfuscateEntry(obfFieldEntry);
310 Token token = index.getDeclarationToken(deobfFieldEntry);
311 if (token != null) {
312 reader.setHighlightedToken(token, m_unmatchedHighlightPainter);
233 } 313 }
234 } 314 }
235 } 315 }
236 316
237 protected void onSelectSource(Entry entry) { 317 private boolean isSelectionMatched() {
238 m_sourceLabel.setText(""); 318 return m_obfSourceField != null && m_obfDestField != null
239 m_obfSourceField = null; 319 && m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField);
240 if (entry != null && entry instanceof FieldEntry) { 320 }
241 FieldEntry fieldEntry = (FieldEntry)entry; 321
242 FieldEntry obfFieldEntry = m_sourceDeobfuscator.obfuscateEntry(fieldEntry); 322 protected void onSelectSource(Entry source) {
243 if (m_obfUnmatchedSourceFields.contains(obfFieldEntry)) { 323
244 m_obfSourceField = obfFieldEntry; 324 // start with no selection
245 m_sourceLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); 325 if (isSelectionMatched()) {
326 setDest(null);
327 }
328 setSource(null);
329
330 // then look for a valid source selection
331 if (source != null && source instanceof FieldEntry) {
332 FieldEntry sourceField = (FieldEntry)source;
333 FieldEntry obfSourceField = m_sourceDeobfuscator.obfuscateEntry(sourceField);
334 if (m_fieldMatches.hasSource(obfSourceField)) {
335 setSource(obfSourceField);
336
337 // look for a matched dest too
338 FieldEntry obfDestField = m_fieldMatches.matches().get(obfSourceField);
339 if (obfDestField != null) {
340 setDest(obfDestField);
341 }
246 } 342 }
247 } 343 }
248 updateMatchButton(); 344
345 updateButtons();
249 } 346 }
250 347
251 protected void onSelectDest(Entry entry) { 348 protected void onSelectDest(Entry dest) {
252 m_destLabel.setText(""); 349
253 m_obfDestField = null; 350 // start with no selection
254 if (entry != null && entry instanceof FieldEntry) { 351 if (isSelectionMatched()) {
255 FieldEntry fieldEntry = (FieldEntry)entry; 352 setSource(null);
256 FieldEntry obfFieldEntry = m_destDeobfuscator.obfuscateEntry(fieldEntry); 353 }
257 if (m_obfUnmatchedDestFields.contains(obfFieldEntry)) { 354 setDest(null);
258 m_obfDestField = obfFieldEntry; 355
259 m_destLabel.setText(fieldEntry.getName() + " " + fieldEntry.getType().toString()); 356 // then look for a valid dest selection
357 if (dest != null && dest instanceof FieldEntry) {
358 FieldEntry destField = (FieldEntry)dest;
359 FieldEntry obfDestField = m_destDeobfuscator.obfuscateEntry(destField);
360 if (m_fieldMatches.hasDest(obfDestField)) {
361 setDest(obfDestField);
362
363 // look for a matched source too
364 FieldEntry obfSourceField = m_fieldMatches.matches().inverse().get(obfDestField);
365 if (obfSourceField != null) {
366 setSource(obfSourceField);
367 }
260 } 368 }
261 } 369 }
262 updateMatchButton(); 370
371 updateButtons();
263 } 372 }
373
374 private void setSource(FieldEntry obfField) {
375 if (obfField == null) {
376 m_obfSourceField = obfField;
377 m_sourceLabel.setText("");
378 } else {
379 m_obfSourceField = obfField;
380 FieldEntry deobfField = m_sourceDeobfuscator.deobfuscateEntry(obfField);
381 m_sourceLabel.setText(deobfField.getName() + " " + deobfField.getType().toString());
382 }
383 }
384
385 private void setDest(FieldEntry obfField) {
386 if (obfField == null) {
387 m_obfDestField = obfField;
388 m_destLabel.setText("");
389 } else {
390 m_obfDestField = obfField;
391 FieldEntry deobfField = m_destDeobfuscator.deobfuscateEntry(obfField);
392 m_destLabel.setText(deobfField.getName() + " " + deobfField.getType().toString());
393 }
394 }
395
396 private void updateButtons() {
264 397
265 private void updateMatchButton() { 398 GuiTricks.deactivateButton(m_matchButton);
266 m_matchButton.setEnabled(m_obfSourceField != null && m_obfDestField != null); 399 GuiTricks.deactivateButton(m_unmatchableButton);
400
401 if (m_obfSourceField != null && m_obfDestField != null) {
402 if (m_fieldMatches.isMatched(m_obfSourceField, m_obfDestField)) {
403 GuiTricks.activateButton(m_matchButton, "Unmatch", new ActionListener() {
404 @Override
405 public void actionPerformed(ActionEvent event) {
406 unmatch();
407 }
408 });
409 } else if (!m_fieldMatches.isMatchedSourceField(m_obfSourceField) && !m_fieldMatches.isMatchedDestField(m_obfDestField)) {
410 GuiTricks.activateButton(m_matchButton, "Match", new ActionListener() {
411 @Override
412 public void actionPerformed(ActionEvent event) {
413 match();
414 }
415 });
416 }
417 } else if (m_obfSourceField != null) {
418 GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", new ActionListener() {
419 @Override
420 public void actionPerformed(ActionEvent event) {
421 unmatchable();
422 }
423 });
424 }
267 } 425 }
268 426
269 protected void match() { 427 protected void match() {
@@ -275,7 +433,34 @@ public class FieldMatchingGui {
275 // update the ui 433 // update the ui
276 onSelectSource(null); 434 onSelectSource(null);
277 onSelectDest(null); 435 onSelectDest(null);
278 updateUnmatchedFields(); 436 updateSourceHighlights();
437 updateDestHighlights();
438 updateSourceClasses();
439 }
440
441 protected void unmatch() {
442
443 // update the field matches
444 m_fieldMatches.unmakeMatch(m_obfSourceField, m_obfDestField);
445 save();
446
447 // update the ui
448 onSelectSource(null);
449 onSelectDest(null);
450 updateSourceHighlights();
451 updateDestHighlights();
452 updateSourceClasses();
453 }
454
455 protected void unmatchable() {
456
457 // update the field matches
458 m_fieldMatches.makeSourceUnmatchable(m_obfSourceField);
459 save();
460
461 // update the ui
462 onSelectSource(null);
463 onSelectDest(null);
279 updateSourceHighlights(); 464 updateSourceHighlights();
280 updateDestHighlights(); 465 updateDestHighlights();
281 updateSourceClasses(); 466 updateSourceClasses();
diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java
index df9e221..7e539a1 100644
--- a/src/cuchaz/enigma/gui/GuiTricks.java
+++ b/src/cuchaz/enigma/gui/GuiTricks.java
@@ -11,8 +11,11 @@
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui;
12 12
13import java.awt.Font; 13import java.awt.Font;
14import java.awt.event.ActionListener;
14import java.awt.event.MouseEvent; 15import java.awt.event.MouseEvent;
16import java.util.Arrays;
15 17
18import javax.swing.JButton;
16import javax.swing.JComponent; 19import javax.swing.JComponent;
17import javax.swing.JLabel; 20import javax.swing.JLabel;
18import javax.swing.ToolTipManager; 21import javax.swing.ToolTipManager;
@@ -33,4 +36,21 @@ public class GuiTricks {
33 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); 36 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
34 manager.setInitialDelay(oldDelay); 37 manager.setInitialDelay(oldDelay);
35 } 38 }
39
40 public static void deactivateButton(JButton button) {
41 button.setEnabled(false);
42 button.setText("");
43 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
44 button.removeActionListener(listener);
45 }
46 }
47
48 public static void activateButton(JButton button, String text, ActionListener newListener) {
49 button.setText(text);
50 button.setEnabled(true);
51 for (ActionListener listener : Arrays.asList(button.getActionListeners())) {
52 button.removeActionListener(listener);
53 }
54 button.addActionListener(newListener);
55 }
36} 56}