diff options
| author | 2014-07-27 22:33:21 -0400 | |
|---|---|---|
| committer | 2014-07-27 22:33:21 -0400 | |
| commit | d7321b5b0d38c575e54c770f7aa18dacbacab3c8 (patch) | |
| tree | ef4b3e0f83b1fe89125c2674fec023871e70c0d8 /src/cuchaz | |
| parent | made gui responsive to caret position and show identifier info (diff) | |
| download | enigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.gz enigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.xz enigma-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.zip | |
added identifier renaming capability
copied some code over from M3L to handle the heavy bytecode magic.
It's ok... M3L will eventually depend on Enigma.
Completely restructured the mappings though. This way is better. =)
Diffstat (limited to 'src/cuchaz')
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 | } | ||