diff options
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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | |||
| 14 | public 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; | |||
| 18 | import cuchaz.enigma.gui.ClassSelectionListener; | 18 | import cuchaz.enigma.gui.ClassSelectionListener; |
| 19 | import cuchaz.enigma.gui.Gui; | 19 | import cuchaz.enigma.gui.Gui; |
| 20 | import cuchaz.enigma.gui.RenameListener; | 20 | import cuchaz.enigma.gui.RenameListener; |
| 21 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 21 | import cuchaz.enigma.mapping.Entry; | 22 | import cuchaz.enigma.mapping.Entry; |
| 23 | import cuchaz.enigma.mapping.EntryPair; | ||
| 22 | 24 | ||
| 23 | public class Controller implements ClassSelectionListener, CaretListener, RenameListener | 25 | public 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 @@ | |||
| 11 | package cuchaz.enigma; | 11 | package cuchaz.enigma; |
| 12 | 12 | ||
| 13 | import java.io.File; | 13 | import java.io.File; |
| 14 | import java.io.FileInputStream; | ||
| 14 | import java.io.IOException; | 15 | import java.io.IOException; |
| 16 | import java.io.InputStream; | ||
| 15 | import java.io.StringWriter; | 17 | import java.io.StringWriter; |
| 16 | import java.util.ArrayList; | 18 | import java.util.ArrayList; |
| 17 | import java.util.Collections; | 19 | import java.util.Collections; |
| @@ -21,16 +23,27 @@ import java.util.List; | |||
| 21 | import java.util.jar.JarEntry; | 23 | import java.util.jar.JarEntry; |
| 22 | import java.util.jar.JarFile; | 24 | import java.util.jar.JarFile; |
| 23 | 25 | ||
| 24 | import com.strobel.assembler.metadata.JarTypeLoader; | ||
| 25 | import com.strobel.decompiler.Decompiler; | 26 | import com.strobel.decompiler.Decompiler; |
| 26 | import com.strobel.decompiler.DecompilerSettings; | 27 | import com.strobel.decompiler.DecompilerSettings; |
| 27 | import com.strobel.decompiler.PlainTextOutput; | 28 | import com.strobel.decompiler.PlainTextOutput; |
| 28 | 29 | ||
| 30 | import cuchaz.enigma.mapping.Ancestries; | ||
| 31 | import cuchaz.enigma.mapping.ArgumentEntry; | ||
| 32 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 33 | import cuchaz.enigma.mapping.Entry; | ||
| 34 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 35 | import cuchaz.enigma.mapping.MethodEntry; | ||
| 36 | import cuchaz.enigma.mapping.TranslationDirection; | ||
| 37 | import cuchaz.enigma.mapping.TranslationMappings; | ||
| 38 | import cuchaz.enigma.mapping.Translator; | ||
| 39 | |||
| 29 | public class Deobfuscator | 40 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.InputStream; | ||
| 15 | import java.util.jar.JarEntry; | ||
| 16 | import java.util.jar.JarFile; | ||
| 17 | |||
| 18 | import javassist.ByteArrayClassPath; | ||
| 19 | import javassist.ClassPool; | ||
| 20 | import javassist.CtClass; | ||
| 21 | |||
| 22 | import com.strobel.assembler.metadata.Buffer; | ||
| 23 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 24 | |||
| 25 | import cuchaz.enigma.bytecode.ClassTranslator; | ||
| 26 | import cuchaz.enigma.mapping.Translator; | ||
| 27 | |||
| 28 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | |||
| 13 | import java.util.Iterator; | ||
| 14 | |||
| 15 | import javassist.bytecode.BadBytecode; | ||
| 16 | import javassist.bytecode.Bytecode; | ||
| 17 | import javassist.bytecode.CodeAttribute; | ||
| 18 | import javassist.bytecode.CodeIterator; | ||
| 19 | import javassist.bytecode.Opcode; | ||
| 20 | |||
| 21 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | |||
| 13 | import java.io.ByteArrayInputStream; | ||
| 14 | import java.io.ByteArrayOutputStream; | ||
| 15 | import java.io.DataInputStream; | ||
| 16 | import java.io.DataOutputStream; | ||
| 17 | import java.io.IOException; | ||
| 18 | import java.util.Map; | ||
| 19 | import java.util.Set; | ||
| 20 | |||
| 21 | import javassist.CtBehavior; | ||
| 22 | import javassist.bytecode.BadBytecode; | ||
| 23 | import javassist.bytecode.Bytecode; | ||
| 24 | import javassist.bytecode.CodeAttribute; | ||
| 25 | import javassist.bytecode.ConstPool; | ||
| 26 | import javassist.bytecode.ExceptionTable; | ||
| 27 | |||
| 28 | import com.google.common.collect.Maps; | ||
| 29 | import com.google.common.collect.Sets; | ||
| 30 | |||
| 31 | import cuchaz.enigma.Util; | ||
| 32 | import cuchaz.enigma.bytecode.BytecodeIndexIterator.Index; | ||
| 33 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 34 | |||
| 35 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | |||
| 13 | import java.util.HashSet; | ||
| 14 | import java.util.Set; | ||
| 15 | |||
| 16 | import javassist.ClassMap; | ||
| 17 | import javassist.CtBehavior; | ||
| 18 | import javassist.CtClass; | ||
| 19 | import javassist.CtField; | ||
| 20 | import javassist.CtMethod; | ||
| 21 | import javassist.bytecode.ConstPool; | ||
| 22 | import javassist.bytecode.Descriptor; | ||
| 23 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 24 | import cuchaz.enigma.mapping.FieldEntry; | ||
| 25 | import cuchaz.enigma.mapping.MethodEntry; | ||
| 26 | import cuchaz.enigma.mapping.Translator; | ||
| 27 | |||
| 28 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | |||
| 13 | import java.io.DataInputStream; | ||
| 14 | import java.io.DataOutputStream; | ||
| 15 | import java.lang.reflect.Constructor; | ||
| 16 | import java.lang.reflect.Field; | ||
| 17 | import java.lang.reflect.Method; | ||
| 18 | import java.util.HashMap; | ||
| 19 | |||
| 20 | import javassist.bytecode.ConstPool; | ||
| 21 | import javassist.bytecode.Descriptor; | ||
| 22 | import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; | ||
| 23 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 24 | import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; | ||
| 25 | |||
| 26 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode; | ||
| 12 | |||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.List; | ||
| 15 | import java.util.Map; | ||
| 16 | |||
| 17 | import com.google.common.collect.Lists; | ||
| 18 | import com.google.common.collect.Maps; | ||
| 19 | |||
| 20 | import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor; | ||
| 21 | import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; | ||
| 22 | import cuchaz.enigma.bytecode.accessors.InvokeDynamicInfoAccessor; | ||
| 23 | import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor; | ||
| 24 | import cuchaz.enigma.bytecode.accessors.MethodHandleInfoAccessor; | ||
| 25 | import cuchaz.enigma.bytecode.accessors.MethodTypeInfoAccessor; | ||
| 26 | import cuchaz.enigma.bytecode.accessors.NameAndTypeInfoAccessor; | ||
| 27 | import cuchaz.enigma.bytecode.accessors.StringInfoAccessor; | ||
| 28 | |||
| 29 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.io.ByteArrayInputStream; | ||
| 14 | import java.io.ByteArrayOutputStream; | ||
| 15 | import java.io.DataInputStream; | ||
| 16 | import java.io.DataOutputStream; | ||
| 17 | import java.io.IOException; | ||
| 18 | import java.io.PrintWriter; | ||
| 19 | import java.lang.reflect.Constructor; | ||
| 20 | import java.lang.reflect.Field; | ||
| 21 | import java.lang.reflect.Method; | ||
| 22 | |||
| 23 | import cuchaz.enigma.bytecode.InfoType; | ||
| 24 | |||
| 25 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | import java.lang.reflect.Field; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.bytecode.accessors; | ||
| 12 | |||
| 13 | public 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; | |||
| 45 | import cuchaz.enigma.analysis.SourceIndex; | 45 | import cuchaz.enigma.analysis.SourceIndex; |
| 46 | import cuchaz.enigma.mapping.ArgumentEntry; | 46 | import cuchaz.enigma.mapping.ArgumentEntry; |
| 47 | import cuchaz.enigma.mapping.ClassEntry; | 47 | import cuchaz.enigma.mapping.ClassEntry; |
| 48 | import cuchaz.enigma.mapping.Entry; | 48 | import cuchaz.enigma.mapping.EntryPair; |
| 49 | import cuchaz.enigma.mapping.FieldEntry; | 49 | import cuchaz.enigma.mapping.FieldEntry; |
| 50 | import cuchaz.enigma.mapping.MethodEntry; | 50 | import 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 | ||
| 15 | public interface RenameListener | 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.ByteArrayOutputStream; | ||
| 14 | import java.io.IOException; | ||
| 15 | import java.io.InputStream; | ||
| 16 | import java.io.Serializable; | ||
| 17 | import java.util.ArrayList; | ||
| 18 | import java.util.List; | ||
| 19 | import java.util.Map; | ||
| 20 | import java.util.zip.ZipEntry; | ||
| 21 | import java.util.zip.ZipInputStream; | ||
| 22 | |||
| 23 | import javassist.ByteArrayClassPath; | ||
| 24 | import javassist.ClassPool; | ||
| 25 | import javassist.CtClass; | ||
| 26 | import javassist.NotFoundException; | ||
| 27 | import javassist.bytecode.Descriptor; | ||
| 28 | |||
| 29 | import com.google.common.collect.Maps; | ||
| 30 | |||
| 31 | import cuchaz.enigma.Constants; | ||
| 32 | |||
| 33 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.Map; | ||
| 15 | |||
| 16 | import com.beust.jcommander.internal.Maps; | ||
| 17 | import com.google.common.collect.BiMap; | ||
| 18 | import com.google.common.collect.HashBiMap; | ||
| 19 | |||
| 20 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import cuchaz.enigma.Util; | ||
| 14 | |||
| 15 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.Serializable; | ||
| 14 | import java.util.Map; | ||
| 15 | import java.util.TreeMap; | ||
| 16 | |||
| 17 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.StringReader; | ||
| 15 | |||
| 16 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | |||
| 14 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.io.IOException; | ||
| 14 | import java.io.InputStream; | ||
| 15 | import java.io.ObjectInputStream; | ||
| 16 | import java.io.ObjectOutputStream; | ||
| 17 | import java.io.OutputStream; | ||
| 18 | import java.io.Serializable; | ||
| 19 | import java.util.Map; | ||
| 20 | import java.util.zip.GZIPInputStream; | ||
| 21 | import java.util.zip.GZIPOutputStream; | ||
| 22 | |||
| 23 | import com.beust.jcommander.internal.Maps; | ||
| 24 | |||
| 25 | import cuchaz.enigma.Util; | ||
| 26 | |||
| 27 | public 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 | ******************************************************************************/ | ||
| 11 | package cuchaz.enigma.mapping; | ||
| 12 | |||
| 13 | import java.util.ArrayList; | ||
| 14 | import java.util.List; | ||
| 15 | import java.util.Map; | ||
| 16 | |||
| 17 | import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; | ||
| 18 | |||
| 19 | public 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 | } | ||