diff options
| author | 2014-08-30 14:14:54 -0400 | |
|---|---|---|
| committer | 2014-08-30 14:14:54 -0400 | |
| commit | 63172120a39a315e29bc38ea6634741797b3dcab (patch) | |
| tree | 9030b8678aaca06982ae4d1032298f52ab833e09 /src/cuchaz/enigma/convert/ClassMapper.java | |
| parent | got a decent class matcher working (diff) | |
| download | enigma-fork-63172120a39a315e29bc38ea6634741797b3dcab.tar.gz enigma-fork-63172120a39a315e29bc38ea6634741797b3dcab.tar.xz enigma-fork-63172120a39a315e29bc38ea6634741797b3dcab.zip | |
finished class matching for now, need to work on class member matching
Diffstat (limited to 'src/cuchaz/enigma/convert/ClassMapper.java')
| -rw-r--r-- | src/cuchaz/enigma/convert/ClassMapper.java | 229 |
1 files changed, 174 insertions, 55 deletions
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 @@ | |||
| 11 | package cuchaz.enigma.convert; | 11 | package cuchaz.enigma.convert; |
| 12 | 12 | ||
| 13 | import java.io.File; | 13 | import java.io.File; |
| 14 | import java.io.FileReader; | ||
| 15 | import java.io.FileWriter; | ||
| 14 | import java.io.IOException; | 16 | import java.io.IOException; |
| 15 | import java.util.Arrays; | 17 | import java.util.Arrays; |
| 18 | import java.util.Collections; | ||
| 19 | import java.util.Iterator; | ||
| 16 | import java.util.List; | 20 | import java.util.List; |
| 17 | import java.util.Map; | 21 | import java.util.Map; |
| 18 | import java.util.Set; | 22 | import java.util.Set; |
| 23 | import java.util.TreeMap; | ||
| 19 | import java.util.jar.JarFile; | 24 | import java.util.jar.JarFile; |
| 20 | 25 | ||
| 21 | import javassist.CtClass; | 26 | import javassist.CtClass; |
| 27 | |||
| 28 | import com.beust.jcommander.internal.Sets; | ||
| 29 | import com.google.common.collect.Maps; | ||
| 30 | |||
| 31 | import cuchaz.enigma.Constants; | ||
| 22 | import cuchaz.enigma.TranslatingTypeLoader; | 32 | import cuchaz.enigma.TranslatingTypeLoader; |
| 23 | import cuchaz.enigma.analysis.JarIndex; | 33 | import cuchaz.enigma.analysis.JarIndex; |
| 24 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 34 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| 25 | import cuchaz.enigma.mapping.ClassEntry; | 35 | import cuchaz.enigma.mapping.ClassEntry; |
| 36 | import cuchaz.enigma.mapping.ClassMapping; | ||
| 37 | import cuchaz.enigma.mapping.MappingParseException; | ||
| 38 | import cuchaz.enigma.mapping.Mappings; | ||
| 39 | import cuchaz.enigma.mapping.MappingsReader; | ||
| 40 | import cuchaz.enigma.mapping.MappingsWriter; | ||
| 26 | 41 | ||
| 27 | public class ClassMapper | 42 | public class ClassMapper |
| 28 | { | 43 | { |
| 29 | public static void main( String[] args ) | 44 | public static void main( String[] args ) |
| 30 | throws IOException | 45 | throws IOException, MappingParseException |
| 31 | { | 46 | { |
| 32 | // TEMP | 47 | // TEMP |
| 33 | JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); | 48 | JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); |
| 34 | JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); | 49 | JarFile toJar = new JarFile( new File( "input/1.8-pre3.jar" ) ); |
| 50 | File inMappingsFile = new File( "../minecraft-mappings/1.8-pre.mappings" ); | ||
| 51 | File outMappingsFile = new File( "../minecraft-mappings/1.8-pre3.mappings" ); | ||
| 35 | 52 | ||
| 36 | // compute the matching | 53 | // compute the matching |
| 37 | ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); | 54 | ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar ); |
| 38 | 55 | ||
| 39 | // TODO: use the matching to convert the mappings | 56 | // use the matching to convert the mappings |
| 57 | Mappings mappings = new MappingsReader().read( new FileReader( inMappingsFile ) ); | ||
| 58 | Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversionMap = matching.getConversionMap(); | ||
| 59 | Map<String,String> finalConversion = Maps.newHashMap(); | ||
| 60 | Set<String> unmatchedSourceClasses = Sets.newHashSet(); | ||
| 61 | for( ClassMapping classMapping : mappings.classes() ) | ||
| 62 | { | ||
| 63 | // is there a match for this class? | ||
| 64 | Map.Entry<ClassIdentity,List<ClassIdentity>> entry = conversionMap.get( classMapping.getObfName() ); | ||
| 65 | ClassIdentity sourceClass = entry.getKey(); | ||
| 66 | List<ClassIdentity> matches = entry.getValue(); | ||
| 67 | |||
| 68 | if( matches.isEmpty() ) | ||
| 69 | { | ||
| 70 | // no match! =( | ||
| 71 | System.out.println( "No exact match for source class " + classMapping.getObfName() ); | ||
| 72 | |||
| 73 | // find the closest classes | ||
| 74 | TreeMap<Integer,ClassIdentity> scoredMatches = Maps.newTreeMap( Collections.reverseOrder() ); | ||
| 75 | for( ClassIdentity c : matching.getUnmatchedDestClasses() ) | ||
| 76 | { | ||
| 77 | scoredMatches.put( sourceClass.getMatchScore( c ), c ); | ||
| 78 | } | ||
| 79 | Iterator<Map.Entry<Integer,ClassIdentity>> iter = scoredMatches.entrySet().iterator(); | ||
| 80 | for( int i=0; i<10 && iter.hasNext(); i++ ) | ||
| 81 | { | ||
| 82 | Map.Entry<Integer,ClassIdentity> score = iter.next(); | ||
| 83 | System.out.println( String.format( "\tScore: %3d %s", score.getKey(), score.getValue().getClassEntry().getName() ) ); | ||
| 84 | } | ||
| 85 | |||
| 86 | // does the best match have a non-zero score and the same name? | ||
| 87 | Map.Entry<Integer,ClassIdentity> bestMatch = scoredMatches.firstEntry(); | ||
| 88 | if( bestMatch.getKey() > 0 && bestMatch.getValue().getClassEntry().equals( sourceClass.getClassEntry() ) ) | ||
| 89 | { | ||
| 90 | // use it | ||
| 91 | System.out.println( "\tAutomatically choosing likely match: " + bestMatch.getValue().getClassEntry().getName() ); | ||
| 92 | addFinalConversion( finalConversion, sourceClass, bestMatch.getValue() ); | ||
| 93 | } | ||
| 94 | else | ||
| 95 | { | ||
| 96 | unmatchedSourceClasses.add( classMapping.getObfName() ); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | if( matches.size() == 1 ) | ||
| 100 | { | ||
| 101 | // unique match! We're good to go! | ||
| 102 | addFinalConversion( finalConversion, sourceClass, matches.get( 0 ) ); | ||
| 103 | } | ||
| 104 | else if( matches.size() > 1 ) | ||
| 105 | { | ||
| 106 | // too many matches! =( | ||
| 107 | unmatchedSourceClasses.add( classMapping.getObfName() ); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | // remove (and warn about) unmatched classes | ||
| 112 | if( !unmatchedSourceClasses.isEmpty() ) | ||
| 113 | { | ||
| 114 | System.err.println( "WARNING: there were unmatched classes!" ); | ||
| 115 | for( String className : unmatchedSourceClasses ) | ||
| 116 | { | ||
| 117 | System.err.println( "\t" + className ); | ||
| 118 | mappings.removeClassByObfName( className ); | ||
| 119 | } | ||
| 120 | System.err.println( "Mappings for these classes have been removed." ); | ||
| 121 | } | ||
| 122 | |||
| 123 | // show the class name changes | ||
| 124 | for( Map.Entry<String,String> entry : finalConversion.entrySet() ) | ||
| 125 | { | ||
| 126 | if( !entry.getKey().equals( entry.getValue() ) ) | ||
| 127 | { | ||
| 128 | System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | // do the final conversion | ||
| 133 | mappings.renameObfClasses( finalConversion ); | ||
| 134 | FileWriter writer = new FileWriter( outMappingsFile ); | ||
| 135 | new MappingsWriter().write( writer, mappings ); | ||
| 136 | writer.close(); | ||
| 137 | System.out.println( "Wrote converted mappings to:\n\t" + outMappingsFile.getAbsolutePath() ); | ||
| 40 | } | 138 | } |
| 41 | 139 | ||
| 42 | public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) | 140 | public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar ) |
| @@ -55,78 +153,81 @@ public class ClassMapper | |||
| 55 | TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); | 153 | TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex ); |
| 56 | 154 | ||
| 57 | ClassMatching matching = null; | 155 | ClassMatching matching = null; |
| 58 | for( boolean useReferences : Arrays.asList( false, true ) ) | 156 | for( boolean useRawNames : Arrays.asList( false, true ) ) |
| 59 | { | 157 | { |
| 60 | int numMatches = 0; | 158 | for( boolean useReferences : Arrays.asList( false, true ) ) |
| 61 | do | ||
| 62 | { | 159 | { |
| 63 | SidedClassNamer sourceNamer = null; | 160 | int numMatches = 0; |
| 64 | SidedClassNamer destNamer = null; | 161 | do |
| 65 | if( matching != null ) | ||
| 66 | { | 162 | { |
| 67 | // build a class namer | 163 | SidedClassNamer sourceNamer = null; |
| 68 | ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); | 164 | SidedClassNamer destNamer = null; |
| 69 | sourceNamer = namer.getSourceNamer(); | 165 | if( matching != null ) |
| 70 | destNamer = namer.getDestNamer(); | 166 | { |
| 167 | // build a class namer | ||
| 168 | ClassNamer namer = new ClassNamer( matching.getUniqueMatches() ); | ||
| 169 | sourceNamer = namer.getSourceNamer(); | ||
| 170 | destNamer = namer.getDestNamer(); | ||
| 171 | |||
| 172 | // note the number of matches | ||
| 173 | numMatches = matching.getUniqueMatches().size(); | ||
| 174 | } | ||
| 71 | 175 | ||
| 72 | // note the number of matches | 176 | // get the entries left to match |
| 73 | numMatches = matching.getUniqueMatches().size(); | 177 | Set<ClassEntry> sourceClassEntries = sourceIndex.getObfClassEntries(); |
| 74 | } | 178 | Set<ClassEntry> destClassEntries = destIndex.getObfClassEntries(); |
| 75 | 179 | if( matching != null ) | |
| 76 | // get the entries left to match | ||
| 77 | Set<ClassEntry> sourceClassEntries = sourceIndex.getObfClassEntries(); | ||
| 78 | Set<ClassEntry> destClassEntries = destIndex.getObfClassEntries(); | ||
| 79 | if( matching != null ) | ||
| 80 | { | ||
| 81 | sourceClassEntries.clear(); | ||
| 82 | destClassEntries.clear(); | ||
| 83 | for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : matching.getAmbiguousMatches().entrySet() ) | ||
| 84 | { | 180 | { |
| 85 | for( ClassIdentity c : entry.getKey() ) | 181 | sourceClassEntries.clear(); |
| 182 | destClassEntries.clear(); | ||
| 183 | for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : matching.getAmbiguousMatches().entrySet() ) | ||
| 184 | { | ||
| 185 | for( ClassIdentity c : entry.getKey() ) | ||
| 186 | { | ||
| 187 | sourceClassEntries.add( c.getClassEntry() ); | ||
| 188 | matching.removeSource( c ); | ||
| 189 | } | ||
| 190 | for( ClassIdentity c : entry.getValue() ) | ||
| 191 | { | ||
| 192 | destClassEntries.add( c.getClassEntry() ); | ||
| 193 | matching.removeDest( c ); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) | ||
| 86 | { | 197 | { |
| 87 | sourceClassEntries.add( c.getClassEntry() ); | 198 | sourceClassEntries.add( c.getClassEntry() ); |
| 88 | matching.removeSource( c ); | 199 | matching.removeSource( c ); |
| 89 | } | 200 | } |
| 90 | for( ClassIdentity c : entry.getValue() ) | 201 | for( ClassIdentity c : matching.getUnmatchedDestClasses() ) |
| 91 | { | 202 | { |
| 92 | destClassEntries.add( c.getClassEntry() ); | 203 | destClassEntries.add( c.getClassEntry() ); |
| 93 | matching.removeDest( c ); | 204 | matching.removeDest( c ); |
| 94 | } | 205 | } |
| 95 | } | 206 | } |
| 96 | for( ClassIdentity c : matching.getUnmatchedSourceClasses() ) | 207 | else |
| 97 | { | 208 | { |
| 98 | sourceClassEntries.add( c.getClassEntry() ); | 209 | matching = new ClassMatching(); |
| 99 | matching.removeSource( c ); | ||
| 100 | } | 210 | } |
| 101 | for( ClassIdentity c : matching.getUnmatchedDestClasses() ) | 211 | |
| 212 | // compute a matching for the classes | ||
| 213 | for( ClassEntry classEntry : sourceClassEntries ) | ||
| 102 | { | 214 | { |
| 103 | destClassEntries.add( c.getClassEntry() ); | 215 | CtClass c = sourceLoader.loadClass( classEntry.getName() ); |
| 104 | matching.removeDest( c ); | 216 | ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences, useRawNames ); |
| 217 | matching.addSource( sourceClass ); | ||
| 105 | } | 218 | } |
| 219 | for( ClassEntry classEntry : destClassEntries ) | ||
| 220 | { | ||
| 221 | CtClass c = destLoader.loadClass( classEntry.getName() ); | ||
| 222 | ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences, useRawNames ); | ||
| 223 | matching.matchDestClass( destClass ); | ||
| 224 | } | ||
| 225 | |||
| 226 | // TEMP | ||
| 227 | System.out.println( matching ); | ||
| 106 | } | 228 | } |
| 107 | else | 229 | while( matching.getUniqueMatches().size() - numMatches > 0 ); |
| 108 | { | ||
| 109 | matching = new ClassMatching(); | ||
| 110 | } | ||
| 111 | |||
| 112 | // compute a matching for the classes | ||
| 113 | for( ClassEntry classEntry : sourceClassEntries ) | ||
| 114 | { | ||
| 115 | CtClass c = sourceLoader.loadClass( classEntry.getName() ); | ||
| 116 | ClassIdentity sourceClass = new ClassIdentity( c, sourceNamer, sourceIndex, useReferences ); | ||
| 117 | matching.addSource( sourceClass ); | ||
| 118 | } | ||
| 119 | for( ClassEntry classEntry : destClassEntries ) | ||
| 120 | { | ||
| 121 | CtClass c = destLoader.loadClass( classEntry.getName() ); | ||
| 122 | ClassIdentity destClass = new ClassIdentity( c, destNamer, destIndex, useReferences ); | ||
| 123 | matching.matchDestClass( destClass ); | ||
| 124 | } | ||
| 125 | |||
| 126 | // TEMP | ||
| 127 | System.out.println( matching ); | ||
| 128 | } | 230 | } |
| 129 | while( matching.getUniqueMatches().size() - numMatches > 0 ); | ||
| 130 | } | 231 | } |
| 131 | 232 | ||
| 132 | /* DEBUG: show some ambiguous matches | 233 | /* DEBUG: show some ambiguous matches |
| @@ -163,6 +264,24 @@ public class ClassMapper | |||
| 163 | return matching; | 264 | return matching; |
| 164 | } | 265 | } |
| 165 | 266 | ||
| 267 | private static void addFinalConversion( Map<String,String> finalConversion, ClassIdentity sourceClass, ClassIdentity destClass ) | ||
| 268 | { | ||
| 269 | // flatten inner classes since these are all obf classes in the none package | ||
| 270 | String sourceClassName = sourceClass.getClassEntry().getName(); | ||
| 271 | if( sourceClass.getClassEntry().isInnerClass() ) | ||
| 272 | { | ||
| 273 | sourceClassName = Constants.NonePackage + "/" + sourceClass.getClassEntry().getInnerClassName(); | ||
| 274 | } | ||
| 275 | |||
| 276 | String destClassName = destClass.getClassEntry().getName(); | ||
| 277 | if( destClass.getClassEntry().isInnerClass() ) | ||
| 278 | { | ||
| 279 | destClassName = Constants.NonePackage + "/" + destClass.getClassEntry().getInnerClassName(); | ||
| 280 | } | ||
| 281 | |||
| 282 | finalConversion.put( sourceClassName, destClassName ); | ||
| 283 | } | ||
| 284 | |||
| 166 | /* DEBUG | 285 | /* DEBUG |
| 167 | private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) | 286 | private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry ) |
| 168 | { | 287 | { |