From 63172120a39a315e29bc38ea6634741797b3dcab Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 30 Aug 2014 14:14:54 -0400 Subject: finished class matching for now, need to work on class member matching --- src/cuchaz/enigma/convert/ClassMapper.java | 229 ++++++++++++++++++++++------- 1 file changed, 174 insertions(+), 55 deletions(-) (limited to 'src/cuchaz/enigma/convert/ClassMapper.java') diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java index fe48c50..fd6ab92 100644 --- a/src/cuchaz/enigma/convert/ClassMapper.java +++ b/src/cuchaz/enigma/convert/ClassMapper.java @@ -11,32 +11,130 @@ package cuchaz.enigma.convert; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.jar.JarFile; import javassist.CtClass; + +import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Maps; + +import cuchaz.enigma.Constants; import cuchaz.enigma.TranslatingTypeLoader; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; public class ClassMapper { public static void main( String[] args ) - throws IOException + throws IOException, MappingParseException { // TEMP JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); - JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); + JarFile toJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); + File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); + File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); // compute the matching ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); - // TODO: use the matching to convert the mappings + // use the matching to convert the mappings + Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); + Map>> conversionMap = matching.getConversionMap(); + Map finalConversion = Maps.newHashMap(); + Set unmatchedSourceClasses = Sets.newHashSet(); + for( ClassMapping classMapping : mappings.classes() ) + { + // is there a match for this class? + Map.Entry> entry = conversionMap.get( classMapping.getObfName() ); + ClassIdentity sourceClass = entry.getKey(); + List matches = entry.getValue(); + + if( matches.isEmpty() ) + { + // no match! =( + System.out.println( "No exact match for source class " + classMapping.getObfName() ); + + // find the closest classes + TreeMap scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + { + scoredMatches.put( sourceClass.getMatchScore( c ), c ); + } + Iterator> iter = scoredMatches.entrySet().iterator(); + for( int i=0; i<10 && iter.hasNext(); i++ ) + { + Map.Entry score = iter.next(); + System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); + } + + // does the best match have a non-zero score and the same name? + Map.Entry bestMatch = scoredMatches.firstEntry(); + if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) + { + // use it + System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); + addFinalConversion( finalConversion, sourceClass, bestMatch.getValue() ); + } + else + { + unmatchedSourceClasses.add( classMapping.getObfName() ); + } + } + if( matches.size() == 1 ) + { + // unique match! We're good to go! + addFinalConversion( finalConversion, sourceClass, matches.get( 0 ) ); + } + else if( matches.size() > 1 ) + { + // too many matches! =( + unmatchedSourceClasses.add( classMapping.getObfName() ); + } + } + + // remove (and warn about) unmatched classes + if( !unmatchedSourceClasses.isEmpty() ) + { + System.err.println( "WARNING: there were unmatched classes!" ); + for( String className : unmatchedSourceClasses ) + { + System.err.println( "\t" + className ); + mappings.removeClassByObfName( className ); + } + System.err.println( "Mappings for these classes have been removed." ); + } + + // show the class name changes + for( Map.Entry entry : finalConversion.entrySet() ) + { + if( !entry.getKey().equals( entry.getValue() ) ) + { + System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); + } + } + + // do the final conversion + mappings.renameObfClasses( finalConversion ); + FileWriter writer = new FileWriter( outMappingsFile ); + new MappingsWriter().write( writer, mappings ); + writer.close(); + System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); } public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) @@ -55,78 +153,81 @@ public class ClassMapper TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); ClassMatching matching = null; - for( boolean useReferences : Arrays.asList( false, true ) ) + for( boolean useRawNames : Arrays.asList( false, true ) ) { - int numMatches = 0; - do + for( boolean useReferences : Arrays.asList( false, true ) ) { - SidedClassNamer sourceNamer = null; - SidedClassNamer destNamer = null; - if( matching != null ) + int numMatches = 0; + do { - // build a class namer - ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); - sourceNamer = namer.getSourceNamer(); - destNamer = namer.getDestNamer(); + SidedClassNamer sourceNamer = null; + SidedClassNamer destNamer = null; + if( matching != null ) + { + // build a class namer + ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); + sourceNamer = namer.getSourceNamer(); + destNamer = namer.getDestNamer(); + + // note the number of matches + numMatches = matching.getUniqueMatches().size(); + } - // note the number of matches - numMatches = matching.getUniqueMatches().size(); - } - - // get the entries left to match - Set sourceClassEntries = sourceIndex.getObfClassEntries(); - Set destClassEntries = destIndex.getObfClassEntries(); - if( matching != null ) - { - sourceClassEntries.clear(); - destClassEntries.clear(); - for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + // get the entries left to match + Set sourceClassEntries = sourceIndex.getObfClassEntries(); + Set destClassEntries = destIndex.getObfClassEntries(); + if( matching != null ) { - for( ClassIdentity c : entry.getKey() ) + sourceClassEntries.clear(); + destClassEntries.clear(); + for( Map.Entry,List> entry : matching.getAmbiguousMatches().entrySet() ) + { + for( ClassIdentity c : entry.getKey() ) + { + sourceClassEntries.add( c.getClassEntry() ); + matching.removeSource( c ); + } + for( ClassIdentity c : entry.getValue() ) + { + destClassEntries.add( c.getClassEntry() ); + matching.removeDest( c ); + } + } + for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) { sourceClassEntries.add( c.getClassEntry() ); matching.removeSource( c ); } - for( ClassIdentity c : entry.getValue() ) + for( ClassIdentity c : matching.getUnmatchedDestClasses() ) { destClassEntries.add( c.getClassEntry() ); matching.removeDest( c ); } } - for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) + else { - sourceClassEntries.add( c.getClassEntry() ); - matching.removeSource( c ); + matching = new ClassMatching(); } - for( ClassIdentity c : matching.getUnmatchedDestClasses() ) + + // compute a matching for the classes + for( ClassEntry classEntry : sourceClassEntries ) { - destClassEntries.add( c.getClassEntry() ); - matching.removeDest( c ); + CtClass c = sourceLoader.loadClass( classEntry.getName() ); + ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); + matching.addSource( sourceClass ); } + for( ClassEntry classEntry : destClassEntries ) + { + CtClass c = destLoader.loadClass( classEntry.getName() ); + ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); + matching.matchDestClass( destClass ); + } + + // TEMP + System.out.println( matching ); } - else - { - matching = new ClassMatching(); - } - - // compute a matching for the classes - for( ClassEntry classEntry : sourceClassEntries ) - { - CtClass c = sourceLoader.loadClass( classEntry.getName() ); - ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); - matching.addSource( sourceClass ); - } - for( ClassEntry classEntry : destClassEntries ) - { - CtClass c = destLoader.loadClass( classEntry.getName() ); - ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); - matching.matchDestClass( destClass ); - } - - // TEMP - System.out.println( matching ); + while( matching.getUniqueMatches().size() - numMatches > 0 ); } - while( matching.getUniqueMatches().size() - numMatches > 0 ); } /* DEBUG: show some ambiguous matches @@ -163,6 +264,24 @@ public class ClassMapper return matching; } + private static void addFinalConversion( Map finalConversion, ClassIdentity sourceClass, ClassIdentity destClass ) + { + // flatten inner classes since these are all obf classes in the none package + String sourceClassName = sourceClass.getClassEntry().getName(); + if( sourceClass.getClassEntry().isInnerClass() ) + { + sourceClassName = Constants.NonePackage + "/" + sourceClass.getClassEntry().getInnerClassName(); + } + + String destClassName = destClass.getClassEntry().getName(); + if( destClass.getClassEntry().isInnerClass() ) + { + destClassName = Constants.NonePackage + "/" + destClass.getClassEntry().getInnerClassName(); + } + + finalConversion.put( sourceClassName, destClassName ); + } + /* DEBUG private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) { -- cgit v1.2.3