summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2014-08-31 16:14:45 -0400
committerGravatar jeff2014-08-31 16:14:45 -0400
commit4e9c52d5fc5d23e8a77857e712654596203acb31 (patch)
treec4a48493e6d60dfec9df326dac2b1ef229aedffd
parentfixed lots of bugs in the mappings converter. It's finally ready. =) (diff)
downloadenigma-4e9c52d5fc5d23e8a77857e712654596203acb31.tar.gz
enigma-4e9c52d5fc5d23e8a77857e712654596203acb31.tar.xz
enigma-4e9c52d5fc5d23e8a77857e712654596203acb31.zip
fixed mapping conversion bug with class rename order
-rw-r--r--src/cuchaz/enigma/convert/ClassMatcher.java83
-rw-r--r--src/cuchaz/enigma/convert/ClassMatching.java2
-rw-r--r--src/cuchaz/enigma/mapping/ClassMapping.java36
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java28
-rw-r--r--src/cuchaz/enigma/mapping/MethodMapping.java16
5 files changed, 103 insertions, 62 deletions
diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java
index b551da25..0821bd3a 100644
--- a/src/cuchaz/enigma/convert/ClassMatcher.java
+++ b/src/cuchaz/enigma/convert/ClassMatcher.java
@@ -19,6 +19,8 @@ import java.util.Arrays;
19import java.util.Collection; 19import java.util.Collection;
20import java.util.Collections; 20import java.util.Collections;
21import java.util.Comparator; 21import java.util.Comparator;
22import java.util.Iterator;
23import java.util.LinkedHashMap;
22import java.util.List; 24import java.util.List;
23import java.util.Map; 25import java.util.Map;
24import java.util.Set; 26import java.util.Set;
@@ -89,9 +91,7 @@ public class ClassMatcher
89 91
90 // compute the matching 92 // compute the matching
91 ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader ); 93 ClassMatching matching = computeMatching( sourceIndex, sourceLoader, destIndex, destLoader );
92 94 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> matchingIndex = matching.getIndex();
93 // start the class conversion map with the unique and ambiguous matchings
94 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversionMap = matching.getConversionMap();
95 95
96 // get all the obf class names used in the mappings 96 // get all the obf class names used in the mappings
97 Set<String> usedClassNames = mappings.getAllObfClassNames(); 97 Set<String> usedClassNames = mappings.getAllObfClassNames();
@@ -101,9 +101,10 @@ public class ClassMatcher
101 allClassNames.add( classEntry.getName() ); 101 allClassNames.add( classEntry.getName() );
102 } 102 }
103 usedClassNames.retainAll( allClassNames ); 103 usedClassNames.retainAll( allClassNames );
104 System.out.println( "Used " + usedClassNames.size() + " classes in the mappings" );
104 105
105 // probabilistically match the non-uniquely-matched source classes 106 // probabilistically match the non-uniquely-matched source classes
106 for( Map.Entry<ClassIdentity,List<ClassIdentity>> entry : conversionMap.values() ) 107 for( Map.Entry<ClassIdentity,List<ClassIdentity>> entry : matchingIndex.values() )
107 { 108 {
108 ClassIdentity sourceClass = entry.getKey(); 109 ClassIdentity sourceClass = entry.getKey();
109 List<ClassIdentity> destClasses = entry.getValue(); 110 List<ClassIdentity> destClasses = entry.getValue();
@@ -148,20 +149,20 @@ public class ClassMatcher
148 } 149 }
149 } 150 }
150 151
151 // use the matching to convert the mappings 152 // group the matching into unique and non-unique matches
152 BiMap<String,String> classConversion = HashBiMap.create(); 153 BiMap<String,String> matchedClassNames = HashBiMap.create();
153 Set<String> unmatchedSourceClasses = Sets.newHashSet(); 154 Set<String> unmatchedSourceClassNames = Sets.newHashSet();
154 for( String className : usedClassNames ) 155 for( String className : usedClassNames )
155 { 156 {
156 // is there a match for this class? 157 // is there a match for this class?
157 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = conversionMap.get( className ); 158 Map.Entry<ClassIdentity,List<ClassIdentity>> entry = matchingIndex.get( className );
158 ClassIdentity sourceClass = entry.getKey(); 159 ClassIdentity sourceClass = entry.getKey();
159 List<ClassIdentity> matches = entry.getValue(); 160 List<ClassIdentity> matches = entry.getValue();
160 161
161 if( matches.size() == 1 ) 162 if( matches.size() == 1 )
162 { 163 {
163 // unique match! We're good to go! 164 // unique match! We're good to go!
164 classConversion.put( 165 matchedClassNames.put(
165 sourceClass.getClassEntry().getName(), 166 sourceClass.getClassEntry().getName(),
166 matches.get( 0 ).getClassEntry().getName() 167 matches.get( 0 ).getClassEntry().getName()
167 ); 168 );
@@ -172,35 +173,36 @@ public class ClassMatcher
172 String fallbackMatch = fallbackMatching.get( className ); 173 String fallbackMatch = fallbackMatching.get( className );
173 if( fallbackMatch != null ) 174 if( fallbackMatch != null )
174 { 175 {
175 classConversion.put( 176 matchedClassNames.put(
176 sourceClass.getClassEntry().getName(), 177 sourceClass.getClassEntry().getName(),
177 fallbackMatch 178 fallbackMatch
178 ); 179 );
179 } 180 }
180 else 181 else
181 { 182 {
182 unmatchedSourceClasses.add( className ); 183 unmatchedSourceClassNames.add( className );
183 } 184 }
184 } 185 }
185 } 186 }
186 187
187 // remove (and warn about) unmatched classes 188 // report unmatched classes
188 if( !unmatchedSourceClasses.isEmpty() ) 189 if( !unmatchedSourceClassNames.isEmpty() )
189 { 190 {
190 System.err.println( "WARNING: there were unmatched classes!" ); 191 System.err.println( "ERROR: there were unmatched classes!" );
191 for( String className : unmatchedSourceClasses ) 192 for( String className : unmatchedSourceClassNames )
192 { 193 {
193 System.err.println( "\t" + className ); 194 System.err.println( "\t" + className );
194 mappings.removeClassByObfName( className );
195 } 195 }
196 System.err.println( "Mappings for these classes have been removed." ); 196 return;
197 } 197 }
198 198
199 // show the class name changes 199 // get the class name changes from the matched class names
200 for( Map.Entry<String,String> entry : classConversion.entrySet() ) 200 Map<String,String> classChanges = Maps.newHashMap();
201 for( Map.Entry<String,String> entry : matchedClassNames.entrySet() )
201 { 202 {
202 if( !entry.getKey().equals( entry.getValue() ) ) 203 if( !entry.getKey().equals( entry.getValue() ) )
203 { 204 {
205 classChanges.put( entry.getKey(), entry.getValue() );
204 System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) ); 206 System.out.println( String.format( "Class change: %s -> %s", entry.getKey(), entry.getValue() ) );
205 /* DEBUG 207 /* DEBUG
206 System.out.println( String.format( "\n%s\n%s", 208 System.out.println( String.format( "\n%s\n%s",
@@ -211,8 +213,45 @@ public class ClassMatcher
211 } 213 }
212 } 214 }
213 215
214 // convert the mappings 216 // sort the changes so classes are renamed in the correct order
215 mappings.renameObfClasses( classConversion ); 217 // ie. if we have the mappings a->b, b->c, we have to apply b->c before a->b
218 LinkedHashMap<String,String> orderedClassChanges = Maps.newLinkedHashMap();
219 int numChangesLeft = classChanges.size();
220 while( !classChanges.isEmpty() )
221 {
222 Iterator<Map.Entry<String,String>> iter = classChanges.entrySet().iterator();
223 while( iter.hasNext() )
224 {
225 Map.Entry<String,String> entry = iter.next();
226 if( classChanges.get( entry.getValue() ) == null )
227 {
228 orderedClassChanges.put( entry.getKey(), entry.getValue() );
229 iter.remove();
230 }
231 }
232
233 // did we remove any changes?
234 if( numChangesLeft - classChanges.size() > 0 )
235 {
236 // keep going
237 numChangesLeft = classChanges.size();
238 }
239 else
240 {
241 // can't sort anymore. There must be a loop
242 break;
243 }
244 }
245 if( classChanges.size() > 0 )
246 {
247 throw new Error( String.format( "Unable to sort %d/%d class changes!", classChanges.size(), matchedClassNames.size() ) );
248 }
249
250 // convert the mappings in the correct class order
251 for( Map.Entry<String,String> entry : orderedClassChanges.entrySet() )
252 {
253 mappings.renameObfClass( entry.getKey(), entry.getValue() );
254 }
216 255
217 // check the method matches 256 // check the method matches
218 System.out.println( "Checking methods..." ); 257 System.out.println( "Checking methods..." );
@@ -250,7 +289,7 @@ public class ClassMatcher
250 } 289 }
251 290
252 System.err.println( "\tAvailable source methods:" ); 291 System.err.println( "\tAvailable source methods:" );
253 c = sourceLoader.loadClass( classConversion.inverse().get( classMapping.getObfName() ) ); 292 c = sourceLoader.loadClass( matchedClassNames.inverse().get( classMapping.getObfName() ) );
254 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 293 for( CtBehavior behavior : c.getDeclaredBehaviors() )
255 { 294 {
256 MethodEntry declaredMethodEntry = new MethodEntry( 295 MethodEntry declaredMethodEntry = new MethodEntry(
diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java
index 6ce8c887..5511902d 100644
--- a/src/cuchaz/enigma/convert/ClassMatching.java
+++ b/src/cuchaz/enigma/convert/ClassMatching.java
@@ -159,7 +159,7 @@ public class ClassMatching
159 return new ArrayList<ClassIdentity>( m_unmatchedDestClasses ); 159 return new ArrayList<ClassIdentity>( m_unmatchedDestClasses );
160 } 160 }
161 161
162 public Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> getConversionMap( ) 162 public Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> getIndex( )
163 { 163 {
164 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversion = Maps.newHashMap(); 164 Map<String,Map.Entry<ClassIdentity,List<ClassIdentity>>> conversion = Maps.newHashMap();
165 for( Map.Entry<ClassIdentity,ClassIdentity> entry : getUniqueMatches().entrySet() ) 165 for( Map.Entry<ClassIdentity,ClassIdentity> entry : getUniqueMatches().entrySet() )
diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java
index 095cb385..1219e7ca 100644
--- a/src/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/cuchaz/enigma/mapping/ClassMapping.java
@@ -301,31 +301,37 @@ public class ClassMapping implements Serializable, Comparable<ClassMapping>
301 return m_obfName.compareTo( other.m_obfName ); 301 return m_obfName.compareTo( other.m_obfName );
302 } 302 }
303 303
304 public void renameObfClasses( Map<String,String> nameMap ) 304 public boolean renameObfClass( String oldObfClassName, String newObfClassName )
305 { 305 {
306 // rename self 306 // rename inner classes
307 for( ClassMapping innerClassMapping : new ArrayList<ClassMapping>( m_innerClassesByObf.values() ) )
307 { 308 {
308 String newName = nameMap.get( m_obfName ); 309 if( innerClassMapping.renameObfClass( oldObfClassName, newObfClassName ) )
309 if( newName != null )
310 { 310 {
311 m_obfName = newName; 311 m_innerClassesByObf.remove( oldObfClassName );
312 m_innerClassesByObf.put( newObfClassName, innerClassMapping );
313 assert( m_innerClassesByObf.size() == m_innerClassesByDeobf.size() );
312 } 314 }
313 } 315 }
314 316
315 // rename inner classes 317 // rename method signatures
316 for( ClassMapping classMapping : new ArrayList<ClassMapping>( m_innerClassesByObf.values() ) ) 318 for( MethodMapping methodMapping : new ArrayList<MethodMapping>( m_methodsByObf.values() ) )
317 { 319 {
318 m_innerClassesByObf.remove( classMapping.getObfName() ); 320 String oldMethodKey = getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() );
319 classMapping.renameObfClasses( nameMap ); 321 if( methodMapping.renameObfClass( oldObfClassName, newObfClassName ) )
320 m_innerClassesByObf.put( classMapping.getObfName(), classMapping ); 322 {
323 m_methodsByObf.remove( oldMethodKey );
324 m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping );
325 assert( m_methodsByObf.size() == m_methodsByDeobf.size() );
326 }
321 } 327 }
322 328
323 // rename method signatures 329 if( m_obfName.equals( oldObfClassName ) )
324 for( MethodMapping methodMapping : new ArrayList<MethodMapping>( m_methodsByObf.values() ) )
325 { 330 {
326 m_methodsByObf.remove( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ) ); 331 // rename this class
327 methodMapping.renameObfClasses( nameMap ); 332 m_obfName = newObfClassName;
328 m_methodsByObf.put( getMethodKey( methodMapping.getObfName(), methodMapping.getObfSignature() ), methodMapping ); 333 return true;
329 } 334 }
335 return false;
330 } 336 }
331} 337}
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java
index 4b47d160..0b03abb0 100644
--- a/src/cuchaz/enigma/mapping/Mappings.java
+++ b/src/cuchaz/enigma/mapping/Mappings.java
@@ -15,6 +15,7 @@ import java.io.InputStream;
15import java.io.ObjectInputStream; 15import java.io.ObjectInputStream;
16import java.io.Serializable; 16import java.io.Serializable;
17import java.util.ArrayList; 17import java.util.ArrayList;
18import java.util.Collection;
18import java.util.Map; 19import java.util.Map;
19import java.util.Set; 20import java.util.Set;
20import java.util.zip.GZIPInputStream; 21import java.util.zip.GZIPInputStream;
@@ -66,7 +67,7 @@ public class Mappings implements Serializable
66 } 67 }
67 } 68 }
68 69
69 public Iterable<ClassMapping> classes( ) 70 public Collection<ClassMapping> classes( )
70 { 71 {
71 assert( m_classesByObf.size() == m_classesByDeobf.size() ); 72 assert( m_classesByObf.size() == m_classesByDeobf.size() );
72 return m_classesByObf.values(); 73 return m_classesByObf.values();
@@ -141,30 +142,19 @@ public class Mappings implements Serializable
141 return buf.toString(); 142 return buf.toString();
142 } 143 }
143 144
144 public void renameObfClasses( Map<String,String> nameMap ) 145 public void renameObfClass( String oldObfName, String newObfName )
145 { 146 {
146 for( ClassMapping classMapping : new ArrayList<ClassMapping>( m_classesByObf.values() ) ) 147 for( ClassMapping classMapping : new ArrayList<ClassMapping>( classes() ) )
147 { 148 {
148 String newName = nameMap.get( classMapping.getObfName() ); 149 if( classMapping.renameObfClass( oldObfName, newObfName ) )
149 if( newName != null )
150 { 150 {
151 m_classesByObf.remove( classMapping.getObfName() ); 151 m_classesByObf.remove( oldObfName );
152 classMapping.renameObfClasses( nameMap ); 152 m_classesByObf.put( newObfName, classMapping );
153 m_classesByObf.put( classMapping.getObfName(), classMapping ); 153 assert( m_classesByObf.size() == m_classesByDeobf.size() );
154 } 154 }
155 } 155 }
156 } 156 }
157 157
158 public void removeClassByObfName( String obfName )
159 {
160 ClassMapping classMapping = m_classesByObf.get( obfName );
161 if( classMapping != null )
162 {
163 m_classesByObf.remove( classMapping.getObfName() );
164 m_classesByDeobf.remove( classMapping.getDeobfName() );
165 }
166 }
167
168 public Set<String> getAllObfClassNames( ) 158 public Set<String> getAllObfClassNames( )
169 { 159 {
170 final Set<String> classNames = Sets.newHashSet(); 160 final Set<String> classNames = Sets.newHashSet();
diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java
index fe4e29b2..b0f7ba84 100644
--- a/src/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/cuchaz/enigma/mapping/MethodMapping.java
@@ -142,21 +142,27 @@ public class MethodMapping implements Serializable, Comparable<MethodMapping>
142 return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) ); 142 return ( m_obfName + m_obfSignature ).compareTo( ( other.m_obfName + other.m_obfSignature ) );
143 } 143 }
144 144
145 public void renameObfClasses( final Map<String,String> nameMap ) 145 public boolean renameObfClass( final String oldObfClassName, final String newObfClassName )
146 { 146 {
147 // rename obf classes in the signature 147 // rename obf classes in the signature
148 m_obfSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( ) 148 String newSignature = SignatureUpdater.update( m_obfSignature, new ClassNameUpdater( )
149 { 149 {
150 @Override 150 @Override
151 public String update( String className ) 151 public String update( String className )
152 { 152 {
153 String newName = nameMap.get( className ); 153 if( className.equals( oldObfClassName ) )
154 if( newName != null )
155 { 154 {
156 return newName; 155 return newObfClassName;
157 } 156 }
158 return className; 157 return className;
159 } 158 }
160 } ); 159 } );
160
161 if( newSignature != m_obfSignature )
162 {
163 m_obfSignature = newSignature;
164 return true;
165 }
166 return false;
161 } 167 }
162} 168}