summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cuchaz/enigma/Constants.java18
-rw-r--r--src/cuchaz/enigma/Controller.java67
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java115
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java93
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java180
-rw-r--r--src/cuchaz/enigma/bytecode/BytecodeTools.java269
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java171
-rw-r--r--src/cuchaz/enigma/bytecode/ConstPoolEditor.java316
-rw-r--r--src/cuchaz/enigma/bytecode/InfoType.java364
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java199
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java96
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java69
-rw-r--r--src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java33
-rw-r--r--src/cuchaz/enigma/gui/Gui.java53
-rw-r--r--src/cuchaz/enigma/gui/RenameListener.java2
-rw-r--r--src/cuchaz/enigma/mapping/Ancestries.java134
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentEntry.java27
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentIndex.java41
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java5
-rw-r--r--src/cuchaz/enigma/mapping/ClassIndex.java159
-rw-r--r--src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java57
-rw-r--r--src/cuchaz/enigma/mapping/EntryPair.java46
-rw-r--r--src/cuchaz/enigma/mapping/FieldEntry.java17
-rw-r--r--src/cuchaz/enigma/mapping/MethodEntry.java21
-rw-r--r--src/cuchaz/enigma/mapping/MethodIndex.java125
-rw-r--r--src/cuchaz/enigma/mapping/SignatureUpdater.java87
-rw-r--r--src/cuchaz/enigma/mapping/TranslationDirection.java34
-rw-r--r--src/cuchaz/enigma/mapping/TranslationMappings.java187
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java201
34 files changed, 3566 insertions, 46 deletions
diff --git a/src/cuchaz/enigma/Constants.java b/src/cuchaz/enigma/Constants.java
new file mode 100644
index 00000000..09787145
--- /dev/null
+++ b/src/cuchaz/enigma/Constants.java
@@ -0,0 +1,18 @@
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;
12
13
14public class Constants
15{
16 public static final int MiB = 1024*1024; // 1 mebibyte
17 public static final int KiB = 1024; // 1 kebibyte
18}
diff --git a/src/cuchaz/enigma/Controller.java b/src/cuchaz/enigma/Controller.java
index debc5e38..3af139e8 100644
--- a/src/cuchaz/enigma/Controller.java
+++ b/src/cuchaz/enigma/Controller.java
@@ -18,19 +18,23 @@ import cuchaz.enigma.analysis.SourceIndex;
18import cuchaz.enigma.gui.ClassSelectionListener; 18import cuchaz.enigma.gui.ClassSelectionListener;
19import cuchaz.enigma.gui.Gui; 19import cuchaz.enigma.gui.Gui;
20import cuchaz.enigma.gui.RenameListener; 20import cuchaz.enigma.gui.RenameListener;
21import cuchaz.enigma.mapping.ClassEntry;
21import cuchaz.enigma.mapping.Entry; 22import cuchaz.enigma.mapping.Entry;
23import cuchaz.enigma.mapping.EntryPair;
22 24
23public class Controller implements ClassSelectionListener, CaretListener, RenameListener 25public class Controller implements ClassSelectionListener, CaretListener, RenameListener
24{ 26{
25 private Deobfuscator m_deobfuscator; 27 private Deobfuscator m_deobfuscator;
26 private Gui m_gui; 28 private Gui m_gui;
27 private SourceIndex m_index; 29 private SourceIndex m_index;
30 private ClassFile m_currentFile;
28 31
29 public Controller( Deobfuscator deobfuscator, Gui gui ) 32 public Controller( Deobfuscator deobfuscator, Gui gui )
30 { 33 {
31 m_deobfuscator = deobfuscator; 34 m_deobfuscator = deobfuscator;
32 m_gui = gui; 35 m_gui = gui;
33 m_index = null; 36 m_index = null;
37 m_currentFile = null;
34 38
35 // update GUI 39 // update GUI
36 gui.setTitle( deobfuscator.getJarName() ); 40 gui.setTitle( deobfuscator.getJarName() );
@@ -43,7 +47,51 @@ public class Controller implements ClassSelectionListener, CaretListener, Rename
43 } 47 }
44 48
45 @Override 49 @Override
46 public void classSelected( final ClassFile classFile ) 50 public void classSelected( ClassFile classFile )
51 {
52 m_currentFile = classFile;
53 deobfuscate( m_currentFile );
54 }
55
56 @Override
57 public void caretUpdate( CaretEvent event )
58 {
59 if( m_index != null )
60 {
61 int pos = event.getDot();
62 Entry deobfEntry = m_index.getEntry( pos );
63 if( deobfEntry != null )
64 {
65 m_gui.showEntryPair( new EntryPair( m_deobfuscator.obfuscate( deobfEntry ), deobfEntry ) );
66 }
67 else
68 {
69 m_gui.clearEntryPair();
70 }
71 }
72 }
73
74 @Override
75 public void rename( Entry obfsEntry, String newName )
76 {
77 m_deobfuscator.rename( obfsEntry, newName );
78
79 // did we rename the current file?
80 if( obfsEntry instanceof ClassEntry )
81 {
82 ClassEntry classEntry = (ClassEntry)obfsEntry;
83
84 // update the current file
85 if( classEntry.getName().equals( m_currentFile.getName() ) )
86 {
87 m_currentFile = new ClassFile( newName );
88 }
89 }
90
91 deobfuscate( m_currentFile );
92 }
93
94 private void deobfuscate( final ClassFile classFile )
47 { 95 {
48 m_gui.setSource( "(deobfuscating...)" ); 96 m_gui.setSource( "(deobfuscating...)" );
49 97
@@ -63,21 +111,4 @@ public class Controller implements ClassSelectionListener, CaretListener, Rename
63 } 111 }
64 }.start(); 112 }.start();
65 } 113 }
66
67 @Override
68 public void caretUpdate( CaretEvent event )
69 {
70 if( m_index != null )
71 {
72 int pos = event.getDot();
73 m_gui.showEntry( m_index.getEntry( pos ) );
74 }
75 }
76
77 @Override
78 public void rename( Entry entry, String newName )
79 {
80 // TEMP
81 System.out.println( "Rename " + entry + " to " + newName );
82 }
83} 114}
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 97c57505..b1abd9e0 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -11,7 +11,9 @@
11package cuchaz.enigma; 11package cuchaz.enigma;
12 12
13import java.io.File; 13import java.io.File;
14import java.io.FileInputStream;
14import java.io.IOException; 15import java.io.IOException;
16import java.io.InputStream;
15import java.io.StringWriter; 17import java.io.StringWriter;
16import java.util.ArrayList; 18import java.util.ArrayList;
17import java.util.Collections; 19import java.util.Collections;
@@ -21,16 +23,27 @@ import java.util.List;
21import java.util.jar.JarEntry; 23import java.util.jar.JarEntry;
22import java.util.jar.JarFile; 24import java.util.jar.JarFile;
23 25
24import com.strobel.assembler.metadata.JarTypeLoader;
25import com.strobel.decompiler.Decompiler; 26import com.strobel.decompiler.Decompiler;
26import com.strobel.decompiler.DecompilerSettings; 27import com.strobel.decompiler.DecompilerSettings;
27import com.strobel.decompiler.PlainTextOutput; 28import com.strobel.decompiler.PlainTextOutput;
28 29
30import cuchaz.enigma.mapping.Ancestries;
31import cuchaz.enigma.mapping.ArgumentEntry;
32import cuchaz.enigma.mapping.ClassEntry;
33import cuchaz.enigma.mapping.Entry;
34import cuchaz.enigma.mapping.FieldEntry;
35import cuchaz.enigma.mapping.MethodEntry;
36import cuchaz.enigma.mapping.TranslationDirection;
37import cuchaz.enigma.mapping.TranslationMappings;
38import cuchaz.enigma.mapping.Translator;
39
29public class Deobfuscator 40public class Deobfuscator
30{ 41{
31 private File m_file; 42 private File m_file;
32 private JarFile m_jar; 43 private JarFile m_jar;
33 private DecompilerSettings m_settings; 44 private DecompilerSettings m_settings;
45 private Ancestries m_ancestries;
46 private TranslationMappings m_mappings;
34 47
35 private static Comparator<ClassFile> m_obfuscatedClassSorter; 48 private static Comparator<ClassFile> m_obfuscatedClassSorter;
36 49
@@ -56,8 +69,30 @@ public class Deobfuscator
56 { 69 {
57 m_file = file; 70 m_file = file;
58 m_jar = new JarFile( m_file ); 71 m_jar = new JarFile( m_file );
72
73 // build the ancestries
74 InputStream jarIn = null;
75 try
76 {
77 m_ancestries = new Ancestries();
78 jarIn = new FileInputStream( m_file );
79 m_ancestries.readFromJar( jarIn );
80 }
81 finally
82 {
83 Util.closeQuietly( jarIn );
84 }
85
86 // init mappings
87 m_mappings = new TranslationMappings( m_ancestries );
88
89 // config the decompiler
59 m_settings = DecompilerSettings.javaDefaults(); 90 m_settings = DecompilerSettings.javaDefaults();
60 m_settings.setTypeLoader( new JarTypeLoader( m_jar ) ); 91 m_settings.setTypeLoader( new TranslatingTypeLoader(
92 m_jar,
93 m_mappings.getTranslator( TranslationDirection.Deobfuscating ),
94 m_mappings.getTranslator( TranslationDirection.Obfuscating )
95 ) );
61 m_settings.setForceExplicitImports( true ); 96 m_settings.setForceExplicitImports( true );
62 m_settings.setShowSyntheticMembers( true ); 97 m_settings.setShowSyntheticMembers( true );
63 } 98 }
@@ -111,4 +146,80 @@ public class Deobfuscator
111 Decompiler.decompile( classFile.getName(), new PlainTextOutput( buf ), m_settings ); 146 Decompiler.decompile( classFile.getName(), new PlainTextOutput( buf ), m_settings );
112 return buf.toString(); 147 return buf.toString();
113 } 148 }
149
150 // NOTE: these methods are a bit messy... oh well
151
152 public void rename( Entry entry, String newName )
153 {
154 if( entry instanceof ClassEntry )
155 {
156 m_mappings.setClassName( (ClassEntry)entry, newName );
157 }
158 else if( entry instanceof FieldEntry )
159 {
160 m_mappings.setFieldName( (FieldEntry)entry, newName );
161 }
162 else if( entry instanceof MethodEntry )
163 {
164 m_mappings.setMethodName( (MethodEntry)entry, newName );
165 }
166 else if( entry instanceof ArgumentEntry )
167 {
168 m_mappings.setArgumentName( (ArgumentEntry)entry, newName );
169 }
170 else
171 {
172 throw new Error( "Unknown entry type: " + entry.getClass().getName() );
173 }
174 }
175
176 public Entry obfuscate( Entry in )
177 {
178 Translator translator = m_mappings.getTranslator( TranslationDirection.Obfuscating );
179 if( in instanceof ClassEntry )
180 {
181 return translator.translateEntry( (ClassEntry)in );
182 }
183 else if( in instanceof FieldEntry )
184 {
185 return translator.translateEntry( (FieldEntry)in );
186 }
187 else if( in instanceof MethodEntry )
188 {
189 return translator.translateEntry( (MethodEntry)in );
190 }
191 else if( in instanceof ArgumentEntry )
192 {
193 return translator.translateEntry( (ArgumentEntry)in );
194 }
195 else
196 {
197 throw new Error( "Unknown entry type: " + in.getClass().getName() );
198 }
199 }
200
201 public Entry deobfuscate( Entry in )
202 {
203 Translator translator = m_mappings.getTranslator( TranslationDirection.Deobfuscating );
204 if( in instanceof ClassEntry )
205 {
206 return translator.translateEntry( (ClassEntry)in );
207 }
208 else if( in instanceof FieldEntry )
209 {
210 return translator.translateEntry( (FieldEntry)in );
211 }
212 else if( in instanceof MethodEntry )
213 {
214 return translator.translateEntry( (MethodEntry)in );
215 }
216 else if( in instanceof ArgumentEntry )
217 {
218 return translator.translateEntry( (ArgumentEntry)in );
219 }
220 else
221 {
222 throw new Error( "Unknown entry type: " + in.getClass().getName() );
223 }
224 }
114} 225}
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
new file mode 100644
index 00000000..872f4861
--- /dev/null
+++ b/src/cuchaz/enigma/TranslatingTypeLoader.java
@@ -0,0 +1,93 @@
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;
12
13import java.io.IOException;
14import java.io.InputStream;
15import java.util.jar.JarEntry;
16import java.util.jar.JarFile;
17
18import javassist.ByteArrayClassPath;
19import javassist.ClassPool;
20import javassist.CtClass;
21
22import com.strobel.assembler.metadata.Buffer;
23import com.strobel.assembler.metadata.ITypeLoader;
24
25import cuchaz.enigma.bytecode.ClassTranslator;
26import cuchaz.enigma.mapping.Translator;
27
28public class TranslatingTypeLoader implements ITypeLoader
29{
30 private JarFile m_jar;
31 private ClassTranslator m_classTranslator;
32 private Translator m_obfuscatingTranslator;
33
34 public TranslatingTypeLoader( JarFile jar, Translator deobfuscatingTranslator, Translator obfuscatingTranslator )
35 {
36 m_jar = jar;
37 m_classTranslator = new ClassTranslator( deobfuscatingTranslator );
38 m_obfuscatingTranslator = obfuscatingTranslator;
39 }
40
41 @Override
42 public boolean tryLoadType( String name, Buffer out )
43 {
44 // is this a deobufscated class name?
45 String obfName = m_obfuscatingTranslator.translateClass( name );
46 if( obfName != null )
47 {
48 // point to the obfuscated class
49 name = obfName;
50 }
51
52 JarEntry entry = m_jar.getJarEntry( name + ".class" );
53 if( entry == null )
54 {
55 return false;
56 }
57
58 try
59 {
60 // read the class file into a buffer
61 byte[] buf = new byte[(int)entry.getSize()];
62 InputStream in = m_jar.getInputStream( entry );
63 int bytesRead = in.read( buf );
64 assert( bytesRead == buf.length );
65
66 // translate the class
67 ClassPool classPool = new ClassPool();
68 classPool.insertClassPath( new ByteArrayClassPath( name, buf ) );
69 try
70 {
71 CtClass c = classPool.get( name );
72 m_classTranslator.translate( c );
73 buf = c.toBytecode();
74 }
75 catch( Exception ex )
76 {
77 throw new Error( ex );
78 }
79
80 // pass it along to the decompiler
81 out.reset( buf.length );
82 System.arraycopy( buf, 0, out.array(), out.position(), buf.length );
83 out.position( 0 );
84
85 return true;
86 }
87 catch( IOException ex )
88 {
89 throw new Error( ex );
90 }
91 }
92
93}
diff --git a/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
new file mode 100644
index 00000000..aadbeb25
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/BytecodeIndexIterator.java
@@ -0,0 +1,180 @@
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.bytecode;
12
13import java.util.Iterator;
14
15import javassist.bytecode.BadBytecode;
16import javassist.bytecode.Bytecode;
17import javassist.bytecode.CodeAttribute;
18import javassist.bytecode.CodeIterator;
19import javassist.bytecode.Opcode;
20
21public class BytecodeIndexIterator implements Iterator<BytecodeIndexIterator.Index>
22{
23 public static class Index
24 {
25 private CodeIterator m_iter;
26 private int m_pos;
27 private boolean m_isWide;
28
29 protected Index( CodeIterator iter, int pos, boolean isWide )
30 {
31 m_iter = iter;
32 m_pos = pos;
33 m_isWide = isWide;
34 }
35
36 public int getIndex( )
37 {
38 if( m_isWide )
39 {
40 return m_iter.s16bitAt( m_pos );
41 }
42 else
43 {
44 return m_iter.byteAt( m_pos );
45 }
46 }
47
48 public void setIndex( int val )
49 throws BadBytecode
50 {
51 if( m_isWide )
52 {
53 m_iter.write16bit( val, m_pos );
54 }
55 else
56 {
57 if( val < 256 )
58 {
59 // we can write the byte
60 m_iter.writeByte( val, m_pos );
61 }
62 else
63 {
64 // we need to upgrade this instruction to LDC_W
65 assert( m_iter.byteAt( m_pos - 1 ) == Opcode.LDC );
66 m_iter.insertGap( m_pos - 1, 1 );
67 m_iter.writeByte( Opcode.LDC_W, m_pos - 1 );
68 m_iter.write16bit( val, m_pos );
69 m_isWide = true;
70
71 // move the iterator to the next opcode
72 m_iter.move( m_pos + 2 );
73 }
74 }
75
76 // sanity check
77 assert( val == getIndex() );
78 }
79
80 public boolean isValid( Bytecode bytecode )
81 {
82 return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize();
83 }
84 }
85
86 private Bytecode m_bytecode;
87 private CodeAttribute m_attribute;
88 private CodeIterator m_iter;
89 private Index m_next;
90
91 public BytecodeIndexIterator( Bytecode bytecode )
92 throws BadBytecode
93 {
94 m_bytecode = bytecode;
95 m_attribute = bytecode.toCodeAttribute();
96 m_iter = m_attribute.iterator();
97
98 m_next = getNext();
99 }
100
101 @Override
102 public boolean hasNext( )
103 {
104 return m_next != null;
105 }
106
107 @Override
108 public Index next( )
109 {
110 Index out = m_next;
111 try
112 {
113 m_next = getNext();
114 }
115 catch( BadBytecode ex )
116 {
117 throw new Error( ex );
118 }
119 return out;
120 }
121
122 @Override
123 public void remove( )
124 {
125 throw new UnsupportedOperationException();
126 }
127
128 private Index getNext( )
129 throws BadBytecode
130 {
131 while( m_iter.hasNext() )
132 {
133 int pos = m_iter.next();
134 int opcode = m_iter.byteAt( pos );
135 switch( opcode )
136 {
137 // for only these opcodes, the next two bytes are a const pool reference
138 case Opcode.ANEWARRAY:
139 case Opcode.CHECKCAST:
140 case Opcode.INSTANCEOF:
141 case Opcode.INVOKEDYNAMIC:
142 case Opcode.INVOKEINTERFACE:
143 case Opcode.INVOKESPECIAL:
144 case Opcode.INVOKESTATIC:
145 case Opcode.INVOKEVIRTUAL:
146 case Opcode.LDC_W:
147 case Opcode.LDC2_W:
148 case Opcode.MULTIANEWARRAY:
149 case Opcode.NEW:
150 case Opcode.PUTFIELD:
151 case Opcode.PUTSTATIC:
152 case Opcode.GETFIELD:
153 case Opcode.GETSTATIC:
154 return new Index( m_iter, pos + 1, true );
155
156 case Opcode.LDC:
157 return new Index( m_iter, pos + 1, false );
158 }
159 }
160
161 return null;
162 }
163
164 public Iterable<Index> indices( )
165 {
166 return new Iterable<Index>( )
167 {
168 @Override
169 public Iterator<Index> iterator( )
170 {
171 return BytecodeIndexIterator.this;
172 }
173 };
174 }
175
176 public void saveChangesToBytecode( )
177 {
178 BytecodeTools.setBytecode( m_bytecode, m_attribute.getCode() );
179 }
180}
diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java
new file mode 100644
index 00000000..664350ea
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java
@@ -0,0 +1,269 @@
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.bytecode;
12
13import java.io.ByteArrayInputStream;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.util.Map;
19import java.util.Set;
20
21import javassist.CtBehavior;
22import javassist.bytecode.BadBytecode;
23import javassist.bytecode.Bytecode;
24import javassist.bytecode.CodeAttribute;
25import javassist.bytecode.ConstPool;
26import javassist.bytecode.ExceptionTable;
27
28import com.google.common.collect.Maps;
29import com.google.common.collect.Sets;
30
31import cuchaz.enigma.Util;
32import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index;
33import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
34
35public class BytecodeTools
36{
37 public static byte[] writeBytecode( Bytecode bytecode )
38 throws IOException
39 {
40 ByteArrayOutputStream buf = new ByteArrayOutputStream();
41 DataOutputStream out = new DataOutputStream( buf );
42 try
43 {
44 // write the constant pool
45 new ConstPoolEditor( bytecode.getConstPool() ).writePool( out );
46
47 // write metadata
48 out.writeShort( bytecode.getMaxStack() );
49 out.writeShort( bytecode.getMaxLocals() );
50 out.writeShort( bytecode.getStackDepth() );
51
52 // write the code
53 out.writeShort( bytecode.getSize() );
54 out.write( bytecode.get() );
55
56 // write the exception table
57 int numEntries = bytecode.getExceptionTable().size();
58 out.writeShort( numEntries );
59 for( int i=0; i<numEntries; i++ )
60 {
61 out.writeShort( bytecode.getExceptionTable().startPc( i ) );
62 out.writeShort( bytecode.getExceptionTable().endPc( i ) );
63 out.writeShort( bytecode.getExceptionTable().handlerPc( i ) );
64 out.writeShort( bytecode.getExceptionTable().catchType( i ) );
65 }
66
67 out.close();
68 return buf.toByteArray();
69 }
70 catch( Exception ex )
71 {
72 Util.closeQuietly( out );
73 throw new Error( ex );
74 }
75 }
76
77 public static Bytecode readBytecode( byte[] bytes )
78 throws IOException
79 {
80 ByteArrayInputStream buf = new ByteArrayInputStream( bytes );
81 DataInputStream in = new DataInputStream( buf );
82 try
83 {
84 // read the constant pool entries and update the class
85 ConstPool pool = ConstPoolEditor.readPool( in );
86
87 // read metadata
88 int maxStack = in.readShort();
89 int maxLocals = in.readShort();
90 int stackDepth = in.readShort();
91
92 Bytecode bytecode = new Bytecode( pool, maxStack, maxLocals );
93 bytecode.setStackDepth( stackDepth );
94
95 // read the code
96 int size = in.readShort();
97 byte[] code = new byte[size];
98 in.read( code );
99 setBytecode( bytecode, code );
100
101 // read the exception table
102 int numEntries = in.readShort();
103 for( int i=0; i<numEntries; i++ )
104 {
105 bytecode.getExceptionTable().add( in.readShort(), in.readShort(), in.readShort(), in.readShort() );
106 }
107
108 in.close();
109 return bytecode;
110 }
111 catch( Exception ex )
112 {
113 Util.closeQuietly( in );
114 throw new Error( ex );
115 }
116 }
117
118 public static Bytecode prepareMethodForBytecode( CtBehavior behavior, Bytecode bytecode )
119 throws BadBytecode
120 {
121 // update the destination class const pool
122 bytecode = copyBytecodeToConstPool( behavior.getMethodInfo().getConstPool(), bytecode );
123
124 // update method locals and stack
125 CodeAttribute attribute = behavior.getMethodInfo().getCodeAttribute();
126 if( bytecode.getMaxLocals() > attribute.getMaxLocals() )
127 {
128 attribute.setMaxLocals( bytecode.getMaxLocals() );
129 }
130 if( bytecode.getMaxStack() > attribute.getMaxStack() )
131 {
132 attribute.setMaxStack( bytecode.getMaxStack() );
133 }
134
135 return bytecode;
136 }
137
138 public static Bytecode copyBytecodeToConstPool( ConstPool dest, Bytecode bytecode )
139 throws BadBytecode
140 {
141 // get the entries this bytecode needs from the const pool
142 Set<Integer> indices = Sets.newTreeSet();
143 ConstPoolEditor editor = new ConstPoolEditor( bytecode.getConstPool() );
144 BytecodeIndexIterator iterator = new BytecodeIndexIterator( bytecode );
145 for( Index index : iterator.indices() )
146 {
147 assert( index.isValid( bytecode ) );
148 InfoType.gatherIndexTree( indices, editor, index.getIndex() );
149 }
150
151 Map<Integer,Integer> indexMap = Maps.newTreeMap();
152
153 ConstPool src = bytecode.getConstPool();
154 ConstPoolEditor editorSrc = new ConstPoolEditor( src );
155 ConstPoolEditor editorDest = new ConstPoolEditor( dest );
156
157 // copy entries over in order of level so the index mapping is easier
158 for( InfoType type : InfoType.getSortedByLevel() )
159 {
160 for( int index : indices )
161 {
162 ConstInfoAccessor entry = editorSrc.getItem( index );
163
164 // skip entries that aren't this type
165 if( entry.getType() != type )
166 {
167 continue;
168 }
169
170 // make sure the source entry is valid before we copy it
171 assert( type.subIndicesAreValid( entry, editorSrc ) );
172 assert( type.selfIndexIsValid( entry, editorSrc ) );
173
174 // make a copy of the entry so we can modify it safely
175 ConstInfoAccessor entryCopy = editorSrc.getItem( index ).copy();
176 assert( type.subIndicesAreValid( entryCopy, editorSrc ) );
177 assert( type.selfIndexIsValid( entryCopy, editorSrc ) );
178
179 // remap the indices
180 type.remapIndices( indexMap, entryCopy );
181 assert( type.subIndicesAreValid( entryCopy, editorDest ) );
182
183 // put the copy in the destination pool
184 int newIndex = editorDest.addItem( entryCopy.getItem() );
185 entryCopy.setIndex( newIndex );
186 assert( type.selfIndexIsValid( entryCopy, editorDest ) ) : type + ", self: " + entryCopy + " dest: " + editorDest.getItem( entryCopy.getIndex() );
187
188 // make sure the source entry is unchanged
189 assert( type.subIndicesAreValid( entry, editorSrc ) );
190 assert( type.selfIndexIsValid( entry, editorSrc ) );
191
192 // add the index mapping so we can update the bytecode later
193 if( indexMap.containsKey( index ) )
194 {
195 throw new Error( "Entry at index " + index + " already copied!" );
196 }
197 indexMap.put( index, newIndex );
198 }
199 }
200
201 // make a new bytecode
202 Bytecode newBytecode = new Bytecode( dest, bytecode.getMaxStack(), bytecode.getMaxLocals() );
203 bytecode.setStackDepth( bytecode.getStackDepth() );
204 setBytecode( newBytecode, bytecode.get() );
205 setExceptionTable( newBytecode, bytecode.getExceptionTable() );
206
207 // apply the mappings to the bytecode
208 BytecodeIndexIterator iter = new BytecodeIndexIterator( newBytecode );
209 for( Index index : iter.indices() )
210 {
211 int oldIndex = index.getIndex();
212 Integer newIndex = indexMap.get( oldIndex );
213 if( newIndex != null )
214 {
215 // make sure this mapping makes sense
216 InfoType typeSrc = editorSrc.getItem( oldIndex ).getType();
217 InfoType typeDest = editorDest.getItem( newIndex ).getType();
218 assert( typeSrc == typeDest );
219
220 // apply the mapping
221 index.setIndex( newIndex );
222 }
223 }
224 iter.saveChangesToBytecode();
225
226 // make sure all the indices are valid
227 iter = new BytecodeIndexIterator( newBytecode );
228 for( Index index : iter.indices() )
229 {
230 assert( index.isValid( newBytecode ) );
231 }
232
233 return newBytecode;
234 }
235
236 public static void setBytecode( Bytecode dest, byte[] src )
237 {
238 if( src.length > dest.getSize() )
239 {
240 dest.addGap( src.length - dest.getSize() );
241 }
242 assert( dest.getSize() == src.length );
243 for( int i=0; i<src.length; i++ )
244 {
245 dest.write( i, src[i] );
246 }
247 }
248
249 public static void setExceptionTable( Bytecode dest, ExceptionTable src )
250 {
251 // clear the dest exception table
252 int size = dest.getExceptionTable().size();
253 for( int i=size-1; i>=0; i-- )
254 {
255 dest.getExceptionTable().remove( i );
256 }
257
258 // copy the exception table
259 for( int i=0; i<src.size(); i++ )
260 {
261 dest.getExceptionTable().add(
262 src.startPc( i ),
263 src.endPc( i ),
264 src.handlerPc( i ),
265 src.catchType( i )
266 );
267 }
268 }
269}
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java
new file mode 100644
index 00000000..3b5beeb2
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -0,0 +1,171 @@
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.bytecode;
12
13import java.util.HashSet;
14import java.util.Set;
15
16import javassist.ClassMap;
17import javassist.CtBehavior;
18import javassist.CtClass;
19import javassist.CtField;
20import javassist.CtMethod;
21import javassist.bytecode.ConstPool;
22import javassist.bytecode.Descriptor;
23import cuchaz.enigma.mapping.ClassEntry;
24import cuchaz.enigma.mapping.FieldEntry;
25import cuchaz.enigma.mapping.MethodEntry;
26import cuchaz.enigma.mapping.Translator;
27
28public class ClassTranslator
29{
30 private Translator m_translator;
31
32 public ClassTranslator( Translator translator )
33 {
34 m_translator = translator;
35 }
36
37 public void translate( CtClass c )
38 {
39 // NOTE: the order of these translations is very important
40
41 // translate all the field and method references in the code by editing the constant pool
42 ConstPool constants = c.getClassFile().getConstPool();
43 ConstPoolEditor editor = new ConstPoolEditor( constants );
44 for( int i=1; i<constants.getSize(); i++ )
45 {
46 switch( constants.getTag( i ) )
47 {
48 case ConstPool.CONST_Fieldref:
49 {
50 // translate the name
51 FieldEntry entry = new FieldEntry(
52 new ClassEntry( Descriptor.toJvmName( constants.getFieldrefClassName( i ) ) ),
53 constants.getFieldrefName( i )
54 );
55 String translatedName = m_translator.translate( entry );
56 if( translatedName == null )
57 {
58 translatedName = entry.getName();
59 }
60
61 // translate the type
62 String type = constants.getFieldrefType( i );
63 String translatedType = m_translator.translateSignature( type );
64
65 if( !entry.getName().equals( translatedName ) || !type.equals( translatedType ) )
66 {
67 editor.changeMemberrefNameAndType( i, translatedName, translatedType );
68 }
69 }
70 break;
71
72 case ConstPool.CONST_Methodref:
73 case ConstPool.CONST_InterfaceMethodref:
74 {
75 // translate the name and type
76 MethodEntry entry = new MethodEntry(
77 new ClassEntry( Descriptor.toJvmName( editor.getMemberrefClassname( i ) ) ),
78 editor.getMemberrefName( i ),
79 editor.getMemberrefType( i )
80 );
81 String translatedName = m_translator.translate( entry );
82 if( translatedName == null )
83 {
84 translatedName = entry.getName();
85 }
86 String translatedSignature = m_translator.translateSignature( entry.getSignature() );
87
88 if( !entry.getName().equals( translatedName ) || !entry.getSignature().equals( translatedSignature ) )
89 {
90 editor.changeMemberrefNameAndType( i, translatedName, translatedSignature );
91 }
92 }
93 break;
94 }
95 }
96
97 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
98
99 // translate all the fields
100 for( CtField field : c.getDeclaredFields() )
101 {
102 // translate the name
103 FieldEntry entry = new FieldEntry( classEntry, field.getName() );
104 String translatedName = m_translator.translate( entry );
105 if( translatedName != null )
106 {
107 field.setName( translatedName );
108 }
109
110 // translate the type
111 String translatedType = m_translator.translateSignature( field.getFieldInfo().getDescriptor() );
112 field.getFieldInfo().setDescriptor( translatedType );
113 }
114
115 // translate all the methods and constructors
116 for( CtBehavior behavior : c.getDeclaredBehaviors() )
117 {
118 // translate the name
119 MethodEntry entry = new MethodEntry( classEntry, behavior.getName(), behavior.getSignature() );
120 String translatedName = m_translator.translate( entry );
121 if( translatedName != null )
122 {
123 if( behavior instanceof CtMethod )
124 {
125 ((CtMethod)behavior).setName( translatedName );
126 }
127 }
128
129 // translate the type
130 String translatedSignature = m_translator.translateSignature( behavior.getMethodInfo().getDescriptor() );
131 behavior.getMethodInfo().setDescriptor( translatedSignature );
132 }
133
134 // translate all the class names referenced in the code
135 // the above code only changed method/field/reference names and types, but not the class names themselves
136 Set<String> classNames = getAllClassNames( c );
137 ClassMap map = new ClassMap();
138 for( String className : classNames )
139 {
140 String translatedName = m_translator.translateClass( className );
141 if( translatedName != null )
142 {
143 map.put( className, translatedName );
144 }
145 }
146 if( !map.isEmpty() )
147 {
148 c.replaceClassName( map );
149 }
150 }
151
152 private Set<String> getAllClassNames( CtClass c )
153 {
154 final Set<String> names = new HashSet<String>();
155 ClassMap map = new ClassMap( )
156 {
157 @Override
158 public Object get( Object obj )
159 {
160 if( obj instanceof String )
161 {
162 names.add( (String)obj );
163 }
164 return null;
165 }
166 private static final long serialVersionUID = -202160293602070641L;
167 };
168 c.replaceClassName( map );
169 return names;
170 }
171}
diff --git a/src/cuchaz/enigma/bytecode/ConstPoolEditor.java b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
new file mode 100644
index 00000000..aa6149c9
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/ConstPoolEditor.java
@@ -0,0 +1,316 @@
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.bytecode;
12
13import java.io.DataInputStream;
14import java.io.DataOutputStream;
15import java.lang.reflect.Constructor;
16import java.lang.reflect.Field;
17import java.lang.reflect.Method;
18import java.util.HashMap;
19
20import javassist.bytecode.ConstPool;
21import javassist.bytecode.Descriptor;
22import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
23import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
24import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
25
26public class ConstPoolEditor
27{
28 private static Method m_getItem;
29 private static Method m_addItem;
30 private static Method m_addItem0;
31 private static Field m_items;
32 private static Field m_cache;
33 private static Field m_numItems;
34 private static Field m_objects;
35 private static Field m_elements;
36 private static Method m_methodWritePool;
37 private static Constructor<ConstPool> m_constructorPool;
38
39 static
40 {
41 try
42 {
43 m_getItem = ConstPool.class.getDeclaredMethod( "getItem", int.class );
44 m_getItem.setAccessible( true );
45
46 m_addItem = ConstPool.class.getDeclaredMethod( "addItem", Class.forName( "javassist.bytecode.ConstInfo" ) );
47 m_addItem.setAccessible( true );
48
49 m_addItem0 = ConstPool.class.getDeclaredMethod( "addItem0", Class.forName( "javassist.bytecode.ConstInfo" ) );
50 m_addItem0.setAccessible( true );
51
52 m_items = ConstPool.class.getDeclaredField( "items" );
53 m_items.setAccessible( true );
54
55 m_cache = ConstPool.class.getDeclaredField( "itemsCache" );
56 m_cache.setAccessible( true );
57
58 m_numItems = ConstPool.class.getDeclaredField( "numOfItems" );
59 m_numItems.setAccessible( true );
60
61 m_objects = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "objects" );
62 m_objects.setAccessible( true );
63
64 m_elements = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "elements" );
65 m_elements.setAccessible( true );
66
67 m_methodWritePool = ConstPool.class.getDeclaredMethod( "write", DataOutputStream.class );
68 m_methodWritePool.setAccessible( true );
69
70 m_constructorPool = ConstPool.class.getDeclaredConstructor( DataInputStream.class );
71 m_constructorPool.setAccessible( true );
72 }
73 catch( Exception ex )
74 {
75 throw new Error( ex );
76 }
77 }
78
79 private ConstPool m_pool;
80
81 public ConstPoolEditor( ConstPool pool )
82 {
83 m_pool = pool;
84 }
85
86 public void writePool( DataOutputStream out )
87 {
88 try
89 {
90 m_methodWritePool.invoke( m_pool, out );
91 }
92 catch( Exception ex )
93 {
94 throw new Error( ex );
95 }
96 }
97
98 public static ConstPool readPool( DataInputStream in )
99 {
100 try
101 {
102 return m_constructorPool.newInstance( in );
103 }
104 catch( Exception ex )
105 {
106 throw new Error( ex );
107 }
108 }
109
110 public String getMemberrefClassname( int memberrefIndex )
111 {
112 return Descriptor.toJvmName( m_pool.getClassInfo( m_pool.getMemberClass( memberrefIndex ) ) );
113 }
114
115 public String getMemberrefName( int memberrefIndex )
116 {
117 return m_pool.getUtf8Info( m_pool.getNameAndTypeName( m_pool.getMemberNameAndType( memberrefIndex ) ) );
118 }
119
120 public String getMemberrefType( int memberrefIndex )
121 {
122 return m_pool.getUtf8Info( m_pool.getNameAndTypeDescriptor( m_pool.getMemberNameAndType( memberrefIndex ) ) );
123 }
124
125 public ConstInfoAccessor getItem( int index )
126 {
127 try
128 {
129 Object entry = m_getItem.invoke( m_pool, index );
130 if( entry == null )
131 {
132 return null;
133 }
134 return new ConstInfoAccessor( entry );
135 }
136 catch( Exception ex )
137 {
138 throw new Error( ex );
139 }
140 }
141
142 public int addItem( Object item )
143 {
144 try
145 {
146 return (Integer)m_addItem.invoke( m_pool, item );
147 }
148 catch( Exception ex )
149 {
150 throw new Error( ex );
151 }
152 }
153
154 public int addItemForceNew( Object item )
155 {
156 try
157 {
158 return (Integer)m_addItem0.invoke( m_pool, item );
159 }
160 catch( Exception ex )
161 {
162 throw new Error( ex );
163 }
164 }
165 @SuppressWarnings( "rawtypes" )
166 public void removeLastItem( )
167 {
168 try
169 {
170 // remove the item from the cache
171 HashMap cache = getCache();
172 if( cache != null )
173 {
174 Object item = getItem( m_pool.getSize() - 1 );
175 cache.remove( item );
176 }
177
178 // remove the actual item
179 // based off of LongVector.addElement()
180 Object items = m_items.get( m_pool );
181 Object[][] objects = (Object[][])m_objects.get( items );
182 int numElements = (Integer)m_elements.get( items ) - 1;
183 int nth = numElements >> 7;
184 int offset = numElements & (128 - 1);
185 objects[nth][offset] = null;
186
187 // decrement the number of items
188 m_elements.set( items, numElements );
189 m_numItems.set( m_pool, (Integer)m_numItems.get( m_pool ) - 1 );
190 }
191 catch( Exception ex )
192 {
193 throw new Error( ex );
194 }
195 }
196
197 @SuppressWarnings( "rawtypes" )
198 /* TEMP */public HashMap getCache( )
199 {
200 try
201 {
202 return (HashMap)m_cache.get( m_pool );
203 }
204 catch( Exception ex )
205 {
206 throw new Error( ex );
207 }
208 }
209
210 @SuppressWarnings( { "rawtypes", "unchecked" } )
211 public void changeMemberrefNameAndType( int memberrefIndex, String newName, String newType )
212 {
213 // NOTE: when changing values, we always need to copy-on-write
214 try
215 {
216 // get the memberref item
217 Object item = getItem( memberrefIndex ).getItem();
218
219 // update the cache
220 HashMap cache = getCache();
221 if( cache != null )
222 {
223 cache.remove( item );
224 }
225
226 new MemberRefInfoAccessor( item ).setNameAndTypeIndex( m_pool.addNameAndTypeInfo( newName, newType ) );
227
228 // update the cache
229 if( cache != null )
230 {
231 cache.put( item, item );
232 }
233 }
234 catch( Exception ex )
235 {
236 throw new Error( ex );
237 }
238
239 // make sure the change worked
240 assert( newName.equals( getMemberrefName( memberrefIndex ) ) );
241 assert( newType.equals( getMemberrefType( memberrefIndex ) ) );
242 }
243
244 @SuppressWarnings( { "rawtypes", "unchecked" } )
245 public void changeClassName( int classNameIndex, String newName )
246 {
247 // NOTE: when changing values, we always need to copy-on-write
248 try
249 {
250 // get the class item
251 Object item = getItem( classNameIndex ).getItem();
252
253 // update the cache
254 HashMap cache = getCache();
255 if( cache != null )
256 {
257 cache.remove( item );
258 }
259
260 // add the new name and repoint the name-and-type to it
261 new ClassInfoAccessor( item ).setNameIndex( m_pool.addUtf8Info( newName ) );
262
263 // update the cache
264 if( cache != null )
265 {
266 cache.put( item, item );
267 }
268 }
269 catch( Exception ex )
270 {
271 throw new Error( ex );
272 }
273 }
274
275 public static ConstPool newConstPool( )
276 {
277 // const pool expects the name of a class to initialize itself
278 // but we want an empty pool
279 // so give it a bogus name, and then clear the entries afterwards
280 ConstPool pool = new ConstPool( "a" );
281
282 ConstPoolEditor editor = new ConstPoolEditor( pool );
283 int size = pool.getSize();
284 for( int i=0; i<size-1; i++ )
285 {
286 editor.removeLastItem();
287 }
288
289 // make sure the pool is actually empty
290 // although, in this case "empty" means one thing in it
291 // the JVM spec says index 0 should be reserved
292 assert( pool.getSize() == 1 );
293 assert( editor.getItem( 0 ) == null );
294 assert( editor.getItem( 1 ) == null );
295 assert( editor.getItem( 2 ) == null );
296 assert( editor.getItem( 3 ) == null );
297
298 // also, clear the cache
299 editor.getCache().clear();
300
301 return pool;
302 }
303
304 public String dump( )
305 {
306 StringBuilder buf = new StringBuilder();
307 for( int i=1; i<m_pool.getSize(); i++ )
308 {
309 buf.append( String.format( "%4d", i ) );
310 buf.append( " " );
311 buf.append( getItem( i ).toString() );
312 buf.append( "\n" );
313 }
314 return buf.toString();
315 }
316}
diff --git a/src/cuchaz/enigma/bytecode/InfoType.java b/src/cuchaz/enigma/bytecode/InfoType.java
new file mode 100644
index 00000000..fe030066
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/InfoType.java
@@ -0,0 +1,364 @@
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.bytecode;
12
13import java.util.Collection;
14import java.util.List;
15import java.util.Map;
16
17import com.google.common.collect.Lists;
18import com.google.common.collect.Maps;
19
20import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
21import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
22import cuchaz.enigma.bytecode.accessors.InvokeDynamicInfoAccessor;
23import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;
24import cuchaz.enigma.bytecode.accessors.MethodHandleInfoAccessor;
25import cuchaz.enigma.bytecode.accessors.MethodTypeInfoAccessor;
26import cuchaz.enigma.bytecode.accessors.NameAndTypeInfoAccessor;
27import cuchaz.enigma.bytecode.accessors.StringInfoAccessor;
28
29public enum InfoType
30{
31 Utf8Info( 1, 0 ),
32 IntegerInfo( 3, 0 ),
33 FloatInfo( 4, 0 ),
34 LongInfo( 5, 0 ),
35 DoubleInfo( 6, 0 ),
36 ClassInfo( 7, 1 )
37 {
38 @Override
39 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
40 {
41 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
42 gatherIndexTree( indices, editor, accessor.getNameIndex() );
43 }
44
45 @Override
46 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
47 {
48 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
49 accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) );
50 }
51
52 @Override
53 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
54 {
55 ClassInfoAccessor accessor = new ClassInfoAccessor( entry.getItem() );
56 ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() );
57 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag();
58 }
59 },
60 StringInfo( 8, 1 )
61 {
62 @Override
63 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
64 {
65 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
66 gatherIndexTree( indices, editor, accessor.getStringIndex() );
67 }
68
69 @Override
70 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
71 {
72 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
73 accessor.setStringIndex( remapIndex( map, accessor.getStringIndex() ) );
74 }
75
76 @Override
77 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
78 {
79 StringInfoAccessor accessor = new StringInfoAccessor( entry.getItem() );
80 ConstInfoAccessor stringEntry = pool.getItem( accessor.getStringIndex() );
81 return stringEntry != null && stringEntry.getTag() == Utf8Info.getTag();
82 }
83 },
84 FieldRefInfo( 9, 2 )
85 {
86 @Override
87 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
88 {
89 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
90 gatherIndexTree( indices, editor, accessor.getClassIndex() );
91 gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() );
92 }
93
94 @Override
95 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
96 {
97 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
98 accessor.setClassIndex( remapIndex( map, accessor.getClassIndex() ) );
99 accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) );
100 }
101
102 @Override
103 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
104 {
105 MemberRefInfoAccessor accessor = new MemberRefInfoAccessor( entry.getItem() );
106 ConstInfoAccessor classEntry = pool.getItem( accessor.getClassIndex() );
107 ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() );
108 return classEntry != null && classEntry.getTag() == ClassInfo.getTag()
109 && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
110 }
111 },
112 MethodRefInfo( 10, 2 ) // same as FieldRefInfo
113 {
114 @Override
115 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
116 {
117 FieldRefInfo.gatherIndexTree( indices, editor, entry );
118 }
119
120 @Override
121 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
122 {
123 FieldRefInfo.remapIndices( map, entry );
124 }
125
126 @Override
127 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
128 {
129 return FieldRefInfo.subIndicesAreValid( entry, pool );
130 }
131 },
132 InterfaceMethodRefInfo( 11, 2 ) // same as FieldRefInfo
133 {
134 @Override
135 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
136 {
137 FieldRefInfo.gatherIndexTree( indices, editor, entry );
138 }
139
140 @Override
141 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
142 {
143 FieldRefInfo.remapIndices( map, entry );
144 }
145
146 @Override
147 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
148 {
149 return FieldRefInfo.subIndicesAreValid( entry, pool );
150 }
151 },
152 NameAndTypeInfo( 12, 1 )
153 {
154 @Override
155 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
156 {
157 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
158 gatherIndexTree( indices, editor, accessor.getNameIndex() );
159 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
160 }
161
162 @Override
163 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
164 {
165 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
166 accessor.setNameIndex( remapIndex( map, accessor.getNameIndex() ) );
167 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
168 }
169
170 @Override
171 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
172 {
173 NameAndTypeInfoAccessor accessor = new NameAndTypeInfoAccessor( entry.getItem() );
174 ConstInfoAccessor nameEntry = pool.getItem( accessor.getNameIndex() );
175 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
176 return nameEntry != null && nameEntry.getTag() == Utf8Info.getTag()
177 && typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
178 }
179 },
180 MethodHandleInfo( 15, 3 )
181 {
182 @Override
183 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
184 {
185 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
186 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
187 gatherIndexTree( indices, editor, accessor.getMethodRefIndex() );
188 }
189
190 @Override
191 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
192 {
193 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
194 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
195 accessor.setMethodRefIndex( remapIndex( map, accessor.getMethodRefIndex() ) );
196 }
197
198 @Override
199 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
200 {
201 MethodHandleInfoAccessor accessor = new MethodHandleInfoAccessor( entry.getItem() );
202 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
203 ConstInfoAccessor methodRefEntry = pool.getItem( accessor.getMethodRefIndex() );
204 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag()
205 && methodRefEntry != null && methodRefEntry.getTag() == MethodRefInfo.getTag();
206 }
207 },
208 MethodTypeInfo( 16, 1 )
209 {
210 @Override
211 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
212 {
213 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
214 gatherIndexTree( indices, editor, accessor.getTypeIndex() );
215 }
216
217 @Override
218 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
219 {
220 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
221 accessor.setTypeIndex( remapIndex( map, accessor.getTypeIndex() ) );
222 }
223
224 @Override
225 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
226 {
227 MethodTypeInfoAccessor accessor = new MethodTypeInfoAccessor( entry.getItem() );
228 ConstInfoAccessor typeEntry = pool.getItem( accessor.getTypeIndex() );
229 return typeEntry != null && typeEntry.getTag() == Utf8Info.getTag();
230 }
231 },
232 InvokeDynamicInfo( 18, 2 )
233 {
234 @Override
235 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
236 {
237 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
238 gatherIndexTree( indices, editor, accessor.getBootstrapIndex() );
239 gatherIndexTree( indices, editor, accessor.getNameAndTypeIndex() );
240 }
241
242 @Override
243 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
244 {
245 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
246 accessor.setBootstrapIndex( remapIndex( map, accessor.getBootstrapIndex() ) );
247 accessor.setNameAndTypeIndex( remapIndex( map, accessor.getNameAndTypeIndex() ) );
248 }
249
250 @Override
251 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
252 {
253 InvokeDynamicInfoAccessor accessor = new InvokeDynamicInfoAccessor( entry.getItem() );
254 ConstInfoAccessor bootstrapEntry = pool.getItem( accessor.getBootstrapIndex() );
255 ConstInfoAccessor nameAndTypeEntry = pool.getItem( accessor.getNameAndTypeIndex() );
256 return bootstrapEntry != null && bootstrapEntry.getTag() == Utf8Info.getTag()
257 && nameAndTypeEntry != null && nameAndTypeEntry.getTag() == NameAndTypeInfo.getTag();
258 }
259 };
260
261 private static Map<Integer,InfoType> m_types;
262
263 static
264 {
265 m_types = Maps.newTreeMap();
266 for( InfoType type : values() )
267 {
268 m_types.put( type.getTag(), type );
269 }
270 }
271
272 private int m_tag;
273 private int m_level;
274
275 private InfoType( int tag, int level )
276 {
277 m_tag = tag;
278 m_level = level;
279 }
280
281 public int getTag( )
282 {
283 return m_tag;
284 }
285
286 public int getLevel( )
287 {
288 return m_level;
289 }
290
291 public void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, ConstInfoAccessor entry )
292 {
293 // by default, do nothing
294 }
295
296 public void remapIndices( Map<Integer,Integer> map, ConstInfoAccessor entry )
297 {
298 // by default, do nothing
299 }
300
301 public boolean subIndicesAreValid( ConstInfoAccessor entry, ConstPoolEditor pool )
302 {
303 // by default, everything is good
304 return true;
305 }
306
307 public boolean selfIndexIsValid( ConstInfoAccessor entry, ConstPoolEditor pool )
308 {
309 ConstInfoAccessor entryCheck = pool.getItem( entry.getIndex() );
310 if( entryCheck == null )
311 {
312 return false;
313 }
314 return entryCheck.getItem().equals( entry.getItem() );
315 }
316
317 public static InfoType getByTag( int tag )
318 {
319 return m_types.get( tag );
320 }
321
322 public static List<InfoType> getByLevel( int level )
323 {
324 List<InfoType> types = Lists.newArrayList();
325 for( InfoType type : values() )
326 {
327 if( type.getLevel() == level )
328 {
329 types.add( type );
330 }
331 }
332 return types;
333 }
334
335 public static List<InfoType> getSortedByLevel( )
336 {
337 List<InfoType> types = Lists.newArrayList();
338 types.addAll( getByLevel( 0 ) );
339 types.addAll( getByLevel( 1 ) );
340 types.addAll( getByLevel( 2 ) );
341 types.addAll( getByLevel( 3 ) );
342 return types;
343 }
344
345 public static void gatherIndexTree( Collection<Integer> indices, ConstPoolEditor editor, int index )
346 {
347 // add own index
348 indices.add( index );
349
350 // recurse
351 ConstInfoAccessor entry = editor.getItem( index );
352 entry.getType().gatherIndexTree( indices, editor, entry );
353 }
354
355 private static int remapIndex( Map<Integer,Integer> map, int index )
356 {
357 Integer newIndex = map.get( index );
358 if( newIndex == null )
359 {
360 newIndex = index;
361 }
362 return newIndex;
363 }
364}
diff --git a/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
new file mode 100644
index 00000000..41e1d047
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/ClassInfoAccessor.java
@@ -0,0 +1,69 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class ClassInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_nameIndex;
19
20 static
21 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.ClassInfo" );
25 m_nameIndex = m_class.getDeclaredField( "name" );
26 m_nameIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public ClassInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getNameIndex( )
47 {
48 try
49 {
50 return (Integer)m_nameIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setNameIndex( int val )
59 {
60 try
61 {
62 m_nameIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
new file mode 100644
index 00000000..3c3d3fa4
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/ConstInfoAccessor.java
@@ -0,0 +1,199 @@
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.bytecode.accessors;
12
13import java.io.ByteArrayInputStream;
14import java.io.ByteArrayOutputStream;
15import java.io.DataInputStream;
16import java.io.DataOutputStream;
17import java.io.IOException;
18import java.io.PrintWriter;
19import java.lang.reflect.Constructor;
20import java.lang.reflect.Field;
21import java.lang.reflect.Method;
22
23import cuchaz.enigma.bytecode.InfoType;
24
25public class ConstInfoAccessor
26{
27 private static Class<?> m_class;
28 private static Field m_index;
29 private static Method m_getTag;
30
31 static
32 {
33 try
34 {
35 m_class = Class.forName( "javassist.bytecode.ConstInfo" );
36 m_index = m_class.getDeclaredField( "index" );
37 m_index.setAccessible( true );
38 m_getTag = m_class.getMethod( "getTag" );
39 m_getTag.setAccessible( true );
40 }
41 catch( Exception ex )
42 {
43 throw new Error( ex );
44 }
45 }
46
47 private Object m_item;
48
49 public ConstInfoAccessor( Object item )
50 {
51 if( item == null )
52 {
53 throw new IllegalArgumentException( "item cannot be null!" );
54 }
55 m_item = item;
56 }
57
58 public ConstInfoAccessor( DataInputStream in )
59 throws IOException
60 {
61 try
62 {
63 // read the entry
64 String className = in.readUTF();
65 int oldIndex = in.readInt();
66
67 // NOTE: ConstInfo instances write a type id (a "tag"), but they don't read it back
68 // so we have to read it here
69 in.readByte();
70
71 Constructor<?> constructor = Class.forName( className ).getConstructor( DataInputStream.class, int.class );
72 constructor.setAccessible( true );
73 m_item = constructor.newInstance( in, oldIndex );
74 }
75 catch( IOException ex )
76 {
77 throw ex;
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public Object getItem( )
86 {
87 return m_item;
88 }
89
90 public int getIndex( )
91 {
92 try
93 {
94 return (Integer)m_index.get( m_item );
95 }
96 catch( Exception ex )
97 {
98 throw new Error( ex );
99 }
100 }
101
102 public void setIndex( int val )
103 {
104 try
105 {
106 m_index.set( m_item, val );
107 }
108 catch( Exception ex )
109 {
110 throw new Error( ex );
111 }
112 }
113
114 public int getTag( )
115 {
116 try
117 {
118 return (Integer)m_getTag.invoke( m_item );
119 }
120 catch( Exception ex )
121 {
122 throw new Error( ex );
123 }
124 }
125
126 public ConstInfoAccessor copy( )
127 {
128 return new ConstInfoAccessor( copyItem() );
129 }
130
131 public Object copyItem( )
132 {
133 // I don't know of a simpler way to copy one of these silly things...
134 try
135 {
136 // serialize the item
137 ByteArrayOutputStream buf = new ByteArrayOutputStream();
138 DataOutputStream out = new DataOutputStream( buf );
139 write( out );
140
141 // deserialize the item
142 DataInputStream in = new DataInputStream( new ByteArrayInputStream( buf.toByteArray() ) );
143 Object item = new ConstInfoAccessor( in ).getItem();
144 in.close();
145
146 return item;
147 }
148 catch( Exception ex )
149 {
150 throw new Error( ex );
151 }
152 }
153
154 public void write( DataOutputStream out )
155 throws IOException
156 {
157 try
158 {
159 out.writeUTF( m_item.getClass().getName() );
160 out.writeInt( getIndex() );
161
162 Method method = m_item.getClass().getMethod( "write", DataOutputStream.class );
163 method.setAccessible( true );
164 method.invoke( m_item, out );
165 }
166 catch( IOException ex )
167 {
168 throw ex;
169 }
170 catch( Exception ex )
171 {
172 throw new Error( ex );
173 }
174 }
175
176 @Override
177 public String toString( )
178 {
179 try
180 {
181 ByteArrayOutputStream buf = new ByteArrayOutputStream();
182 PrintWriter out = new PrintWriter( buf );
183 Method print = m_item.getClass().getMethod( "print", PrintWriter.class );
184 print.setAccessible( true );
185 print.invoke( m_item, out );
186 out.close();
187 return buf.toString().replace( "\n", "" );
188 }
189 catch( Exception ex )
190 {
191 throw new Error( ex );
192 }
193 }
194
195 public InfoType getType( )
196 {
197 return InfoType.getByTag( getTag() );
198 }
199}
diff --git a/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
new file mode 100644
index 00000000..169306a4
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/InvokeDynamicInfoAccessor.java
@@ -0,0 +1,96 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class InvokeDynamicInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_bootstrapIndex;
19 private static Field m_nameAndTypeIndex;
20
21 static
22 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.InvokeDynamicInfo" );
26 m_bootstrapIndex = m_class.getDeclaredField( "bootstrap" );
27 m_bootstrapIndex.setAccessible( true );
28 m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndType" );
29 m_nameAndTypeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public InvokeDynamicInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getBootstrapIndex( )
50 {
51 try
52 {
53 return (Integer)m_bootstrapIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setBootstrapIndex( int val )
62 {
63 try
64 {
65 m_bootstrapIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getNameAndTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_nameAndTypeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setNameAndTypeIndex( int val )
86 {
87 try
88 {
89 m_nameAndTypeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
new file mode 100644
index 00000000..2ee3aff8
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MemberRefInfoAccessor.java
@@ -0,0 +1,96 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class MemberRefInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_classIndex;
19 private static Field m_nameAndTypeIndex;
20
21 static
22 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.MemberrefInfo" );
26 m_classIndex = m_class.getDeclaredField( "classIndex" );
27 m_classIndex.setAccessible( true );
28 m_nameAndTypeIndex = m_class.getDeclaredField( "nameAndTypeIndex" );
29 m_nameAndTypeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public MemberRefInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getClassIndex( )
50 {
51 try
52 {
53 return (Integer)m_classIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setClassIndex( int val )
62 {
63 try
64 {
65 m_classIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getNameAndTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_nameAndTypeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setNameAndTypeIndex( int val )
86 {
87 try
88 {
89 m_nameAndTypeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
new file mode 100644
index 00000000..27b7aee8
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MethodHandleInfoAccessor.java
@@ -0,0 +1,96 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class MethodHandleInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_kindIndex;
19 private static Field m_indexIndex;
20
21 static
22 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.MethodHandleInfo" );
26 m_kindIndex = m_class.getDeclaredField( "refKind" );
27 m_kindIndex.setAccessible( true );
28 m_indexIndex = m_class.getDeclaredField( "refIndex" );
29 m_indexIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public MethodHandleInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getTypeIndex( )
50 {
51 try
52 {
53 return (Integer)m_kindIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setTypeIndex( int val )
62 {
63 try
64 {
65 m_kindIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getMethodRefIndex( )
74 {
75 try
76 {
77 return (Integer)m_indexIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setMethodRefIndex( int val )
86 {
87 try
88 {
89 m_indexIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
new file mode 100644
index 00000000..4cba6a2a
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/MethodTypeInfoAccessor.java
@@ -0,0 +1,69 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class MethodTypeInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_descriptorIndex;
19
20 static
21 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.MethodTypeInfo" );
25 m_descriptorIndex = m_class.getDeclaredField( "descriptor" );
26 m_descriptorIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public MethodTypeInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getTypeIndex( )
47 {
48 try
49 {
50 return (Integer)m_descriptorIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setTypeIndex( int val )
59 {
60 try
61 {
62 m_descriptorIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
new file mode 100644
index 00000000..03b4de3c
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/NameAndTypeInfoAccessor.java
@@ -0,0 +1,96 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class NameAndTypeInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_nameIndex;
19 private static Field m_typeIndex;
20
21 static
22 {
23 try
24 {
25 m_class = Class.forName( "javassist.bytecode.NameAndTypeInfo" );
26 m_nameIndex = m_class.getDeclaredField( "memberName" );
27 m_nameIndex.setAccessible( true );
28 m_typeIndex = m_class.getDeclaredField( "typeDescriptor" );
29 m_typeIndex.setAccessible( true );
30 }
31 catch( Exception ex )
32 {
33 throw new Error( ex );
34 }
35 }
36
37 public static boolean isType( ConstInfoAccessor accessor )
38 {
39 return m_class.isAssignableFrom( accessor.getItem().getClass() );
40 }
41
42 private Object m_item;
43
44 public NameAndTypeInfoAccessor( Object item )
45 {
46 m_item = item;
47 }
48
49 public int getNameIndex( )
50 {
51 try
52 {
53 return (Integer)m_nameIndex.get( m_item );
54 }
55 catch( Exception ex )
56 {
57 throw new Error( ex );
58 }
59 }
60
61 public void setNameIndex( int val )
62 {
63 try
64 {
65 m_nameIndex.set( m_item, val );
66 }
67 catch( Exception ex )
68 {
69 throw new Error( ex );
70 }
71 }
72
73 public int getTypeIndex( )
74 {
75 try
76 {
77 return (Integer)m_typeIndex.get( m_item );
78 }
79 catch( Exception ex )
80 {
81 throw new Error( ex );
82 }
83 }
84
85 public void setTypeIndex( int val )
86 {
87 try
88 {
89 m_typeIndex.set( m_item, val );
90 }
91 catch( Exception ex )
92 {
93 throw new Error( ex );
94 }
95 }
96}
diff --git a/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
new file mode 100644
index 00000000..5cdfce4d
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/StringInfoAccessor.java
@@ -0,0 +1,69 @@
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.bytecode.accessors;
12
13import java.lang.reflect.Field;
14
15public class StringInfoAccessor
16{
17 private static Class<?> m_class;
18 private static Field m_stringIndex;
19
20 static
21 {
22 try
23 {
24 m_class = Class.forName( "javassist.bytecode.StringInfo" );
25 m_stringIndex = m_class.getDeclaredField( "string" );
26 m_stringIndex.setAccessible( true );
27 }
28 catch( Exception ex )
29 {
30 throw new Error( ex );
31 }
32 }
33
34 public static boolean isType( ConstInfoAccessor accessor )
35 {
36 return m_class.isAssignableFrom( accessor.getItem().getClass() );
37 }
38
39 private Object m_item;
40
41 public StringInfoAccessor( Object item )
42 {
43 m_item = item;
44 }
45
46 public int getStringIndex( )
47 {
48 try
49 {
50 return (Integer)m_stringIndex.get( m_item );
51 }
52 catch( Exception ex )
53 {
54 throw new Error( ex );
55 }
56 }
57
58 public void setStringIndex( int val )
59 {
60 try
61 {
62 m_stringIndex.set( m_item, val );
63 }
64 catch( Exception ex )
65 {
66 throw new Error( ex );
67 }
68 }
69}
diff --git a/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
new file mode 100644
index 00000000..1cadd836
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/accessors/Utf8InfoAccessor.java
@@ -0,0 +1,33 @@
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.bytecode.accessors;
12
13public class Utf8InfoAccessor
14{
15 private static Class<?> m_class;
16
17 static
18 {
19 try
20 {
21 m_class = Class.forName( "javassist.bytecode.Utf8Info" );
22 }
23 catch( Exception ex )
24 {
25 throw new Error( ex );
26 }
27 }
28
29 public static boolean isType( ConstInfoAccessor accessor )
30 {
31 return m_class.isAssignableFrom( accessor.getItem().getClass() );
32 }
33}
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java
index d1a3cb21..2a539a3f 100644
--- a/src/cuchaz/enigma/gui/Gui.java
+++ b/src/cuchaz/enigma/gui/Gui.java
@@ -45,7 +45,7 @@ import cuchaz.enigma.ClassFile;
45import cuchaz.enigma.analysis.SourceIndex; 45import cuchaz.enigma.analysis.SourceIndex;
46import cuchaz.enigma.mapping.ArgumentEntry; 46import cuchaz.enigma.mapping.ArgumentEntry;
47import cuchaz.enigma.mapping.ClassEntry; 47import cuchaz.enigma.mapping.ClassEntry;
48import cuchaz.enigma.mapping.Entry; 48import cuchaz.enigma.mapping.EntryPair;
49import cuchaz.enigma.mapping.FieldEntry; 49import cuchaz.enigma.mapping.FieldEntry;
50import cuchaz.enigma.mapping.MethodEntry; 50import cuchaz.enigma.mapping.MethodEntry;
51 51
@@ -69,7 +69,7 @@ public class Gui
69 private RenameListener m_renameListener; 69 private RenameListener m_renameListener;
70 70
71 private BoxHighlightPainter m_highlightPainter; 71 private BoxHighlightPainter m_highlightPainter;
72 private Entry m_selectedEntry; 72 private EntryPair m_selectedEntryPair;
73 73
74 public Gui( ) 74 public Gui( )
75 { 75 {
@@ -128,9 +128,9 @@ public class Gui
128 @Override 128 @Override
129 public void actionPerformed( ActionEvent event ) 129 public void actionPerformed( ActionEvent event )
130 { 130 {
131 if( m_renameListener != null && m_selectedEntry != null ) 131 if( m_renameListener != null && m_selectedEntryPair != null )
132 { 132 {
133 m_renameListener.rename( m_selectedEntry, m_nameField.getText() ); 133 m_renameListener.rename( m_selectedEntryPair.obf, m_nameField.getText() );
134 } 134 }
135 } 135 }
136 } ); 136 } );
@@ -142,7 +142,7 @@ public class Gui
142 m_renamePanel.add( m_typeLabel ); 142 m_renamePanel.add( m_typeLabel );
143 m_renamePanel.add( m_nameField ); 143 m_renamePanel.add( m_nameField );
144 m_renamePanel.add( m_renameButton ); 144 m_renamePanel.add( m_renameButton );
145 clearEntry(); 145 clearEntryPair();
146 146
147 // init editor 147 // init editor
148 DefaultSyntaxKit.initKit(); 148 DefaultSyntaxKit.initKit();
@@ -241,7 +241,7 @@ public class Gui
241 m_editor.addCaretListener( listener ); 241 m_editor.addCaretListener( listener );
242 } 242 }
243 243
244 public void clearEntry( ) 244 public void clearEntryPair( )
245 { 245 {
246 m_actionPanel.removeAll(); 246 m_actionPanel.removeAll();
247 JLabel label = new JLabel( "No identifier selected" ); 247 JLabel label = new JLabel( "No identifier selected" );
@@ -251,67 +251,72 @@ public class Gui
251 251
252 redraw(); 252 redraw();
253 } 253 }
254 254
255 public void showEntry( Entry entry ) 255 public void showEntryPair( EntryPair pair )
256 { 256 {
257 if( entry == null ) 257 if( pair == null )
258 { 258 {
259 clearEntry(); 259 clearEntryPair();
260 return; 260 return;
261 } 261 }
262 262
263 // TEMP
264 System.out.println( "Pair:\n" + pair.obf + "\n" + pair.deobf );
265
266 m_selectedEntryPair = pair;
267
263 // layout the action panel 268 // layout the action panel
264 m_actionPanel.removeAll(); 269 m_actionPanel.removeAll();
265 m_actionPanel.add( m_renamePanel ); 270 m_actionPanel.add( m_renamePanel );
266 m_nameField.setText( entry.getName() ); 271 m_nameField.setText( pair.deobf.getName() );
267 272
268 // layout the dynamic section 273 // layout the dynamic section
269 JPanel dynamicPanel = new JPanel(); 274 JPanel dynamicPanel = new JPanel();
270 dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) ); 275 dynamicPanel.setLayout( new GridLayout( 3, 1, 0, 0 ) );
271 m_actionPanel.add( dynamicPanel ); 276 m_actionPanel.add( dynamicPanel );
272 if( entry instanceof ClassEntry ) 277 if( pair.deobf instanceof ClassEntry )
273 { 278 {
274 showEntry( (ClassEntry)entry, dynamicPanel ); 279 showEntry( (ClassEntry)pair.deobf, dynamicPanel );
275 } 280 }
276 else if( entry instanceof FieldEntry ) 281 else if( pair.deobf instanceof FieldEntry )
277 { 282 {
278 showEntry( (FieldEntry)entry, dynamicPanel ); 283 showEntry( (FieldEntry)pair.deobf, dynamicPanel );
279 } 284 }
280 else if( entry instanceof MethodEntry ) 285 else if( pair.deobf instanceof MethodEntry )
281 { 286 {
282 showEntry( (MethodEntry)entry, dynamicPanel ); 287 showEntry( (MethodEntry)pair.deobf, dynamicPanel );
283 } 288 }
284 else if( entry instanceof ArgumentEntry ) 289 else if( pair.deobf instanceof ArgumentEntry )
285 { 290 {
286 showEntry( (ArgumentEntry)entry, dynamicPanel ); 291 showEntry( (ArgumentEntry)pair.deobf, dynamicPanel );
287 } 292 }
288 else 293 else
289 { 294 {
290 throw new Error( "Unknown entry type: " + entry.getClass().getName() ); 295 throw new Error( "Unknown entry type: " + pair.deobf.getClass().getName() );
291 } 296 }
292 297
293 redraw(); 298 redraw();
294 } 299 }
295 300
296 public void showEntry( ClassEntry entry, JPanel panel ) 301 private void showEntry( ClassEntry entry, JPanel panel )
297 { 302 {
298 m_typeLabel.setText( "Class: " ); 303 m_typeLabel.setText( "Class: " );
299 } 304 }
300 305
301 public void showEntry( FieldEntry entry, JPanel panel ) 306 private void showEntry( FieldEntry entry, JPanel panel )
302 { 307 {
303 m_typeLabel.setText( "Field: " ); 308 m_typeLabel.setText( "Field: " );
304 addNameValue( panel, "Class", entry.getClassEntry().getName() ); 309 addNameValue( panel, "Class", entry.getClassEntry().getName() );
305 } 310 }
306 311
307 public void showEntry( MethodEntry entry, JPanel panel ) 312 private void showEntry( MethodEntry entry, JPanel panel )
308 { 313 {
309 m_typeLabel.setText( "Method: " ); 314 m_typeLabel.setText( "Method: " );
310 addNameValue( panel, "Class", entry.getClassEntry().getName() ); 315 addNameValue( panel, "Class", entry.getClassEntry().getName() );
311 addNameValue( panel, "Signature", entry.getSignature() ); 316 addNameValue( panel, "Signature", entry.getSignature() );
312 } 317 }
313 318
314 public void showEntry( ArgumentEntry entry, JPanel panel ) 319 private void showEntry( ArgumentEntry entry, JPanel panel )
315 { 320 {
316 m_typeLabel.setText( "Argument: " ); 321 m_typeLabel.setText( "Argument: " );
317 addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() ); 322 addNameValue( panel, "Class", entry.getMethodEntry().getClassEntry().getName() );
diff --git a/src/cuchaz/enigma/gui/RenameListener.java b/src/cuchaz/enigma/gui/RenameListener.java
index 58cdadb0..7d45505b 100644
--- a/src/cuchaz/enigma/gui/RenameListener.java
+++ b/src/cuchaz/enigma/gui/RenameListener.java
@@ -14,5 +14,5 @@ import cuchaz.enigma.mapping.Entry;
14 14
15public interface RenameListener 15public interface RenameListener
16{ 16{
17 void rename( Entry entry, String newName ); 17 void rename( Entry obfEntry, String newName );
18} 18}
diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java
new file mode 100644
index 00000000..b7a5e249
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Ancestries.java
@@ -0,0 +1,134 @@
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.mapping;
12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.Serializable;
17import java.util.ArrayList;
18import java.util.List;
19import java.util.Map;
20import java.util.zip.ZipEntry;
21import java.util.zip.ZipInputStream;
22
23import javassist.ByteArrayClassPath;
24import javassist.ClassPool;
25import javassist.CtClass;
26import javassist.NotFoundException;
27import javassist.bytecode.Descriptor;
28
29import com.google.common.collect.Maps;
30
31import cuchaz.enigma.Constants;
32
33public class Ancestries implements Serializable
34{
35 private static final long serialVersionUID = 738687982126844179L;
36
37 private Map<String,String> m_superclasses;
38
39 public Ancestries( )
40 {
41 m_superclasses = Maps.newHashMap();
42 }
43
44 public void readFromJar( InputStream in )
45 throws IOException
46 {
47 ClassPool classPool = new ClassPool();
48
49 ZipInputStream zin = new ZipInputStream( in );
50 ZipEntry entry;
51 while( ( entry = zin.getNextEntry() ) != null )
52 {
53 // filter out non-classes
54 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) )
55 {
56 continue;
57 }
58
59 // read the class into a buffer
60 ByteArrayOutputStream bos = new ByteArrayOutputStream();
61 byte[] buf = new byte[Constants.KiB];
62 int totalNumBytesRead = 0;
63 while( zin.available() > 0 )
64 {
65 int numBytesRead = zin.read( buf );
66 if( numBytesRead < 0 )
67 {
68 break;
69 }
70 bos.write( buf, 0, numBytesRead );
71
72 // sanity checking
73 totalNumBytesRead += numBytesRead;
74 if( totalNumBytesRead > Constants.MiB )
75 {
76 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
77 }
78 }
79
80 // determine the class name (ie chop off the ".class")
81 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
82
83 // get a javassist handle for the class
84 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
85 try
86 {
87 CtClass c = classPool.get( className );
88 addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
89 }
90 catch( NotFoundException ex )
91 {
92 throw new Error( "Unable to load class: " + className );
93 }
94 }
95 }
96
97 public void addSuperclass( String className, String superclassName )
98 {
99 className = Descriptor.toJvmName( className );
100 superclassName = Descriptor.toJvmName( superclassName );
101
102 if( className.equals( superclassName ) )
103 {
104 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
105 }
106
107 if( !isJre( className ) && !isJre( superclassName ) )
108 {
109 m_superclasses.put( className, superclassName );
110 }
111 }
112
113 public String getSuperclassName( String className )
114 {
115 return m_superclasses.get( className );
116 }
117
118 public List<String> getAncestry( String className )
119 {
120 List<String> ancestors = new ArrayList<String>();
121 while( className != null )
122 {
123 className = getSuperclassName( className );
124 ancestors.add( className );
125 }
126 return ancestors;
127 }
128
129 private boolean isJre( String className )
130 {
131 return className.startsWith( "java/" )
132 || className.startsWith( "javax/" );
133 }
134}
diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java
index dc3b4df7..c1624a83 100644
--- a/src/cuchaz/enigma/mapping/ArgumentEntry.java
+++ b/src/cuchaz/enigma/mapping/ArgumentEntry.java
@@ -42,6 +42,13 @@ public class ArgumentEntry implements Entry, Serializable
42 m_name = name; 42 m_name = name;
43 } 43 }
44 44
45 public ArgumentEntry( ArgumentEntry other )
46 {
47 m_methodEntry = new MethodEntry( other.m_methodEntry );
48 m_index = other.m_index;
49 m_name = other.m_name;
50 }
51
45 public MethodEntry getMethodEntry( ) 52 public MethodEntry getMethodEntry( )
46 { 53 {
47 return m_methodEntry; 54 return m_methodEntry;
@@ -58,6 +65,26 @@ public class ArgumentEntry implements Entry, Serializable
58 return m_name; 65 return m_name;
59 } 66 }
60 67
68 public ClassEntry getClassEntry( )
69 {
70 return m_methodEntry.getClassEntry();
71 }
72
73 public String getClassName( )
74 {
75 return m_methodEntry.getClassName();
76 }
77
78 public String getMethodName( )
79 {
80 return m_methodEntry.getName();
81 }
82
83 public String getMethodSignature( )
84 {
85 return m_methodEntry.getSignature();
86 }
87
61 @Override 88 @Override
62 public int hashCode( ) 89 public int hashCode( )
63 { 90 {
diff --git a/src/cuchaz/enigma/mapping/ArgumentIndex.java b/src/cuchaz/enigma/mapping/ArgumentIndex.java
new file mode 100644
index 00000000..57488d14
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ArgumentIndex.java
@@ -0,0 +1,41 @@
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.mapping;
12
13import java.io.Serializable;
14
15public class ArgumentIndex implements Serializable
16{
17 private static final long serialVersionUID = 8610742471440861315L;
18
19 private String m_obfName;
20 private String m_deobfName;
21
22 public ArgumentIndex( String obfName, String deobfName )
23 {
24 m_obfName = obfName;
25 m_deobfName = deobfName;
26 }
27
28 public String getObfName( )
29 {
30 return m_obfName;
31 }
32
33 public String getDeobfName( )
34 {
35 return m_deobfName;
36 }
37 public void setDeobfName( String val )
38 {
39 m_deobfName = val;
40 }
41}
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java
index 3a757675..0968e955 100644
--- a/src/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/cuchaz/enigma/mapping/ClassEntry.java
@@ -33,6 +33,11 @@ public class ClassEntry implements Entry, Serializable
33 m_name = className; 33 m_name = className;
34 } 34 }
35 35
36 public ClassEntry( ClassEntry other )
37 {
38 m_name = other.m_name;
39 }
40
36 @Override 41 @Override
37 public String getName( ) 42 public String getName( )
38 { 43 {
diff --git a/src/cuchaz/enigma/mapping/ClassIndex.java b/src/cuchaz/enigma/mapping/ClassIndex.java
new file mode 100644
index 00000000..699807b8
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ClassIndex.java
@@ -0,0 +1,159 @@
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.mapping;
12
13import java.io.Serializable;
14import java.util.Map;
15
16import com.beust.jcommander.internal.Maps;
17import com.google.common.collect.BiMap;
18import com.google.common.collect.HashBiMap;
19
20public class ClassIndex implements Serializable
21{
22 private static final long serialVersionUID = -5148491146902340107L;
23
24 private String m_obfName;
25 private String m_deobfName;
26 private BiMap<String,String> m_fieldsObfToDeobf;
27 private Map<String,MethodIndex> m_methodsByObf;
28 private Map<String,MethodIndex> m_methodsByDeobf;
29
30 public ClassIndex( String obfName, String deobfName )
31 {
32 m_obfName = obfName;
33 m_deobfName = deobfName;
34 m_fieldsObfToDeobf = HashBiMap.create();
35 m_methodsByObf = Maps.newHashMap();
36 m_methodsByDeobf = Maps.newHashMap();
37 }
38
39 public String getObfName( )
40 {
41 return m_obfName;
42 }
43
44 public String getDeobfName( )
45 {
46 return m_deobfName;
47 }
48 public void setDeobfName( String val )
49 {
50 m_deobfName = val;
51 }
52
53 public String getObfFieldName( String deobfName )
54 {
55 return m_fieldsObfToDeobf.inverse().get( deobfName );
56 }
57
58 public String getDeobfFieldName( String obfName )
59 {
60 return m_fieldsObfToDeobf.get( obfName );
61 }
62
63 public void setFieldName( String obfName, String deobfName )
64 {
65 m_fieldsObfToDeobf.put( obfName, deobfName );
66 }
67
68 public MethodIndex getMethodByObf( String obfName, String signature )
69 {
70 return m_methodsByObf.get( getMethodKey( obfName, signature ) );
71 }
72
73 public MethodIndex getMethodByDeobf( String deobfName, String signature )
74 {
75 return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) );
76 }
77
78 private String getMethodKey( String name, String signature )
79 {
80 return name + signature;
81 }
82
83 public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature )
84 {
85 if( deobfName == null )
86 {
87 throw new IllegalArgumentException( "deobf name cannot be null!" );
88 }
89
90 MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) );
91 if( methodIndex == null )
92 {
93 methodIndex = createMethodIndex( obfName, obfSignature );
94 }
95
96 m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) );
97 methodIndex.setDeobfName( deobfName );
98 methodIndex.setDeobfSignature( deobfSignature );
99 m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex );
100 }
101
102 public void updateDeobfMethodSignatures( Translator translator )
103 {
104 for( MethodIndex methodIndex : m_methodsByObf.values() )
105 {
106 methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) );
107 }
108 }
109
110 public void setArgumentName( String obfMethodName, String obfMethodSignature, int index, String obfName, String deobfName )
111 {
112 if( deobfName == null )
113 {
114 throw new IllegalArgumentException( "deobf name cannot be null!" );
115 }
116
117 MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) );
118 if( methodIndex == null )
119 {
120 methodIndex = createMethodIndex( obfMethodName, obfMethodSignature );
121 }
122 methodIndex.setArgumentName( index, obfName, deobfName );
123 }
124
125 private MethodIndex createMethodIndex( String obfName, String obfSignature )
126 {
127 MethodIndex methodIndex = new MethodIndex( obfName, obfSignature, obfName, obfSignature );
128 String key = getMethodKey( obfName, obfSignature );
129 m_methodsByObf.put( key, methodIndex );
130 m_methodsByDeobf.put( key, methodIndex );
131 return methodIndex;
132 }
133
134 @Override
135 public String toString( )
136 {
137 StringBuilder buf = new StringBuilder();
138 buf.append( m_obfName );
139 buf.append( " <-> " );
140 buf.append( m_deobfName );
141 buf.append( "\n" );
142 buf.append( "Fields:\n" );
143 for( Map.Entry<String,String> entry : m_fieldsObfToDeobf.entrySet() )
144 {
145 buf.append( "\t" );
146 buf.append( entry.getKey() );
147 buf.append( " <-> " );
148 buf.append( entry.getValue() );
149 buf.append( "\n" );
150 }
151 buf.append( "Methods:\n" );
152 for( MethodIndex methodIndex : m_methodsByObf.values() )
153 {
154 buf.append( methodIndex.toString() );
155 buf.append( "\n" );
156 }
157 return buf.toString();
158 }
159}
diff --git a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java
new file mode 100644
index 00000000..5320f110
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java
@@ -0,0 +1,57 @@
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.mapping;
12
13import java.util.Map;
14
15public class DeobfuscatedAncestries extends Ancestries
16{
17 private static final long serialVersionUID = 8316248774892618324L;
18
19 private Ancestries m_ancestries;
20 private Map<String,ClassIndex> m_classesByObf;
21 private Map<String,ClassIndex> m_classesByDeobf;
22
23 protected DeobfuscatedAncestries( Ancestries ancestries, Map<String,ClassIndex> classesByObf, Map<String,ClassIndex> classesByDeobf )
24 {
25 m_ancestries = ancestries;
26 m_classesByObf = classesByObf;
27 m_classesByDeobf = classesByDeobf;
28 }
29
30 @Override
31 public String getSuperclassName( String deobfClassName )
32 {
33 // obfuscate the class name
34 ClassIndex classIndex = m_classesByDeobf.get( deobfClassName );
35 if( classIndex == null )
36 {
37 return null;
38 }
39 String obfClassName = classIndex.getObfName();
40
41 // get the superclass
42 String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName );
43 if( obfSuperclassName == null )
44 {
45 return null;
46 }
47
48 // deobfuscate the superclass name
49 classIndex = m_classesByObf.get( obfSuperclassName );
50 if( classIndex == null )
51 {
52 return null;
53 }
54
55 return classIndex.getDeobfName();
56 }
57}
diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java
new file mode 100644
index 00000000..e40e9992
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/EntryPair.java
@@ -0,0 +1,46 @@
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.mapping;
12
13import cuchaz.enigma.Util;
14
15public class EntryPair
16{
17 public Entry obf;
18 public Entry deobf;
19
20 public EntryPair( Entry obf, Entry deobf )
21 {
22 this.obf = obf;
23 this.deobf = deobf;
24 }
25
26 @Override
27 public int hashCode( )
28 {
29 return Util.combineHashesOrdered( obf, deobf );
30 }
31
32 @Override
33 public boolean equals( Object other )
34 {
35 if( other instanceof EntryPair )
36 {
37 return equals( (EntryPair)other );
38 }
39 return false;
40 }
41
42 public boolean equals( EntryPair other )
43 {
44 return obf.equals( other.obf ) && deobf.equals( other.deobf );
45 }
46}
diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java
index 25b665ab..b9f42394 100644
--- a/src/cuchaz/enigma/mapping/FieldEntry.java
+++ b/src/cuchaz/enigma/mapping/FieldEntry.java
@@ -36,6 +36,18 @@ public class FieldEntry implements Entry, Serializable
36 m_name = name; 36 m_name = name;
37 } 37 }
38 38
39 public FieldEntry( FieldEntry other )
40 {
41 m_classEntry = new ClassEntry( other.m_classEntry );
42 m_name = other.m_name;
43 }
44
45 public FieldEntry( FieldEntry other, String newClassName )
46 {
47 m_classEntry = new ClassEntry( newClassName );
48 m_name = other.m_name;
49 }
50
39 public ClassEntry getClassEntry( ) 51 public ClassEntry getClassEntry( )
40 { 52 {
41 return m_classEntry; 53 return m_classEntry;
@@ -47,6 +59,11 @@ public class FieldEntry implements Entry, Serializable
47 return m_name; 59 return m_name;
48 } 60 }
49 61
62 public String getClassName( )
63 {
64 return m_classEntry.getName();
65 }
66
50 @Override 67 @Override
51 public int hashCode( ) 68 public int hashCode( )
52 { 69 {
diff --git a/src/cuchaz/enigma/mapping/MethodEntry.java b/src/cuchaz/enigma/mapping/MethodEntry.java
index 4afc099b..9ea2d08e 100644
--- a/src/cuchaz/enigma/mapping/MethodEntry.java
+++ b/src/cuchaz/enigma/mapping/MethodEntry.java
@@ -42,6 +42,20 @@ public class MethodEntry implements Entry, Serializable
42 m_signature = signature; 42 m_signature = signature;
43 } 43 }
44 44
45 public MethodEntry( MethodEntry other )
46 {
47 m_classEntry = new ClassEntry( other.m_classEntry );
48 m_name = other.m_name;
49 m_signature = other.m_signature;
50 }
51
52 public MethodEntry( MethodEntry other, String newClassName )
53 {
54 m_classEntry = new ClassEntry( newClassName );
55 m_name = other.m_name;
56 m_signature = other.m_signature;
57 }
58
45 public ClassEntry getClassEntry( ) 59 public ClassEntry getClassEntry( )
46 { 60 {
47 return m_classEntry; 61 return m_classEntry;
@@ -58,6 +72,11 @@ public class MethodEntry implements Entry, Serializable
58 return m_signature; 72 return m_signature;
59 } 73 }
60 74
75 public String getClassName( )
76 {
77 return m_classEntry.getName();
78 }
79
61 @Override 80 @Override
62 public int hashCode( ) 81 public int hashCode( )
63 { 82 {
@@ -84,6 +103,6 @@ public class MethodEntry implements Entry, Serializable
84 @Override 103 @Override
85 public String toString( ) 104 public String toString( )
86 { 105 {
87 return m_classEntry.getName() + "." + m_name + ":" + m_signature; 106 return m_classEntry.getName() + "." + m_name + m_signature;
88 } 107 }
89} 108}
diff --git a/src/cuchaz/enigma/mapping/MethodIndex.java b/src/cuchaz/enigma/mapping/MethodIndex.java
new file mode 100644
index 00000000..f965355d
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MethodIndex.java
@@ -0,0 +1,125 @@
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.mapping;
12
13import java.io.Serializable;
14import java.util.Map;
15import java.util.TreeMap;
16
17public class MethodIndex implements Serializable
18{
19 private static final long serialVersionUID = -4409570216084263978L;
20
21 private String m_obfName;
22 private String m_deobfName;
23 private String m_obfSignature;
24 private String m_deobfSignature;
25 private Map<Integer,ArgumentIndex> m_arguments;
26
27 public MethodIndex( String obfName, String obfSignature, String deobfName, String deobfSignature )
28 {
29 m_obfName = obfName;
30 m_deobfName = deobfName;
31 m_obfSignature = obfSignature;
32 m_deobfSignature = deobfSignature;
33 m_arguments = new TreeMap<Integer,ArgumentIndex>();
34 }
35
36 public String getObfName( )
37 {
38 return m_obfName;
39 }
40
41 public String getDeobfName( )
42 {
43 return m_deobfName;
44 }
45 public void setDeobfName( String val )
46 {
47 m_deobfName = val;
48 }
49
50 public String getObfSignature( )
51 {
52 return m_obfSignature;
53 }
54
55 public String getDeobfSignature( )
56 {
57 return m_deobfSignature;
58 }
59 public void setDeobfSignature( String val )
60 {
61 m_deobfSignature = val;
62 }
63
64 public String getObfArgumentName( int index )
65 {
66 ArgumentIndex argumentIndex = m_arguments.get( index );
67 if( argumentIndex != null )
68 {
69 return argumentIndex.getObfName();
70 }
71
72 return null;
73 }
74
75 public String getDeobfArgumentName( int index )
76 {
77 ArgumentIndex argumentIndex = m_arguments.get( index );
78 if( argumentIndex != null )
79 {
80 return argumentIndex.getDeobfName();
81 }
82
83 return null;
84 }
85
86 public void setArgumentName( int index, String obfName, String deobfName )
87 {
88 ArgumentIndex argumentIndex = m_arguments.get( index );
89 if( argumentIndex == null )
90 {
91 argumentIndex = new ArgumentIndex( obfName, deobfName );
92 m_arguments.put( index, argumentIndex );
93 }
94 else
95 {
96 argumentIndex.setDeobfName( deobfName );
97 }
98 }
99
100 @Override
101 public String toString( )
102 {
103 StringBuilder buf = new StringBuilder();
104 buf.append( "\t" );
105 buf.append( m_obfName );
106 buf.append( " <-> " );
107 buf.append( m_deobfName );
108 buf.append( "\n" );
109 buf.append( "\t" );
110 buf.append( m_obfSignature );
111 buf.append( " <-> " );
112 buf.append( m_deobfSignature );
113 buf.append( "\n" );
114 buf.append( "\tArguments:\n" );
115 for( ArgumentIndex argumentIndex : m_arguments.values() )
116 {
117 buf.append( "\t\t" );
118 buf.append( argumentIndex.getObfName() );
119 buf.append( " <-> " );
120 buf.append( argumentIndex.getDeobfName() );
121 buf.append( "\n" );
122 }
123 return buf.toString();
124 }
125}
diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java
new file mode 100644
index 00000000..4c0dbac1
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java
@@ -0,0 +1,87 @@
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.mapping;
12
13import java.io.IOException;
14import java.io.StringReader;
15
16public class SignatureUpdater
17{
18 public interface ClassNameUpdater
19 {
20 String update( String className );
21 }
22
23 public static String update( String signature, ClassNameUpdater updater )
24 {
25 try
26 {
27 StringBuilder buf = new StringBuilder();
28
29 // read the signature character-by-character
30 StringReader reader = new StringReader( signature );
31 int i = -1;
32 while( ( i = reader.read() ) != -1 )
33 {
34 char c = (char)i;
35
36 // does this character start a class name?
37 if( c == 'L' )
38 {
39 // update the class name and add it to the buffer
40 buf.append( 'L' );
41 String className = readClass( reader );
42 if( className == null )
43 {
44 throw new IllegalArgumentException( "Malformed signature: " + signature );
45 }
46 buf.append( updater.update( className ) );
47 buf.append( ';' );
48 }
49 else
50 {
51 // copy the character into the buffer
52 buf.append( c );
53 }
54 }
55
56 return buf.toString();
57 }
58 catch( IOException ex )
59 {
60 // I'm pretty sure a StringReader will never throw one of these
61 throw new Error( ex );
62 }
63 }
64
65 private static String readClass( StringReader reader )
66 throws IOException
67 {
68 // read all the characters in the buffer until we hit a ';'
69 StringBuilder buf = new StringBuilder();
70 int i = -1;
71 while( ( i = reader.read() ) != -1 )
72 {
73 char c = (char)i;
74
75 if( c == ';' )
76 {
77 return buf.toString();
78 }
79 else
80 {
81 buf.append( c );
82 }
83 }
84
85 return null;
86 }
87}
diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java
new file mode 100644
index 00000000..79ae0d32
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/TranslationDirection.java
@@ -0,0 +1,34 @@
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.mapping;
12
13
14public enum TranslationDirection
15{
16 Deobfuscating
17 {
18 @Override
19 public <T> T choose( T deobfChoice, T obfChoice )
20 {
21 return deobfChoice;
22 }
23 },
24 Obfuscating
25 {
26 @Override
27 public <T> T choose( T deobfChoice, T obfChoice )
28 {
29 return obfChoice;
30 }
31 };
32
33 public abstract <T> T choose( T deobfChoice, T obfChoice );
34}
diff --git a/src/cuchaz/enigma/mapping/TranslationMappings.java b/src/cuchaz/enigma/mapping/TranslationMappings.java
new file mode 100644
index 00000000..d6cd4491
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/TranslationMappings.java
@@ -0,0 +1,187 @@
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.mapping;
12
13import java.io.IOException;
14import java.io.InputStream;
15import java.io.ObjectInputStream;
16import java.io.ObjectOutputStream;
17import java.io.OutputStream;
18import java.io.Serializable;
19import java.util.Map;
20import java.util.zip.GZIPInputStream;
21import java.util.zip.GZIPOutputStream;
22
23import com.beust.jcommander.internal.Maps;
24
25import cuchaz.enigma.Util;
26
27public class TranslationMappings implements Serializable
28{
29 private static final long serialVersionUID = 4649790259460259026L;
30
31 private Map<String,ClassIndex> m_classesByObf;
32 private Map<String,ClassIndex> m_classesByDeobf;
33 private Ancestries m_ancestries;
34
35 public TranslationMappings( Ancestries ancestries )
36 {
37 m_classesByObf = Maps.newHashMap();
38 m_classesByDeobf = Maps.newHashMap();
39 m_ancestries = ancestries;
40 }
41
42 public static TranslationMappings newFromResource( String resource )
43 throws IOException
44 {
45 InputStream in = null;
46 try
47 {
48 in = TranslationMappings.class.getResourceAsStream( resource );
49 return newFromStream( in );
50 }
51 finally
52 {
53 Util.closeQuietly( in );
54 }
55 }
56
57 public Translator getTranslator( TranslationDirection direction )
58 {
59 return new Translator(
60 direction,
61 direction.choose( m_classesByObf, m_classesByDeobf ),
62 direction.choose( m_ancestries, new DeobfuscatedAncestries( m_ancestries, m_classesByObf, m_classesByDeobf ) )
63 );
64 }
65
66 public void setClassName( ClassEntry obf, String deobfName )
67 {
68 ClassIndex classIndex = m_classesByObf.get( obf.getName() );
69 if( classIndex == null )
70 {
71 classIndex = createClassIndex( obf );
72 }
73
74 m_classesByDeobf.remove( classIndex.getDeobfName() );
75 classIndex.setDeobfName( deobfName );
76 m_classesByDeobf.put( deobfName, classIndex );
77
78 updateDeobfMethodSignatures();
79
80 // TEMP
81 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
82 assert( translatedName != null && translatedName.equals( deobfName ) );
83 }
84
85 public void setFieldName( FieldEntry obf, String deobfName )
86 {
87 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
88 if( classIndex == null )
89 {
90 classIndex = createClassIndex( obf.getClassEntry() );
91 }
92
93 classIndex.setFieldName( obf.getName(), deobfName );
94
95 // TEMP
96 System.out.println( classIndex );
97 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
98 assert( translatedName != null && translatedName.equals( deobfName ) );
99 }
100
101 public void setMethodName( MethodEntry obf, String deobfName )
102 {
103 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
104 if( classIndex == null )
105 {
106 classIndex = createClassIndex( obf.getClassEntry() );
107 }
108
109 String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() );
110 classIndex.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature );
111
112 // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too
113
114 // TEMP
115 System.out.println( classIndex );
116 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
117 assert( translatedName != null && translatedName.equals( deobfName ) );
118 }
119
120 public void setArgumentName( ArgumentEntry obf, String deobfName )
121 {
122 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
123 if( classIndex == null )
124 {
125 classIndex = createClassIndex( obf.getClassEntry() );
126 }
127
128 classIndex.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName(), deobfName );
129
130 // TEMP
131 System.out.println( classIndex );
132 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
133 assert( translatedName != null && translatedName.equals( deobfName ) );
134 }
135
136 public void write( OutputStream out )
137 throws IOException
138 {
139 // TEMP: just use the object output for now. We can find a more efficient storage format later
140 GZIPOutputStream gzipout = new GZIPOutputStream( out );
141 ObjectOutputStream oout = new ObjectOutputStream( gzipout );
142 oout.writeObject( this );
143 gzipout.finish();
144 }
145
146 public static TranslationMappings newFromStream( InputStream in )
147 throws IOException
148 {
149 try
150 {
151 return (TranslationMappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject();
152 }
153 catch( ClassNotFoundException ex )
154 {
155 throw new Error( ex );
156 }
157 }
158
159 private ClassIndex createClassIndex( ClassEntry obf )
160 {
161 ClassIndex classIndex = new ClassIndex( obf.getName(), obf.getName() );
162 m_classesByObf.put( classIndex.getObfName(), classIndex );
163 m_classesByDeobf.put( classIndex.getDeobfName(), classIndex );
164 return classIndex;
165 }
166
167 private void updateDeobfMethodSignatures( )
168 {
169 Translator translator = getTranslator( TranslationDirection.Deobfuscating );
170 for( ClassIndex classIndex : m_classesByObf.values() )
171 {
172 classIndex.updateDeobfMethodSignatures( translator );
173 }
174 }
175
176 @Override
177 public String toString( )
178 {
179 StringBuilder buf = new StringBuilder();
180 for( ClassIndex classIndex : m_classesByObf.values() )
181 {
182 buf.append( classIndex.toString() );
183 buf.append( "\n" );
184 }
185 return buf.toString();
186 }
187}
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
new file mode 100644
index 00000000..bae0dce7
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -0,0 +1,201 @@
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.mapping;
12
13import java.util.ArrayList;
14import java.util.List;
15import java.util.Map;
16
17import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
18
19public class Translator
20{
21 private TranslationDirection m_direction;
22 private Map<String,ClassIndex> m_classes;
23 private Ancestries m_ancestries;
24
25 protected Translator( TranslationDirection direction, Map<String,ClassIndex> classes, Ancestries ancestries )
26 {
27 m_direction = direction;
28 m_classes = classes;
29 m_ancestries = ancestries;
30 }
31
32 public String translate( ClassEntry in )
33 {
34 return translateClass( in.getName() );
35 }
36
37 public String translateClass( String in )
38 {
39 ClassIndex classIndex = m_classes.get( in );
40 if( classIndex != null )
41 {
42 return m_direction.choose(
43 classIndex.getDeobfName(),
44 classIndex.getObfName()
45 );
46 }
47
48 return null;
49 }
50
51 public ClassEntry translateEntry( ClassEntry in )
52 {
53 String name = translate( in );
54 if( name == null )
55 {
56 return in;
57 }
58 return new ClassEntry( name );
59 }
60
61 public String translate( FieldEntry in )
62 {
63 for( String className : getSelfAndAncestors( in.getClassName() ) )
64 {
65 // look for the class
66 ClassIndex classIndex = m_classes.get( className );
67 if( classIndex != null )
68 {
69 // look for the field
70 String deobfName = m_direction.choose(
71 classIndex.getDeobfFieldName( in.getName() ),
72 classIndex.getObfFieldName( in.getName() )
73 );
74 if( deobfName != null )
75 {
76 return deobfName;
77 }
78 }
79 }
80
81 return null;
82 }
83
84 public FieldEntry translateEntry( FieldEntry in )
85 {
86 String name = translate( in );
87 if( name == null )
88 {
89 name = in.getName();
90 }
91 return new FieldEntry(
92 translateEntry( in.getClassEntry() ),
93 name
94 );
95 }
96
97 public String translate( MethodEntry in )
98 {
99 for( String className : getSelfAndAncestors( in.getClassName() ) )
100 {
101 // look for the class
102 ClassIndex classIndex = m_classes.get( className );
103 if( classIndex != null )
104 {
105 // look for the method
106 MethodIndex methodIndex = m_direction.choose(
107 classIndex.getMethodByObf( in.getName(), in.getSignature() ),
108 classIndex.getMethodByDeobf( in.getName(), in.getSignature() )
109 );
110 if( methodIndex != null )
111 {
112 return m_direction.choose(
113 methodIndex.getDeobfName(),
114 methodIndex.getObfName()
115 );
116 }
117 }
118 }
119
120 return null;
121 }
122
123 public MethodEntry translateEntry( MethodEntry in )
124 {
125 String name = translate( in );
126 if( name == null )
127 {
128 name = in.getName();
129 }
130 return new MethodEntry(
131 translateEntry( in.getClassEntry() ),
132 name,
133 translateSignature( in.getSignature() )
134 );
135 }
136
137 public String translate( ArgumentEntry in )
138 {
139 for( String className : getSelfAndAncestors( in.getClassName() ) )
140 {
141 // look for the class
142 ClassIndex classIndex = m_classes.get( className );
143 if( classIndex != null )
144 {
145 // look for the method
146 MethodIndex methodIndex = m_direction.choose(
147 classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ),
148 classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() )
149 );
150 if( methodIndex != null )
151 {
152 return m_direction.choose(
153 methodIndex.getDeobfArgumentName( in.getIndex() ),
154 methodIndex.getObfArgumentName( in.getIndex() )
155 );
156 }
157 }
158 }
159
160 return null;
161 }
162
163 public ArgumentEntry translateEntry( ArgumentEntry in )
164 {
165 String name = translate( in );
166 if( name == null )
167 {
168 name = in.getName();
169 }
170 return new ArgumentEntry(
171 translateEntry( in.getMethodEntry() ),
172 in.getIndex(),
173 name
174 );
175 }
176
177 public String translateSignature( String signature )
178 {
179 return SignatureUpdater.update( signature, new ClassNameUpdater( )
180 {
181 @Override
182 public String update( String className )
183 {
184 String translatedName = translateClass( className );
185 if( translatedName != null )
186 {
187 return translatedName;
188 }
189 return className;
190 }
191 } );
192 }
193
194 private List<String> getSelfAndAncestors( String className )
195 {
196 List<String> ancestry = new ArrayList<String>();
197 ancestry.add( className );
198 ancestry.addAll( m_ancestries.getAncestry( className ) );
199 return ancestry;
200 }
201}