summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2014-08-29 01:18:10 -0400
committerGravatar jeff2014-08-29 01:18:10 -0400
commit81767097df4a119489ae8cbd9c5d8265f54daf7b (patch)
treed6b2545f085f46e6afcb1f95d33e7a7ea3a14ebf /src
parentShow public/protected/private access on field/method/constructor references (diff)
downloadenigma-81767097df4a119489ae8cbd9c5d8265f54daf7b.tar.gz
enigma-81767097df4a119489ae8cbd9c5d8265f54daf7b.tar.xz
enigma-81767097df4a119489ae8cbd9c5d8265f54daf7b.zip
started on mapping converter tool so we can update to newer Minecraft jars
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/Util.java6
-rw-r--r--src/cuchaz/enigma/convert/ClassIdentity.java255
-rw-r--r--src/cuchaz/enigma/convert/ClassMapper.java73
3 files changed, 334 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java
index 3686ef02..678de546 100644
--- a/src/cuchaz/enigma/Util.java
+++ b/src/cuchaz/enigma/Util.java
@@ -19,6 +19,7 @@ import java.io.InputStream;
19import java.io.InputStreamReader; 19import java.io.InputStreamReader;
20import java.net.URI; 20import java.net.URI;
21import java.net.URISyntaxException; 21import java.net.URISyntaxException;
22import java.util.Arrays;
22import java.util.jar.JarFile; 23import java.util.jar.JarFile;
23 24
24import javassist.CannotCompileException; 25import javassist.CannotCompileException;
@@ -32,6 +33,11 @@ public class Util
32{ 33{
33 public static int combineHashesOrdered( Object ... objs ) 34 public static int combineHashesOrdered( Object ... objs )
34 { 35 {
36 return combineHashesOrdered( Arrays.asList( objs ) );
37 }
38
39 public static int combineHashesOrdered( Iterable<Object> objs )
40 {
35 final int prime = 67; 41 final int prime = 67;
36 int result = 1; 42 int result = 1;
37 for( Object obj : objs ) 43 for( Object obj : objs )
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java
new file mode 100644
index 00000000..aecf7fcb
--- /dev/null
+++ b/src/cuchaz/enigma/convert/ClassIdentity.java
@@ -0,0 +1,255 @@
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.io.UnsupportedEncodingException;
14import java.security.MessageDigest;
15import java.security.NoSuchAlgorithmException;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19
20import javassist.CtBehavior;
21import javassist.CtClass;
22import javassist.CtConstructor;
23import javassist.CtField;
24import javassist.CtMethod;
25import javassist.bytecode.BadBytecode;
26import javassist.bytecode.CodeIterator;
27import javassist.bytecode.ConstPool;
28import javassist.bytecode.Descriptor;
29import javassist.bytecode.Opcode;
30
31import com.beust.jcommander.internal.Maps;
32import com.beust.jcommander.internal.Sets;
33import com.google.common.collect.Lists;
34
35import cuchaz.enigma.Util;
36import cuchaz.enigma.bytecode.ConstPoolEditor;
37import cuchaz.enigma.bytecode.InfoType;
38import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
39import cuchaz.enigma.mapping.ClassEntry;
40import cuchaz.enigma.mapping.SignatureUpdater;
41import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
42
43public class ClassIdentity
44{
45 private ClassEntry m_classEntry;
46 private Set<String> m_fields;
47 private Set<String> m_methods;
48 private Set<String> m_constructors;
49 private String m_staticInitializer;
50
51 public ClassIdentity( CtClass c )
52 {
53 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
54 m_fields = Sets.newHashSet();
55 for( CtField field : c.getDeclaredFields() )
56 {
57 m_fields.add( scrubSignature( scrubSignature( field.getSignature() ) ) );
58 }
59 m_methods = Sets.newHashSet();
60 for( CtMethod method : c.getDeclaredMethods() )
61 {
62 m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) );
63 }
64 m_constructors = Sets.newHashSet();
65 for( CtConstructor constructor : c.getDeclaredConstructors() )
66 {
67 m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) );
68 }
69 m_staticInitializer = "";
70 if( c.getClassInitializer() != null )
71 {
72 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() );
73 }
74 }
75
76 public ClassEntry getClassEntry( )
77 {
78 return m_classEntry;
79 }
80
81 @Override
82 public String toString( )
83 {
84 StringBuilder buf = new StringBuilder();
85 buf.append( "class: " );
86 buf.append( hashCode() );
87 buf.append( "\n" );
88 for( String field : m_fields )
89 {
90 buf.append( "\tfield " );
91 buf.append( field );
92 buf.append( "\n" );
93 }
94 for( String method : m_methods )
95 {
96 buf.append( "\tmethod " );
97 buf.append( method );
98 buf.append( "\n" );
99 }
100 for( String constructor : m_constructors )
101 {
102 buf.append( "\tconstructor " );
103 buf.append( constructor );
104 buf.append( "\n" );
105 }
106 if( m_staticInitializer.length() > 0 )
107 {
108 buf.append( "\tinitializer " );
109 buf.append( m_staticInitializer );
110 buf.append( "\n" );
111 }
112 return buf.toString();
113 }
114
115 private String scrubSignature( String signature )
116 {
117 return SignatureUpdater.update( signature, new ClassNameUpdater( )
118 {
119 private Map<String,String> m_classNames = Maps.newHashMap();
120
121 @Override
122 public String update( String className )
123 {
124 // does the class have a package?
125 if( className.indexOf( '/' ) >= 0 )
126 {
127 return className;
128 }
129
130 if( !m_classNames.containsKey( className ) )
131 {
132 m_classNames.put( className, getNewClassName() );
133 }
134 return m_classNames.get( className );
135 }
136
137 private String getNewClassName( )
138 {
139 return String.format( "C%03d", m_classNames.size() );
140 }
141 } );
142 }
143
144 private String getBehaviorSignature( CtBehavior behavior )
145 {
146 try
147 {
148 // does this method have an implementation?
149 if( behavior.getMethodInfo().getCodeAttribute() == null )
150 {
151 return "(none)";
152 }
153
154 // compute the hash from the opcodes
155 ConstPool constants = behavior.getMethodInfo().getConstPool();
156 ConstPoolEditor editor = new ConstPoolEditor( constants );
157 MessageDigest digest = MessageDigest.getInstance( "MD5" );
158 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
159 while( iter.hasNext() )
160 {
161 int pos = iter.next();
162
163 // update the hash with the opcode
164 int opcode = iter.byteAt( pos );
165 digest.update( (byte)opcode );
166
167 // is there a constant value here?
168 int constIndex = -1;
169 switch( opcode )
170 {
171 case Opcode.LDC:
172 constIndex = iter.byteAt( pos + 1 );
173 break;
174
175 case Opcode.LDC_W:
176 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
177 break;
178
179 case Opcode.LDC2_W:
180 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
181 break;
182 }
183
184 if( constIndex >= 0 )
185 {
186 // update the hash with the constant value
187 ConstInfoAccessor item = editor.getItem( constIndex );
188 if( item.getType() == InfoType.StringInfo )
189 {
190 String val = constants.getStringInfo( constIndex );
191 try
192 {
193 digest.update( val.getBytes( "UTF8" ) );
194 }
195 catch( UnsupportedEncodingException ex )
196 {
197 throw new Error( ex );
198 }
199 }
200 }
201 }
202
203 // convert the hash to a hex string
204 return toHex( digest.digest() );
205 }
206 catch( BadBytecode | NoSuchAlgorithmException ex )
207 {
208 throw new Error( ex );
209 }
210 }
211
212 private String toHex( byte[] bytes )
213 {
214 // function taken from:
215 // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
216 final char[] hexArray = "0123456789ABCDEF".toCharArray();
217 char[] hexChars = new char[bytes.length * 2];
218 for( int j = 0; j < bytes.length; j++ )
219 {
220 int v = bytes[j] & 0xFF;
221 hexChars[j * 2] = hexArray[v >>> 4];
222 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
223 }
224 return new String( hexChars );
225 }
226
227 @Override
228 public boolean equals( Object other )
229 {
230 if( other instanceof ClassIdentity )
231 {
232 return equals( (ClassIdentity)other );
233 }
234 return false;
235 }
236
237 public boolean equals( ClassIdentity other )
238 {
239 return m_fields.equals( other.m_fields )
240 && m_methods.equals( other.m_methods )
241 && m_constructors.equals( other.m_constructors )
242 && m_staticInitializer.equals( other.m_staticInitializer );
243 }
244
245 @Override
246 public int hashCode( )
247 {
248 List<Object> objs = Lists.newArrayList();
249 objs.addAll( m_fields );
250 objs.addAll( m_methods );
251 objs.addAll( m_constructors );
252 objs.add( m_staticInitializer );
253 return Util.combineHashesOrdered( objs );
254 }
255}
diff --git a/src/cuchaz/enigma/convert/ClassMapper.java b/src/cuchaz/enigma/convert/ClassMapper.java
new file mode 100644
index 00000000..a0d5a3f1
--- /dev/null
+++ b/src/cuchaz/enigma/convert/ClassMapper.java
@@ -0,0 +1,73 @@
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.io.File;
14import java.io.IOException;
15import java.util.jar.JarFile;
16
17import javassist.CtClass;
18
19import com.google.common.collect.HashMultiset;
20import com.google.common.collect.Multiset;
21
22import cuchaz.enigma.analysis.JarClassIterator;
23
24public class ClassMapper
25{
26 public static void main( String[] args )
27 throws IOException
28 {
29 // TEMP
30 JarFile fromJar = new JarFile( new File( "input/1.8-pre1.jar" ) );
31 JarFile toJar = new JarFile( new File( "input/1.8-pre2.jar" ) );
32
33 new ClassMapper( fromJar, toJar );
34 }
35
36 public ClassMapper( JarFile a, JarFile b )
37 {
38 int numAClasses = JarClassIterator.getClassEntries( a ).size();
39 int numBClasses = JarClassIterator.getClassEntries( b ).size();
40
41 // TEMP
42 System.out.println( "A classes: " + numAClasses );
43 System.out.println( "B classes: " + numBClasses );
44
45 // compute the a classes
46 Multiset<ClassIdentity> aclasses = HashMultiset.create();
47 for( CtClass c : JarClassIterator.classes( a ) )
48 {
49 ClassIdentity aclass = new ClassIdentity( c );
50 aclasses.add( aclass );
51 }
52
53 int numMatches = 0;
54
55 // match the b classes to the a classes
56 for( CtClass c : JarClassIterator.classes( b ) )
57 {
58 ClassIdentity bclass = new ClassIdentity( c );
59 if( aclasses.contains( bclass ) )
60 {
61 numMatches++;
62 }
63
64 // TEMP
65 //System.out.println( bclass );
66 }
67
68 // TEMP
69 System.out.println( String.format( "Class matches: %d/%d (missing %d)",
70 numMatches, aclasses.size(), aclasses.size() - numMatches
71 ) );
72 }
73}