summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2014-07-27 22:33:21 -0400
committerGravatar jeff2014-07-27 22:33:21 -0400
commitd7321b5b0d38c575e54c770f7aa18dacbacab3c8 (patch)
treeef4b3e0f83b1fe89125c2674fec023871e70c0d8 /src
parentmade gui responsive to caret position and show identifier info (diff)
downloadenigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.gz
enigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.xz
enigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.zip
added identifier renaming capability
copied some code over from M3L to handle the heavy bytecode magic. It's ok... M3L will eventually depend on Enigma. Completely restructured the mappings though. This way is better. =)
Diffstat (limited to 'src')
-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}