summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/mapping
diff options
context:
space:
mode:
authorGravatar jeff2014-07-27 22:33:21 -0400
committerGravatar jeff2014-07-27 22:33:21 -0400
commitd7321b5b0d38c575e54c770f7aa18dacbacab3c8 (patch)
treeef4b3e0f83b1fe89125c2674fec023871e70c0d8 /src/cuchaz/enigma/mapping
parentmade gui responsive to caret position and show identifier info (diff)
downloadenigma-fork-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.gz
enigma-fork-d7321b5b0d38c575e54c770f7aa18dacbacab3c8.tar.xz
enigma-fork-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/enigma/mapping')
-rw-r--r--src/cuchaz/enigma/mapping/Ancestries.java134
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentEntry.java27
-rw-r--r--src/cuchaz/enigma/mapping/ArgumentIndex.java41
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java5
-rw-r--r--src/cuchaz/enigma/mapping/ClassIndex.java159
-rw-r--r--src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java57
-rw-r--r--src/cuchaz/enigma/mapping/EntryPair.java46
-rw-r--r--src/cuchaz/enigma/mapping/FieldEntry.java17
-rw-r--r--src/cuchaz/enigma/mapping/MethodEntry.java21
-rw-r--r--src/cuchaz/enigma/mapping/MethodIndex.java125
-rw-r--r--src/cuchaz/enigma/mapping/SignatureUpdater.java87
-rw-r--r--src/cuchaz/enigma/mapping/TranslationDirection.java34
-rw-r--r--src/cuchaz/enigma/mapping/TranslationMappings.java187
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java201
14 files changed, 1140 insertions, 1 deletions
diff --git a/src/cuchaz/enigma/mapping/Ancestries.java b/src/cuchaz/enigma/mapping/Ancestries.java
new file mode 100644
index 0000000..b7a5e24
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Ancestries.java
@@ -0,0 +1,134 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.Serializable;
17import java.util.ArrayList;
18import java.util.List;
19import java.util.Map;
20import java.util.zip.ZipEntry;
21import java.util.zip.ZipInputStream;
22
23import javassist.ByteArrayClassPath;
24import javassist.ClassPool;
25import javassist.CtClass;
26import javassist.NotFoundException;
27import javassist.bytecode.Descriptor;
28
29import com.google.common.collect.Maps;
30
31import cuchaz.enigma.Constants;
32
33public class Ancestries implements Serializable
34{
35 private static final long serialVersionUID = 738687982126844179L;
36
37 private Map<String,String> m_superclasses;
38
39 public Ancestries( )
40 {
41 m_superclasses = Maps.newHashMap();
42 }
43
44 public void readFromJar( InputStream in )
45 throws IOException
46 {
47 ClassPool classPool = new ClassPool();
48
49 ZipInputStream zin = new ZipInputStream( in );
50 ZipEntry entry;
51 while( ( entry = zin.getNextEntry() ) != null )
52 {
53 // filter out non-classes
54 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) )
55 {
56 continue;
57 }
58
59 // read the class into a buffer
60 ByteArrayOutputStream bos = new ByteArrayOutputStream();
61 byte[] buf = new byte[Constants.KiB];
62 int totalNumBytesRead = 0;
63 while( zin.available() > 0 )
64 {
65 int numBytesRead = zin.read( buf );
66 if( numBytesRead < 0 )
67 {
68 break;
69 }
70 bos.write( buf, 0, numBytesRead );
71
72 // sanity checking
73 totalNumBytesRead += numBytesRead;
74 if( totalNumBytesRead > Constants.MiB )
75 {
76 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
77 }
78 }
79
80 // determine the class name (ie chop off the ".class")
81 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
82
83 // get a javassist handle for the class
84 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
85 try
86 {
87 CtClass c = classPool.get( className );
88 addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
89 }
90 catch( NotFoundException ex )
91 {
92 throw new Error( "Unable to load class: " + className );
93 }
94 }
95 }
96
97 public void addSuperclass( String className, String superclassName )
98 {
99 className = Descriptor.toJvmName( className );
100 superclassName = Descriptor.toJvmName( superclassName );
101
102 if( className.equals( superclassName ) )
103 {
104 throw new IllegalArgumentException( "Class cannot be its own superclass! " + className );
105 }
106
107 if( !isJre( className ) && !isJre( superclassName ) )
108 {
109 m_superclasses.put( className, superclassName );
110 }
111 }
112
113 public String getSuperclassName( String className )
114 {
115 return m_superclasses.get( className );
116 }
117
118 public List<String> getAncestry( String className )
119 {
120 List<String> ancestors = new ArrayList<String>();
121 while( className != null )
122 {
123 className = getSuperclassName( className );
124 ancestors.add( className );
125 }
126 return ancestors;
127 }
128
129 private boolean isJre( String className )
130 {
131 return className.startsWith( "java/" )
132 || className.startsWith( "javax/" );
133 }
134}
diff --git a/src/cuchaz/enigma/mapping/ArgumentEntry.java b/src/cuchaz/enigma/mapping/ArgumentEntry.java
index dc3b4df..c1624a8 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 0000000..57488d1
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ArgumentIndex.java
@@ -0,0 +1,41 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14
15public class ArgumentIndex implements Serializable
16{
17 private static final long serialVersionUID = 8610742471440861315L;
18
19 private String m_obfName;
20 private String m_deobfName;
21
22 public ArgumentIndex( String obfName, String deobfName )
23 {
24 m_obfName = obfName;
25 m_deobfName = deobfName;
26 }
27
28 public String getObfName( )
29 {
30 return m_obfName;
31 }
32
33 public String getDeobfName( )
34 {
35 return m_deobfName;
36 }
37 public void setDeobfName( String val )
38 {
39 m_deobfName = val;
40 }
41}
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java
index 3a75767..0968e95 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 0000000..699807b
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/ClassIndex.java
@@ -0,0 +1,159 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14import java.util.Map;
15
16import com.beust.jcommander.internal.Maps;
17import com.google.common.collect.BiMap;
18import com.google.common.collect.HashBiMap;
19
20public class ClassIndex implements Serializable
21{
22 private static final long serialVersionUID = -5148491146902340107L;
23
24 private String m_obfName;
25 private String m_deobfName;
26 private BiMap<String,String> m_fieldsObfToDeobf;
27 private Map<String,MethodIndex> m_methodsByObf;
28 private Map<String,MethodIndex> m_methodsByDeobf;
29
30 public ClassIndex( String obfName, String deobfName )
31 {
32 m_obfName = obfName;
33 m_deobfName = deobfName;
34 m_fieldsObfToDeobf = HashBiMap.create();
35 m_methodsByObf = Maps.newHashMap();
36 m_methodsByDeobf = Maps.newHashMap();
37 }
38
39 public String getObfName( )
40 {
41 return m_obfName;
42 }
43
44 public String getDeobfName( )
45 {
46 return m_deobfName;
47 }
48 public void setDeobfName( String val )
49 {
50 m_deobfName = val;
51 }
52
53 public String getObfFieldName( String deobfName )
54 {
55 return m_fieldsObfToDeobf.inverse().get( deobfName );
56 }
57
58 public String getDeobfFieldName( String obfName )
59 {
60 return m_fieldsObfToDeobf.get( obfName );
61 }
62
63 public void setFieldName( String obfName, String deobfName )
64 {
65 m_fieldsObfToDeobf.put( obfName, deobfName );
66 }
67
68 public MethodIndex getMethodByObf( String obfName, String signature )
69 {
70 return m_methodsByObf.get( getMethodKey( obfName, signature ) );
71 }
72
73 public MethodIndex getMethodByDeobf( String deobfName, String signature )
74 {
75 return m_methodsByDeobf.get( getMethodKey( deobfName, signature ) );
76 }
77
78 private String getMethodKey( String name, String signature )
79 {
80 return name + signature;
81 }
82
83 public void setMethodNameAndSignature( String obfName, String obfSignature, String deobfName, String deobfSignature )
84 {
85 if( deobfName == null )
86 {
87 throw new IllegalArgumentException( "deobf name cannot be null!" );
88 }
89
90 MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfName, obfSignature ) );
91 if( methodIndex == null )
92 {
93 methodIndex = createMethodIndex( obfName, obfSignature );
94 }
95
96 m_methodsByDeobf.remove( getMethodKey( methodIndex.getDeobfName(), methodIndex.getDeobfSignature() ) );
97 methodIndex.setDeobfName( deobfName );
98 methodIndex.setDeobfSignature( deobfSignature );
99 m_methodsByDeobf.put( getMethodKey( deobfName, deobfSignature ), methodIndex );
100 }
101
102 public void updateDeobfMethodSignatures( Translator translator )
103 {
104 for( MethodIndex methodIndex : m_methodsByObf.values() )
105 {
106 methodIndex.setDeobfSignature( translator.translateSignature( methodIndex.getObfSignature() ) );
107 }
108 }
109
110 public void setArgumentName( String obfMethodName, String obfMethodSignature, int index, String obfName, String deobfName )
111 {
112 if( deobfName == null )
113 {
114 throw new IllegalArgumentException( "deobf name cannot be null!" );
115 }
116
117 MethodIndex methodIndex = m_methodsByObf.get( getMethodKey( obfMethodName, obfMethodSignature ) );
118 if( methodIndex == null )
119 {
120 methodIndex = createMethodIndex( obfMethodName, obfMethodSignature );
121 }
122 methodIndex.setArgumentName( index, obfName, deobfName );
123 }
124
125 private MethodIndex createMethodIndex( String obfName, String obfSignature )
126 {
127 MethodIndex methodIndex = new MethodIndex( obfName, obfSignature, obfName, obfSignature );
128 String key = getMethodKey( obfName, obfSignature );
129 m_methodsByObf.put( key, methodIndex );
130 m_methodsByDeobf.put( key, methodIndex );
131 return methodIndex;
132 }
133
134 @Override
135 public String toString( )
136 {
137 StringBuilder buf = new StringBuilder();
138 buf.append( m_obfName );
139 buf.append( " <-> " );
140 buf.append( m_deobfName );
141 buf.append( "\n" );
142 buf.append( "Fields:\n" );
143 for( Map.Entry<String,String> entry : m_fieldsObfToDeobf.entrySet() )
144 {
145 buf.append( "\t" );
146 buf.append( entry.getKey() );
147 buf.append( " <-> " );
148 buf.append( entry.getValue() );
149 buf.append( "\n" );
150 }
151 buf.append( "Methods:\n" );
152 for( MethodIndex methodIndex : m_methodsByObf.values() )
153 {
154 buf.append( methodIndex.toString() );
155 buf.append( "\n" );
156 }
157 return buf.toString();
158 }
159}
diff --git a/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java
new file mode 100644
index 0000000..5320f11
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/DeobfuscatedAncestries.java
@@ -0,0 +1,57 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.util.Map;
14
15public class DeobfuscatedAncestries extends Ancestries
16{
17 private static final long serialVersionUID = 8316248774892618324L;
18
19 private Ancestries m_ancestries;
20 private Map<String,ClassIndex> m_classesByObf;
21 private Map<String,ClassIndex> m_classesByDeobf;
22
23 protected DeobfuscatedAncestries( Ancestries ancestries, Map<String,ClassIndex> classesByObf, Map<String,ClassIndex> classesByDeobf )
24 {
25 m_ancestries = ancestries;
26 m_classesByObf = classesByObf;
27 m_classesByDeobf = classesByDeobf;
28 }
29
30 @Override
31 public String getSuperclassName( String deobfClassName )
32 {
33 // obfuscate the class name
34 ClassIndex classIndex = m_classesByDeobf.get( deobfClassName );
35 if( classIndex == null )
36 {
37 return null;
38 }
39 String obfClassName = classIndex.getObfName();
40
41 // get the superclass
42 String obfSuperclassName = m_ancestries.getSuperclassName( obfClassName );
43 if( obfSuperclassName == null )
44 {
45 return null;
46 }
47
48 // deobfuscate the superclass name
49 classIndex = m_classesByObf.get( obfSuperclassName );
50 if( classIndex == null )
51 {
52 return null;
53 }
54
55 return classIndex.getDeobfName();
56 }
57}
diff --git a/src/cuchaz/enigma/mapping/EntryPair.java b/src/cuchaz/enigma/mapping/EntryPair.java
new file mode 100644
index 0000000..e40e999
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/EntryPair.java
@@ -0,0 +1,46 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import cuchaz.enigma.Util;
14
15public class EntryPair
16{
17 public Entry obf;
18 public Entry deobf;
19
20 public EntryPair( Entry obf, Entry deobf )
21 {
22 this.obf = obf;
23 this.deobf = deobf;
24 }
25
26 @Override
27 public int hashCode( )
28 {
29 return Util.combineHashesOrdered( obf, deobf );
30 }
31
32 @Override
33 public boolean equals( Object other )
34 {
35 if( other instanceof EntryPair )
36 {
37 return equals( (EntryPair)other );
38 }
39 return false;
40 }
41
42 public boolean equals( EntryPair other )
43 {
44 return obf.equals( other.obf ) && deobf.equals( other.deobf );
45 }
46}
diff --git a/src/cuchaz/enigma/mapping/FieldEntry.java b/src/cuchaz/enigma/mapping/FieldEntry.java
index 25b665a..b9f4239 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 4afc099..9ea2d08 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 0000000..f965355
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/MethodIndex.java
@@ -0,0 +1,125 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.Serializable;
14import java.util.Map;
15import java.util.TreeMap;
16
17public class MethodIndex implements Serializable
18{
19 private static final long serialVersionUID = -4409570216084263978L;
20
21 private String m_obfName;
22 private String m_deobfName;
23 private String m_obfSignature;
24 private String m_deobfSignature;
25 private Map<Integer,ArgumentIndex> m_arguments;
26
27 public MethodIndex( String obfName, String obfSignature, String deobfName, String deobfSignature )
28 {
29 m_obfName = obfName;
30 m_deobfName = deobfName;
31 m_obfSignature = obfSignature;
32 m_deobfSignature = deobfSignature;
33 m_arguments = new TreeMap<Integer,ArgumentIndex>();
34 }
35
36 public String getObfName( )
37 {
38 return m_obfName;
39 }
40
41 public String getDeobfName( )
42 {
43 return m_deobfName;
44 }
45 public void setDeobfName( String val )
46 {
47 m_deobfName = val;
48 }
49
50 public String getObfSignature( )
51 {
52 return m_obfSignature;
53 }
54
55 public String getDeobfSignature( )
56 {
57 return m_deobfSignature;
58 }
59 public void setDeobfSignature( String val )
60 {
61 m_deobfSignature = val;
62 }
63
64 public String getObfArgumentName( int index )
65 {
66 ArgumentIndex argumentIndex = m_arguments.get( index );
67 if( argumentIndex != null )
68 {
69 return argumentIndex.getObfName();
70 }
71
72 return null;
73 }
74
75 public String getDeobfArgumentName( int index )
76 {
77 ArgumentIndex argumentIndex = m_arguments.get( index );
78 if( argumentIndex != null )
79 {
80 return argumentIndex.getDeobfName();
81 }
82
83 return null;
84 }
85
86 public void setArgumentName( int index, String obfName, String deobfName )
87 {
88 ArgumentIndex argumentIndex = m_arguments.get( index );
89 if( argumentIndex == null )
90 {
91 argumentIndex = new ArgumentIndex( obfName, deobfName );
92 m_arguments.put( index, argumentIndex );
93 }
94 else
95 {
96 argumentIndex.setDeobfName( deobfName );
97 }
98 }
99
100 @Override
101 public String toString( )
102 {
103 StringBuilder buf = new StringBuilder();
104 buf.append( "\t" );
105 buf.append( m_obfName );
106 buf.append( " <-> " );
107 buf.append( m_deobfName );
108 buf.append( "\n" );
109 buf.append( "\t" );
110 buf.append( m_obfSignature );
111 buf.append( " <-> " );
112 buf.append( m_deobfSignature );
113 buf.append( "\n" );
114 buf.append( "\tArguments:\n" );
115 for( ArgumentIndex argumentIndex : m_arguments.values() )
116 {
117 buf.append( "\t\t" );
118 buf.append( argumentIndex.getObfName() );
119 buf.append( " <-> " );
120 buf.append( argumentIndex.getDeobfName() );
121 buf.append( "\n" );
122 }
123 return buf.toString();
124 }
125}
diff --git a/src/cuchaz/enigma/mapping/SignatureUpdater.java b/src/cuchaz/enigma/mapping/SignatureUpdater.java
new file mode 100644
index 0000000..4c0dbac
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/SignatureUpdater.java
@@ -0,0 +1,87 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.IOException;
14import java.io.StringReader;
15
16public class SignatureUpdater
17{
18 public interface ClassNameUpdater
19 {
20 String update( String className );
21 }
22
23 public static String update( String signature, ClassNameUpdater updater )
24 {
25 try
26 {
27 StringBuilder buf = new StringBuilder();
28
29 // read the signature character-by-character
30 StringReader reader = new StringReader( signature );
31 int i = -1;
32 while( ( i = reader.read() ) != -1 )
33 {
34 char c = (char)i;
35
36 // does this character start a class name?
37 if( c == 'L' )
38 {
39 // update the class name and add it to the buffer
40 buf.append( 'L' );
41 String className = readClass( reader );
42 if( className == null )
43 {
44 throw new IllegalArgumentException( "Malformed signature: " + signature );
45 }
46 buf.append( updater.update( className ) );
47 buf.append( ';' );
48 }
49 else
50 {
51 // copy the character into the buffer
52 buf.append( c );
53 }
54 }
55
56 return buf.toString();
57 }
58 catch( IOException ex )
59 {
60 // I'm pretty sure a StringReader will never throw one of these
61 throw new Error( ex );
62 }
63 }
64
65 private static String readClass( StringReader reader )
66 throws IOException
67 {
68 // read all the characters in the buffer until we hit a ';'
69 StringBuilder buf = new StringBuilder();
70 int i = -1;
71 while( ( i = reader.read() ) != -1 )
72 {
73 char c = (char)i;
74
75 if( c == ';' )
76 {
77 return buf.toString();
78 }
79 else
80 {
81 buf.append( c );
82 }
83 }
84
85 return null;
86 }
87}
diff --git a/src/cuchaz/enigma/mapping/TranslationDirection.java b/src/cuchaz/enigma/mapping/TranslationDirection.java
new file mode 100644
index 0000000..79ae0d3
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/TranslationDirection.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13
14public enum TranslationDirection
15{
16 Deobfuscating
17 {
18 @Override
19 public <T> T choose( T deobfChoice, T obfChoice )
20 {
21 return deobfChoice;
22 }
23 },
24 Obfuscating
25 {
26 @Override
27 public <T> T choose( T deobfChoice, T obfChoice )
28 {
29 return obfChoice;
30 }
31 };
32
33 public abstract <T> T choose( T deobfChoice, T obfChoice );
34}
diff --git a/src/cuchaz/enigma/mapping/TranslationMappings.java b/src/cuchaz/enigma/mapping/TranslationMappings.java
new file mode 100644
index 0000000..d6cd449
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/TranslationMappings.java
@@ -0,0 +1,187 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.io.IOException;
14import java.io.InputStream;
15import java.io.ObjectInputStream;
16import java.io.ObjectOutputStream;
17import java.io.OutputStream;
18import java.io.Serializable;
19import java.util.Map;
20import java.util.zip.GZIPInputStream;
21import java.util.zip.GZIPOutputStream;
22
23import com.beust.jcommander.internal.Maps;
24
25import cuchaz.enigma.Util;
26
27public class TranslationMappings implements Serializable
28{
29 private static final long serialVersionUID = 4649790259460259026L;
30
31 private Map<String,ClassIndex> m_classesByObf;
32 private Map<String,ClassIndex> m_classesByDeobf;
33 private Ancestries m_ancestries;
34
35 public TranslationMappings( Ancestries ancestries )
36 {
37 m_classesByObf = Maps.newHashMap();
38 m_classesByDeobf = Maps.newHashMap();
39 m_ancestries = ancestries;
40 }
41
42 public static TranslationMappings newFromResource( String resource )
43 throws IOException
44 {
45 InputStream in = null;
46 try
47 {
48 in = TranslationMappings.class.getResourceAsStream( resource );
49 return newFromStream( in );
50 }
51 finally
52 {
53 Util.closeQuietly( in );
54 }
55 }
56
57 public Translator getTranslator( TranslationDirection direction )
58 {
59 return new Translator(
60 direction,
61 direction.choose( m_classesByObf, m_classesByDeobf ),
62 direction.choose( m_ancestries, new DeobfuscatedAncestries( m_ancestries, m_classesByObf, m_classesByDeobf ) )
63 );
64 }
65
66 public void setClassName( ClassEntry obf, String deobfName )
67 {
68 ClassIndex classIndex = m_classesByObf.get( obf.getName() );
69 if( classIndex == null )
70 {
71 classIndex = createClassIndex( obf );
72 }
73
74 m_classesByDeobf.remove( classIndex.getDeobfName() );
75 classIndex.setDeobfName( deobfName );
76 m_classesByDeobf.put( deobfName, classIndex );
77
78 updateDeobfMethodSignatures();
79
80 // TEMP
81 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
82 assert( translatedName != null && translatedName.equals( deobfName ) );
83 }
84
85 public void setFieldName( FieldEntry obf, String deobfName )
86 {
87 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
88 if( classIndex == null )
89 {
90 classIndex = createClassIndex( obf.getClassEntry() );
91 }
92
93 classIndex.setFieldName( obf.getName(), deobfName );
94
95 // TEMP
96 System.out.println( classIndex );
97 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
98 assert( translatedName != null && translatedName.equals( deobfName ) );
99 }
100
101 public void setMethodName( MethodEntry obf, String deobfName )
102 {
103 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
104 if( classIndex == null )
105 {
106 classIndex = createClassIndex( obf.getClassEntry() );
107 }
108
109 String deobfSignature = getTranslator( TranslationDirection.Deobfuscating ).translateSignature( obf.getSignature() );
110 classIndex.setMethodNameAndSignature( obf.getName(), obf.getSignature(), deobfName, deobfSignature );
111
112 // TODO: update ancestor/descendant methods in other classes in the inheritance hierarchy too
113
114 // TEMP
115 System.out.println( classIndex );
116 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
117 assert( translatedName != null && translatedName.equals( deobfName ) );
118 }
119
120 public void setArgumentName( ArgumentEntry obf, String deobfName )
121 {
122 ClassIndex classIndex = m_classesByObf.get( obf.getClassName() );
123 if( classIndex == null )
124 {
125 classIndex = createClassIndex( obf.getClassEntry() );
126 }
127
128 classIndex.setArgumentName( obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName(), deobfName );
129
130 // TEMP
131 System.out.println( classIndex );
132 String translatedName = getTranslator( TranslationDirection.Deobfuscating ).translate( obf );
133 assert( translatedName != null && translatedName.equals( deobfName ) );
134 }
135
136 public void write( OutputStream out )
137 throws IOException
138 {
139 // TEMP: just use the object output for now. We can find a more efficient storage format later
140 GZIPOutputStream gzipout = new GZIPOutputStream( out );
141 ObjectOutputStream oout = new ObjectOutputStream( gzipout );
142 oout.writeObject( this );
143 gzipout.finish();
144 }
145
146 public static TranslationMappings newFromStream( InputStream in )
147 throws IOException
148 {
149 try
150 {
151 return (TranslationMappings)new ObjectInputStream( new GZIPInputStream( in ) ).readObject();
152 }
153 catch( ClassNotFoundException ex )
154 {
155 throw new Error( ex );
156 }
157 }
158
159 private ClassIndex createClassIndex( ClassEntry obf )
160 {
161 ClassIndex classIndex = new ClassIndex( obf.getName(), obf.getName() );
162 m_classesByObf.put( classIndex.getObfName(), classIndex );
163 m_classesByDeobf.put( classIndex.getDeobfName(), classIndex );
164 return classIndex;
165 }
166
167 private void updateDeobfMethodSignatures( )
168 {
169 Translator translator = getTranslator( TranslationDirection.Deobfuscating );
170 for( ClassIndex classIndex : m_classesByObf.values() )
171 {
172 classIndex.updateDeobfMethodSignatures( translator );
173 }
174 }
175
176 @Override
177 public String toString( )
178 {
179 StringBuilder buf = new StringBuilder();
180 for( ClassIndex classIndex : m_classesByObf.values() )
181 {
182 buf.append( classIndex.toString() );
183 buf.append( "\n" );
184 }
185 return buf.toString();
186 }
187}
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
new file mode 100644
index 0000000..bae0dce
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -0,0 +1,201 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.mapping;
12
13import java.util.ArrayList;
14import java.util.List;
15import java.util.Map;
16
17import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
18
19public class Translator
20{
21 private TranslationDirection m_direction;
22 private Map<String,ClassIndex> m_classes;
23 private Ancestries m_ancestries;
24
25 protected Translator( TranslationDirection direction, Map<String,ClassIndex> classes, Ancestries ancestries )
26 {
27 m_direction = direction;
28 m_classes = classes;
29 m_ancestries = ancestries;
30 }
31
32 public String translate( ClassEntry in )
33 {
34 return translateClass( in.getName() );
35 }
36
37 public String translateClass( String in )
38 {
39 ClassIndex classIndex = m_classes.get( in );
40 if( classIndex != null )
41 {
42 return m_direction.choose(
43 classIndex.getDeobfName(),
44 classIndex.getObfName()
45 );
46 }
47
48 return null;
49 }
50
51 public ClassEntry translateEntry( ClassEntry in )
52 {
53 String name = translate( in );
54 if( name == null )
55 {
56 return in;
57 }
58 return new ClassEntry( name );
59 }
60
61 public String translate( FieldEntry in )
62 {
63 for( String className : getSelfAndAncestors( in.getClassName() ) )
64 {
65 // look for the class
66 ClassIndex classIndex = m_classes.get( className );
67 if( classIndex != null )
68 {
69 // look for the field
70 String deobfName = m_direction.choose(
71 classIndex.getDeobfFieldName( in.getName() ),
72 classIndex.getObfFieldName( in.getName() )
73 );
74 if( deobfName != null )
75 {
76 return deobfName;
77 }
78 }
79 }
80
81 return null;
82 }
83
84 public FieldEntry translateEntry( FieldEntry in )
85 {
86 String name = translate( in );
87 if( name == null )
88 {
89 name = in.getName();
90 }
91 return new FieldEntry(
92 translateEntry( in.getClassEntry() ),
93 name
94 );
95 }
96
97 public String translate( MethodEntry in )
98 {
99 for( String className : getSelfAndAncestors( in.getClassName() ) )
100 {
101 // look for the class
102 ClassIndex classIndex = m_classes.get( className );
103 if( classIndex != null )
104 {
105 // look for the method
106 MethodIndex methodIndex = m_direction.choose(
107 classIndex.getMethodByObf( in.getName(), in.getSignature() ),
108 classIndex.getMethodByDeobf( in.getName(), in.getSignature() )
109 );
110 if( methodIndex != null )
111 {
112 return m_direction.choose(
113 methodIndex.getDeobfName(),
114 methodIndex.getObfName()
115 );
116 }
117 }
118 }
119
120 return null;
121 }
122
123 public MethodEntry translateEntry( MethodEntry in )
124 {
125 String name = translate( in );
126 if( name == null )
127 {
128 name = in.getName();
129 }
130 return new MethodEntry(
131 translateEntry( in.getClassEntry() ),
132 name,
133 translateSignature( in.getSignature() )
134 );
135 }
136
137 public String translate( ArgumentEntry in )
138 {
139 for( String className : getSelfAndAncestors( in.getClassName() ) )
140 {
141 // look for the class
142 ClassIndex classIndex = m_classes.get( className );
143 if( classIndex != null )
144 {
145 // look for the method
146 MethodIndex methodIndex = m_direction.choose(
147 classIndex.getMethodByObf( in.getMethodName(), in.getMethodSignature() ),
148 classIndex.getMethodByDeobf( in.getMethodName(), in.getMethodSignature() )
149 );
150 if( methodIndex != null )
151 {
152 return m_direction.choose(
153 methodIndex.getDeobfArgumentName( in.getIndex() ),
154 methodIndex.getObfArgumentName( in.getIndex() )
155 );
156 }
157 }
158 }
159
160 return null;
161 }
162
163 public ArgumentEntry translateEntry( ArgumentEntry in )
164 {
165 String name = translate( in );
166 if( name == null )
167 {
168 name = in.getName();
169 }
170 return new ArgumentEntry(
171 translateEntry( in.getMethodEntry() ),
172 in.getIndex(),
173 name
174 );
175 }
176
177 public String translateSignature( String signature )
178 {
179 return SignatureUpdater.update( signature, new ClassNameUpdater( )
180 {
181 @Override
182 public String update( String className )
183 {
184 String translatedName = translateClass( className );
185 if( translatedName != null )
186 {
187 return translatedName;
188 }
189 return className;
190 }
191 } );
192 }
193
194 private List<String> getSelfAndAncestors( String className )
195 {
196 List<String> ancestry = new ArrayList<String>();
197 ancestry.add( className );
198 ancestry.addAll( m_ancestries.getAncestry( className ) );
199 return ancestry;
200 }
201}