diff options
Diffstat (limited to 'src/cuchaz/enigma/convert/MappingsConverter.java')
| -rw-r--r-- | src/cuchaz/enigma/convert/MappingsConverter.java | 196 |
1 files changed, 150 insertions, 46 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 | } |