diff options
| author | 2015-03-11 11:03:16 -0400 | |
|---|---|---|
| committer | 2015-03-11 11:03:16 -0400 | |
| commit | e33b4003a5c423894e7aef575faff359dd1d33b1 (patch) | |
| tree | 6fa674b5ff8dbc699a44b6423149ad7e6eaae250 /src/cuchaz/enigma/convert | |
| parent | nothing of consequence (diff) | |
| download | enigma-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.java | 196 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/MatchesReader.java | 46 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/MatchesWriter.java | 50 | ||||
| -rw-r--r-- | src/cuchaz/enigma/convert/MemberMatches.java | 145 |
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 @@ | |||
| 11 | package cuchaz.enigma.convert; | 11 | package cuchaz.enigma.convert; |
| 12 | 12 | ||
| 13 | import java.util.Arrays; | 13 | import java.util.Arrays; |
| 14 | import java.util.Collection; | ||
| 14 | import java.util.Collections; | 15 | import java.util.Collections; |
| 15 | import java.util.Iterator; | 16 | import java.util.Iterator; |
| 16 | import java.util.LinkedHashMap; | 17 | import java.util.LinkedHashMap; |
| 17 | import java.util.List; | 18 | import java.util.List; |
| 18 | import java.util.Map; | 19 | import java.util.Map; |
| 19 | import java.util.Map.Entry; | ||
| 20 | import java.util.Set; | 20 | import java.util.Set; |
| 21 | import java.util.jar.JarFile; | 21 | import java.util.jar.JarFile; |
| 22 | 22 | ||
| @@ -30,15 +30,20 @@ import com.google.common.collect.Sets; | |||
| 30 | import cuchaz.enigma.Deobfuscator; | 30 | import cuchaz.enigma.Deobfuscator; |
| 31 | import cuchaz.enigma.analysis.JarIndex; | 31 | import cuchaz.enigma.analysis.JarIndex; |
| 32 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 32 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| 33 | import cuchaz.enigma.mapping.BehaviorEntry; | ||
| 33 | import cuchaz.enigma.mapping.ClassEntry; | 34 | import cuchaz.enigma.mapping.ClassEntry; |
| 34 | import cuchaz.enigma.mapping.ClassMapping; | 35 | import cuchaz.enigma.mapping.ClassMapping; |
| 35 | import cuchaz.enigma.mapping.ClassNameReplacer; | 36 | import cuchaz.enigma.mapping.ClassNameReplacer; |
| 36 | import cuchaz.enigma.mapping.EntryFactory; | 37 | import cuchaz.enigma.mapping.ConstructorEntry; |
| 38 | import cuchaz.enigma.mapping.Entry; | ||
| 37 | import cuchaz.enigma.mapping.FieldEntry; | 39 | import cuchaz.enigma.mapping.FieldEntry; |
| 38 | import cuchaz.enigma.mapping.FieldMapping; | 40 | import cuchaz.enigma.mapping.FieldMapping; |
| 39 | import cuchaz.enigma.mapping.Mappings; | 41 | import cuchaz.enigma.mapping.Mappings; |
| 40 | import cuchaz.enigma.mapping.MappingsChecker; | 42 | import cuchaz.enigma.mapping.MappingsChecker; |
| 43 | import cuchaz.enigma.mapping.MemberMapping; | ||
| 44 | import cuchaz.enigma.mapping.MethodEntry; | ||
| 41 | import cuchaz.enigma.mapping.MethodMapping; | 45 | import cuchaz.enigma.mapping.MethodMapping; |
| 46 | import cuchaz.enigma.mapping.Signature; | ||
| 42 | import cuchaz.enigma.mapping.Type; | 47 | import cuchaz.enigma.mapping.Type; |
| 43 | 48 | ||
| 44 | public class MappingsConverter { | 49 | public 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; | |||
| 10 | import com.beust.jcommander.internal.Lists; | 10 | import com.beust.jcommander.internal.Lists; |
| 11 | 11 | ||
| 12 | import cuchaz.enigma.mapping.ClassEntry; | 12 | import cuchaz.enigma.mapping.ClassEntry; |
| 13 | import cuchaz.enigma.mapping.Entry; | ||
| 14 | import cuchaz.enigma.mapping.EntryFactory; | ||
| 13 | import cuchaz.enigma.mapping.FieldEntry; | 15 | import cuchaz.enigma.mapping.FieldEntry; |
| 14 | import cuchaz.enigma.mapping.Type; | 16 | import 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; | |||
| 5 | import java.io.IOException; | 5 | import java.io.IOException; |
| 6 | import java.util.Map; | 6 | import java.util.Map; |
| 7 | 7 | ||
| 8 | import cuchaz.enigma.mapping.BehaviorEntry; | ||
| 8 | import cuchaz.enigma.mapping.ClassEntry; | 9 | import cuchaz.enigma.mapping.ClassEntry; |
| 10 | import cuchaz.enigma.mapping.Entry; | ||
| 9 | import cuchaz.enigma.mapping.FieldEntry; | 11 | import 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 @@ | |||
| 1 | package cuchaz.enigma.convert; | ||
| 2 | |||
| 3 | import java.util.Collection; | ||
| 4 | import java.util.Set; | ||
| 5 | |||
| 6 | import com.google.common.collect.BiMap; | ||
| 7 | import com.google.common.collect.HashBiMap; | ||
| 8 | import com.google.common.collect.HashMultimap; | ||
| 9 | import com.google.common.collect.Multimap; | ||
| 10 | import com.google.common.collect.Sets; | ||
| 11 | |||
| 12 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 13 | import cuchaz.enigma.mapping.Entry; | ||
| 14 | |||
| 15 | |||
| 16 | public 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 | } | ||