summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert
diff options
context:
space:
mode:
authorGravatar jeff2015-03-11 11:03:16 -0400
committerGravatar jeff2015-03-11 11:03:16 -0400
commite33b4003a5c423894e7aef575faff359dd1d33b1 (patch)
tree6fa674b5ff8dbc699a44b6423149ad7e6eaae250 /src/cuchaz/enigma/convert
parentnothing of consequence (diff)
downloadenigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.tar.gz
enigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.tar.xz
enigma-fork-e33b4003a5c423894e7aef575faff359dd1d33b1.zip
generalized field matching
added method matching
Diffstat (limited to 'src/cuchaz/enigma/convert')
-rw-r--r--src/cuchaz/enigma/convert/MappingsConverter.java196
-rw-r--r--src/cuchaz/enigma/convert/MatchesReader.java46
-rw-r--r--src/cuchaz/enigma/convert/MatchesWriter.java50
-rw-r--r--src/cuchaz/enigma/convert/MemberMatches.java145
4 files changed, 361 insertions, 76 deletions
diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java
index 9ab1baa..2987ea0 100644
--- a/src/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/cuchaz/enigma/convert/MappingsConverter.java
@@ -11,12 +11,12 @@
11package cuchaz.enigma.convert; 11package cuchaz.enigma.convert;
12 12
13import java.util.Arrays; 13import java.util.Arrays;
14import java.util.Collection;
14import java.util.Collections; 15import java.util.Collections;
15import java.util.Iterator; 16import java.util.Iterator;
16import java.util.LinkedHashMap; 17import java.util.LinkedHashMap;
17import java.util.List; 18import java.util.List;
18import java.util.Map; 19import java.util.Map;
19import java.util.Map.Entry;
20import java.util.Set; 20import java.util.Set;
21import java.util.jar.JarFile; 21import java.util.jar.JarFile;
22 22
@@ -30,15 +30,20 @@ import com.google.common.collect.Sets;
30import cuchaz.enigma.Deobfuscator; 30import cuchaz.enigma.Deobfuscator;
31import cuchaz.enigma.analysis.JarIndex; 31import cuchaz.enigma.analysis.JarIndex;
32import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; 32import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
33import cuchaz.enigma.mapping.BehaviorEntry;
33import cuchaz.enigma.mapping.ClassEntry; 34import cuchaz.enigma.mapping.ClassEntry;
34import cuchaz.enigma.mapping.ClassMapping; 35import cuchaz.enigma.mapping.ClassMapping;
35import cuchaz.enigma.mapping.ClassNameReplacer; 36import cuchaz.enigma.mapping.ClassNameReplacer;
36import cuchaz.enigma.mapping.EntryFactory; 37import cuchaz.enigma.mapping.ConstructorEntry;
38import cuchaz.enigma.mapping.Entry;
37import cuchaz.enigma.mapping.FieldEntry; 39import cuchaz.enigma.mapping.FieldEntry;
38import cuchaz.enigma.mapping.FieldMapping; 40import cuchaz.enigma.mapping.FieldMapping;
39import cuchaz.enigma.mapping.Mappings; 41import cuchaz.enigma.mapping.Mappings;
40import cuchaz.enigma.mapping.MappingsChecker; 42import cuchaz.enigma.mapping.MappingsChecker;
43import cuchaz.enigma.mapping.MemberMapping;
44import cuchaz.enigma.mapping.MethodEntry;
41import cuchaz.enigma.mapping.MethodMapping; 45import cuchaz.enigma.mapping.MethodMapping;
46import cuchaz.enigma.mapping.Signature;
42import cuchaz.enigma.mapping.Type; 47import cuchaz.enigma.mapping.Type;
43 48
44public class MappingsConverter { 49public class MappingsConverter {
@@ -124,8 +129,8 @@ public class MappingsConverter {
124 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) { 129 public static Mappings newMappings(ClassMatches matches, Mappings oldMappings, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
125 130
126 // sort the unique matches by size of inner class chain 131 // sort the unique matches by size of inner class chain
127 Multimap<Integer,Entry<ClassEntry,ClassEntry>> matchesByDestChainSize = HashMultimap.create(); 132 Multimap<Integer,java.util.Map.Entry<ClassEntry,ClassEntry>> matchesByDestChainSize = HashMultimap.create();
128 for (Entry<ClassEntry,ClassEntry> match : matches.getUniqueMatches().entrySet()) { 133 for (java.util.Map.Entry<ClassEntry,ClassEntry> match : matches.getUniqueMatches().entrySet()) {
129 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size(); 134 int chainSize = destDeobfuscator.getJarIndex().getObfClassChain(match.getValue()).size();
130 matchesByDestChainSize.put(chainSize, match); 135 matchesByDestChainSize.put(chainSize, match);
131 } 136 }
@@ -135,7 +140,7 @@ public class MappingsConverter {
135 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet()); 140 List<Integer> chainSizes = Lists.newArrayList(matchesByDestChainSize.keySet());
136 Collections.sort(chainSizes); 141 Collections.sort(chainSizes);
137 for (int chainSize : chainSizes) { 142 for (int chainSize : chainSizes) {
138 for (Entry<ClassEntry,ClassEntry> match : matchesByDestChainSize.get(chainSize)) { 143 for (java.util.Map.Entry<ClassEntry,ClassEntry> match : matchesByDestChainSize.get(chainSize)) {
139 144
140 // get class info 145 // get class info
141 ClassEntry obfSourceClassEntry = match.getKey(); 146 ClassEntry obfSourceClassEntry = match.getKey();
@@ -251,89 +256,172 @@ public class MappingsConverter {
251 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName()); 256 mappings.renameObfClass(entry.getKey().getName(), entry.getValue().getName());
252 } 257 }
253 } 258 }
259
260 public static interface Doer<T extends Entry> {
261 Collection<T> getDroppedEntries(MappingsChecker checker);
262 Collection<T> getObfEntries(JarIndex jarIndex);
263 Collection<? extends MemberMapping<T>> getMappings(ClassMapping destClassMapping);
264 Set<T> filterEntries(Collection<T> obfEntries, T obfSourceEntry, ClassMatches classMatches);
265 }
266
267 public static MemberMatches<FieldEntry> computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) {
268 return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer<FieldEntry>() {
269
270 @Override
271 public Collection<FieldEntry> getDroppedEntries(MappingsChecker checker) {
272 return checker.getDroppedFieldMappings().keySet();
273 }
274
275 @Override
276 public Collection<FieldEntry> getObfEntries(JarIndex jarIndex) {
277 return jarIndex.getObfFieldEntries();
278 }
279
280 @Override
281 public Collection<? extends MemberMapping<FieldEntry>> getMappings(ClassMapping destClassMapping) {
282 return (Collection<? extends MemberMapping<FieldEntry>>)destClassMapping.fields();
283 }
284
285 @Override
286 public Set<FieldEntry> filterEntries(Collection<FieldEntry> obfDestFields, FieldEntry obfSourceField, ClassMatches classMatches) {
287 Set<FieldEntry> out = Sets.newHashSet();
288 for (FieldEntry obfDestField : obfDestFields) {
289 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
290 if (translatedDestType.equals(obfSourceField.getType())) {
291 out.add(obfDestField);
292 }
293 }
294 return out;
295 }
296 });
297 }
298
299 public static MemberMatches<BehaviorEntry> computeBehaviorMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) {
300 return computeMemberMatches(destDeobfuscator, destMappings, classMatches, new Doer<BehaviorEntry>() {
301
302 @Override
303 public Collection<BehaviorEntry> getDroppedEntries(MappingsChecker checker) {
304 return checker.getDroppedMethodMappings().keySet();
305 }
306
307 @Override
308 public Collection<BehaviorEntry> getObfEntries(JarIndex jarIndex) {
309 return jarIndex.getObfBehaviorEntries();
310 }
254 311
255 public static FieldMatches computeFieldMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches) { 312 @Override
313 public Collection<? extends MemberMapping<BehaviorEntry>> getMappings(ClassMapping destClassMapping) {
314 return (Collection<? extends MemberMapping<BehaviorEntry>>)destClassMapping.methods();
315 }
316
317 @Override
318 public Set<BehaviorEntry> filterEntries(Collection<BehaviorEntry> obfDestFields, BehaviorEntry obfSourceField, ClassMatches classMatches) {
319 Set<BehaviorEntry> out = Sets.newHashSet();
320 for (BehaviorEntry obfDestField : obfDestFields) {
321 Signature translatedDestSignature = translate(obfDestField.getSignature(), classMatches.getUniqueMatches().inverse());
322 if (translatedDestSignature == null && obfSourceField.getSignature() == null) {
323 out.add(obfDestField);
324 } else if (translatedDestSignature == null || obfSourceField.getSignature() == null) {
325 // skip it
326 } else if (translatedDestSignature.equals(obfSourceField.getSignature())) {
327 out.add(obfDestField);
328 }
329 }
330 return out;
331 }
332 });
333 }
334
335 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
256 336
257 FieldMatches fieldMatches = new FieldMatches(); 337 MemberMatches<T> memberMatches = new MemberMatches<T>();
258 338
259 // unmatched source fields are easy 339 // unmatched source fields are easy
260 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex()); 340 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
261 checker.dropBrokenMappings(destMappings); 341 checker.dropBrokenMappings(destMappings);
262 for (FieldEntry destObfField : checker.getDroppedFieldMappings().keySet()) { 342 for (T destObfEntry : doer.getDroppedEntries(checker)) {
263 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); 343 T srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
264 fieldMatches.addUnmatchedSourceField(srcObfField); 344 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
265 } 345 }
266 346
267 // get matched fields (anything that's left after the checks/drops is matched( 347 // get matched fields (anything that's left after the checks/drops is matched(
268 for (ClassMapping classMapping : destMappings.classes()) { 348 for (ClassMapping classMapping : destMappings.classes()) {
269 collectMatchedFields(fieldMatches, classMapping, classMatches); 349 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
270 } 350 }
271 351
272 // get unmatched dest fields 352 // get unmatched dest fields
273 for (FieldEntry destFieldEntry : destDeobfuscator.getJarIndex().getObfFieldEntries()) { 353 for (T destEntry : doer.getObfEntries(destDeobfuscator.getJarIndex())) {
274 if (!fieldMatches.isMatchedDestField(destFieldEntry)) { 354 if (!memberMatches.isMatchedDestEntry(destEntry)) {
275 fieldMatches.addUnmatchedDestField(destFieldEntry); 355 memberMatches.addUnmatchedDestEntry(destEntry);
276 } 356 }
277 } 357 }
278 358
279 System.out.println("Automatching " + fieldMatches.getUnmatchedSourceFields().size() + " unmatched source fields..."); 359 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
280 360
281 // go through the unmatched source fields and try to pick out the easy matches 361 // go through the unmatched source fields and try to pick out the easy matches
282 for (ClassEntry obfSourceClass : Lists.newArrayList(fieldMatches.getSourceClassesWithUnmatchedFields())) { 362 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
283 for (FieldEntry obfSourceField : Lists.newArrayList(fieldMatches.getUnmatchedSourceFields(obfSourceClass))) { 363 for (T obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
284 364
285 // get the possible dest matches 365 // get the possible dest matches
286 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass); 366 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
287 367
288 // filter by type 368 // filter by type/signature
289 Set<FieldEntry> obfDestFields = Sets.newHashSet(); 369 Set<T> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
290 for (FieldEntry obfDestField : fieldMatches.getUnmatchedDestFields(obfDestClass)) {
291 Type translatedDestType = translate(obfDestField.getType(), classMatches.getUniqueMatches().inverse());
292 if (translatedDestType.equals(obfSourceField.getType())) {
293 obfDestFields.add(obfDestField);
294 }
295 }
296 370
297 if (obfDestFields.size() == 1) { 371 if (obfDestEntries.size() == 1) {
298 // make the easy match 372 // make the easy match
299 FieldEntry obfDestField = obfDestFields.iterator().next(); 373 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
300 fieldMatches.makeMatch(obfSourceField, obfDestField); 374 } else if (obfDestEntries.isEmpty()) {
301 } else if (obfDestFields.isEmpty()) {
302 // no match is possible =( 375 // no match is possible =(
303 fieldMatches.makeSourceUnmatchable(obfSourceField); 376 memberMatches.makeSourceUnmatchable(obfSourceEntry);
304 } 377 }
305 } 378 }
306 } 379 }
307 380
308 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source fields", 381 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
309 fieldMatches.getUnmatchedSourceFields().size(), 382 memberMatches.getUnmatchedSourceEntries().size(),
310 fieldMatches.getUnmatchableSourceFields().size() 383 memberMatches.getUnmatchableSourceEntries().size()
311 )); 384 ));
312 385
313 return fieldMatches; 386 return memberMatches;
314 } 387 }
315 388
316 private static void collectMatchedFields(FieldMatches fieldMatches, ClassMapping destClassMapping, ClassMatches classMatches) { 389 private static <T extends Entry> void collectMatchedFields(MemberMatches<T> memberMatches, ClassMapping destClassMapping, ClassMatches classMatches, Doer<T> doer) {
317 390
318 // get the fields for this class 391 // get the fields for this class
319 for (FieldMapping destFieldMapping : destClassMapping.fields()) { 392 for (MemberMapping<T> destEntryMapping : doer.getMappings(destClassMapping)) {
320 FieldEntry destObfField = EntryFactory.getObfFieldEntry(destClassMapping, destFieldMapping); 393 T destObfField = destEntryMapping.getObfEntry(destClassMapping.getObfEntry());
321 FieldEntry srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse()); 394 T srcObfField = translate(destObfField, classMatches.getUniqueMatches().inverse());
322 fieldMatches.addMatch(srcObfField, destObfField); 395 memberMatches.addMatch(srcObfField, destObfField);
323 } 396 }
324 397
325 // recurse 398 // recurse
326 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) { 399 for (ClassMapping destInnerClassMapping : destClassMapping.innerClasses()) {
327 collectMatchedFields(fieldMatches, destInnerClassMapping, classMatches); 400 collectMatchedFields(memberMatches, destInnerClassMapping, classMatches, doer);
328 } 401 }
329 } 402 }
330 403
331 private static FieldEntry translate(FieldEntry in, BiMap<ClassEntry,ClassEntry> map) { 404 @SuppressWarnings("unchecked")
332 return new FieldEntry( 405 private static <T extends Entry> T translate(T in, BiMap<ClassEntry,ClassEntry> map) {
333 map.get(in.getClassEntry()), 406 if (in instanceof FieldEntry) {
334 in.getName(), 407 return (T)new FieldEntry(
335 translate(in.getType(), map) 408 map.get(in.getClassEntry()),
336 ); 409 in.getName(),
410 translate(((FieldEntry)in).getType(), map)
411 );
412 } else if (in instanceof MethodEntry) {
413 return (T)new MethodEntry(
414 map.get(in.getClassEntry()),
415 in.getName(),
416 translate(((MethodEntry)in).getSignature(), map)
417 );
418 } else if (in instanceof ConstructorEntry) {
419 return (T)new ConstructorEntry(
420 map.get(in.getClassEntry()),
421 translate(((ConstructorEntry)in).getSignature(), map)
422 );
423 }
424 throw new Error("Unhandled entry type: " + in.getClass());
337 } 425 }
338 426
339 private static Type translate(Type type, final BiMap<ClassEntry,ClassEntry> map) { 427 private static Type translate(Type type, final BiMap<ClassEntry,ClassEntry> map) {
@@ -348,4 +436,20 @@ public class MappingsConverter {
348 } 436 }
349 }); 437 });
350 } 438 }
439
440 private static Signature translate(Signature signature, final BiMap<ClassEntry,ClassEntry> map) {
441 if (signature == null) {
442 return null;
443 }
444 return new Signature(signature, new ClassNameReplacer() {
445 @Override
446 public String replace(String inClassName) {
447 ClassEntry outClassEntry = map.get(new ClassEntry(inClassName));
448 if (outClassEntry == null) {
449 return null;
450 }
451 return outClassEntry.getName();
452 }
453 });
454 }
351} 455}
diff --git a/src/cuchaz/enigma/convert/MatchesReader.java b/src/cuchaz/enigma/convert/MatchesReader.java
index 921ab1d..dac2f05 100644
--- a/src/cuchaz/enigma/convert/MatchesReader.java
+++ b/src/cuchaz/enigma/convert/MatchesReader.java
@@ -10,6 +10,8 @@ import java.util.List;
10import com.beust.jcommander.internal.Lists; 10import com.beust.jcommander.internal.Lists;
11 11
12import cuchaz.enigma.mapping.ClassEntry; 12import cuchaz.enigma.mapping.ClassEntry;
13import cuchaz.enigma.mapping.Entry;
14import cuchaz.enigma.mapping.EntryFactory;
13import cuchaz.enigma.mapping.FieldEntry; 15import cuchaz.enigma.mapping.FieldEntry;
14import cuchaz.enigma.mapping.Type; 16import cuchaz.enigma.mapping.Type;
15 17
@@ -45,45 +47,57 @@ public class MatchesReader {
45 return entries; 47 return entries;
46 } 48 }
47 49
48 public static FieldMatches readFields(File file) 50 public static <T extends Entry> MemberMatches<T> readMembers(File file)
49 throws IOException { 51 throws IOException {
50 try (BufferedReader in = new BufferedReader(new FileReader(file))) { 52 try (BufferedReader in = new BufferedReader(new FileReader(file))) {
51 FieldMatches matches = new FieldMatches(); 53 MemberMatches<T> matches = new MemberMatches<T>();
52 String line = null; 54 String line = null;
53 while ((line = in.readLine()) != null) { 55 while ((line = in.readLine()) != null) {
54 readFieldMatch(matches, line); 56 readMemberMatch(matches, line);
55 } 57 }
56 return matches; 58 return matches;
57 } 59 }
58 } 60 }
59 61
60 private static void readFieldMatch(FieldMatches matches, String line) { 62 private static <T extends Entry> void readMemberMatch(MemberMatches<T> matches, String line) {
61 if (line.startsWith("!")) { 63 if (line.startsWith("!")) {
62 matches.addUnmatchableSourceField(readField(line.substring(1))); 64 T source = readEntry(line.substring(1));
65 matches.addUnmatchableSourceEntry(source);
63 } else { 66 } else {
64 String[] parts = line.split(":", 2); 67 String[] parts = line.split(":", 2);
65 FieldEntry source = readField(parts[0]); 68 T source = readEntry(parts[0]);
66 FieldEntry dest = readField(parts[1]); 69 T dest = readEntry(parts[1]);
67 if (source != null && dest != null) { 70 if (source != null && dest != null) {
68 matches.addMatch(source, dest); 71 matches.addMatch(source, dest);
69 } else if (source != null) { 72 } else if (source != null) {
70 matches.addUnmatchedSourceField(source); 73 matches.addUnmatchedSourceEntry(source);
71 } else if (dest != null) { 74 } else if (dest != null) {
72 matches.addUnmatchedDestField(dest); 75 matches.addUnmatchedDestEntry(dest);
73 } 76 }
74 } 77 }
75 } 78 }
76 79
77 private static FieldEntry readField(String in) { 80 @SuppressWarnings("unchecked")
81 private static <T extends Entry> T readEntry(String in) {
78 if (in.length() <= 0) { 82 if (in.length() <= 0) {
79 return null; 83 return null;
80 } 84 }
81 String[] parts = in.split(" "); 85 String[] parts = in.split(" ");
82 assert(parts.length == 3); 86 if (parts.length == 3 && parts[2].indexOf('(') < 0) {
83 return new FieldEntry( 87 return (T)new FieldEntry(
84 new ClassEntry(parts[0]), 88 new ClassEntry(parts[0]),
85 parts[1], 89 parts[1],
86 new Type(parts[2]) 90 new Type(parts[2])
87 ); 91 );
92 } else {
93 assert(parts.length == 2 || parts.length == 3);
94 if (parts.length == 2) {
95 return (T)EntryFactory.getBehaviorEntry(parts[0], parts[1]);
96 } else if (parts.length == 3) {
97 return (T)EntryFactory.getBehaviorEntry(parts[0], parts[1], parts[2]);
98 } else {
99 throw new Error("Malformed behavior entry: " + in);
100 }
101 }
88 } 102 }
89} 103}
diff --git a/src/cuchaz/enigma/convert/MatchesWriter.java b/src/cuchaz/enigma/convert/MatchesWriter.java
index 2118dd0..9e9ead0 100644
--- a/src/cuchaz/enigma/convert/MatchesWriter.java
+++ b/src/cuchaz/enigma/convert/MatchesWriter.java
@@ -5,7 +5,9 @@ import java.io.FileWriter;
5import java.io.IOException; 5import java.io.IOException;
6import java.util.Map; 6import java.util.Map;
7 7
8import cuchaz.enigma.mapping.BehaviorEntry;
8import cuchaz.enigma.mapping.ClassEntry; 9import cuchaz.enigma.mapping.ClassEntry;
10import cuchaz.enigma.mapping.Entry;
9import cuchaz.enigma.mapping.FieldEntry; 11import cuchaz.enigma.mapping.FieldEntry;
10 12
11 13
@@ -41,43 +43,52 @@ public class MatchesWriter {
41 } 43 }
42 } 44 }
43 45
44 public static void writeFields(FieldMatches fieldMatches, File file) 46 public static <T extends Entry> void writeMembers(MemberMatches<T> matches, File file)
45 throws IOException { 47 throws IOException {
46 try (FileWriter out = new FileWriter(file)) { 48 try (FileWriter out = new FileWriter(file)) {
47 for (Map.Entry<FieldEntry,FieldEntry> match : fieldMatches.matches().entrySet()) { 49 for (Map.Entry<T,T> match : matches.matches().entrySet()) {
48 writeFieldMatch(out, match.getKey(), match.getValue()); 50 writeMemberMatch(out, match.getKey(), match.getValue());
49 } 51 }
50 for (FieldEntry fieldEntry : fieldMatches.getUnmatchedSourceFields()) { 52 for (T entry : matches.getUnmatchedSourceEntries()) {
51 writeFieldMatch(out, fieldEntry, null); 53 writeMemberMatch(out, entry, null);
52 } 54 }
53 for (FieldEntry fieldEntry : fieldMatches.getUnmatchedDestFields()) { 55 for (T entry : matches.getUnmatchedDestEntries()) {
54 writeFieldMatch(out, null, fieldEntry); 56 writeMemberMatch(out, null, entry);
55 } 57 }
56 for (FieldEntry fieldEntry : fieldMatches.getUnmatchableSourceFields()) { 58 for (T entry : matches.getUnmatchableSourceEntries()) {
57 writeUnmatchableField(out, fieldEntry); 59 writeUnmatchableEntry(out, entry);
58 } 60 }
59 } 61 }
60 } 62 }
61 63
62 private static void writeFieldMatch(FileWriter out, FieldEntry source, FieldEntry dest) 64 private static <T extends Entry> void writeMemberMatch(FileWriter out, T source, T dest)
63 throws IOException { 65 throws IOException {
64 if (source != null) { 66 if (source != null) {
65 writeField(out, source); 67 writeEntry(out, source);
66 } 68 }
67 out.write(":"); 69 out.write(":");
68 if (dest != null) { 70 if (dest != null) {
69 writeField(out, dest); 71 writeEntry(out, dest);
70 } 72 }
71 out.write("\n"); 73 out.write("\n");
72 } 74 }
73 75
74 private static void writeUnmatchableField(FileWriter out, FieldEntry fieldEntry) 76 private static <T extends Entry> void writeUnmatchableEntry(FileWriter out, T entry)
75 throws IOException { 77 throws IOException {
76 out.write("!"); 78 out.write("!");
77 writeField(out, fieldEntry); 79 writeEntry(out, entry);
78 out.write("\n"); 80 out.write("\n");
79 } 81 }
80 82
83 private static <T extends Entry> void writeEntry(FileWriter out, T entry)
84 throws IOException {
85 if (entry instanceof FieldEntry) {
86 writeField(out, (FieldEntry)entry);
87 } else if (entry instanceof BehaviorEntry) {
88 writeBehavior(out, (BehaviorEntry)entry);
89 }
90 }
91
81 private static void writeField(FileWriter out, FieldEntry fieldEntry) 92 private static void writeField(FileWriter out, FieldEntry fieldEntry)
82 throws IOException { 93 throws IOException {
83 out.write(fieldEntry.getClassName()); 94 out.write(fieldEntry.getClassName());
@@ -86,4 +97,15 @@ public class MatchesWriter {
86 out.write(" "); 97 out.write(" ");
87 out.write(fieldEntry.getType().toString()); 98 out.write(fieldEntry.getType().toString());
88 } 99 }
100
101 private static void writeBehavior(FileWriter out, BehaviorEntry behaviorEntry)
102 throws IOException {
103 out.write(behaviorEntry.getClassName());
104 out.write(" ");
105 out.write(behaviorEntry.getName());
106 out.write(" ");
107 if (behaviorEntry.getSignature() != null) {
108 out.write(behaviorEntry.getSignature().toString());
109 }
110 }
89} 111}
diff --git a/src/cuchaz/enigma/convert/MemberMatches.java b/src/cuchaz/enigma/convert/MemberMatches.java
new file mode 100644
index 0000000..1078ab7
--- /dev/null
+++ b/src/cuchaz/enigma/convert/MemberMatches.java
@@ -0,0 +1,145 @@
1package cuchaz.enigma.convert;
2
3import java.util.Collection;
4import java.util.Set;
5
6import com.google.common.collect.BiMap;
7import com.google.common.collect.HashBiMap;
8import com.google.common.collect.HashMultimap;
9import com.google.common.collect.Multimap;
10import com.google.common.collect.Sets;
11
12import cuchaz.enigma.mapping.ClassEntry;
13import cuchaz.enigma.mapping.Entry;
14
15
16public class MemberMatches<T extends Entry> {
17
18 private BiMap<T,T> m_matches;
19 private Multimap<ClassEntry,T> m_matchedSourceEntries;
20 private Multimap<ClassEntry,T> m_unmatchedSourceEntries;
21 private Multimap<ClassEntry,T> m_unmatchedDestEntries;
22 private Multimap<ClassEntry,T> m_unmatchableSourceEntries;
23
24 public MemberMatches() {
25 m_matches = HashBiMap.create();
26 m_matchedSourceEntries = HashMultimap.create();
27 m_unmatchedSourceEntries = HashMultimap.create();
28 m_unmatchedDestEntries = HashMultimap.create();
29 m_unmatchableSourceEntries = HashMultimap.create();
30 }
31
32 public void addMatch(T srcEntry, T destEntry) {
33 boolean wasAdded = m_matches.put(srcEntry, destEntry) == null;
34 assert (wasAdded);
35 wasAdded = m_matchedSourceEntries.put(srcEntry.getClassEntry(), srcEntry);
36 assert (wasAdded);
37 }
38
39 public void addUnmatchedSourceEntry(T sourceEntry) {
40 boolean wasAdded = m_unmatchedSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
41 assert (wasAdded);
42 }
43
44 public void addUnmatchedSourceEntries(Iterable<T> sourceEntries) {
45 for (T sourceEntry : sourceEntries) {
46 addUnmatchedSourceEntry(sourceEntry);
47 }
48 }
49
50 public void addUnmatchedDestEntry(T destEntry) {
51 boolean wasAdded = m_unmatchedDestEntries.put(destEntry.getClassEntry(), destEntry);
52 assert (wasAdded);
53 }
54
55 public void addUnmatchedDestEntries(Iterable<T> destEntriesntries) {
56 for (T entry : destEntriesntries) {
57 addUnmatchedDestEntry(entry);
58 }
59 }
60
61 public void addUnmatchableSourceEntry(T sourceEntry) {
62 boolean wasAdded = m_unmatchableSourceEntries.put(sourceEntry.getClassEntry(), sourceEntry);
63 assert (wasAdded);
64 }
65
66 public Set<ClassEntry> getSourceClassesWithUnmatchedEntries() {
67 return m_unmatchedSourceEntries.keySet();
68 }
69
70 public Collection<ClassEntry> getSourceClassesWithoutUnmatchedEntries() {
71 Set<ClassEntry> out = Sets.newHashSet();
72 out.addAll(m_matchedSourceEntries.keySet());
73 out.removeAll(m_unmatchedSourceEntries.keySet());
74 return out;
75 }
76
77 public Collection<T> getUnmatchedSourceEntries() {
78 return m_unmatchedSourceEntries.values();
79 }
80
81 public Collection<T> getUnmatchedSourceEntries(ClassEntry sourceClass) {
82 return m_unmatchedSourceEntries.get(sourceClass);
83 }
84
85 public Collection<T> getUnmatchedDestEntries() {
86 return m_unmatchedDestEntries.values();
87 }
88
89 public Collection<T> getUnmatchedDestEntries(ClassEntry destClass) {
90 return m_unmatchedDestEntries.get(destClass);
91 }
92
93 public Collection<T> getUnmatchableSourceEntries() {
94 return m_unmatchableSourceEntries.values();
95 }
96
97 public boolean hasSource(T sourceEntry) {
98 return m_matches.containsKey(sourceEntry) || m_unmatchedSourceEntries.containsValue(sourceEntry);
99 }
100
101 public boolean hasDest(T destEntry) {
102 return m_matches.containsValue(destEntry) || m_unmatchedDestEntries.containsValue(destEntry);
103 }
104
105 public BiMap<T,T> matches() {
106 return m_matches;
107 }
108
109 public boolean isMatchedSourceEntry(T sourceEntry) {
110 return m_matches.containsKey(sourceEntry);
111 }
112
113 public boolean isMatchedDestEntry(T destEntry) {
114 return m_matches.containsValue(destEntry);
115 }
116
117 public void makeMatch(T sourceEntry, T destEntry) {
118 boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
119 assert (wasRemoved);
120 wasRemoved = m_unmatchedDestEntries.remove(destEntry.getClassEntry(), destEntry);
121 assert (wasRemoved);
122 addMatch(sourceEntry, destEntry);
123 }
124
125 public boolean isMatched(T sourceEntry, T destEntry) {
126 T match = m_matches.get(sourceEntry);
127 return match != null && match.equals(destEntry);
128 }
129
130 public void unmakeMatch(T sourceEntry, T destEntry) {
131 boolean wasRemoved = m_matches.remove(sourceEntry) != null;
132 assert (wasRemoved);
133 wasRemoved = m_matchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
134 assert (wasRemoved);
135 addUnmatchedSourceEntry(sourceEntry);
136 addUnmatchedDestEntry(destEntry);
137 }
138
139 public void makeSourceUnmatchable(T sourceEntry) {
140 assert(!isMatchedSourceEntry(sourceEntry));
141 boolean wasRemoved = m_unmatchedSourceEntries.remove(sourceEntry.getClassEntry(), sourceEntry);
142 assert (wasRemoved);
143 addUnmatchableSourceEntry(sourceEntry);
144 }
145}