summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2014-08-30 11:41:17 -0400
committerGravatar jeff2014-08-30 11:41:17 -0400
commite43fac9f55cfeebacd869352bfb090b7d8d063c1 (patch)
treedd4b01daa04dbdcecc765c7270e18bdf1b63d97f
parentworking on version conversion (diff)
downloadenigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.tar.gz
enigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.tar.xz
enigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.zip
got a decent class matcher working
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java27
-rw-r--r--src/cuchaz/enigma/analysis/JarClassIterator.java72
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java34
-rw-r--r--src/cuchaz/enigma/bytecode/ClassRenamer.java35
-rw-r--r--src/cuchaz/enigma/convert/ClassIdentity.java255
-rw-r--r--src/cuchaz/enigma/convert/ClassMapper.java202
-rw-r--r--src/cuchaz/enigma/convert/ClassMatching.java184
-rw-r--r--src/cuchaz/enigma/convert/ClassNamer.java75
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java9
9 files changed, 748 insertions, 145 deletions
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
index 1e0e95a..e70093e 100644
--- a/src/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/cuchaz/enigma/TranslatingTypeLoader.java
@@ -47,6 +47,11 @@ public class TranslatingTypeLoader implements ITypeLoader
47 private Map<String,byte[]> m_cache; 47 private Map<String,byte[]> m_cache;
48 private ClasspathTypeLoader m_defaultTypeLoader; 48 private ClasspathTypeLoader m_defaultTypeLoader;
49 49
50 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex )
51 {
52 this( jar, jarIndex, new Translator(), new Translator() );
53 }
54
50 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) 55 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator )
51 { 56 {
52 m_jar = jar; 57 m_jar = jar;
@@ -90,6 +95,28 @@ public class TranslatingTypeLoader implements ITypeLoader
90 return true; 95 return true;
91 } 96 }
92 97
98 public CtClass loadClass( String deobfClassName )
99 {
100 byte[] data = loadType( deobfClassName );
101 if( data == null )
102 {
103 return null;
104 }
105
106 // return a javassist handle for the class
107 String javaClassFileName = Descriptor.toJavaName( deobfClassName );
108 ClassPool classPool = new ClassPool();
109 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, data ) );
110 try
111 {
112 return classPool.get( javaClassFileName );
113 }
114 catch( NotFoundException ex )
115 {
116 throw new Error( ex );
117 }
118 }
119
93 private byte[] loadType( String deobfClassName ) 120 private byte[] loadType( String deobfClassName )
94 { 121 {
95 // what class file should we actually load? 122 // what class file should we actually load?
diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java
index 6c9f124..10ae805 100644
--- a/src/cuchaz/enigma/analysis/JarClassIterator.java
+++ b/src/cuchaz/enigma/analysis/JarClassIterator.java
@@ -67,33 +67,7 @@ public class JarClassIterator implements Iterator<CtClass>
67 JarEntry entry = m_iter.next(); 67 JarEntry entry = m_iter.next();
68 try 68 try
69 { 69 {
70 // read the class into a buffer 70 return getClass( m_jar, entry );
71 ByteArrayOutputStream bos = new ByteArrayOutputStream();
72 byte[] buf = new byte[Constants.KiB];
73 int totalNumBytesRead = 0;
74 InputStream in = m_jar.getInputStream( entry );
75 while( in.available() > 0 )
76 {
77 int numBytesRead = in.read( buf );
78 if( numBytesRead < 0 )
79 {
80 break;
81 }
82 bos.write( buf, 0, numBytesRead );
83
84 // sanity checking
85 totalNumBytesRead += numBytesRead;
86 if( totalNumBytesRead > Constants.MiB )
87 {
88 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
89 }
90 }
91
92 // get a javassist handle for the class
93 String className = Descriptor.toJavaName( getClassEntry( entry ).getName() );
94 ClassPool classPool = new ClassPool();
95 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
96 return classPool.get( className );
97 } 71 }
98 catch( IOException | NotFoundException ex ) 72 catch( IOException | NotFoundException ex )
99 { 73 {
@@ -136,6 +110,50 @@ public class JarClassIterator implements Iterator<CtClass>
136 }; 110 };
137 } 111 }
138 112
113 public static CtClass getClass( JarFile jar, ClassEntry classEntry )
114 {
115 try
116 {
117 return getClass( jar, new JarEntry( classEntry.getName() + ".class" ) );
118 }
119 catch( IOException | NotFoundException ex )
120 {
121 throw new Error( "Unable to load class: " + classEntry.getName() );
122 }
123 }
124
125 private static CtClass getClass( JarFile jar, JarEntry entry )
126 throws IOException, NotFoundException
127 {
128 // read the class into a buffer
129 ByteArrayOutputStream bos = new ByteArrayOutputStream();
130 byte[] buf = new byte[Constants.KiB];
131 int totalNumBytesRead = 0;
132 InputStream in = jar.getInputStream( entry );
133 while( in.available() > 0 )
134 {
135 int numBytesRead = in.read( buf );
136 if( numBytesRead < 0 )
137 {
138 break;
139 }
140 bos.write( buf, 0, numBytesRead );
141
142 // sanity checking
143 totalNumBytesRead += numBytesRead;
144 if( totalNumBytesRead > Constants.MiB )
145 {
146 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
147 }
148 }
149
150 // get a javassist handle for the class
151 String className = Descriptor.toJavaName( getClassEntry( entry ).getName() );
152 ClassPool classPool = new ClassPool();
153 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
154 return classPool.get( className );
155 }
156
139 private static ClassEntry getClassEntry( JarEntry entry ) 157 private static ClassEntry getClassEntry( JarEntry entry )
140 { 158 {
141 return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) ); 159 return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index deacf16..b479b69 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -95,7 +95,7 @@ public class JarIndex
95 // step 2: index method/field access 95 // step 2: index method/field access
96 for( CtClass c : JarClassIterator.classes( jar ) ) 96 for( CtClass c : JarClassIterator.classes( jar ) )
97 { 97 {
98 fixClass( c ); 98 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage );
99 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 99 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
100 for( CtField field : c.getDeclaredFields() ) 100 for( CtField field : c.getDeclaredFields() )
101 { 101 {
@@ -112,7 +112,7 @@ public class JarIndex
112 // step 3: index the types, methods 112 // step 3: index the types, methods
113 for( CtClass c : JarClassIterator.classes( jar ) ) 113 for( CtClass c : JarClassIterator.classes( jar ) )
114 { 114 {
115 fixClass( c ); 115 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage );
116 String className = Descriptor.toJvmName( c.getName() ); 116 String className = Descriptor.toJvmName( c.getName() );
117 m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) ); 117 m_ancestries.addSuperclass( className, Descriptor.toJvmName( c.getClassFile().getSuperclass() ) );
118 for( String interfaceName : c.getClassFile().getInterfaces() ) 118 for( String interfaceName : c.getClassFile().getInterfaces() )
@@ -128,8 +128,7 @@ public class JarIndex
128 // step 4: index inner classes and anonymous classes 128 // step 4: index inner classes and anonymous classes
129 for( CtClass c : JarClassIterator.classes( jar ) ) 129 for( CtClass c : JarClassIterator.classes( jar ) )
130 { 130 {
131 fixClass( c ); 131 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage );
132
133 String outerClassName = findOuterClass( c ); 132 String outerClassName = findOuterClass( c );
134 if( outerClassName != null ) 133 if( outerClassName != null )
135 { 134 {
@@ -164,17 +163,6 @@ public class JarIndex
164 renameMethods( m_bridgeMethods ); 163 renameMethods( m_bridgeMethods );
165 } 164 }
166 165
167 private void fixClass( CtClass c )
168 {
169 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
170 if( classEntry.isInDefaultPackage() )
171 {
172 // move class out of default package
173 classEntry = new ClassEntry( Constants.NonePackage + "/" + classEntry.getName() );
174 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, Constants.NonePackage );
175 }
176 }
177
178 private void indexBehavior( CtBehavior behavior ) 166 private void indexBehavior( CtBehavior behavior )
179 { 167 {
180 // get the method entry 168 // get the method entry
@@ -729,6 +717,22 @@ public class JarIndex
729 717
730 private void renameClasses( Map<String,String> renames ) 718 private void renameClasses( Map<String,String> renames )
731 { 719 {
720 // rename class entries
721 Set<ClassEntry> obfClassEntries = Sets.newHashSet();
722 for( ClassEntry classEntry : m_obfClassEntries )
723 {
724 if( renames.containsKey( classEntry.getName() ) )
725 {
726 obfClassEntries.add( new ClassEntry( renames.get( classEntry.getName() ) ) );
727 }
728 else
729 {
730 obfClassEntries.add( classEntry );
731 }
732 }
733 m_obfClassEntries = obfClassEntries;
734
735 // rename others
732 m_ancestries.renameClasses( renames ); 736 m_ancestries.renameClasses( renames );
733 renameClassesInMultimap( renames, m_methodImplementations ); 737 renameClassesInMultimap( renames, m_methodImplementations );
734 renameClassesInMultimap( renames, m_behaviorReferences ); 738 renameClassesInMultimap( renames, m_behaviorReferences );
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java
index f3a8c0e..efe22a1 100644
--- a/src/cuchaz/enigma/bytecode/ClassRenamer.java
+++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -14,6 +14,7 @@ import java.util.Map;
14import java.util.Set; 14import java.util.Set;
15 15
16import javassist.ClassMap; 16import javassist.ClassMap;
17import javassist.CtBehavior;
17import javassist.CtClass; 18import javassist.CtClass;
18import javassist.bytecode.ConstPool; 19import javassist.bytecode.ConstPool;
19import javassist.bytecode.Descriptor; 20import javassist.bytecode.Descriptor;
@@ -23,6 +24,8 @@ import com.beust.jcommander.internal.Sets;
23import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
24 25
25import cuchaz.enigma.mapping.ClassEntry; 26import cuchaz.enigma.mapping.ClassEntry;
27import cuchaz.enigma.mapping.SignatureUpdater;
28import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
26 29
27public class ClassRenamer 30public class ClassRenamer
28{ 31{
@@ -115,5 +118,37 @@ public class ClassRenamer
115 } 118 }
116 } 119 }
117 ClassRenamer.renameClasses( c, map ); 120 ClassRenamer.renameClasses( c, map );
121
122 // TEMP
123 for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) )
124 {
125 if( classEntry.isInDefaultPackage() )
126 {
127 throw new Error( "!!! " + classEntry );
128 }
129 }
130
131 // TEMP
132 for( CtBehavior behavior : c.getDeclaredBehaviors() )
133 {
134 if( behavior.getSignature() == null )
135 {
136 continue;
137 }
138
139 SignatureUpdater.update( behavior.getSignature(), new ClassNameUpdater( )
140 {
141 @Override
142 public String update( String className )
143 {
144 ClassEntry classEntry = new ClassEntry( className );
145 if( classEntry.isInDefaultPackage() )
146 {
147 throw new Error( "!!! " + className );
148 }
149 return className;
150 }
151 } );
152 }
118 } 153 }
119} 154}
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java
index aecf7fc..0a3a449 100644
--- a/src/cuchaz/enigma/convert/ClassIdentity.java
+++ b/src/cuchaz/enigma/convert/ClassIdentity.java
@@ -13,10 +13,12 @@ package cuchaz.enigma.convert;
13import java.io.UnsupportedEncodingException; 13import java.io.UnsupportedEncodingException;
14import java.security.MessageDigest; 14import java.security.MessageDigest;
15import java.security.NoSuchAlgorithmException; 15import java.security.NoSuchAlgorithmException;
16import java.util.Enumeration;
16import java.util.List; 17import java.util.List;
17import java.util.Map; 18import java.util.Map;
18import java.util.Set; 19import java.util.Set;
19 20
21import javassist.CannotCompileException;
20import javassist.CtBehavior; 22import javassist.CtBehavior;
21import javassist.CtClass; 23import javassist.CtClass;
22import javassist.CtConstructor; 24import javassist.CtConstructor;
@@ -27,34 +29,58 @@ import javassist.bytecode.CodeIterator;
27import javassist.bytecode.ConstPool; 29import javassist.bytecode.ConstPool;
28import javassist.bytecode.Descriptor; 30import javassist.bytecode.Descriptor;
29import javassist.bytecode.Opcode; 31import javassist.bytecode.Opcode;
32import javassist.expr.ConstructorCall;
33import javassist.expr.ExprEditor;
34import javassist.expr.FieldAccess;
35import javassist.expr.MethodCall;
36import javassist.expr.NewExpr;
30 37
31import com.beust.jcommander.internal.Maps; 38import com.beust.jcommander.internal.Maps;
32import com.beust.jcommander.internal.Sets; 39import com.beust.jcommander.internal.Sets;
33import com.google.common.collect.Lists; 40import com.google.common.collect.Lists;
34 41
42import cuchaz.enigma.Constants;
35import cuchaz.enigma.Util; 43import cuchaz.enigma.Util;
44import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
45import cuchaz.enigma.analysis.EntryReference;
46import cuchaz.enigma.analysis.JarIndex;
36import cuchaz.enigma.bytecode.ConstPoolEditor; 47import cuchaz.enigma.bytecode.ConstPoolEditor;
37import cuchaz.enigma.bytecode.InfoType; 48import cuchaz.enigma.bytecode.InfoType;
38import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; 49import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
50import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
51import cuchaz.enigma.mapping.BehaviorEntry;
39import cuchaz.enigma.mapping.ClassEntry; 52import cuchaz.enigma.mapping.ClassEntry;
53import cuchaz.enigma.mapping.ConstructorEntry;
54import cuchaz.enigma.mapping.Entry;
55import cuchaz.enigma.mapping.FieldEntry;
56import cuchaz.enigma.mapping.MethodEntry;
40import cuchaz.enigma.mapping.SignatureUpdater; 57import cuchaz.enigma.mapping.SignatureUpdater;
41import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 58import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
42 59
43public class ClassIdentity 60public class ClassIdentity
44{ 61{
45 private ClassEntry m_classEntry; 62 private ClassEntry m_classEntry;
63 private SidedClassNamer m_namer;
46 private Set<String> m_fields; 64 private Set<String> m_fields;
47 private Set<String> m_methods; 65 private Set<String> m_methods;
48 private Set<String> m_constructors; 66 private Set<String> m_constructors;
49 private String m_staticInitializer; 67 private String m_staticInitializer;
68 private String m_extends;
69 private Set<String> m_implements;
70 private Set<String> m_implementations;
71 private Set<String> m_references;
50 72
51 public ClassIdentity( CtClass c ) 73 public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences )
52 { 74 {
75 m_namer = namer;
76
77 // stuff from the bytecode
78
53 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 79 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
54 m_fields = Sets.newHashSet(); 80 m_fields = Sets.newHashSet();
55 for( CtField field : c.getDeclaredFields() ) 81 for( CtField field : c.getDeclaredFields() )
56 { 82 {
57 m_fields.add( scrubSignature( scrubSignature( field.getSignature() ) ) ); 83 m_fields.add( scrubSignature( field.getSignature() ) );
58 } 84 }
59 m_methods = Sets.newHashSet(); 85 m_methods = Sets.newHashSet();
60 for( CtMethod method : c.getDeclaredMethods() ) 86 for( CtMethod method : c.getDeclaredMethods() )
@@ -71,6 +97,73 @@ public class ClassIdentity
71 { 97 {
72 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); 98 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() );
73 } 99 }
100 m_extends = "";
101 if( c.getClassFile().getSuperclass() != null )
102 {
103 m_extends = scrubClassName( c.getClassFile().getSuperclass() );
104 }
105 m_implements = Sets.newHashSet();
106 for( String interfaceName : c.getClassFile().getInterfaces() )
107 {
108 m_implements.add( scrubClassName( interfaceName ) );
109 }
110
111 // stuff from the jar index
112
113 m_implementations = Sets.newHashSet();
114 @SuppressWarnings( "unchecked" )
115 Enumeration<ClassImplementationsTreeNode> implementations = index.getClassImplementations( null, m_classEntry ).children();
116 while( implementations.hasMoreElements() )
117 {
118 ClassImplementationsTreeNode node = implementations.nextElement();
119 m_implementations.add( scrubClassName( node.getClassEntry().getName() ) );
120 }
121
122 m_references = Sets.newHashSet();
123 if( useReferences )
124 {
125 for( CtField field : c.getDeclaredFields() )
126 {
127 FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() );
128 for( EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences( fieldEntry ) )
129 {
130 addReference( reference );
131 }
132 }
133 for( CtMethod method : c.getDeclaredMethods() )
134 {
135 MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() );
136 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( methodEntry ) )
137 {
138 addReference( reference );
139 }
140 }
141 for( CtConstructor constructor : c.getDeclaredConstructors() )
142 {
143 ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() );
144 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( constructorEntry ) )
145 {
146 addReference( reference );
147 }
148 }
149 }
150 }
151
152 private void addReference( EntryReference<? extends Entry,BehaviorEntry> reference )
153 {
154 if( reference.context.getSignature() != null )
155 {
156 m_references.add( String.format( "%s_%s",
157 scrubClassName( reference.context.getClassName() ),
158 scrubSignature( reference.context.getSignature() )
159 ) );
160 }
161 else
162 {
163 m_references.add( String.format( "%s_<clinit>",
164 scrubClassName( reference.context.getClassName() )
165 ) );
166 }
74 } 167 }
75 168
76 public ClassEntry getClassEntry( ) 169 public ClassEntry getClassEntry( )
@@ -109,9 +202,38 @@ public class ClassIdentity
109 buf.append( m_staticInitializer ); 202 buf.append( m_staticInitializer );
110 buf.append( "\n" ); 203 buf.append( "\n" );
111 } 204 }
205 if( m_extends.length() > 0 )
206 {
207 buf.append( "\textends " );
208 buf.append( m_extends );
209 buf.append( "\n" );
210 }
211 for( String interfaceName : m_implements )
212 {
213 buf.append( "\timplements " );
214 buf.append( interfaceName );
215 buf.append( "\n" );
216 }
217 for( String implementation : m_implementations )
218 {
219 buf.append( "\timplemented by " );
220 buf.append( implementation );
221 buf.append( "\n" );
222 }
223 for( String reference : m_references )
224 {
225 buf.append( "\treference " );
226 buf.append( reference );
227 buf.append( "\n" );
228 }
112 return buf.toString(); 229 return buf.toString();
113 } 230 }
114 231
232 private String scrubClassName( String className )
233 {
234 return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" );
235 }
236
115 private String scrubSignature( String signature ) 237 private String scrubSignature( String signature )
116 { 238 {
117 return SignatureUpdater.update( signature, new ClassNameUpdater( ) 239 return SignatureUpdater.update( signature, new ClassNameUpdater( )
@@ -121,12 +243,30 @@ public class ClassIdentity
121 @Override 243 @Override
122 public String update( String className ) 244 public String update( String className )
123 { 245 {
124 // does the class have a package? 246 // classes not in the none package can be passed through
125 if( className.indexOf( '/' ) >= 0 ) 247 ClassEntry classEntry = new ClassEntry( className );
248 if( !classEntry.getPackageName().equals( Constants.NonePackage ) )
126 { 249 {
127 return className; 250 return className;
128 } 251 }
129 252
253 // is this class ourself?
254 if( className.equals( m_classEntry.getName() ) )
255 {
256 return "CSelf";
257 }
258
259 // try the namer
260 if( m_namer != null )
261 {
262 String newName = m_namer.getName( className );
263 if( newName != null )
264 {
265 return newName;
266 }
267 }
268
269 // otherwise, use local naming
130 if( !m_classNames.containsKey( className ) ) 270 if( !m_classNames.containsKey( className ) )
131 { 271 {
132 m_classNames.put( className, getNewClassName() ); 272 m_classNames.put( className, getNewClassName() );
@@ -141,6 +281,11 @@ public class ClassIdentity
141 } ); 281 } );
142 } 282 }
143 283
284 private boolean isClassMatchedUniquely( String className )
285 {
286 return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null;
287 }
288
144 private String getBehaviorSignature( CtBehavior behavior ) 289 private String getBehaviorSignature( CtBehavior behavior )
145 { 290 {
146 try 291 try
@@ -153,8 +298,7 @@ public class ClassIdentity
153 298
154 // compute the hash from the opcodes 299 // compute the hash from the opcodes
155 ConstPool constants = behavior.getMethodInfo().getConstPool(); 300 ConstPool constants = behavior.getMethodInfo().getConstPool();
156 ConstPoolEditor editor = new ConstPoolEditor( constants ); 301 final MessageDigest digest = MessageDigest.getInstance( "MD5" );
157 MessageDigest digest = MessageDigest.getInstance( "MD5" );
158 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); 302 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
159 while( iter.hasNext() ) 303 while( iter.hasNext() )
160 { 304 {
@@ -164,46 +308,91 @@ public class ClassIdentity
164 int opcode = iter.byteAt( pos ); 308 int opcode = iter.byteAt( pos );
165 digest.update( (byte)opcode ); 309 digest.update( (byte)opcode );
166 310
167 // is there a constant value here?
168 int constIndex = -1;
169 switch( opcode ) 311 switch( opcode )
170 { 312 {
171 case Opcode.LDC: 313 case Opcode.LDC:
172 constIndex = iter.byteAt( pos + 1 ); 314 {
315 int constIndex = iter.byteAt( pos + 1 );
316 updateHashWithConstant( digest, constants, constIndex );
317 }
173 break; 318 break;
174 319
175 case Opcode.LDC_W: 320 case Opcode.LDC_W:
176 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
177 break;
178
179 case Opcode.LDC2_W: 321 case Opcode.LDC2_W:
180 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); 322 {
323 int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
324 updateHashWithConstant( digest, constants, constIndex );
325 }
181 break; 326 break;
182 } 327 }
328 }
329
330 // update hash with method and field accesses
331 behavior.instrument( new ExprEditor( )
332 {
333 @Override
334 public void edit( MethodCall call )
335 {
336 updateHashWithString( digest, scrubClassName( call.getClassName() ) );
337 updateHashWithString( digest, scrubSignature( call.getSignature() ) );
338 if( isClassMatchedUniquely( call.getClassName() ) )
339 {
340 updateHashWithString( digest, call.getMethodName() );
341 }
342 }
183 343
184 if( constIndex >= 0 ) 344 @Override
345 public void edit( FieldAccess access )
185 { 346 {
186 // update the hash with the constant value 347 updateHashWithString( digest, scrubClassName( access.getClassName() ) );
187 ConstInfoAccessor item = editor.getItem( constIndex ); 348 updateHashWithString( digest, scrubSignature( access.getSignature() ) );
188 if( item.getType() == InfoType.StringInfo ) 349 if( isClassMatchedUniquely( access.getClassName() ) )
189 { 350 {
190 String val = constants.getStringInfo( constIndex ); 351 updateHashWithString( digest, access.getFieldName() );
191 try
192 {
193 digest.update( val.getBytes( "UTF8" ) );
194 }
195 catch( UnsupportedEncodingException ex )
196 {
197 throw new Error( ex );
198 }
199 } 352 }
200 } 353 }
201 } 354
355 @Override
356 public void edit( ConstructorCall call )
357 {
358 updateHashWithString( digest, scrubClassName( call.getClassName() ) );
359 updateHashWithString( digest, scrubSignature( call.getSignature() ) );
360 }
361
362 @Override
363 public void edit( NewExpr expr )
364 {
365 updateHashWithString( digest, scrubClassName( expr.getClassName() ) );
366 }
367 } );
202 368
203 // convert the hash to a hex string 369 // convert the hash to a hex string
204 return toHex( digest.digest() ); 370 return toHex( digest.digest() );
205 } 371 }
206 catch( BadBytecode | NoSuchAlgorithmException ex ) 372 catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex )
373 {
374 throw new Error( ex );
375 }
376 }
377
378 private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index )
379 {
380 ConstPoolEditor editor = new ConstPoolEditor( constants );
381 ConstInfoAccessor item = editor.getItem( index );
382 if( item.getType() == InfoType.StringInfo )
383 {
384 updateHashWithString( digest, constants.getStringInfo( index ) );
385 }
386 // TODO: other constants
387 }
388
389 private void updateHashWithString( MessageDigest digest, String val )
390 {
391 try
392 {
393 digest.update( val.getBytes( "UTF8" ) );
394 }
395 catch( UnsupportedEncodingException ex )
207 { 396 {
208 throw new Error( ex ); 397 throw new Error( ex );
209 } 398 }
@@ -239,7 +428,11 @@ public class ClassIdentity
239 return m_fields.equals( other.m_fields ) 428 return m_fields.equals( other.m_fields )
240 && m_methods.equals( other.m_methods ) 429 && m_methods.equals( other.m_methods )
241 && m_constructors.equals( other.m_constructors ) 430 && m_constructors.equals( other.m_constructors )
242 && m_staticInitializer.equals( other.m_staticInitializer ); 431 && m_staticInitializer.equals( other.m_staticInitializer )
432 && m_extends.equals( other.m_extends )
433 && m_implements.equals( other.m_implements )
434 && m_implementations.equals( other.m_implementations )
435 && m_references.equals( other.m_references );
243 } 436 }
244 437
245 @Override 438 @Override
@@ -250,6 +443,10 @@ public class ClassIdentity
250 objs.addAll( m_methods ); 443 objs.addAll( m_methods );
251 objs.addAll( m_constructors ); 444 objs.addAll( m_constructors );
252 objs.add( m_staticInitializer ); 445 objs.add( m_staticInitializer );
446 objs.add( m_extends );
447 objs.addAll( m_implements );
448 objs.addAll( m_implementations );
449 objs.addAll( m_references );
253 return Util.combineHashesOrdered( objs ); 450 return Util.combineHashesOrdered( objs );
254 } 451 }
255} 452}
diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java
index 5a16a1c..fe48c50 100644
--- a/src/cuchaz/enigma/convert/ClassMapper.java
+++ b/src/cuchaz/enigma/convert/ClassMapper.java
@@ -12,31 +12,20 @@ package cuchaz.enigma.convert;
12 12
13import java.io.File; 13import java.io.File;
14import java.io.IOException; 14import java.io.IOException;
15import java.util.Collection; 15import java.util.Arrays;
16import java.util.List; 16import java.util.List;
17import java.util.Map; 17import java.util.Map;
18import java.util.Set;
18import java.util.jar.JarFile; 19import java.util.jar.JarFile;
19 20
20import javassist.CtClass; 21import javassist.CtClass;
21 22import cuchaz.enigma.TranslatingTypeLoader;
22import com.beust.jcommander.internal.Lists; 23import cuchaz.enigma.analysis.JarIndex;
23import com.beust.jcommander.internal.Maps; 24import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
24import com.google.common.collect.ArrayListMultimap;
25import com.google.common.collect.Multimap;
26
27import cuchaz.enigma.analysis.JarClassIterator;
28import cuchaz.enigma.mapping.ClassEntry; 25import cuchaz.enigma.mapping.ClassEntry;
29 26
30public class ClassMapper 27public class ClassMapper
31{ 28{
32 private int m_numSourceClasses;
33 private int m_numDestClasses;
34 private Multimap<ClassIdentity,ClassIdentity> m_sourceClasses;
35 private Multimap<ClassIdentity,ClassIdentity> m_destClasses;
36 private List<ClassIdentity> m_unmatchedSourceClasses;
37 private List<ClassIdentity> m_unmatchedDestClasses;
38 private Map<ClassEntry,ClassIdentity> m_sourceKeyIndex;
39
40 public static void main( String[] args ) 29 public static void main( String[] args )
41 throws IOException 30 throws IOException
42 { 31 {
@@ -44,81 +33,146 @@ public class ClassMapper
44 JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) ); 33 JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) );
45 JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) ); 34 JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) );
46 35
47 ClassMapper mapper = new ClassMapper( fromJar, toJar ); 36 // compute the matching
48 System.out.println( String.format( "Mapped %d/%d source classes (%d unmatched) to %d/%d dest classes (%d unmatched)", 37 ClassMatching matching = ClassMapper.computeMatching( fromJar, toJar );
49 mapper.m_sourceClasses.size(), mapper.m_numSourceClasses, mapper.m_unmatchedSourceClasses.size(), 38
50 mapper.m_destClasses.size(), mapper.m_numDestClasses, mapper.m_unmatchedDestClasses.size() 39 // TODO: use the matching to convert the mappings
51 ) );
52 } 40 }
53 41
54 public ClassMapper( JarFile sourceJar, JarFile destJar ) 42 public static ClassMatching computeMatching( JarFile sourceJar, JarFile destJar )
55 { 43 {
56 m_numSourceClasses = JarClassIterator.getClassEntries( sourceJar ).size(); 44 // index jars
57 m_numDestClasses = JarClassIterator.getClassEntries( destJar ).size(); 45 System.out.println( "Indexing source jar..." );
46 JarIndex sourceIndex = new JarIndex();
47 sourceIndex.indexJar( sourceJar );
48 System.out.println( "Indexing dest jar..." );
49 JarIndex destIndex = new JarIndex();
50 destIndex.indexJar( destJar );
58 51
59 // compute identities for the source classes 52 System.out.println( "Computing matching..." );
60 m_sourceClasses = ArrayListMultimap.create();
61 m_sourceKeyIndex = Maps.newHashMap();
62 for( CtClass c : JarClassIterator.classes( sourceJar ) )
63 {
64 ClassIdentity sourceClass = new ClassIdentity( c );
65 m_sourceClasses.put( sourceClass, sourceClass );
66 m_sourceKeyIndex.put( sourceClass.getClassEntry(), sourceClass );
67 }
68 53
69 // match the dest classes to the source classes 54 TranslatingTypeLoader sourceLoader = new TranslatingTypeLoader( sourceJar, sourceIndex );
70 m_destClasses = ArrayListMultimap.create(); 55 TranslatingTypeLoader destLoader = new TranslatingTypeLoader( destJar, destIndex );
71 m_unmatchedDestClasses = Lists.newArrayList(); 56
72 for( CtClass c : JarClassIterator.classes( destJar ) ) 57 ClassMatching matching = null;
58 for( boolean useReferences : Arrays.asList( false, true ) )
73 { 59 {
74 ClassIdentity destClass = new ClassIdentity( c ); 60 int numMatches = 0;
75 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( destClass ); 61 do
76 if( matchedSourceClasses.isEmpty() )
77 {
78 // unmatched dest class
79 m_unmatchedDestClasses.add( destClass );
80 }
81 else
82 { 62 {
83 ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); 63 SidedClassNamer sourceNamer = null;
84 m_destClasses.put( sourceClass, destClass ); 64 SidedClassNamer destNamer = null;
65 if( matching != null )
66 {
67 // build a class namer
68 ClassNamer namer = new ClassNamer( matching.getUniqueMatches() );
69 sourceNamer = namer.getSourceNamer();
70 destNamer = namer.getDestNamer();
71
72 // note the number of matches
73 numMatches = matching.getUniqueMatches().size();
74 }
75
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 {
85 for( ClassIdentity c : entry.getKey() )
86 {
87 sourceClassEntries.add( c.getClassEntry() );
88 matching.removeSource( c );
89 }
90 for( ClassIdentity c : entry.getValue() )
91 {
92 destClassEntries.add( c.getClassEntry() );
93 matching.removeDest( c );
94 }
95 }
96 for( ClassIdentity c : matching.getUnmatchedSourceClasses() )
97 {
98 sourceClassEntries.add( c.getClassEntry() );
99 matching.removeSource( c );
100 }
101 for( ClassIdentity c : matching.getUnmatchedDestClasses() )
102 {
103 destClassEntries.add( c.getClassEntry() );
104 matching.removeDest( c );
105 }
106 }
107 else
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 );
85 } 128 }
129 while( matching.getUniqueMatches().size() - numMatches > 0 );
86 } 130 }
87 131
88 // get unmatched source classes 132 /* DEBUG: show some ambiguous matches
89 m_unmatchedSourceClasses = Lists.newArrayList(); 133 List<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>> ambiguousMatches = new ArrayList<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( matching.getAmbiguousMatches().entrySet() );
90 for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) 134 Collections.sort( ambiguousMatches, new Comparator<Map.Entry<List<ClassIdentity>,List<ClassIdentity>>>( )
91 { 135 {
92 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass ); 136 @Override
93 Collection<ClassIdentity> matchedDestClasses = m_destClasses.get( sourceClass ); 137 public int compare( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> a, Map.Entry<List<ClassIdentity>,List<ClassIdentity>> b )
94 if( matchedDestClasses.isEmpty() )
95 { 138 {
96 m_unmatchedSourceClasses.add( sourceClass ); 139 String aName = a.getKey().get( 0 ).getClassEntry().getName();
140 String bName = b.getKey().get( 0 ).getClassEntry().getName();
141 return aName.compareTo( bName );
97 } 142 }
98 else if( matchedDestClasses.size() > 1 ) 143 } );
144 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : ambiguousMatches )
145 {
146 for( ClassIdentity c : entry.getKey() )
99 { 147 {
100 // warn about identity collisions 148 System.out.print( c.getClassEntry().getName() + " " );
101 System.err.println( String.format( "WARNING: identity collision:\n\tSource: %s\n\t Dest: %s",
102 getClassEntries( matchedSourceClasses ),
103 getClassEntries( matchedDestClasses )
104 ) );
105 } 149 }
150 System.out.println();
151 }
152 Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry = ambiguousMatches.get( 7 );
153 for( ClassIdentity c : entry.getKey() )
154 {
155 System.out.println( c );
106 } 156 }
157 for( ClassIdentity c : entry.getKey() )
158 {
159 System.out.println( decompile( sourceLoader, c.getClassEntry() ) );
160 }
161 */
162
163 return matching;
107 } 164 }
108 165
109 public Map.Entry<Collection<ClassEntry>,Collection<ClassEntry>> getMapping( ClassEntry sourceEntry ) 166 /* DEBUG
167 private static String decompile( TranslatingTypeLoader loader, ClassEntry classEntry )
110 { 168 {
111 // TODO 169 PlainTextOutput output = new PlainTextOutput();
112 return null; 170 DecompilerSettings settings = DecompilerSettings.javaDefaults();
113 } 171 settings.setForceExplicitImports( true );
114 172 settings.setShowSyntheticMembers( true );
115 private Collection<ClassEntry> getClassEntries( Collection<ClassIdentity> classes ) 173 settings.setTypeLoader( loader );
116 { 174 Decompiler.decompile( classEntry.getName(), output, settings );
117 List<ClassEntry> entries = Lists.newArrayList(); 175 return output.toString();
118 for( ClassIdentity c : classes )
119 {
120 entries.add( c.getClassEntry() );
121 }
122 return entries;
123 } 176 }
177 */
124} 178}
diff --git a/src/cuchaz/enigma/convert/ClassMatching.java b/src/cuchaz/enigma/convert/ClassMatching.java
new file mode 100644
index 0000000..fea8438
--- /dev/null
+++ b/src/cuchaz/enigma/convert/ClassMatching.java
@@ -0,0 +1,184 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import java.util.ArrayList;
14import java.util.Collection;
15import java.util.List;
16import java.util.Map;
17
18import com.beust.jcommander.internal.Lists;
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.BiMap;
21import com.google.common.collect.HashBiMap;
22import com.google.common.collect.Multimap;
23
24public class ClassMatching
25{
26 private Multimap<ClassIdentity,ClassIdentity> m_sourceClasses;
27 private Multimap<ClassIdentity,ClassIdentity> m_matchedDestClasses;
28 private List<ClassIdentity> m_unmatchedDestClasses;
29
30 public ClassMatching( )
31 {
32 m_sourceClasses = ArrayListMultimap.create();
33 m_matchedDestClasses = ArrayListMultimap.create();
34 m_unmatchedDestClasses = Lists.newArrayList();
35 }
36
37 public void addSource( ClassIdentity c )
38 {
39 m_sourceClasses.put( c, c );
40 }
41
42 public void matchDestClass( ClassIdentity destClass )
43 {
44 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( destClass );
45 if( matchedSourceClasses.isEmpty() )
46 {
47 // no match
48 m_unmatchedDestClasses.add( destClass );
49 }
50 else
51 {
52 // found a match
53 m_matchedDestClasses.put( destClass, destClass );
54
55 // DEBUG
56 ClassIdentity sourceClass = matchedSourceClasses.iterator().next();
57 assert( sourceClass.hashCode() == destClass.hashCode() );
58 assert( sourceClass.equals( destClass ) );
59 }
60 }
61
62 public void removeSource( ClassIdentity sourceClass )
63 {
64 m_sourceClasses.remove( sourceClass, sourceClass );
65 }
66
67 public void removeDest( ClassIdentity destClass )
68 {
69 m_matchedDestClasses.remove( destClass, destClass );
70 m_unmatchedDestClasses.remove( destClass );
71 }
72
73 public List<ClassIdentity> getSourceClasses( )
74 {
75 return new ArrayList<ClassIdentity>( m_sourceClasses.values() );
76 }
77
78 public List<ClassIdentity> getDestClasses( )
79 {
80 List<ClassIdentity> classes = Lists.newArrayList();
81 classes.addAll( m_matchedDestClasses.values() );
82 classes.addAll( m_unmatchedDestClasses );
83 return classes;
84 }
85
86 public BiMap<ClassIdentity,ClassIdentity> getUniqueMatches( )
87 {
88 BiMap<ClassIdentity,ClassIdentity> uniqueMatches = HashBiMap.create();
89 for( ClassIdentity sourceClass : m_sourceClasses.keySet() )
90 {
91 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass );
92 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get( sourceClass );
93 if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 )
94 {
95 ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next();
96 ClassIdentity matchedDestClass = matchedSourceClasses.iterator().next();
97 uniqueMatches.put( matchedSourceClass, matchedDestClass );
98 }
99 }
100 return uniqueMatches;
101 }
102
103 public BiMap<List<ClassIdentity>,List<ClassIdentity>> getAmbiguousMatches( )
104 {
105 BiMap<List<ClassIdentity>,List<ClassIdentity>> ambiguousMatches = HashBiMap.create();
106 for( ClassIdentity sourceClass : m_sourceClasses.keySet() )
107 {
108 Collection<ClassIdentity> matchedSourceClasses = m_sourceClasses.get( sourceClass );
109 Collection<ClassIdentity> matchedDestClasses = m_matchedDestClasses.get( sourceClass );
110 if( matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1 )
111 {
112 ambiguousMatches.put(
113 new ArrayList<ClassIdentity>( matchedSourceClasses ),
114 new ArrayList<ClassIdentity>( matchedDestClasses )
115 );
116 }
117 }
118 return ambiguousMatches;
119 }
120
121 public int getNumAmbiguousSourceMatches( )
122 {
123 int num = 0;
124 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet() )
125 {
126 num += entry.getKey().size();
127 }
128 return num;
129 }
130
131 public int getNumAmbiguousDestMatches( )
132 {
133 int num = 0;
134 for( Map.Entry<List<ClassIdentity>,List<ClassIdentity>> entry : getAmbiguousMatches().entrySet() )
135 {
136 num += entry.getValue().size();
137 }
138 return num;
139 }
140
141 public List<ClassIdentity> getUnmatchedSourceClasses( )
142 {
143 List<ClassIdentity> classes = Lists.newArrayList();
144 for( ClassIdentity sourceClass : getSourceClasses() )
145 {
146 if( m_matchedDestClasses.get( sourceClass ).isEmpty() )
147 {
148 classes.add( sourceClass );
149 }
150 }
151 return classes;
152 }
153
154 public List<ClassIdentity> getUnmatchedDestClasses( )
155 {
156 return new ArrayList<ClassIdentity>( m_unmatchedDestClasses );
157 }
158
159 @Override
160 public String toString( )
161 {
162 StringBuilder buf = new StringBuilder();
163
164 buf.append( "Source classes: " );
165 buf.append( getSourceClasses().size() );
166 buf.append( "\n\tUnique: " );
167 buf.append( getUniqueMatches().size() );
168 buf.append( "\n\tAmbiguous: " );
169 buf.append( getNumAmbiguousSourceMatches() );
170 buf.append( "\n\tUnmatched: " );
171 buf.append( getUnmatchedSourceClasses().size() );
172
173 buf.append( "\nDest classes: " );
174 buf.append( getDestClasses().size() );
175 buf.append( "\n\tUnique: " );
176 buf.append( getUniqueMatches().size() );
177 buf.append( "\n\tAmbiguous: " );
178 buf.append( getNumAmbiguousDestMatches() );
179 buf.append( "\n\tUnmatched: " );
180 buf.append( getUnmatchedDestClasses().size() );
181
182 return buf.toString();
183 }
184}
diff --git a/src/cuchaz/enigma/convert/ClassNamer.java b/src/cuchaz/enigma/convert/ClassNamer.java
new file mode 100644
index 0000000..1cd9665
--- /dev/null
+++ b/src/cuchaz/enigma/convert/ClassNamer.java
@@ -0,0 +1,75 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.convert;
12
13import java.util.Map;
14
15import com.beust.jcommander.internal.Maps;
16import com.google.common.collect.BiMap;
17
18public class ClassNamer
19{
20 public interface SidedClassNamer
21 {
22 String getName( String name );
23 }
24
25 private Map<String,String> m_sourceNames;
26 private Map<String,String> m_destNames;
27
28 public ClassNamer( BiMap<ClassIdentity,ClassIdentity> mappings )
29 {
30 // convert the identity mappings to name maps
31 m_sourceNames = Maps.newHashMap();
32 m_destNames = Maps.newHashMap();
33 int i = 0;
34 for( Map.Entry<ClassIdentity,ClassIdentity> entry : mappings.entrySet() )
35 {
36 String name = String.format( "M%04d", i++ );
37 m_sourceNames.put( entry.getKey().getClassEntry().getName(), name );
38 m_destNames.put( entry.getValue().getClassEntry().getName(), name );
39 }
40 }
41
42 public String getSourceName( String name )
43 {
44 return m_sourceNames.get( name );
45 }
46
47 public String getDestName( String name )
48 {
49 return m_destNames.get( name );
50 }
51
52 public SidedClassNamer getSourceNamer( )
53 {
54 return new SidedClassNamer( )
55 {
56 @Override
57 public String getName( String name )
58 {
59 return getSourceName( name );
60 }
61 };
62 }
63
64 public SidedClassNamer getDestNamer( )
65 {
66 return new SidedClassNamer( )
67 {
68 @Override
69 public String getName( String name )
70 {
71 return getDestName( name );
72 }
73 };
74 }
75}
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
index a671c27..23bf095 100644
--- a/src/cuchaz/enigma/mapping/Translator.java
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -14,6 +14,8 @@ import java.util.ArrayList;
14import java.util.List; 14import java.util.List;
15import java.util.Map; 15import java.util.Map;
16 16
17import com.beust.jcommander.internal.Maps;
18
17import cuchaz.enigma.analysis.Ancestries; 19import cuchaz.enigma.analysis.Ancestries;
18import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 20import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
19 21
@@ -23,6 +25,13 @@ public class Translator
23 public Map<String,ClassMapping> m_classes; 25 public Map<String,ClassMapping> m_classes;
24 private Ancestries m_ancestries; 26 private Ancestries m_ancestries;
25 27
28 public Translator( )
29 {
30 m_direction = null;
31 m_classes = Maps.newHashMap();
32 m_ancestries = new Ancestries();
33 }
34
26 protected Translator( TranslationDirection direction, Map<String,ClassMapping> classes, Ancestries ancestries ) 35 protected Translator( TranslationDirection direction, Map<String,ClassMapping> classes, Ancestries ancestries )
27 { 36 {
28 m_direction = direction; 37 m_direction = direction;