summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert/ClassIdentity.java
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/cuchaz/enigma/convert/ClassIdentity.java
parentShow public/protected/private access on field/method/constructor references (diff)
downloadenigma-fork-81767097df4a119489ae8cbd9c5d8265f54daf7b.tar.gz
enigma-fork-81767097df4a119489ae8cbd9c5d8265f54daf7b.tar.xz
enigma-fork-81767097df4a119489ae8cbd9c5d8265f54daf7b.zip
started on mapping converter tool so we can update to newer Minecraft jars
Diffstat (limited to 'src/cuchaz/enigma/convert/ClassIdentity.java')
-rw-r--r--src/cuchaz/enigma/convert/ClassIdentity.java255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java
new file mode 100644
index 0000000..aecf7fc
--- /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}