diff options
| author | 2019-01-30 21:05:32 +0200 | |
|---|---|---|
| committer | 2019-01-30 21:05:32 +0200 | |
| commit | ba7a354efae7d49833c887cf147ac940c975a1fa (patch) | |
| tree | 02e14fda81dd5984e24f2df392c57c6e829fc875 /src | |
| parent | Rewrite the Jenkinsfile to use the new declarative pipeline syntax, lets hope... (diff) | |
| download | enigma-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.gz enigma-ba7a354efae7d49833c887cf147ac940c975a1fa.tar.xz enigma-ba7a354efae7d49833c887cf147ac940c975a1fa.zip | |
Remap sources (#106)
* Source remapping beginnings
* Fix navigation to remapped classes
* Translate identifier info reference
* Remap local variables with default names in source
* Caching translator
* Fix lack of highlighting for first opened class
* Fix unicode variable names
* Unicode checker shouldn't be checking just alphanumeric
* Fix package tree being built from obf names
* Don't index `this` as method call for method::reference
* Apply proposed names
* Fix source export issues
* Replace unicode var names at bytecode level uniquely
* Drop imports from editor source
* Class selector fixes
* Delta keep track of base mappings to enable lookup of old names
* Optimize source remapping by remapping source with a StringBuffer instead of copying
* Bump version
Diffstat (limited to 'src')
58 files changed, 1264 insertions, 1203 deletions
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java index fe13321c..b2aed84d 100644 --- a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java +++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java | |||
| @@ -16,6 +16,7 @@ public class CachingClasspathTypeLoader extends CachingTypeLoader { | |||
| 16 | 16 | ||
| 17 | private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); | 17 | private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); |
| 18 | 18 | ||
| 19 | @Override | ||
| 19 | protected byte[] doLoad(String className) { | 20 | protected byte[] doLoad(String className) { |
| 20 | Buffer parentBuf = new Buffer(); | 21 | Buffer parentBuf = new Buffer(); |
| 21 | if (classpathLoader.tryLoadType(className, parentBuf)) { | 22 | if (classpathLoader.tryLoadType(className, parentBuf)) { |
diff --git a/src/main/java/cuchaz/enigma/CommandMain.java b/src/main/java/cuchaz/enigma/CommandMain.java index a84cd5e7..7c0a3d53 100644 --- a/src/main/java/cuchaz/enigma/CommandMain.java +++ b/src/main/java/cuchaz/enigma/CommandMain.java | |||
| @@ -58,7 +58,7 @@ public class CommandMain { | |||
| 58 | File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); | 58 | File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); |
| 59 | Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); | 59 | Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); |
| 60 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 60 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 61 | deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); | 61 | deobfuscator.writeSources(fileJarOut.toPath(), new ConsoleProgressListener()); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | private static void deobfuscate(String[] args) throws Exception { | 64 | private static void deobfuscate(String[] args) throws Exception { |
| @@ -66,7 +66,7 @@ public class CommandMain { | |||
| 66 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); | 66 | File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); |
| 67 | Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); | 67 | Path fileMappings = getReadablePath(getArg(args, 3, "mappings file", false)); |
| 68 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); | 68 | Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); |
| 69 | deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); | 69 | deobfuscator.writeTransformedJar(fileJarOut, new ConsoleProgressListener()); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception { | 72 | private static Deobfuscator getDeobfuscator(Path fileMappings, JarFile jar) throws Exception { |
diff --git a/src/main/java/cuchaz/enigma/CompiledSource.java b/src/main/java/cuchaz/enigma/CompiledSource.java new file mode 100644 index 00000000..fc051d33 --- /dev/null +++ b/src/main/java/cuchaz/enigma/CompiledSource.java | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import org.objectweb.asm.tree.ClassNode; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | public interface CompiledSource { | ||
| 8 | @Nullable | ||
| 9 | ClassNode getClassNode(String name); | ||
| 10 | } | ||
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java index 4c1f695b..b1a8cd58 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java | |||
| @@ -14,11 +14,6 @@ package cuchaz.enigma; | |||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.strobel.assembler.metadata.Buffer; | 15 | import com.strobel.assembler.metadata.Buffer; |
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 17 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 18 | import cuchaz.enigma.analysis.ParsedJar; | ||
| 19 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | ||
| 20 | import cuchaz.enigma.translation.Translator; | ||
| 21 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 23 | import org.objectweb.asm.ClassWriter; | 18 | import org.objectweb.asm.ClassWriter; |
| 24 | import org.objectweb.asm.Opcodes; | 19 | import org.objectweb.asm.Opcodes; |
| @@ -27,61 +22,57 @@ import org.objectweb.asm.tree.ClassNode; | |||
| 27 | import org.objectweb.asm.tree.MethodInsnNode; | 22 | import org.objectweb.asm.tree.MethodInsnNode; |
| 28 | import org.objectweb.asm.tree.MethodNode; | 23 | import org.objectweb.asm.tree.MethodNode; |
| 29 | 24 | ||
| 25 | import java.util.Collection; | ||
| 30 | import java.util.List; | 26 | import java.util.List; |
| 31 | 27 | ||
| 32 | public class TranslatingTypeLoader extends CachingTypeLoader implements ITranslatingTypeLoader { | 28 | public class CompiledSourceTypeLoader extends CachingTypeLoader { |
| 33 | //Store one instance as the classpath shouldnt change during load | 29 | //Store one instance as the classpath shouldn't change during load |
| 34 | private static final ITypeLoader defaultTypeLoader = new CachingClasspathTypeLoader(); | 30 | private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader(); |
| 35 | 31 | ||
| 36 | private final ParsedJar jar; | 32 | private final CompiledSource compiledSource; |
| 37 | private final JarIndex jarIndex; | 33 | |
| 38 | private final ReferencedEntryPool entryPool; | 34 | public CompiledSourceTypeLoader(CompiledSource compiledSource) { |
| 39 | private final Translator obfuscatingTranslator; | 35 | this.compiledSource = compiledSource; |
| 40 | private final Translator deobfuscatingTranslator; | ||
| 41 | |||
| 42 | public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { | ||
| 43 | this.jar = jar; | ||
| 44 | this.jarIndex = jarIndex; | ||
| 45 | this.entryPool = entryPool; | ||
| 46 | this.obfuscatingTranslator = obfuscatingTranslator; | ||
| 47 | this.deobfuscatingTranslator = deobfuscatingTranslator; | ||
| 48 | } | 36 | } |
| 49 | 37 | ||
| 38 | @Override | ||
| 50 | protected byte[] doLoad(String className) { | 39 | protected byte[] doLoad(String className) { |
| 51 | byte[] data = loadType(className); | 40 | byte[] data = loadType(className); |
| 52 | if (data == null) { | 41 | if (data == null) { |
| 53 | // chain to default desc loader | 42 | return loadClasspath(className); |
| 54 | Buffer parentBuf = new Buffer(); | ||
| 55 | if (defaultTypeLoader.tryLoadType(className, parentBuf)) { | ||
| 56 | return parentBuf.array(); | ||
| 57 | } | ||
| 58 | return EMPTY_ARRAY;//need to return *something* as null means no store | ||
| 59 | } | 43 | } |
| 44 | |||
| 60 | return data; | 45 | return data; |
| 61 | } | 46 | } |
| 62 | 47 | ||
| 63 | private byte[] loadType(String className) { | 48 | private byte[] loadClasspath(String name) { |
| 64 | 49 | Buffer parentBuf = new Buffer(); | |
| 65 | // NOTE: don't know if class name is obf or deobf | 50 | if (CLASSPATH_TYPE_LOADER.tryLoadType(name, parentBuf)) { |
| 66 | ClassEntry classEntry = new ClassEntry(className); | 51 | return parentBuf.array(); |
| 67 | ClassEntry obfClassEntry = this.obfuscatingTranslator.translate(classEntry); | ||
| 68 | |||
| 69 | // is this a class we should even know about? | ||
| 70 | if (!jarIndex.getEntryIndex().hasClass(obfClassEntry)) { | ||
| 71 | return null; | ||
| 72 | } | 52 | } |
| 53 | return EMPTY_ARRAY; | ||
| 54 | } | ||
| 73 | 55 | ||
| 74 | // DEBUG | 56 | private byte[] loadType(String className) { |
| 75 | //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); | 57 | ClassEntry entry = new ClassEntry(className); |
| 76 | 58 | ||
| 77 | // find the class in the jar | 59 | // find the class in the jar |
| 78 | ClassNode node = findClassInJar(obfClassEntry); | 60 | ClassNode node = findClassNode(entry); |
| 79 | if (node == null) { | 61 | if (node == null) { |
| 80 | // couldn't find it | 62 | // couldn't find it |
| 81 | return null; | 63 | return null; |
| 82 | } | 64 | } |
| 83 | 65 | ||
| 66 | removeRedundantClassCalls(node); | ||
| 67 | |||
| 68 | ClassWriter writer = new ClassWriter(0); | ||
| 69 | node.accept(writer); | ||
| 84 | 70 | ||
| 71 | // we have a transformed class! | ||
| 72 | return writer.toByteArray(); | ||
| 73 | } | ||
| 74 | |||
| 75 | private void removeRedundantClassCalls(ClassNode node) { | ||
| 85 | // remove <obj>.getClass() calls that are seemingly injected | 76 | // remove <obj>.getClass() calls that are seemingly injected |
| 86 | // DUP | 77 | // DUP |
| 87 | // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; | 78 | // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; |
| @@ -105,19 +96,12 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla | |||
| 105 | insnNode = insnNode.getNext(); | 96 | insnNode = insnNode.getNext(); |
| 106 | } | 97 | } |
| 107 | } | 98 | } |
| 108 | |||
| 109 | ClassWriter writer = new ClassWriter(0); | ||
| 110 | transformInto(node, writer); | ||
| 111 | |||
| 112 | // we have a transformed class! | ||
| 113 | return writer.toByteArray(); | ||
| 114 | } | 99 | } |
| 115 | 100 | ||
| 116 | private ClassNode findClassInJar(ClassEntry obfClassEntry) { | 101 | private ClassNode findClassNode(ClassEntry entry) { |
| 117 | |||
| 118 | // try to find the class in the jar | 102 | // try to find the class in the jar |
| 119 | for (String className : getClassNamesToTry(obfClassEntry)) { | 103 | for (String className : getClassNamesToTry(entry)) { |
| 120 | ClassNode node = this.jar.getClassNode(className); | 104 | ClassNode node = compiledSource.getClassNode(className); |
| 121 | if (node != null) { | 105 | if (node != null) { |
| 122 | return node; | 106 | return node; |
| 123 | } | 107 | } |
| @@ -127,28 +111,15 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla | |||
| 127 | return null; | 111 | return null; |
| 128 | } | 112 | } |
| 129 | 113 | ||
| 130 | @Override | 114 | private Collection<String> getClassNamesToTry(ClassEntry entry) { |
| 131 | public List<String> getClassNamesToTry(String className) { | ||
| 132 | return getClassNamesToTry(this.obfuscatingTranslator.translate(new ClassEntry(className))); | ||
| 133 | } | ||
| 134 | |||
| 135 | @Override | ||
| 136 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | ||
| 137 | List<String> classNamesToTry = Lists.newArrayList(); | 115 | List<String> classNamesToTry = Lists.newArrayList(); |
| 138 | classNamesToTry.add(obfClassEntry.getFullName()); | 116 | classNamesToTry.add(entry.getFullName()); |
| 139 | 117 | ||
| 140 | ClassEntry outerClass = obfClassEntry.getOuterClass(); | 118 | ClassEntry outerClass = entry.getOuterClass(); |
| 141 | if (outerClass != null) { | 119 | if (outerClass != null) { |
| 142 | classNamesToTry.addAll(getClassNamesToTry(outerClass)); | 120 | classNamesToTry.addAll(getClassNamesToTry(outerClass)); |
| 143 | } | 121 | } |
| 144 | 122 | ||
| 145 | return classNamesToTry; | 123 | return classNamesToTry; |
| 146 | } | 124 | } |
| 147 | |||
| 148 | @Override | ||
| 149 | public String transformInto(ClassNode node, ClassWriter writer) { | ||
| 150 | node.accept(new TranslationClassVisitor(deobfuscatingTranslator, entryPool, Opcodes.ASM5, writer)); | ||
| 151 | return deobfuscatingTranslator.translate(new ClassEntry(node.name)).getFullName(); | ||
| 152 | } | ||
| 153 | |||
| 154 | } | 125 | } |
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 076c5468..ef452b0f 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java | |||
| @@ -11,36 +11,39 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Functions; | ||
| 14 | import com.google.common.base.Stopwatch; | 15 | import com.google.common.base.Stopwatch; |
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 17 | import com.strobel.assembler.metadata.MetadataSystem; | 17 | import com.strobel.assembler.metadata.MetadataSystem; |
| 18 | import com.strobel.assembler.metadata.TypeDefinition; | 18 | import com.strobel.assembler.metadata.TypeDefinition; |
| 19 | import com.strobel.assembler.metadata.TypeReference; | 19 | import com.strobel.assembler.metadata.TypeReference; |
| 20 | import com.strobel.decompiler.DecompilerContext; | ||
| 21 | import com.strobel.decompiler.DecompilerSettings; | 20 | import com.strobel.decompiler.DecompilerSettings; |
| 22 | import com.strobel.decompiler.PlainTextOutput; | ||
| 23 | import com.strobel.decompiler.languages.java.JavaOutputVisitor; | ||
| 24 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | ||
| 25 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 21 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 26 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | 22 | import cuchaz.enigma.analysis.EntryReference; |
| 27 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | 23 | import cuchaz.enigma.analysis.IndexTreeBuilder; |
| 28 | import cuchaz.enigma.analysis.*; | 24 | import cuchaz.enigma.analysis.ParsedJar; |
| 29 | import cuchaz.enigma.analysis.index.JarIndex; | 25 | import cuchaz.enigma.analysis.index.JarIndex; |
| 30 | import cuchaz.enigma.api.EnigmaPlugin; | 26 | import cuchaz.enigma.api.EnigmaPlugin; |
| 27 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | ||
| 28 | import cuchaz.enigma.translation.Translatable; | ||
| 29 | import cuchaz.enigma.translation.Translator; | ||
| 31 | import cuchaz.enigma.translation.mapping.*; | 30 | import cuchaz.enigma.translation.mapping.*; |
| 32 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | 31 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| 33 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 32 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 34 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 35 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 33 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 36 | import cuchaz.enigma.translation.representation.entry.Entry; | 34 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 37 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 35 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 38 | import cuchaz.enigma.utils.Utils; | 36 | import org.objectweb.asm.ClassVisitor; |
| 39 | import oml.ast.transformers.*; | ||
| 40 | import org.objectweb.asm.ClassWriter; | 37 | import org.objectweb.asm.ClassWriter; |
| 38 | import org.objectweb.asm.Opcodes; | ||
| 41 | import org.objectweb.asm.tree.ClassNode; | 39 | import org.objectweb.asm.tree.ClassNode; |
| 42 | 40 | ||
| 43 | import java.io.*; | 41 | import java.io.File; |
| 42 | import java.io.FileOutputStream; | ||
| 43 | import java.io.IOException; | ||
| 44 | import java.io.Writer; | ||
| 45 | import java.nio.file.Files; | ||
| 46 | import java.nio.file.Path; | ||
| 44 | import java.util.*; | 47 | import java.util.*; |
| 45 | import java.util.concurrent.ConcurrentHashMap; | 48 | import java.util.concurrent.ConcurrentHashMap; |
| 46 | import java.util.concurrent.atomic.AtomicInteger; | 49 | import java.util.concurrent.atomic.AtomicInteger; |
| @@ -53,11 +56,12 @@ import java.util.stream.Collectors; | |||
| 53 | public class Deobfuscator { | 56 | public class Deobfuscator { |
| 54 | 57 | ||
| 55 | private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class); | 58 | private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class); |
| 56 | private final ReferencedEntryPool entryPool = new ReferencedEntryPool(); | ||
| 57 | private final ParsedJar parsedJar; | 59 | private final ParsedJar parsedJar; |
| 58 | private final DecompilerSettings settings; | ||
| 59 | private final JarIndex jarIndex; | 60 | private final JarIndex jarIndex; |
| 60 | private final IndexTreeBuilder indexTreeBuilder; | 61 | private final IndexTreeBuilder indexTreeBuilder; |
| 62 | |||
| 63 | private final SourceProvider obfSourceProvider; | ||
| 64 | |||
| 61 | private EntryRemapper mapper; | 65 | private EntryRemapper mapper; |
| 62 | 66 | ||
| 63 | public Deobfuscator(ParsedJar jar, Consumer<String> listener) { | 67 | public Deobfuscator(ParsedJar jar, Consumer<String> listener) { |
| @@ -75,15 +79,8 @@ public class Deobfuscator { | |||
| 75 | this.indexTreeBuilder = new IndexTreeBuilder(jarIndex); | 79 | this.indexTreeBuilder = new IndexTreeBuilder(jarIndex); |
| 76 | 80 | ||
| 77 | listener.accept("Preparing..."); | 81 | listener.accept("Preparing..."); |
| 78 | // config the decompiler | 82 | |
| 79 | this.settings = DecompilerSettings.javaDefaults(); | 83 | this.obfSourceProvider = new SourceProvider(SourceProvider.createSettings(), new CompiledSourceTypeLoader(parsedJar)); |
| 80 | this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); | ||
| 81 | this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); | ||
| 82 | this.settings.setForceExplicitTypeArguments( | ||
| 83 | Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); | ||
| 84 | // DEBUG | ||
| 85 | this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); | ||
| 86 | this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); | ||
| 87 | 84 | ||
| 88 | // init mappings | 85 | // init mappings |
| 89 | mapper = new EntryRemapper(jarIndex); | 86 | mapper = new EntryRemapper(jarIndex); |
| @@ -93,7 +90,7 @@ public class Deobfuscator { | |||
| 93 | this(new ParsedJar(jar), listener); | 90 | this(new ParsedJar(jar), listener); |
| 94 | } | 91 | } |
| 95 | 92 | ||
| 96 | public Deobfuscator(ParsedJar jar) throws IOException { | 93 | public Deobfuscator(ParsedJar jar) { |
| 97 | this(jar, (msg) -> { | 94 | this(jar, (msg) -> { |
| 98 | }); | 95 | }); |
| 99 | } | 96 | } |
| @@ -128,9 +125,9 @@ public class Deobfuscator { | |||
| 128 | Collection<Entry<?>> dropped = dropMappings(mappings); | 125 | Collection<Entry<?>> dropped = dropMappings(mappings); |
| 129 | mapper = new EntryRemapper(jarIndex, mappings); | 126 | mapper = new EntryRemapper(jarIndex, mappings); |
| 130 | 127 | ||
| 131 | DeltaTrackingTree<EntryMapping> deobfToObf = mapper.getDeobfToObf(); | 128 | DeltaTrackingTree<EntryMapping> obfToDeobf = mapper.getObfToDeobf(); |
| 132 | for (Entry<?> entry : dropped) { | 129 | for (Entry<?> entry : dropped) { |
| 133 | deobfToObf.trackDeletion(entry); | 130 | obfToDeobf.trackDeletion(entry); |
| 134 | } | 131 | } |
| 135 | } else { | 132 | } else { |
| 136 | mapper = new EntryRemapper(jarIndex); | 133 | mapper = new EntryRemapper(jarIndex); |
| @@ -161,7 +158,7 @@ public class Deobfuscator { | |||
| 161 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); | 158 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); |
| 162 | if (!deobfClassEntry.equals(obfClassEntry)) { | 159 | if (!deobfClassEntry.equals(obfClassEntry)) { |
| 163 | // if the class has a mapping, clearly it's deobfuscated | 160 | // if the class has a mapping, clearly it's deobfuscated |
| 164 | deobfClasses.add(deobfClassEntry); | 161 | deobfClasses.add(obfClassEntry); |
| 165 | } else if (obfClassEntry.getPackageName() != null) { | 162 | } else if (obfClassEntry.getPackageName() != null) { |
| 166 | // also call it deobufscated if it's not in the none package | 163 | // also call it deobufscated if it's not in the none package |
| 167 | deobfClasses.add(obfClassEntry); | 164 | deobfClasses.add(obfClassEntry); |
| @@ -172,151 +169,126 @@ public class Deobfuscator { | |||
| 172 | } | 169 | } |
| 173 | } | 170 | } |
| 174 | 171 | ||
| 175 | public TranslatingTypeLoader createTypeLoader() { | 172 | public SourceProvider getObfSourceProvider() { |
| 176 | return new TranslatingTypeLoader( | 173 | return obfSourceProvider; |
| 177 | this.parsedJar, | ||
| 178 | this.jarIndex, | ||
| 179 | this.entryPool, | ||
| 180 | this.mapper.getObfuscator(), | ||
| 181 | this.mapper.getDeobfuscator() | ||
| 182 | ); | ||
| 183 | } | ||
| 184 | |||
| 185 | public CompilationUnit getSourceTree(String className) { | ||
| 186 | return getSourceTree(className, createTypeLoader()); | ||
| 187 | } | 174 | } |
| 188 | 175 | ||
| 189 | public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { | 176 | public void writeSources(Path outputDirectory, ProgressListener progress) { |
| 190 | return getSourceTree(className, loader, new NoRetryMetadataSystem(loader)); | 177 | // get the classes to decompile |
| 191 | } | 178 | Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses(); |
| 192 | 179 | ||
| 193 | public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader, MetadataSystem metadataSystem) { | 180 | Stopwatch stopwatch = Stopwatch.createStarted(); |
| 194 | 181 | ||
| 195 | // we don't know if this class name is obfuscated or deobfuscated | 182 | try { |
| 196 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out | 183 | Translator deobfuscator = mapper.getDeobfuscator(); |
| 197 | // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one | ||
| 198 | 184 | ||
| 199 | String deobfClassName = mapper.deobfuscate(new ClassEntry(className)).getFullName(); | 185 | // deobfuscate everything first |
| 186 | Map<String, ClassNode> translatedNodes = deobfuscateClasses(progress, classEntries, deobfuscator); | ||
| 200 | 187 | ||
| 201 | // set the desc loader | 188 | decompileClasses(outputDirectory, progress, translatedNodes); |
| 202 | this.settings.setTypeLoader(loader); | 189 | } finally { |
| 190 | stopwatch.stop(); | ||
| 203 | 191 | ||
| 204 | // see if procyon can find the desc | 192 | System.out.println("writeSources Done in : " + stopwatch.toString()); |
| 205 | TypeReference type = metadataSystem.lookupType(deobfClassName); | ||
| 206 | if (type == null) { | ||
| 207 | throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s", | ||
| 208 | className, deobfClassName, loader.getClassNamesToTry(deobfClassName) | ||
| 209 | )); | ||
| 210 | } | 193 | } |
| 211 | TypeDefinition resolvedType = type.resolve(); | ||
| 212 | |||
| 213 | // decompile it! | ||
| 214 | DecompilerContext context = new DecompilerContext(); | ||
| 215 | context.setCurrentType(resolvedType); | ||
| 216 | context.setSettings(this.settings); | ||
| 217 | AstBuilder builder = new AstBuilder(context); | ||
| 218 | builder.addType(resolvedType); | ||
| 219 | builder.runTransformations(null); | ||
| 220 | runCustomTransforms(builder, context); | ||
| 221 | return builder.getCompilationUnit(); | ||
| 222 | } | 194 | } |
| 223 | 195 | ||
| 224 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { | 196 | private Map<String, ClassNode> deobfuscateClasses(ProgressListener progress, Collection<ClassEntry> classEntries, Translator translator) { |
| 225 | return getSourceIndex(sourceTree, source, true); | 197 | AtomicInteger count = new AtomicInteger(); |
| 226 | } | 198 | if (progress != null) { |
| 227 | 199 | progress.init(classEntries.size(), "Deobfuscating classes..."); | |
| 228 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, boolean ignoreBadTokens) { | ||
| 229 | |||
| 230 | // build the source index | ||
| 231 | SourceIndex index = new SourceIndex(source, ignoreBadTokens); | ||
| 232 | sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index); | ||
| 233 | |||
| 234 | EntryResolver resolver = mapper.getDeobfResolver(); | ||
| 235 | |||
| 236 | Collection<Token> tokens = Lists.newArrayList(index.referenceTokens()); | ||
| 237 | |||
| 238 | // resolve all the classes in the source references | ||
| 239 | for (Token token : tokens) { | ||
| 240 | EntryReference<Entry<?>, Entry<?>> deobfReference = index.getDeobfReference(token); | ||
| 241 | index.replaceDeobfReference(token, resolver.resolveFirstReference(deobfReference, ResolutionStrategy.RESOLVE_CLOSEST)); | ||
| 242 | } | 200 | } |
| 243 | 201 | ||
| 244 | return index; | 202 | return classEntries.parallelStream() |
| 245 | } | 203 | .map(entry -> { |
| 204 | ClassEntry translatedEntry = translator.translate(entry); | ||
| 205 | if (progress != null) { | ||
| 206 | progress.step(count.getAndIncrement(), translatedEntry.toString()); | ||
| 207 | } | ||
| 208 | |||
| 209 | ClassNode node = parsedJar.getClassNode(entry.getFullName()); | ||
| 210 | if (node != null) { | ||
| 211 | ClassNode translatedNode = new ClassNode(); | ||
| 212 | node.accept(new TranslationClassVisitor(translator, Opcodes.ASM5, translatedNode)); | ||
| 213 | return translatedNode; | ||
| 214 | } | ||
| 246 | 215 | ||
| 247 | public String getSource(CompilationUnit sourceTree) { | 216 | return null; |
| 248 | // render the AST into source | 217 | }) |
| 249 | StringWriter buf = new StringWriter(); | 218 | .filter(Objects::nonNull) |
| 250 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | 219 | .collect(Collectors.toMap(n -> n.name, Functions.identity())); |
| 251 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); | ||
| 252 | return buf.toString(); | ||
| 253 | } | 220 | } |
| 254 | 221 | ||
| 255 | public void writeSources(File dirOut, ProgressListener progress) { | 222 | private void decompileClasses(Path outputDirectory, ProgressListener progress, Map<String, ClassNode> translatedClasses) { |
| 256 | // get the classes to decompile | 223 | Collection<ClassNode> decompileClasses = translatedClasses.values().stream() |
| 257 | Set<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses().stream() | 224 | .filter(classNode -> classNode.name.indexOf('$') == -1) |
| 258 | .filter(classEntry -> !classEntry.isInnerClass()) | 225 | .collect(Collectors.toList()); |
| 259 | .collect(Collectors.toSet()); | ||
| 260 | 226 | ||
| 261 | if (progress != null) { | 227 | if (progress != null) { |
| 262 | progress.init(classEntries.size(), "Decompiling classes..."); | 228 | progress.init(decompileClasses.size(), "Decompiling classes..."); |
| 263 | } | 229 | } |
| 264 | 230 | ||
| 265 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening | 231 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening |
| 266 | //synchronized to make sure the parallelStream doesn't CME with the cache | 232 | //synchronized to make sure the parallelStream doesn't CME with the cache |
| 267 | ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); | 233 | ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(translatedClasses::get)); |
| 268 | 234 | ||
| 269 | MetadataSystem metadataSystem = new NoRetryMetadataSystem(typeLoader); | 235 | MetadataSystem metadataSystem = new Deobfuscator.NoRetryMetadataSystem(typeLoader); |
| 270 | metadataSystem.setEagerMethodLoadingEnabled(true);//ensures methods are loaded on classload and prevents race conditions | 236 | |
| 237 | //ensures methods are loaded on classload and prevents race conditions | ||
| 238 | metadataSystem.setEagerMethodLoadingEnabled(true); | ||
| 239 | |||
| 240 | DecompilerSettings settings = SourceProvider.createSettings(); | ||
| 241 | SourceProvider sourceProvider = new SourceProvider(settings, typeLoader, metadataSystem); | ||
| 271 | 242 | ||
| 272 | // DEOBFUSCATE ALL THE THINGS!! @_@ | ||
| 273 | Stopwatch stopwatch = Stopwatch.createStarted(); | ||
| 274 | AtomicInteger count = new AtomicInteger(); | 243 | AtomicInteger count = new AtomicInteger(); |
| 275 | classEntries.parallelStream().forEach(obfClassEntry -> { | 244 | |
| 276 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); | 245 | decompileClasses.parallelStream().forEach(translatedNode -> { |
| 277 | if (progress != null) { | 246 | if (progress != null) { |
| 278 | progress.step(count.getAndIncrement(), deobfClassEntry.toString()); | 247 | progress.step(count.getAndIncrement(), translatedNode.name); |
| 279 | } | 248 | } |
| 280 | 249 | ||
| 281 | try { | 250 | decompileClass(outputDirectory, translatedNode, sourceProvider); |
| 282 | // get the source | 251 | }); |
| 283 | CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader, metadataSystem); | 252 | } |
| 284 | 253 | ||
| 285 | // write the file | 254 | private void decompileClass(Path outputDirectory, ClassNode translatedNode, SourceProvider sourceProvider) { |
| 286 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); | 255 | try { |
| 287 | file.getParentFile().mkdirs(); | 256 | // get the source |
| 288 | try (Writer writer = new BufferedWriter(new FileWriter(file))) { | 257 | CompilationUnit sourceTree = sourceProvider.getSources(translatedNode.name); |
| 289 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | 258 | |
| 290 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); | 259 | Path path = outputDirectory.resolve(translatedNode.name.replace('.', '/') + ".java"); |
| 291 | } | 260 | Files.createDirectories(path.getParent()); |
| 292 | } catch (Throwable t) { | 261 | |
| 293 | // don't crash the whole world here, just log the error and keep going | 262 | try (Writer writer = Files.newBufferedWriter(path)) { |
| 294 | // TODO: set up logback via log4j | 263 | sourceProvider.writeSource(writer, sourceTree); |
| 295 | System.err.println("Unable to decompile class " + deobfClassEntry + " (" + obfClassEntry + ")"); | ||
| 296 | t.printStackTrace(System.err); | ||
| 297 | } | 264 | } |
| 298 | }); | 265 | } catch (Throwable t) { |
| 299 | stopwatch.stop(); | 266 | // don't crash the whole world here, just log the error and keep going |
| 300 | System.out.println("writeSources Done in : " + stopwatch.toString()); | 267 | // TODO: set up logback via log4j |
| 301 | if (progress != null) { | 268 | System.err.println("Unable to decompile class " + translatedNode.name); |
| 302 | progress.step(count.get(), "Done:"); | 269 | t.printStackTrace(System.err); |
| 303 | } | 270 | } |
| 304 | } | 271 | } |
| 305 | 272 | ||
| 306 | public void writeJar(File out, ProgressListener progress) { | 273 | public void writeTransformedJar(File out, ProgressListener progress) { |
| 307 | transformJar(out, progress, createTypeLoader()::transformInto); | 274 | Translator deobfuscator = mapper.getDeobfuscator(); |
| 275 | writeTransformedJar(out, progress, (node, visitor) -> { | ||
| 276 | ClassEntry entry = new ClassEntry(node.name); | ||
| 277 | node.accept(new TranslationClassVisitor(deobfuscator, Opcodes.ASM5, visitor)); | ||
| 278 | return deobfuscator.translate(entry).getFullName(); | ||
| 279 | }); | ||
| 308 | } | 280 | } |
| 309 | 281 | ||
| 310 | public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { | 282 | public void writeTransformedJar(File out, ProgressListener progress, ClassTransformer transformer) { |
| 311 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { | 283 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { |
| 312 | if (progress != null) { | 284 | if (progress != null) { |
| 313 | progress.init(parsedJar.getClassCount(), "Transforming classes..."); | 285 | progress.init(parsedJar.getClassCount(), "Transforming classes..."); |
| 314 | } | 286 | } |
| 315 | 287 | ||
| 316 | AtomicInteger i = new AtomicInteger(); | 288 | AtomicInteger count = new AtomicInteger(); |
| 317 | parsedJar.visitNode(node -> { | 289 | parsedJar.visitNode(node -> { |
| 318 | if (progress != null) { | 290 | if (progress != null) { |
| 319 | progress.step(i.getAndIncrement(), node.name); | 291 | progress.step(count.getAndIncrement(), node.name); |
| 320 | } | 292 | } |
| 321 | 293 | ||
| 322 | try { | 294 | try { |
| @@ -329,10 +301,6 @@ public class Deobfuscator { | |||
| 329 | throw new Error("Unable to transform class " + node.name, t); | 301 | throw new Error("Unable to transform class " + node.name, t); |
| 330 | } | 302 | } |
| 331 | }); | 303 | }); |
| 332 | |||
| 333 | if (progress != null) { | ||
| 334 | progress.step(i.get(), "Done!"); | ||
| 335 | } | ||
| 336 | } catch (IOException ex) { | 304 | } catch (IOException ex) { |
| 337 | throw new Error("Unable to write to Jar file!"); | 305 | throw new Error("Unable to write to Jar file!"); |
| 338 | } | 306 | } |
| @@ -355,7 +323,7 @@ public class Deobfuscator { | |||
| 355 | } | 323 | } |
| 356 | } | 324 | } |
| 357 | 325 | ||
| 358 | public boolean isObfuscatedIdentifier(Entry<?> obfEntry) { | 326 | public boolean isRenamable(Entry<?> obfEntry) { |
| 359 | if (obfEntry instanceof MethodEntry) { | 327 | if (obfEntry instanceof MethodEntry) { |
| 360 | // HACKHACK: Object methods are not obfuscated identifiers | 328 | // HACKHACK: Object methods are not obfuscated identifiers |
| 361 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; | 329 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; |
| @@ -389,12 +357,15 @@ public class Deobfuscator { | |||
| 389 | return this.jarIndex.getEntryIndex().hasEntry(obfEntry); | 357 | return this.jarIndex.getEntryIndex().hasEntry(obfEntry); |
| 390 | } | 358 | } |
| 391 | 359 | ||
| 392 | public boolean isRenameable(EntryReference<Entry<?>, Entry<?>> obfReference) { | 360 | public boolean isRenamable(EntryReference<Entry<?>, Entry<?>> obfReference) { |
| 393 | return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); | 361 | return obfReference.isNamed() && isRenamable(obfReference.getNameableEntry()); |
| 394 | } | 362 | } |
| 395 | 363 | ||
| 396 | public boolean hasDeobfuscatedName(Entry<?> obfEntry) { | 364 | public boolean isRemapped(Entry<?> entry) { |
| 397 | return mapper.hasDeobfMapping(obfEntry); | 365 | EntryResolver resolver = mapper.getObfResolver(); |
| 366 | DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf(); | ||
| 367 | return resolver.resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT).stream() | ||
| 368 | .anyMatch(mappings::contains); | ||
| 398 | } | 369 | } |
| 399 | 370 | ||
| 400 | public void rename(Entry<?> obfEntry, String newName) { | 371 | public void rename(Entry<?> obfEntry, String newName) { |
| @@ -409,21 +380,12 @@ public class Deobfuscator { | |||
| 409 | mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName())); | 380 | mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName())); |
| 410 | } | 381 | } |
| 411 | 382 | ||
| 412 | public static void runCustomTransforms(AstBuilder builder, DecompilerContext context) { | 383 | public <T extends Translatable> T deobfuscate(T translatable) { |
| 413 | List<IAstTransform> transformers = Arrays.asList( | 384 | return mapper.deobfuscate(translatable); |
| 414 | new ObfuscatedEnumSwitchRewriterTransform(context), | ||
| 415 | new VarargsFixer(context), | ||
| 416 | new RemoveObjectCasts(context), | ||
| 417 | new Java8Generics(), | ||
| 418 | new InvalidIdentifierFix() | ||
| 419 | ); | ||
| 420 | for (IAstTransform transform : transformers) { | ||
| 421 | transform.run(builder.getCompilationUnit()); | ||
| 422 | } | ||
| 423 | } | 385 | } |
| 424 | 386 | ||
| 425 | public interface ClassTransformer { | 387 | public interface ClassTransformer { |
| 426 | String transform(ClassNode node, ClassWriter writer); | 388 | String transform(ClassNode node, ClassVisitor visitor); |
| 427 | } | 389 | } |
| 428 | 390 | ||
| 429 | public static class NoRetryMetadataSystem extends MetadataSystem { | 391 | public static class NoRetryMetadataSystem extends MetadataSystem { |
| @@ -448,6 +410,7 @@ public class Deobfuscator { | |||
| 448 | return result; | 410 | return result; |
| 449 | } | 411 | } |
| 450 | 412 | ||
| 413 | @Override | ||
| 451 | public synchronized TypeDefinition resolve(final TypeReference type) { | 414 | public synchronized TypeDefinition resolve(final TypeReference type) { |
| 452 | return super.resolve(type); | 415 | return super.resolve(type); |
| 453 | } | 416 | } |
diff --git a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java deleted file mode 100644 index 24822dd3..00000000 --- a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 5 | import org.objectweb.asm.ClassWriter; | ||
| 6 | import org.objectweb.asm.tree.ClassNode; | ||
| 7 | |||
| 8 | import java.util.List; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * For delegation of TranslatingTypeLoader without needing the subclass the whole thing | ||
| 12 | */ | ||
| 13 | public interface ITranslatingTypeLoader extends ITypeLoader { | ||
| 14 | List<String> getClassNamesToTry(String className); | ||
| 15 | |||
| 16 | List<String> getClassNamesToTry(ClassEntry obfClassEntry); | ||
| 17 | |||
| 18 | String transformInto(ClassNode node, ClassWriter writer); | ||
| 19 | } | ||
diff --git a/src/main/java/cuchaz/enigma/SourceProvider.java b/src/main/java/cuchaz/enigma/SourceProvider.java new file mode 100644 index 00000000..48e5a590 --- /dev/null +++ b/src/main/java/cuchaz/enigma/SourceProvider.java | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 4 | import com.strobel.assembler.metadata.MetadataSystem; | ||
| 5 | import com.strobel.assembler.metadata.TypeDefinition; | ||
| 6 | import com.strobel.assembler.metadata.TypeReference; | ||
| 7 | import com.strobel.decompiler.DecompilerContext; | ||
| 8 | import com.strobel.decompiler.DecompilerSettings; | ||
| 9 | import com.strobel.decompiler.PlainTextOutput; | ||
| 10 | import com.strobel.decompiler.languages.java.JavaOutputVisitor; | ||
| 11 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | ||
| 12 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 13 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | ||
| 14 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 15 | import cuchaz.enigma.utils.Utils; | ||
| 16 | import oml.ast.transformers.*; | ||
| 17 | |||
| 18 | import java.io.StringWriter; | ||
| 19 | import java.io.Writer; | ||
| 20 | import java.util.Arrays; | ||
| 21 | import java.util.List; | ||
| 22 | |||
| 23 | public class SourceProvider { | ||
| 24 | private final DecompilerSettings settings; | ||
| 25 | |||
| 26 | private final ITypeLoader typeLoader; | ||
| 27 | private final MetadataSystem metadataSystem; | ||
| 28 | |||
| 29 | public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader, MetadataSystem metadataSystem) { | ||
| 30 | this.settings = settings; | ||
| 31 | this.typeLoader = typeLoader; | ||
| 32 | this.metadataSystem = metadataSystem; | ||
| 33 | } | ||
| 34 | |||
| 35 | public SourceProvider(DecompilerSettings settings, ITypeLoader typeLoader) { | ||
| 36 | this(settings, typeLoader, new Deobfuscator.NoRetryMetadataSystem(typeLoader)); | ||
| 37 | } | ||
| 38 | |||
| 39 | public static DecompilerSettings createSettings() { | ||
| 40 | DecompilerSettings settings = DecompilerSettings.javaDefaults(); | ||
| 41 | settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); | ||
| 42 | settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); | ||
| 43 | settings.setForceExplicitTypeArguments(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); | ||
| 44 | settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); | ||
| 45 | settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); | ||
| 46 | |||
| 47 | return settings; | ||
| 48 | } | ||
| 49 | |||
| 50 | public CompilationUnit getSources(String name) { | ||
| 51 | TypeReference type = metadataSystem.lookupType(name); | ||
| 52 | if (type == null) { | ||
| 53 | throw new Error(String.format("Unable to find desc: %s", name)); | ||
| 54 | } | ||
| 55 | |||
| 56 | TypeDefinition resolvedType = type.resolve(); | ||
| 57 | |||
| 58 | settings.setTypeLoader(typeLoader); | ||
| 59 | |||
| 60 | // decompile it! | ||
| 61 | DecompilerContext context = new DecompilerContext(); | ||
| 62 | context.setCurrentType(resolvedType); | ||
| 63 | context.setSettings(settings); | ||
| 64 | |||
| 65 | AstBuilder builder = new AstBuilder(context); | ||
| 66 | builder.addType(resolvedType); | ||
| 67 | builder.runTransformations(null); | ||
| 68 | runCustomTransforms(builder, context); | ||
| 69 | |||
| 70 | return builder.getCompilationUnit(); | ||
| 71 | } | ||
| 72 | |||
| 73 | public void writeSource(Writer writer, CompilationUnit sourceTree) { | ||
| 74 | // render the AST into source | ||
| 75 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | ||
| 76 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); | ||
| 77 | } | ||
| 78 | |||
| 79 | public String writeSourceToString(CompilationUnit sourceTree) { | ||
| 80 | StringWriter writer = new StringWriter(); | ||
| 81 | writeSource(writer, sourceTree); | ||
| 82 | return writer.toString(); | ||
| 83 | } | ||
| 84 | |||
| 85 | private static void runCustomTransforms(AstBuilder builder, DecompilerContext context) { | ||
| 86 | List<IAstTransform> transformers = Arrays.asList( | ||
| 87 | new ObfuscatedEnumSwitchRewriterTransform(context), | ||
| 88 | new VarargsFixer(context), | ||
| 89 | new RemoveObjectCasts(context), | ||
| 90 | new Java8Generics(), | ||
| 91 | new InvalidIdentifierFix() | ||
| 92 | ); | ||
| 93 | for (IAstTransform transform : transformers) { | ||
| 94 | transform.run(builder.getCompilationUnit()); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java index 657bee42..f6eee690 100644 --- a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java +++ b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java | |||
| @@ -1,38 +1,19 @@ | |||
| 1 | package cuchaz.enigma; | 1 | package cuchaz.enigma; |
| 2 | 2 | ||
| 3 | import com.strobel.assembler.metadata.Buffer; | 3 | import com.strobel.assembler.metadata.Buffer; |
| 4 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 4 | import com.strobel.assembler.metadata.ITypeLoader; |
| 5 | import org.objectweb.asm.ClassWriter; | ||
| 6 | import org.objectweb.asm.tree.ClassNode; | ||
| 7 | |||
| 8 | import java.util.List; | ||
| 9 | 5 | ||
| 10 | /** | 6 | /** |
| 11 | * Typeloader with synchronized tryLoadType method | 7 | * Typeloader with synchronized tryLoadType method |
| 12 | */ | 8 | */ |
| 13 | public class SynchronizedTypeLoader implements ITranslatingTypeLoader { | 9 | public class SynchronizedTypeLoader implements ITypeLoader { |
| 14 | private final TranslatingTypeLoader delegate; | 10 | private final ITypeLoader delegate; |
| 15 | 11 | ||
| 16 | public SynchronizedTypeLoader(TranslatingTypeLoader delegate) { | 12 | public SynchronizedTypeLoader(ITypeLoader delegate) { |
| 17 | this.delegate = delegate; | 13 | this.delegate = delegate; |
| 18 | } | 14 | } |
| 19 | 15 | ||
| 20 | @Override | 16 | @Override |
| 21 | public List<String> getClassNamesToTry(String className) { | ||
| 22 | return delegate.getClassNamesToTry(className); | ||
| 23 | } | ||
| 24 | |||
| 25 | @Override | ||
| 26 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | ||
| 27 | return delegate.getClassNamesToTry(obfClassEntry); | ||
| 28 | } | ||
| 29 | |||
| 30 | @Override | ||
| 31 | public String transformInto(ClassNode node, ClassWriter writer) { | ||
| 32 | return delegate.transformInto(node, writer); | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public synchronized boolean tryLoadType(String internalName, Buffer buffer) { | 17 | public synchronized boolean tryLoadType(String internalName, Buffer buffer) { |
| 37 | return delegate.tryLoadType(internalName, buffer); | 18 | return delegate.tryLoadType(internalName, buffer); |
| 38 | } | 19 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java b/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java new file mode 100644 index 00000000..991e91d4 --- /dev/null +++ b/src/main/java/cuchaz/enigma/analysis/DropImportAstTransform.java | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | package cuchaz.enigma.analysis; | ||
| 2 | |||
| 3 | import com.strobel.decompiler.languages.java.ast.AstNode; | ||
| 4 | import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; | ||
| 5 | import com.strobel.decompiler.languages.java.ast.ImportDeclaration; | ||
| 6 | import com.strobel.decompiler.languages.java.ast.PackageDeclaration; | ||
| 7 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | ||
| 8 | |||
| 9 | public final class DropImportAstTransform implements IAstTransform { | ||
| 10 | public static final DropImportAstTransform INSTANCE = new DropImportAstTransform(); | ||
| 11 | |||
| 12 | private DropImportAstTransform() { | ||
| 13 | } | ||
| 14 | |||
| 15 | @Override | ||
| 16 | public void run(AstNode compilationUnit) { | ||
| 17 | compilationUnit.acceptVisitor(new Visitor(), null); | ||
| 18 | } | ||
| 19 | |||
| 20 | static class Visitor extends DepthFirstAstVisitor<Void, Void> { | ||
| 21 | @Override | ||
| 22 | public Void visitPackageDeclaration(PackageDeclaration node, Void data) { | ||
| 23 | node.remove(); | ||
| 24 | return null; | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public Void visitImportDeclaration(ImportDeclaration node, Void data) { | ||
| 29 | node.remove(); | ||
| 30 | return null; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java index ad3aceb0..ddcda3ed 100644 --- a/src/main/java/cuchaz/enigma/analysis/ParsedJar.java +++ b/src/main/java/cuchaz/enigma/analysis/ParsedJar.java | |||
| @@ -12,9 +12,12 @@ | |||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.io.ByteStreams; | 14 | import com.google.common.io.ByteStreams; |
| 15 | import cuchaz.enigma.CompiledSource; | ||
| 16 | import cuchaz.enigma.bytecode.translators.LocalVariableFixVisitor; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 16 | import org.objectweb.asm.ClassReader; | 18 | import org.objectweb.asm.ClassReader; |
| 17 | import org.objectweb.asm.ClassVisitor; | 19 | import org.objectweb.asm.ClassVisitor; |
| 20 | import org.objectweb.asm.Opcodes; | ||
| 18 | import org.objectweb.asm.tree.ClassNode; | 21 | import org.objectweb.asm.tree.ClassNode; |
| 19 | 22 | ||
| 20 | import javax.annotation.Nullable; | 23 | import javax.annotation.Nullable; |
| @@ -28,12 +31,12 @@ import java.util.jar.JarEntry; | |||
| 28 | import java.util.jar.JarFile; | 31 | import java.util.jar.JarFile; |
| 29 | import java.util.jar.JarInputStream; | 32 | import java.util.jar.JarInputStream; |
| 30 | 33 | ||
| 31 | public class ParsedJar { | 34 | public class ParsedJar implements CompiledSource { |
| 32 | private final Map<String, byte[]> classBytes; | 35 | private final Map<String, byte[]> classBytes; |
| 33 | private final Map<String, ClassNode> nodeCache = new HashMap<>(); | 36 | private final Map<String, ClassNode> nodeCache = new HashMap<>(); |
| 34 | 37 | ||
| 35 | public ParsedJar(JarFile jar) throws IOException { | 38 | public ParsedJar(JarFile jar) throws IOException { |
| 36 | Map<String, byte[]> uClassBytes = new LinkedHashMap<>();; | 39 | Map<String, byte[]> uClassBytes = new LinkedHashMap<>(); |
| 37 | try { | 40 | try { |
| 38 | // get the jar entries that correspond to classes | 41 | // get the jar entries that correspond to classes |
| 39 | Enumeration<JarEntry> entries = jar.entries(); | 42 | Enumeration<JarEntry> entries = jar.entries(); |
| @@ -93,29 +96,34 @@ public class ParsedJar { | |||
| 93 | return classBytes.size(); | 96 | return classBytes.size(); |
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | public List<ClassEntry> getClassEntries() { | ||
| 97 | List<ClassEntry> entries = new ArrayList<>(classBytes.size()); | ||
| 98 | for (String s : classBytes.keySet()) { | ||
| 99 | entries.add(new ClassEntry(s)); | ||
| 100 | } | ||
| 101 | return entries; | ||
| 102 | } | ||
| 103 | |||
| 104 | @Nullable | 99 | @Nullable |
| 100 | @Override | ||
| 105 | public ClassNode getClassNode(String name) { | 101 | public ClassNode getClassNode(String name) { |
| 106 | return nodeCache.computeIfAbsent(name, (n) -> { | 102 | return nodeCache.computeIfAbsent(name, (n) -> { |
| 107 | byte[] bytes = classBytes.get(name); | 103 | byte[] bytes = classBytes.get(name); |
| 108 | if (bytes == null) { | 104 | if (bytes == null) { |
| 109 | return null; | 105 | return null; |
| 110 | } | 106 | } |
| 107 | |||
| 111 | ClassReader reader = new ClassReader(bytes); | 108 | ClassReader reader = new ClassReader(bytes); |
| 112 | ClassNode node = new ClassNode(); | 109 | ClassNode node = new ClassNode(); |
| 113 | reader.accept(node, 0); | 110 | |
| 111 | LocalVariableFixVisitor visitor = new LocalVariableFixVisitor(Opcodes.ASM5, node); | ||
| 112 | reader.accept(visitor, 0); | ||
| 113 | |||
| 114 | return node; | 114 | return node; |
| 115 | }); | 115 | }); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | public Map<String, byte[]> getClassDataMap() { | 118 | public List<ClassEntry> getClassEntries() { |
| 119 | List<ClassEntry> entries = new ArrayList<>(classBytes.size()); | ||
| 120 | for (String s : classBytes.keySet()) { | ||
| 121 | entries.add(new ClassEntry(s)); | ||
| 122 | } | ||
| 123 | return entries; | ||
| 124 | } | ||
| 125 | |||
| 126 | public Map<String, byte[]> getClassDataMap() { | ||
| 119 | return classBytes; | 127 | return classBytes; |
| 120 | } | 128 | } |
| 121 | } | 129 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index abdec92e..ed12ce37 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java | |||
| @@ -11,17 +11,24 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.analysis; | 12 | package cuchaz.enigma.analysis; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.*; | 14 | import com.google.common.collect.HashMultimap; |
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.google.common.collect.Maps; | ||
| 17 | import com.google.common.collect.Multimap; | ||
| 15 | import com.strobel.decompiler.languages.Region; | 18 | import com.strobel.decompiler.languages.Region; |
| 16 | import com.strobel.decompiler.languages.java.ast.AstNode; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 17 | import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; | 20 | import cuchaz.enigma.gui.SourceRemapper; |
| 18 | import com.strobel.decompiler.languages.java.ast.Identifier; | 21 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 19 | import com.strobel.decompiler.languages.java.ast.TypeDeclaration; | 22 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; |
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | 23 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 21 | 24 | ||
| 22 | import javax.annotation.Nullable; | 25 | import javax.annotation.Nullable; |
| 23 | import java.util.*; | 26 | import java.util.Collection; |
| 27 | import java.util.List; | ||
| 28 | import java.util.Map; | ||
| 29 | import java.util.TreeMap; | ||
| 24 | import java.util.regex.Pattern; | 30 | import java.util.regex.Pattern; |
| 31 | import java.util.stream.Collectors; | ||
| 25 | 32 | ||
| 26 | public class SourceIndex { | 33 | public class SourceIndex { |
| 27 | private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); | 34 | private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); |
| @@ -46,6 +53,13 @@ public class SourceIndex { | |||
| 46 | calculateLineOffsets(); | 53 | calculateLineOffsets(); |
| 47 | } | 54 | } |
| 48 | 55 | ||
| 56 | public static SourceIndex buildIndex(String sourceString, CompilationUnit sourceTree, boolean ignoreBadTokens) { | ||
| 57 | SourceIndex index = new SourceIndex(sourceString, ignoreBadTokens); | ||
| 58 | sourceTree.acceptVisitor(new SourceIndexVisitor(), index); | ||
| 59 | |||
| 60 | return index; | ||
| 61 | } | ||
| 62 | |||
| 49 | private void calculateLineOffsets() { | 63 | private void calculateLineOffsets() { |
| 50 | // count the lines | 64 | // count the lines |
| 51 | this.lineOffsets = Lists.newArrayList(); | 65 | this.lineOffsets = Lists.newArrayList(); |
| @@ -57,32 +71,29 @@ public class SourceIndex { | |||
| 57 | } | 71 | } |
| 58 | } | 72 | } |
| 59 | 73 | ||
| 60 | public void remap(String source, Map<Token, Token> tokenMap) { | 74 | public SourceIndex remapTo(SourceRemapper.Result result) { |
| 61 | this.source = source; | 75 | SourceIndex remapped = new SourceIndex(result.getSource(), ignoreBadTokens); |
| 62 | calculateLineOffsets(); | ||
| 63 | 76 | ||
| 64 | for (Entry<?> entry : Lists.newArrayList(declarationToToken.keySet())) { | 77 | for (Map.Entry<Entry<?>, Token> entry : declarationToToken.entrySet()) { |
| 65 | Token token = declarationToToken.get(entry); | 78 | remapped.declarationToToken.put(entry.getKey(), result.getRemappedToken(entry.getValue())); |
| 66 | declarationToToken.put(entry, tokenMap.getOrDefault(token, token)); | ||
| 67 | } | 79 | } |
| 68 | 80 | ||
| 69 | for (EntryReference<Entry<?>, Entry<?>> ref : referenceToTokens.keySet()) { | 81 | for (Map.Entry<EntryReference<Entry<?>, Entry<?>>, Collection<Token>> entry : referenceToTokens.asMap().entrySet()) { |
| 70 | Collection<Token> oldTokens = referenceToTokens.get(ref); | 82 | EntryReference<Entry<?>, Entry<?>> reference = entry.getKey(); |
| 71 | List<Token> newTokens = new ArrayList<>(oldTokens.size()); | 83 | Collection<Token> oldTokens = entry.getValue(); |
| 72 | 84 | ||
| 73 | for (Token token : oldTokens) { | 85 | Collection<Token> newTokens = oldTokens.stream() |
| 74 | newTokens.add(tokenMap.getOrDefault(token, token)); | 86 | .map(result::getRemappedToken) |
| 75 | } | 87 | .collect(Collectors.toList()); |
| 76 | 88 | ||
| 77 | referenceToTokens.replaceValues(ref, newTokens); | 89 | remapped.referenceToTokens.putAll(reference, newTokens); |
| 78 | } | 90 | } |
| 79 | 91 | ||
| 80 | TreeMap<Token, EntryReference<Entry<?>, Entry<?>>> tokenToReferenceCopy = new TreeMap<>(tokenToReference); | 92 | for (Map.Entry<Token, EntryReference<Entry<?>, Entry<?>>> entry : tokenToReference.entrySet()) { |
| 81 | 93 | remapped.tokenToReference.put(result.getRemappedToken(entry.getKey()), entry.getValue()); | |
| 82 | tokenToReference.clear(); | ||
| 83 | for (Token token : tokenToReferenceCopy.keySet()) { | ||
| 84 | tokenToReference.put(tokenMap.getOrDefault(token, token), tokenToReferenceCopy.get(token)); | ||
| 85 | } | 94 | } |
| 95 | |||
| 96 | return remapped; | ||
| 86 | } | 97 | } |
| 87 | 98 | ||
| 88 | public String getSource() { | 99 | public String getSource() { |
| @@ -164,20 +175,13 @@ public class SourceIndex { | |||
| 164 | } | 175 | } |
| 165 | 176 | ||
| 166 | @Nullable | 177 | @Nullable |
| 167 | public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { | 178 | public EntryReference<Entry<?>, Entry<?>> getReference(Token token) { |
| 168 | if (token == null) { | 179 | if (token == null) { |
| 169 | return null; | 180 | return null; |
| 170 | } | 181 | } |
| 171 | return this.tokenToReference.get(token); | 182 | return this.tokenToReference.get(token); |
| 172 | } | 183 | } |
| 173 | 184 | ||
| 174 | public void replaceDeobfReference(Token token, EntryReference<Entry<?>, Entry<?>> newDeobfReference) { | ||
| 175 | EntryReference<Entry<?>, Entry<?>> oldDeobfReferences = this.tokenToReference.replace(token, newDeobfReference); | ||
| 176 | |||
| 177 | Collection<Token> tokens = this.referenceToTokens.removeAll(oldDeobfReferences); | ||
| 178 | this.referenceToTokens.putAll(newDeobfReference, tokens); | ||
| 179 | } | ||
| 180 | |||
| 181 | public Iterable<Token> referenceTokens() { | 185 | public Iterable<Token> referenceTokens() { |
| 182 | return this.tokenToReference.keySet(); | 186 | return this.tokenToReference.keySet(); |
| 183 | } | 187 | } |
| @@ -190,8 +194,8 @@ public class SourceIndex { | |||
| 190 | return this.declarationToToken.keySet(); | 194 | return this.declarationToToken.keySet(); |
| 191 | } | 195 | } |
| 192 | 196 | ||
| 193 | public Token getDeclarationToken(Entry<?> deobfEntry) { | 197 | public Token getDeclarationToken(Entry<?> entry) { |
| 194 | return this.declarationToToken.get(deobfEntry); | 198 | return this.declarationToToken.get(entry); |
| 195 | } | 199 | } |
| 196 | 200 | ||
| 197 | public int getLineNumber(int pos) { | 201 | public int getLineNumber(int pos) { |
| @@ -215,4 +219,18 @@ public class SourceIndex { | |||
| 215 | // line and col are 1-based | 219 | // line and col are 1-based |
| 216 | return this.lineOffsets.get(line - 1) + col - 1; | 220 | return this.lineOffsets.get(line - 1) + col - 1; |
| 217 | } | 221 | } |
| 222 | |||
| 223 | public void resolveReferences(EntryResolver resolver) { | ||
| 224 | // resolve all the classes in the source references | ||
| 225 | for (Token token : Lists.newArrayList(referenceToTokens.values())) { | ||
| 226 | EntryReference<Entry<?>, Entry<?>> reference = tokenToReference.get(token); | ||
| 227 | EntryReference<Entry<?>, Entry<?>> resolvedReference = resolver.resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST); | ||
| 228 | |||
| 229 | // replace the reference | ||
| 230 | tokenToReference.replace(token, resolvedReference); | ||
| 231 | |||
| 232 | Collection<Token> tokens = referenceToTokens.removeAll(reference); | ||
| 233 | referenceToTokens.putAll(resolvedReference, tokens); | ||
| 234 | } | ||
| 235 | } | ||
| 218 | } | 236 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index 486603ce..a4fe9ee9 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | |||
| @@ -18,21 +18,17 @@ import com.strobel.assembler.metadata.TypeReference; | |||
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.translation.representation.ProcyonEntryFactory; | 20 | import cuchaz.enigma.translation.representation.ProcyonEntryFactory; |
| 21 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | 21 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 23 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 24 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; | 23 | import cuchaz.enigma.translation.representation.entry.FieldDefEntry; |
| 25 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | 24 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; |
| 26 | 25 | ||
| 27 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 26 | public class SourceIndexClassVisitor extends SourceIndexVisitor { |
| 28 | private final ReferencedEntryPool entryPool; | ||
| 29 | private final ProcyonEntryFactory entryFactory; | 27 | private final ProcyonEntryFactory entryFactory; |
| 30 | private ClassDefEntry classEntry; | 28 | private ClassDefEntry classEntry; |
| 31 | 29 | ||
| 32 | public SourceIndexClassVisitor(ReferencedEntryPool entryPool, ClassDefEntry classEntry) { | 30 | public SourceIndexClassVisitor(ClassDefEntry classEntry) { |
| 33 | super(entryPool); | 31 | this.entryFactory = new ProcyonEntryFactory(); |
| 34 | this.entryPool = entryPool; | ||
| 35 | this.entryFactory = new ProcyonEntryFactory(entryPool); | ||
| 36 | this.classEntry = classEntry; | 32 | this.classEntry = classEntry; |
| 37 | } | 33 | } |
| 38 | 34 | ||
| @@ -44,7 +40,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 44 | if (!classEntry.equals(this.classEntry)) { | 40 | if (!classEntry.equals(this.classEntry)) { |
| 45 | // it's a subtype, recurse | 41 | // it's a subtype, recurse |
| 46 | index.addDeclaration(node.getNameToken(), classEntry); | 42 | index.addDeclaration(node.getNameToken(), classEntry); |
| 47 | return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); | 43 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); |
| 48 | } | 44 | } |
| 49 | 45 | ||
| 50 | return recurse(node, index); | 46 | return recurse(node, index); |
| @@ -71,7 +67,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 71 | tokenNode = node.getModifiers().firstOrNullObject(); | 67 | tokenNode = node.getModifiers().firstOrNullObject(); |
| 72 | } | 68 | } |
| 73 | index.addDeclaration(tokenNode, methodEntry); | 69 | index.addDeclaration(tokenNode, methodEntry); |
| 74 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); | 70 | return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); |
| 75 | } | 71 | } |
| 76 | 72 | ||
| 77 | @Override | 73 | @Override |
| @@ -79,7 +75,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 79 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); | 75 | MethodDefinition def = node.getUserData(Keys.METHOD_DEFINITION); |
| 80 | MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); | 76 | MethodDefEntry methodEntry = entryFactory.getMethodDefEntry(def); |
| 81 | index.addDeclaration(node.getNameToken(), methodEntry); | 77 | index.addDeclaration(node.getNameToken(), methodEntry); |
| 82 | return node.acceptVisitor(new SourceIndexMethodVisitor(entryPool, methodEntry), index); | 78 | return node.acceptVisitor(new SourceIndexMethodVisitor(methodEntry), index); |
| 83 | } | 79 | } |
| 84 | 80 | ||
| 85 | @Override | 81 | @Override |
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java index 73db28fb..c4785b67 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java | |||
| @@ -17,8 +17,8 @@ import com.strobel.assembler.metadata.*; | |||
| 17 | import com.strobel.decompiler.ast.Variable; | 17 | import com.strobel.decompiler.ast.Variable; |
| 18 | import com.strobel.decompiler.languages.TextLocation; | 18 | import com.strobel.decompiler.languages.TextLocation; |
| 19 | import com.strobel.decompiler.languages.java.ast.*; | 19 | import com.strobel.decompiler.languages.java.ast.*; |
| 20 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 20 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 21 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 21 | import cuchaz.enigma.translation.representation.*; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.*; | 22 | import cuchaz.enigma.translation.representation.entry.*; |
| 23 | 23 | ||
| 24 | import java.lang.Error; | 24 | import java.lang.Error; |
| @@ -26,16 +26,12 @@ import java.util.HashMap; | |||
| 26 | import java.util.Map; | 26 | import java.util.Map; |
| 27 | 27 | ||
| 28 | public class SourceIndexMethodVisitor extends SourceIndexVisitor { | 28 | public class SourceIndexMethodVisitor extends SourceIndexVisitor { |
| 29 | private final ReferencedEntryPool entryPool; | ||
| 30 | |||
| 31 | private final MethodDefEntry methodEntry; | 29 | private final MethodDefEntry methodEntry; |
| 32 | 30 | ||
| 33 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); | 31 | private Multimap<String, Identifier> unmatchedIdentifier = HashMultimap.create(); |
| 34 | private Map<String, Entry<?>> identifierEntryCache = new HashMap<>(); | 32 | private Map<String, Entry<?>> identifierEntryCache = new HashMap<>(); |
| 35 | 33 | ||
| 36 | public SourceIndexMethodVisitor(ReferencedEntryPool entryPool, MethodDefEntry methodEntry) { | 34 | public SourceIndexMethodVisitor(MethodDefEntry methodEntry) { |
| 37 | super(entryPool); | ||
| 38 | this.entryPool = entryPool; | ||
| 39 | this.methodEntry = methodEntry; | 35 | this.methodEntry = methodEntry; |
| 40 | } | 36 | } |
| 41 | 37 | ||
| @@ -44,10 +40,10 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 44 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 40 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 45 | 41 | ||
| 46 | // get the behavior entry | 42 | // get the behavior entry |
| 47 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 43 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 48 | MethodEntry methodEntry = null; | 44 | MethodEntry methodEntry = null; |
| 49 | if (ref instanceof MethodReference) { | 45 | if (ref instanceof MethodReference) { |
| 50 | methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature()); | 46 | methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature())); |
| 51 | } | 47 | } |
| 52 | if (methodEntry != null) { | 48 | if (methodEntry != null) { |
| 53 | // get the node for the token | 49 | // get the node for the token |
| @@ -80,11 +76,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 80 | throw new Error("Expected a field here! got " + ref); | 76 | throw new Error("Expected a field here! got " + ref); |
| 81 | } | 77 | } |
| 82 | 78 | ||
| 83 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 79 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 84 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); | 80 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(erasedSignature)); |
| 85 | if (fieldEntry == null) { | ||
| 86 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); | ||
| 87 | } | ||
| 88 | index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); | 81 | index.addReference(node.getMemberNameToken(), fieldEntry, this.methodEntry); |
| 89 | } | 82 | } |
| 90 | 83 | ||
| @@ -95,7 +88,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 95 | public Void visitSimpleType(SimpleType node, SourceIndex index) { | 88 | public Void visitSimpleType(SimpleType node, SourceIndex index) { |
| 96 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); | 89 | TypeReference ref = node.getUserData(Keys.TYPE_REFERENCE); |
| 97 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { | 90 | if (node.getIdentifierToken().getStartLocation() != TextLocation.EMPTY) { |
| 98 | ClassEntry classEntry = entryPool.getClass(ref.getInternalName()); | 91 | ClassEntry classEntry = new ClassEntry(ref.getInternalName()); |
| 99 | index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry); | 92 | index.addReference(node.getIdentifierToken(), classEntry, this.methodEntry); |
| 100 | } | 93 | } |
| 101 | 94 | ||
| @@ -108,7 +101,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 108 | int parameterIndex = def.getSlot(); | 101 | int parameterIndex = def.getSlot(); |
| 109 | 102 | ||
| 110 | if (parameterIndex >= 0) { | 103 | if (parameterIndex >= 0) { |
| 111 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, parameterIndex, node.getName(), true); | 104 | TypeDescriptor parameterType = TypeDescriptor.parse(def.getParameterType()); |
| 105 | LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(methodEntry, parameterIndex, node.getName(), true, parameterType); | ||
| 112 | Identifier identifier = node.getNameToken(); | 106 | Identifier identifier = node.getNameToken(); |
| 113 | // cache the argument entry and the identifier | 107 | // cache the argument entry and the identifier |
| 114 | identifierEntryCache.put(identifier.getName(), localVariableEntry); | 108 | identifierEntryCache.put(identifier.getName(), localVariableEntry); |
| @@ -122,11 +116,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 122 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { | 116 | public Void visitIdentifierExpression(IdentifierExpression node, SourceIndex index) { |
| 123 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 117 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 124 | if (ref != null) { | 118 | if (ref != null) { |
| 125 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 119 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 126 | FieldEntry fieldEntry = entryPool.getField(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); | 120 | FieldEntry fieldEntry = new FieldEntry(classEntry, ref.getName(), new TypeDescriptor(ref.getErasedSignature())); |
| 127 | if (fieldEntry == null) { | ||
| 128 | throw new Error("Failed to find field " + ref.getName() + " on " + classEntry.getFullName()); | ||
| 129 | } | ||
| 130 | index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); | 121 | index.addReference(node.getIdentifierToken(), fieldEntry, this.methodEntry); |
| 131 | } else | 122 | } else |
| 132 | this.checkIdentifier(node, index); | 123 | this.checkIdentifier(node, index); |
| @@ -154,13 +145,11 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 154 | @Override | 145 | @Override |
| 155 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { | 146 | public Void visitObjectCreationExpression(ObjectCreationExpression node, SourceIndex index) { |
| 156 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); | 147 | MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); |
| 157 | if (ref != null) { | 148 | if (ref != null && node.getType() instanceof SimpleType) { |
| 158 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 149 | SimpleType simpleTypeNode = (SimpleType) node.getType(); |
| 159 | MethodEntry constructorEntry = entryPool.getMethod(classEntry, "<init>", ref.getErasedSignature()); | 150 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 160 | if (node.getType() instanceof SimpleType) { | 151 | MethodEntry constructorEntry = new MethodEntry(classEntry, "<init>", new MethodDescriptor(ref.getErasedSignature())); |
| 161 | SimpleType simpleTypeNode = (SimpleType) node.getType(); | 152 | index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry); |
| 162 | index.addReference(simpleTypeNode.getIdentifierToken(), constructorEntry, this.methodEntry); | ||
| 163 | } | ||
| 164 | } | 153 | } |
| 165 | 154 | ||
| 166 | return recurse(node, index); | 155 | return recurse(node, index); |
| @@ -181,7 +170,8 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 181 | if (originalVariable != null) { | 170 | if (originalVariable != null) { |
| 182 | int variableIndex = originalVariable.getSlot(); | 171 | int variableIndex = originalVariable.getSlot(); |
| 183 | if (variableIndex >= 0) { | 172 | if (variableIndex >= 0) { |
| 184 | LocalVariableEntry localVariableEntry = new LocalVariableEntry(methodEntry, variableIndex, initializer.getName(), false); | 173 | TypeDescriptor variableType = TypeDescriptor.parse(originalVariable.getVariableType()); |
| 174 | LocalVariableDefEntry localVariableEntry = new LocalVariableDefEntry(methodEntry, variableIndex, initializer.getName(), false, variableType); | ||
| 185 | identifierEntryCache.put(identifier.getName(), localVariableEntry); | 175 | identifierEntryCache.put(identifier.getName(), localVariableEntry); |
| 186 | addDeclarationToUnmatched(identifier.getName(), index); | 176 | addDeclarationToUnmatched(identifier.getName(), index); |
| 187 | index.addDeclaration(identifier, localVariableEntry); | 177 | index.addDeclaration(identifier, localVariableEntry); |
| @@ -199,17 +189,19 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { | |||
| 199 | 189 | ||
| 200 | if (ref instanceof MethodReference) { | 190 | if (ref instanceof MethodReference) { |
| 201 | // get the behavior entry | 191 | // get the behavior entry |
| 202 | ClassEntry classEntry = entryPool.getClass(ref.getDeclaringType().getInternalName()); | 192 | ClassEntry classEntry = new ClassEntry(ref.getDeclaringType().getInternalName()); |
| 203 | MethodEntry methodEntry = null; | 193 | MethodEntry methodEntry = new MethodEntry(classEntry, ref.getName(), new MethodDescriptor(ref.getErasedSignature())); |
| 204 | 194 | ||
| 205 | methodEntry = entryPool.getMethod(classEntry, ref.getName(), ref.getErasedSignature()); | ||
| 206 | // get the node for the token | 195 | // get the node for the token |
| 207 | AstNode tokenNode = node.getMethodNameToken(); | 196 | AstNode methodNameToken = node.getMethodNameToken(); |
| 208 | if (tokenNode == null || (tokenNode.getRegion().getBeginLine() == 0 || tokenNode.getRegion().getEndLine() == 0)){ | 197 | AstNode targetToken = node.getTarget(); |
| 209 | tokenNode = node.getTarget(); | 198 | |
| 199 | if (methodNameToken != null) { | ||
| 200 | index.addReference(methodNameToken, methodEntry, this.methodEntry); | ||
| 210 | } | 201 | } |
| 211 | if (tokenNode != null) { | 202 | |
| 212 | index.addReference(tokenNode, methodEntry, this.methodEntry); | 203 | if (targetToken != null && !(targetToken instanceof ThisReferenceExpression)) { |
| 204 | index.addReference(targetToken, methodEntry.getParent(), this.methodEntry); | ||
| 213 | } | 205 | } |
| 214 | } | 206 | } |
| 215 | 207 | ||
diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java index 564830c6..75a66a2d 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexVisitor.java | |||
| @@ -14,23 +14,16 @@ package cuchaz.enigma.analysis; | |||
| 14 | import com.strobel.assembler.metadata.TypeDefinition; | 14 | import com.strobel.assembler.metadata.TypeDefinition; |
| 15 | import com.strobel.decompiler.languages.java.ast.*; | 15 | import com.strobel.decompiler.languages.java.ast.*; |
| 16 | import com.strobel.decompiler.patterns.Pattern; | 16 | import com.strobel.decompiler.patterns.Pattern; |
| 17 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 19 | 18 | ||
| 20 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { | 19 | public class SourceIndexVisitor implements IAstVisitor<SourceIndex, Void> { |
| 21 | private final ReferencedEntryPool entryPool; | ||
| 22 | |||
| 23 | public SourceIndexVisitor(ReferencedEntryPool entryPool) { | ||
| 24 | this.entryPool = entryPool; | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | 20 | @Override |
| 28 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { | 21 | public Void visitTypeDeclaration(TypeDeclaration node, SourceIndex index) { |
| 29 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); | 22 | TypeDefinition def = node.getUserData(Keys.TYPE_DEFINITION); |
| 30 | ClassDefEntry classEntry = ClassDefEntry.parse(def); | 23 | ClassDefEntry classEntry = ClassDefEntry.parse(def); |
| 31 | index.addDeclaration(node.getNameToken(), classEntry); | 24 | index.addDeclaration(node.getNameToken(), classEntry); |
| 32 | 25 | ||
| 33 | return node.acceptVisitor(new SourceIndexClassVisitor(entryPool, classEntry), index); | 26 | return node.acceptVisitor(new SourceIndexClassVisitor(classEntry), index); |
| 34 | } | 27 | } |
| 35 | 28 | ||
| 36 | protected Void recurse(AstNode node, SourceIndex index) { | 29 | protected Void recurse(AstNode node, SourceIndex index) { |
diff --git a/src/main/java/cuchaz/enigma/analysis/Token.java b/src/main/java/cuchaz/enigma/analysis/Token.java index 14fa7ca4..12e0aa6c 100644 --- a/src/main/java/cuchaz/enigma/analysis/Token.java +++ b/src/main/java/cuchaz/enigma/analysis/Token.java | |||
| @@ -30,12 +30,12 @@ public class Token implements Comparable<Token> { | |||
| 30 | return to.length() - length; | 30 | return to.length() - length; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | public String rename(String source, String to) { | 33 | public void rename(StringBuffer source, String to) { |
| 34 | int oldEnd = this.end; | 34 | int oldEnd = this.end; |
| 35 | this.text = to; | 35 | this.text = to; |
| 36 | this.end = this.start + to.length(); | 36 | this.end = this.start + to.length(); |
| 37 | 37 | ||
| 38 | return source.substring(0, this.start) + to + source.substring(oldEnd); | 38 | source.replace(start, oldEnd, to); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | public Token move(int offset) { | 41 | public Token move(int offset) { |
| @@ -64,7 +64,7 @@ public class Token implements Comparable<Token> { | |||
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | public boolean equals(Token other) { | 66 | public boolean equals(Token other) { |
| 67 | return start == other.start && end == other.end; | 67 | return start == other.start && end == other.end && text.equals(other.text); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | @Override | 70 | @Override |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java index e1903d9f..8f6bd462 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 2 | ||
| 3 | import com.google.common.collect.Maps; | 3 | import com.google.common.collect.Maps; |
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryResolver; | 4 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 6 | import cuchaz.enigma.translation.representation.AccessFlags; | 5 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 7 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 6 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| @@ -10,7 +9,7 @@ import javax.annotation.Nullable; | |||
| 10 | import java.util.Collection; | 9 | import java.util.Collection; |
| 11 | import java.util.Map; | 10 | import java.util.Map; |
| 12 | 11 | ||
| 13 | public class BridgeMethodIndex implements JarIndexer, RemappableIndex { | 12 | public class BridgeMethodIndex implements JarIndexer { |
| 14 | private final EntryIndex entryIndex; | 13 | private final EntryIndex entryIndex; |
| 15 | private final ReferenceIndex referenceIndex; | 14 | private final ReferenceIndex referenceIndex; |
| 16 | 15 | ||
| @@ -22,19 +21,6 @@ public class BridgeMethodIndex implements JarIndexer, RemappableIndex { | |||
| 22 | } | 21 | } |
| 23 | 22 | ||
| 24 | @Override | 23 | @Override |
| 25 | public void remap(Translator translator) { | ||
| 26 | accessedToBridge = translator.translate(accessedToBridge); | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public BridgeMethodIndex remapped(Translator translator) { | ||
| 31 | BridgeMethodIndex index = new BridgeMethodIndex(entryIndex, referenceIndex); | ||
| 32 | index.accessedToBridge = translator.translate(accessedToBridge); | ||
| 33 | |||
| 34 | return index; | ||
| 35 | } | ||
| 36 | |||
| 37 | @Override | ||
| 38 | public void processIndex(EntryResolver resolver) { | 24 | public void processIndex(EntryResolver resolver) { |
| 39 | // look for access and bridged methods | 25 | // look for access and bridged methods |
| 40 | for (MethodEntry methodEntry : entryIndex.getMethods()) { | 26 | for (MethodEntry methodEntry : entryIndex.getMethods()) { |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java index 55bfbc24..773eaf18 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | 1 | package cuchaz.enigma.analysis.index; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.representation.AccessFlags; | 3 | import cuchaz.enigma.translation.representation.AccessFlags; |
| 5 | import cuchaz.enigma.translation.representation.entry.*; | 4 | import cuchaz.enigma.translation.representation.entry.*; |
| 6 | 5 | ||
| @@ -9,29 +8,12 @@ import java.util.Collection; | |||
| 9 | import java.util.HashMap; | 8 | import java.util.HashMap; |
| 10 | import java.util.Map; | 9 | import java.util.Map; |
| 11 | 10 | ||
| 12 | public class EntryIndex implements JarIndexer, RemappableIndex { | 11 | public class EntryIndex implements JarIndexer { |
| 13 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); | 12 | private Map<ClassEntry, AccessFlags> classes = new HashMap<>(); |
| 14 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); | 13 | private Map<FieldEntry, AccessFlags> fields = new HashMap<>(); |
| 15 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); | 14 | private Map<MethodEntry, AccessFlags> methods = new HashMap<>(); |
| 16 | 15 | ||
| 17 | @Override | 16 | @Override |
| 18 | public void remap(Translator translator) { | ||
| 19 | classes = translator.translateKeys(classes); | ||
| 20 | fields = translator.translateKeys(fields); | ||
| 21 | methods = translator.translateKeys(methods); | ||
| 22 | } | ||
| 23 | |||
| 24 | @Override | ||
| 25 | public EntryIndex remapped(Translator translator) { | ||
| 26 | EntryIndex index = new EntryIndex(); | ||
| 27 | index.classes = translator.translateKeys(classes); | ||
| 28 | index.fields = translator.translateKeys(fields); | ||
| 29 | index.methods = translator.translateKeys(methods); | ||
| 30 | |||
| 31 | return index; | ||
| 32 | } | ||
| 33 | |||
| 34 | @Override | ||
| 35 | public void indexClass(ClassDefEntry classEntry) { | 17 | public void indexClass(ClassDefEntry classEntry) { |
| 36 | classes.put(classEntry, classEntry.getAccess()); | 18 | classes.put(classEntry, classEntry.getAccess()); |
| 37 | } | 19 | } |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java index d165cc83..17bed54c 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java | |||
| @@ -14,7 +14,6 @@ package cuchaz.enigma.analysis.index; | |||
| 14 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 15 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 16 | import com.google.common.collect.Sets; | 16 | import com.google.common.collect.Sets; |
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; |
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 20 | 19 | ||
| @@ -22,26 +21,11 @@ import java.util.Collection; | |||
| 22 | import java.util.LinkedList; | 21 | import java.util.LinkedList; |
| 23 | import java.util.Set; | 22 | import java.util.Set; |
| 24 | 23 | ||
| 25 | public class InheritanceIndex implements JarIndexer, RemappableIndex { | 24 | public class InheritanceIndex implements JarIndexer { |
| 26 | private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); | 25 | private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); |
| 27 | private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); | 26 | private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); |
| 28 | 27 | ||
| 29 | @Override | 28 | @Override |
| 30 | public void remap(Translator translator) { | ||
| 31 | classChildren = translator.translate(classChildren); | ||
| 32 | classParents = translator.translate(classParents); | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public InheritanceIndex remapped(Translator translator) { | ||
| 37 | InheritanceIndex index = new InheritanceIndex(); | ||
| 38 | index.classParents = translator.translate(classParents); | ||
| 39 | index.classChildren = translator.translate(classChildren); | ||
| 40 | |||
| 41 | return index; | ||
| 42 | } | ||
| 43 | |||
| 44 | @Override | ||
| 45 | public void indexClass(ClassDefEntry classEntry) { | 29 | public void indexClass(ClassDefEntry classEntry) { |
| 46 | ClassEntry superClass = classEntry.getSuperClass(); | 30 | ClassEntry superClass = classEntry.getSuperClass(); |
| 47 | if (superClass != null) { | 31 | if (superClass != null) { |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java index 0880244a..9b21cbae 100644 --- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java | |||
| @@ -14,7 +14,6 @@ package cuchaz.enigma.analysis.index; | |||
| 14 | import com.google.common.collect.HashMultimap; | 14 | import com.google.common.collect.HashMultimap; |
| 15 | import com.google.common.collect.Multimap; | 15 | import com.google.common.collect.Multimap; |
| 16 | import cuchaz.enigma.analysis.ParsedJar; | 16 | import cuchaz.enigma.analysis.ParsedJar; |
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | 17 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 19 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; | 18 | import cuchaz.enigma.translation.mapping.IndexEntryResolver; |
| 20 | import cuchaz.enigma.translation.representation.entry.*; | 19 | import cuchaz.enigma.translation.representation.entry.*; |
| @@ -25,7 +24,7 @@ import java.util.Arrays; | |||
| 25 | import java.util.Collection; | 24 | import java.util.Collection; |
| 26 | import java.util.function.Consumer; | 25 | import java.util.function.Consumer; |
| 27 | 26 | ||
| 28 | public class JarIndex implements JarIndexer, RemappableIndex { | 27 | public class JarIndex implements JarIndexer { |
| 29 | private final EntryIndex entryIndex; | 28 | private final EntryIndex entryIndex; |
| 30 | private final InheritanceIndex inheritanceIndex; | 29 | private final InheritanceIndex inheritanceIndex; |
| 31 | private final ReferenceIndex referenceIndex; | 30 | private final ReferenceIndex referenceIndex; |
| @@ -53,25 +52,6 @@ public class JarIndex implements JarIndexer, RemappableIndex { | |||
| 53 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); | 52 | return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); |
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | @Override | ||
| 57 | public void remap(Translator translator) { | ||
| 58 | entryIndex.remap(translator); | ||
| 59 | inheritanceIndex.remap(translator); | ||
| 60 | bridgeMethodIndex.remap(translator); | ||
| 61 | } | ||
| 62 | |||
| 63 | @Override | ||
| 64 | public JarIndex remapped(Translator translator) { | ||
| 65 | EntryIndex entryIndex = this.entryIndex.remapped(translator); | ||
| 66 | InheritanceIndex inheritanceIndex = this.inheritanceIndex.remapped(translator); | ||
| 67 | BridgeMethodIndex bridgeMethodIndex = this.bridgeMethodIndex.remapped(translator); | ||
| 68 | |||
| 69 | JarIndex remappedIndex = new JarIndex(entryIndex, inheritanceIndex, this.referenceIndex, bridgeMethodIndex); | ||
| 70 | remappedIndex.methodImplementations.putAll(methodImplementations); | ||
| 71 | |||
| 72 | return remappedIndex; | ||
| 73 | } | ||
| 74 | |||
| 75 | public void indexJar(ParsedJar jar, Consumer<String> progress) { | 55 | public void indexJar(ParsedJar jar, Consumer<String> progress) { |
| 76 | progress.accept("Indexing entries (1/3)"); | 56 | progress.accept("Indexing entries (1/3)"); |
| 77 | jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); | 57 | jar.visitReader(name -> new IndexClassVisitor(this, Opcodes.ASM5), ClassReader.SKIP_CODE); |
diff --git a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java b/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java deleted file mode 100644 index 537e7726..00000000 --- a/src/main/java/cuchaz/enigma/analysis/index/RemappableIndex.java +++ /dev/null | |||
| @@ -1,9 +0,0 @@ | |||
| 1 | package cuchaz.enigma.analysis.index; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | |||
| 5 | public interface RemappableIndex { | ||
| 6 | void remap(Translator translator); | ||
| 7 | |||
| 8 | RemappableIndex remapped(Translator translator); | ||
| 9 | } | ||
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java new file mode 100644 index 00000000..16dbba1b --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableFixVisitor.java | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | ||
| 2 | |||
| 3 | import com.google.common.base.CharMatcher; | ||
| 4 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.ClassDefEntry; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.MethodDefEntry; | ||
| 7 | import org.objectweb.asm.ClassVisitor; | ||
| 8 | import org.objectweb.asm.Label; | ||
| 9 | import org.objectweb.asm.MethodVisitor; | ||
| 10 | |||
| 11 | import java.util.List; | ||
| 12 | |||
| 13 | public class LocalVariableFixVisitor extends ClassVisitor { | ||
| 14 | private ClassDefEntry ownerEntry; | ||
| 15 | |||
| 16 | public LocalVariableFixVisitor(int api, ClassVisitor visitor) { | ||
| 17 | super(api, visitor); | ||
| 18 | } | ||
| 19 | |||
| 20 | @Override | ||
| 21 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||
| 22 | ownerEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces); | ||
| 23 | super.visit(version, access, name, signature, superName, interfaces); | ||
| 24 | } | ||
| 25 | |||
| 26 | @Override | ||
| 27 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { | ||
| 28 | MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature); | ||
| 29 | return new Method(api, methodEntry, super.visitMethod(access, name, descriptor, signature, exceptions)); | ||
| 30 | } | ||
| 31 | |||
| 32 | private class Method extends MethodVisitor { | ||
| 33 | private final MethodDefEntry methodEntry; | ||
| 34 | private boolean hasLvt; | ||
| 35 | |||
| 36 | Method(int api, MethodDefEntry methodEntry, MethodVisitor visitor) { | ||
| 37 | super(api, visitor); | ||
| 38 | this.methodEntry = methodEntry; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { | ||
| 43 | hasLvt = true; | ||
| 44 | |||
| 45 | String translatedName = name; | ||
| 46 | |||
| 47 | if (isInvalidName(name)) { | ||
| 48 | int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index); | ||
| 49 | |||
| 50 | if (argumentIndex >= 0) { | ||
| 51 | List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); | ||
| 52 | boolean argument = argumentIndex < arguments.size(); | ||
| 53 | if (argument) { | ||
| 54 | translatedName = "arg" + (argumentIndex + 1); | ||
| 55 | } else { | ||
| 56 | translatedName = "var" + (argumentIndex + 1); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | super.visitLocalVariable(translatedName, desc, signature, start, end, index); | ||
| 62 | } | ||
| 63 | |||
| 64 | private boolean isInvalidName(String name) { | ||
| 65 | return !CharMatcher.ascii().matchesAllOf(name); | ||
| 66 | } | ||
| 67 | |||
| 68 | @Override | ||
| 69 | public void visitEnd() { | ||
| 70 | if (!hasLvt) { | ||
| 71 | List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); | ||
| 72 | for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { | ||
| 73 | super.visitParameter("arg" + (argumentIndex + 1), 0); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | super.visitEnd(); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java index 53d09bb6..e4c41d32 100644 --- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java +++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationClassVisitor.java | |||
| @@ -13,7 +13,6 @@ package cuchaz.enigma.bytecode.translators; | |||
| 13 | 13 | ||
| 14 | import cuchaz.enigma.translation.Translator; | 14 | import cuchaz.enigma.translation.Translator; |
| 15 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 15 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 16 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 17 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 16 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 18 | import cuchaz.enigma.translation.representation.entry.*; | 17 | import cuchaz.enigma.translation.representation.entry.*; |
| 19 | import org.objectweb.asm.*; | 18 | import org.objectweb.asm.*; |
| @@ -22,14 +21,12 @@ import java.util.Arrays; | |||
| 22 | 21 | ||
| 23 | public class TranslationClassVisitor extends ClassVisitor { | 22 | public class TranslationClassVisitor extends ClassVisitor { |
| 24 | private final Translator translator; | 23 | private final Translator translator; |
| 25 | private final ReferencedEntryPool entryPool; | ||
| 26 | 24 | ||
| 27 | private ClassDefEntry obfClassEntry; | 25 | private ClassDefEntry obfClassEntry; |
| 28 | 26 | ||
| 29 | public TranslationClassVisitor(Translator translator, ReferencedEntryPool entryPool, int api, ClassVisitor cv) { | 27 | public TranslationClassVisitor(Translator translator, int api, ClassVisitor cv) { |
| 30 | super(api, cv); | 28 | super(api, cv); |
| 31 | this.translator = translator; | 29 | this.translator = translator; |
| 32 | this.entryPool = entryPool; | ||
| 33 | } | 30 | } |
| 34 | 31 | ||
| 35 | @Override | 32 | @Override |
| @@ -57,7 +54,7 @@ public class TranslationClassVisitor extends ClassVisitor { | |||
| 57 | MethodDefEntry translatedEntry = translator.translate(entry); | 54 | MethodDefEntry translatedEntry = translator.translate(entry); |
| 58 | String[] translatedExceptions = new String[exceptions.length]; | 55 | String[] translatedExceptions = new String[exceptions.length]; |
| 59 | for (int i = 0; i < exceptions.length; i++) { | 56 | for (int i = 0; i < exceptions.length; i++) { |
| 60 | translatedExceptions[i] = translator.translate(entryPool.getClass(exceptions[i])).getFullName(); | 57 | translatedExceptions[i] = translator.translate(new ClassEntry(exceptions[i])).getFullName(); |
| 61 | } | 58 | } |
| 62 | MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); | 59 | MethodVisitor mv = super.visitMethod(translatedEntry.getAccess().getFlags(), translatedEntry.getName(), translatedEntry.getDesc().toString(), translatedEntry.getSignature().toString(), translatedExceptions); |
| 63 | return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); | 60 | return new TranslationMethodVisitor(translator, obfClassEntry, entry, api, mv); |
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java index a5a33e69..c824265f 100644 --- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java +++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationMethodVisitor.java | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | package cuchaz.enigma.bytecode.translators; | 1 | package cuchaz.enigma.bytecode.translators; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.LocalNameGenerator; | ||
| 3 | import cuchaz.enigma.translation.Translator; | 4 | import cuchaz.enigma.translation.Translator; |
| 4 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; | 5 | import cuchaz.enigma.translation.representation.MethodDescriptor; |
| 6 | import cuchaz.enigma.translation.representation.Signature; | 6 | import cuchaz.enigma.translation.representation.Signature; |
| 7 | import cuchaz.enigma.translation.representation.TypeDescriptor; | 7 | import cuchaz.enigma.translation.representation.TypeDescriptor; |
| 8 | import cuchaz.enigma.translation.representation.entry.*; | 8 | import cuchaz.enigma.translation.representation.entry.*; |
| 9 | import org.objectweb.asm.*; | 9 | import org.objectweb.asm.*; |
| 10 | 10 | ||
| 11 | import java.util.Collection; | ||
| 12 | import java.util.List; | 11 | import java.util.List; |
| 13 | import java.util.Locale; | ||
| 14 | import java.util.stream.Collectors; | 12 | import java.util.stream.Collectors; |
| 15 | 13 | ||
| 16 | public class TranslationMethodVisitor extends MethodVisitor { | 14 | public class TranslationMethodVisitor extends MethodVisitor { |
| @@ -84,40 +82,6 @@ public class TranslationMethodVisitor extends MethodVisitor { | |||
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | @Override | 84 | @Override |
| 87 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { | ||
| 88 | hasParameterMeta = true; | ||
| 89 | |||
| 90 | String translatedSignature = translator.translate(Signature.createTypedSignature(signature)).toString(); | ||
| 91 | int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index); | ||
| 92 | |||
| 93 | if (argumentIndex >= 0) { | ||
| 94 | LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, true, new TypeDescriptor(desc)); | ||
| 95 | LocalVariableDefEntry translatedEntry = translator.translate(entry); | ||
| 96 | String translatedName = translatedEntry.getName(); | ||
| 97 | |||
| 98 | if (translatedName.equals(entry.getName())) { | ||
| 99 | List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); | ||
| 100 | List<TypeDescriptor> translatedArguments = arguments.stream() | ||
| 101 | .map(translator::translate) | ||
| 102 | .collect(Collectors.toList()); | ||
| 103 | |||
| 104 | boolean argument = argumentIndex < arguments.size(); | ||
| 105 | if (argument) { | ||
| 106 | translatedName = inferArgumentName(argumentIndex, translatedEntry.getDesc(), translatedArguments); | ||
| 107 | } else { | ||
| 108 | translatedName = inferLocalVariableName(argumentIndex, translatedEntry.getDesc()); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index); | ||
| 113 | } else { | ||
| 114 | // Handle "this" variable | ||
| 115 | TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc)); | ||
| 116 | super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | @Override | ||
| 121 | public void visitTypeInsn(int opcode, String type) { | 85 | public void visitTypeInsn(int opcode, String type) { |
| 122 | ClassEntry translatedEntry = translator.translate(new ClassEntry(type)); | 86 | ClassEntry translatedEntry = translator.translate(new ClassEntry(type)); |
| 123 | super.visitTypeInsn(opcode, translatedEntry.getFullName()); | 87 | super.visitTypeInsn(opcode, translatedEntry.getFullName()); |
| @@ -154,10 +118,44 @@ public class TranslationMethodVisitor extends MethodVisitor { | |||
| 154 | } | 118 | } |
| 155 | 119 | ||
| 156 | @Override | 120 | @Override |
| 121 | public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { | ||
| 122 | hasParameterMeta = true; | ||
| 123 | |||
| 124 | String translatedSignature = translator.translate(Signature.createTypedSignature(signature)).toString(); | ||
| 125 | int argumentIndex = methodEntry.getArgumentIndex(ownerEntry, index); | ||
| 126 | |||
| 127 | if (argumentIndex >= 0) { | ||
| 128 | LocalVariableDefEntry entry = new LocalVariableDefEntry(methodEntry, index, name, true, new TypeDescriptor(desc)); | ||
| 129 | LocalVariableDefEntry translatedEntry = translator.translate(entry); | ||
| 130 | String translatedName = translatedEntry.getName(); | ||
| 131 | |||
| 132 | if (translatedName.equals(entry.getName())) { | ||
| 133 | List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); | ||
| 134 | List<TypeDescriptor> translatedArguments = arguments.stream() | ||
| 135 | .map(translator::translate) | ||
| 136 | .collect(Collectors.toList()); | ||
| 137 | |||
| 138 | boolean argument = argumentIndex < arguments.size(); | ||
| 139 | if (argument) { | ||
| 140 | translatedName = LocalNameGenerator.generateArgumentName(argumentIndex, translatedEntry.getDesc(), translatedArguments); | ||
| 141 | } else { | ||
| 142 | translatedName = LocalNameGenerator.generateLocalVariableName(argumentIndex, translatedEntry.getDesc()); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | super.visitLocalVariable(translatedName, translatedEntry.getDesc().toString(), translatedSignature, start, end, index); | ||
| 147 | } else { | ||
| 148 | // Handle "this" variable | ||
| 149 | TypeDescriptor translatedDesc = translator.translate(new TypeDescriptor(desc)); | ||
| 150 | super.visitLocalVariable(name, translatedDesc.toString(), translatedSignature, start, end, index); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | @Override | ||
| 157 | public void visitEnd() { | 155 | public void visitEnd() { |
| 158 | // If we didn't receive any parameter metadata, generate it | 156 | // If we didn't receive any parameter metadata, generate it |
| 159 | if (!hasParameterMeta) { | 157 | if (!hasParameterMeta) { |
| 160 | List<TypeDescriptor> arguments = methodEntry.getDesc().getArgumentDescs(); | 158 | List<TypeDescriptor> arguments = translator.translate(methodEntry.getDesc()).getArgumentDescs(); |
| 161 | int offset = ((methodEntry.getAccess().getFlags() & Opcodes.ACC_ABSTRACT) != 0) ? 1 : 0; | 159 | int offset = ((methodEntry.getAccess().getFlags() & Opcodes.ACC_ABSTRACT) != 0) ? 1 : 0; |
| 162 | 160 | ||
| 163 | for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { | 161 | for (int argumentIndex = 0; argumentIndex < arguments.size(); argumentIndex++) { |
| @@ -165,7 +163,7 @@ public class TranslationMethodVisitor extends MethodVisitor { | |||
| 165 | LocalVariableEntry translatedEntry = translator.translate(entry); | 163 | LocalVariableEntry translatedEntry = translator.translate(entry); |
| 166 | String translatedName = translatedEntry.getName(); | 164 | String translatedName = translatedEntry.getName(); |
| 167 | if (translatedName.equals(entry.getName())) { | 165 | if (translatedName.equals(entry.getName())) { |
| 168 | super.visitParameter(inferArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0); | 166 | super.visitParameter(LocalNameGenerator.generateArgumentName(argumentIndex, arguments.get(argumentIndex), arguments), 0); |
| 169 | } else { | 167 | } else { |
| 170 | super.visitParameter(translatedName, 0); | 168 | super.visitParameter(translatedName, 0); |
| 171 | } | 169 | } |
| @@ -175,39 +173,4 @@ public class TranslationMethodVisitor extends MethodVisitor { | |||
| 175 | } | 173 | } |
| 176 | super.visitEnd(); | 174 | super.visitEnd(); |
| 177 | } | 175 | } |
| 178 | |||
| 179 | private String inferArgumentName(int index, TypeDescriptor desc, Collection<TypeDescriptor> arguments) { | ||
| 180 | boolean uniqueType = arguments.stream().filter(desc::equals).count() <= 1; | ||
| 181 | String translatedName; | ||
| 182 | int nameIndex = index + 1; | ||
| 183 | StringBuilder nameBuilder = new StringBuilder(getTypeName(desc)); | ||
| 184 | if (!uniqueType || NameValidator.isReserved(nameBuilder.toString())) { | ||
| 185 | nameBuilder.append(nameIndex); | ||
| 186 | } | ||
| 187 | translatedName = nameBuilder.toString(); | ||
| 188 | return translatedName; | ||
| 189 | } | ||
| 190 | |||
| 191 | private String inferLocalVariableName(int index, TypeDescriptor desc) { | ||
| 192 | int nameIndex = index + 1; | ||
| 193 | return getTypeName(desc) + nameIndex; | ||
| 194 | } | ||
| 195 | |||
| 196 | private String getTypeName(TypeDescriptor desc) { | ||
| 197 | // Unfortunately each of these have different name getters, so they have different code paths | ||
| 198 | if (desc.isPrimitive()) { | ||
| 199 | TypeDescriptor.Primitive argCls = desc.getPrimitive(); | ||
| 200 | return argCls.name().toLowerCase(Locale.ROOT); | ||
| 201 | } else if (desc.isArray()) { | ||
| 202 | // List types would require this whole block again, so just go with aListx | ||
| 203 | return "arr"; | ||
| 204 | } else if (desc.isType()) { | ||
| 205 | String typeName = desc.getTypeEntry().getSimpleName().replace("$", ""); | ||
| 206 | typeName = typeName.substring(0, 1).toLowerCase(Locale.ROOT) + typeName.substring(1); | ||
| 207 | return typeName; | ||
| 208 | } else { | ||
| 209 | System.err.println("Encountered invalid argument type descriptor " + desc.toString()); | ||
| 210 | return "var"; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | 176 | } |
diff --git a/src/main/java/cuchaz/enigma/config/Config.java b/src/main/java/cuchaz/enigma/config/Config.java index 55f867ee..6f5a337c 100644 --- a/src/main/java/cuchaz/enigma/config/Config.java +++ b/src/main/java/cuchaz/enigma/config/Config.java | |||
| @@ -74,7 +74,6 @@ public class Config { | |||
| 74 | config.proposedColorOutline = new AlphaColorEntry(0x000000, 0.15f); | 74 | config.proposedColorOutline = new AlphaColorEntry(0x000000, 0.15f); |
| 75 | config.deobfuscatedColor = new AlphaColorEntry(0xDCFFDC, 1.0f); | 75 | config.deobfuscatedColor = new AlphaColorEntry(0xDCFFDC, 1.0f); |
| 76 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50A050, 1.0f); | 76 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50A050, 1.0f); |
| 77 | config.otherColorOutline = new AlphaColorEntry(0xB4B4B4, 1.0f); | ||
| 78 | config.editorBackground = 0xFFFFFF; | 77 | config.editorBackground = 0xFFFFFF; |
| 79 | config.highlightColor = 0x3333EE; | 78 | config.highlightColor = 0x3333EE; |
| 80 | config.caretColor = 0x000000; | 79 | config.caretColor = 0x000000; |
| @@ -98,7 +97,6 @@ public class Config { | |||
| 98 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50FA7B, 0.5f); | 97 | config.deobfuscatedColorOutline = new AlphaColorEntry(0x50FA7B, 0.5f); |
| 99 | config.proposedColor = new AlphaColorEntry(0x606366, 0.3f); | 98 | config.proposedColor = new AlphaColorEntry(0x606366, 0.3f); |
| 100 | config.proposedColorOutline = new AlphaColorEntry(0x606366, 0.5f); | 99 | config.proposedColorOutline = new AlphaColorEntry(0x606366, 0.5f); |
| 101 | config.otherColorOutline = new AlphaColorEntry(0xB4B4B4, 0.0f); | ||
| 102 | config.editorBackground = 0x282A36; | 100 | config.editorBackground = 0x282A36; |
| 103 | config.highlightColor = 0xFF79C6; | 101 | config.highlightColor = 0xFF79C6; |
| 104 | config.caretColor = 0xF8F8F2; | 102 | config.caretColor = 0xF8F8F2; |
| @@ -128,7 +126,6 @@ public class Config { | |||
| 128 | public AlphaColorEntry proposedColorOutline; | 126 | public AlphaColorEntry proposedColorOutline; |
| 129 | public AlphaColorEntry deobfuscatedColor; | 127 | public AlphaColorEntry deobfuscatedColor; |
| 130 | public AlphaColorEntry deobfuscatedColorOutline; | 128 | public AlphaColorEntry deobfuscatedColorOutline; |
| 131 | public AlphaColorEntry otherColorOutline; | ||
| 132 | 129 | ||
| 133 | public Integer editorBackground; | 130 | public Integer editorBackground; |
| 134 | public Integer highlightColor; | 131 | public Integer highlightColor; |
| @@ -198,12 +195,14 @@ public class Config { | |||
| 198 | } | 195 | } |
| 199 | 196 | ||
| 200 | private static class IntSerializer implements JsonSerializer<Integer> { | 197 | private static class IntSerializer implements JsonSerializer<Integer> { |
| 198 | @Override | ||
| 201 | public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { | 199 | public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) { |
| 202 | return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase()); | 200 | return new JsonPrimitive("#" + Integer.toHexString(src).toUpperCase()); |
| 203 | } | 201 | } |
| 204 | } | 202 | } |
| 205 | 203 | ||
| 206 | private static class IntDeserializer implements JsonDeserializer<Integer> { | 204 | private static class IntDeserializer implements JsonDeserializer<Integer> { |
| 205 | @Override | ||
| 207 | public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { | 206 | public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { |
| 208 | return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); | 207 | return (int) Long.parseLong(json.getAsString().replace("#", ""), 16); |
| 209 | } | 208 | } |
diff --git a/src/main/java/cuchaz/enigma/config/Themes.java b/src/main/java/cuchaz/enigma/config/Themes.java index 00324f47..400ea3f0 100644 --- a/src/main/java/cuchaz/enigma/config/Themes.java +++ b/src/main/java/cuchaz/enigma/config/Themes.java | |||
| @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; | |||
| 4 | import cuchaz.enigma.gui.Gui; | 4 | import cuchaz.enigma.gui.Gui; |
| 5 | import cuchaz.enigma.gui.EnigmaSyntaxKit; | 5 | import cuchaz.enigma.gui.EnigmaSyntaxKit; |
| 6 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; | 6 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; |
| 7 | import cuchaz.enigma.gui.highlight.TokenHighlightType; | ||
| 7 | import de.sciss.syntaxpane.DefaultSyntaxKit; | 8 | import de.sciss.syntaxpane.DefaultSyntaxKit; |
| 8 | 9 | ||
| 9 | import javax.swing.*; | 10 | import javax.swing.*; |
| @@ -17,10 +18,11 @@ public class Themes { | |||
| 17 | } | 18 | } |
| 18 | 19 | ||
| 19 | public static void updateTheme(Gui gui) { | 20 | public static void updateTheme(Gui gui) { |
| 20 | Config.getInstance().lookAndFeel.apply(Config.getInstance()); | 21 | Config config = Config.getInstance(); |
| 21 | Config.getInstance().lookAndFeel.setGlobalLAF(); | 22 | config.lookAndFeel.apply(config); |
| 23 | config.lookAndFeel.setGlobalLAF(); | ||
| 22 | try { | 24 | try { |
| 23 | Config.getInstance().saveConfig(); | 25 | config.saveConfig(); |
| 24 | } catch (IOException e) { | 26 | } catch (IOException e) { |
| 25 | e.printStackTrace(); | 27 | e.printStackTrace(); |
| 26 | } | 28 | } |
| @@ -28,12 +30,11 @@ public class Themes { | |||
| 28 | DefaultSyntaxKit.initKit(); | 30 | DefaultSyntaxKit.initKit(); |
| 29 | DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName()); | 31 | DefaultSyntaxKit.registerContentType("text/enigma-sources", EnigmaSyntaxKit.class.getName()); |
| 30 | gui.boxHighlightPainters = ImmutableMap.of( | 32 | gui.boxHighlightPainters = ImmutableMap.of( |
| 31 | "obfuscated", BoxHighlightPainter.create(Config.getInstance().obfuscatedColor, Config.getInstance().obfuscatedColorOutline), | 33 | TokenHighlightType.OBFUSCATED, BoxHighlightPainter.create(config.obfuscatedColor, config.obfuscatedColorOutline), |
| 32 | "proposed", BoxHighlightPainter.create(Config.getInstance().proposedColor, Config.getInstance().proposedColorOutline), | 34 | TokenHighlightType.PROPOSED, BoxHighlightPainter.create(config.proposedColor, config.proposedColorOutline), |
| 33 | "deobfuscated", BoxHighlightPainter.create(Config.getInstance().deobfuscatedColor, Config.getInstance().deobfuscatedColorOutline), | 35 | TokenHighlightType.DEOBFUSCATED, BoxHighlightPainter.create(config.deobfuscatedColor, config.deobfuscatedColorOutline) |
| 34 | "other", BoxHighlightPainter.create(null, Config.getInstance().otherColorOutline) | ||
| 35 | ); | 36 | ); |
| 36 | gui.setEditorTheme(Config.getInstance().lookAndFeel); | 37 | gui.setEditorTheme(config.lookAndFeel); |
| 37 | SwingUtilities.updateComponentTreeUI(gui.getFrame()); | 38 | SwingUtilities.updateComponentTreeUI(gui.getFrame()); |
| 38 | } | 39 | } |
| 39 | } | 40 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java index c3b7288c..39d0333b 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java | |||
| @@ -17,9 +17,11 @@ import com.google.common.collect.Maps; | |||
| 17 | import com.google.common.collect.Multimap; | 17 | import com.google.common.collect.Multimap; |
| 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; | 18 | import cuchaz.enigma.gui.node.ClassSelectorClassNode; |
| 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; | 19 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; |
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.throwables.IllegalNameException; | 20 | import cuchaz.enigma.throwables.IllegalNameException; |
| 21 | import cuchaz.enigma.translation.Translator; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 22 | 23 | ||
| 24 | import javax.annotation.Nullable; | ||
| 23 | import javax.swing.*; | 25 | import javax.swing.*; |
| 24 | import javax.swing.event.CellEditorListener; | 26 | import javax.swing.event.CellEditorListener; |
| 25 | import javax.swing.event.ChangeEvent; | 27 | import javax.swing.event.ChangeEvent; |
| @@ -27,21 +29,26 @@ import javax.swing.tree.*; | |||
| 27 | import java.awt.event.MouseAdapter; | 29 | import java.awt.event.MouseAdapter; |
| 28 | import java.awt.event.MouseEvent; | 30 | import java.awt.event.MouseEvent; |
| 29 | import java.util.*; | 31 | import java.util.*; |
| 30 | import java.util.List; | ||
| 31 | 32 | ||
| 32 | public class ClassSelector extends JTree { | 33 | public class ClassSelector extends JTree { |
| 33 | 34 | ||
| 34 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); | 35 | public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); |
| 36 | |||
| 37 | private final GuiController controller; | ||
| 38 | |||
| 35 | private DefaultMutableTreeNode rootNodes; | 39 | private DefaultMutableTreeNode rootNodes; |
| 36 | private ClassSelectionListener selectionListener; | 40 | private ClassSelectionListener selectionListener; |
| 37 | private RenameSelectionListener renameSelectionListener; | 41 | private RenameSelectionListener renameSelectionListener; |
| 38 | private Comparator<ClassEntry> comparator; | 42 | private Comparator<ClassEntry> comparator; |
| 39 | 43 | ||
| 44 | private final Map<ClassEntry, ClassEntry> displayedObfToDeobf = new HashMap<>(); | ||
| 45 | |||
| 40 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { | 46 | public ClassSelector(Gui gui, Comparator<ClassEntry> comparator, boolean isRenamable) { |
| 41 | this.comparator = comparator; | 47 | this.comparator = comparator; |
| 48 | this.controller = gui.getController(); | ||
| 42 | 49 | ||
| 43 | // configure the tree control | 50 | // configure the tree control |
| 44 | setEditable(gui != null); | 51 | setEditable(true); |
| 45 | setRootVisible(false); | 52 | setRootVisible(false); |
| 46 | setShowsRootHandles(false); | 53 | setShowsRootHandles(false); |
| 47 | setModel(null); | 54 | setModel(null); |
| @@ -55,66 +62,64 @@ public class ClassSelector extends JTree { | |||
| 55 | TreePath path = getSelectionPath(); | 62 | TreePath path = getSelectionPath(); |
| 56 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { | 63 | if (path != null && path.getLastPathComponent() instanceof ClassSelectorClassNode) { |
| 57 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); | 64 | ClassSelectorClassNode node = (ClassSelectorClassNode) path.getLastPathComponent(); |
| 58 | selectionListener.onSelectClass(node.getClassEntry()); | 65 | selectionListener.onSelectClass(node.getObfEntry()); |
| 59 | } | 66 | } |
| 60 | } | 67 | } |
| 61 | } | 68 | } |
| 62 | }); | 69 | }); |
| 63 | 70 | ||
| 64 | if (gui != null) { | 71 | final JTree tree = this; |
| 65 | final JTree tree = this; | ||
| 66 | 72 | ||
| 67 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, | 73 | final DefaultTreeCellEditor editor = new DefaultTreeCellEditor(tree, |
| 68 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { | 74 | (DefaultTreeCellRenderer) tree.getCellRenderer()) { |
| 69 | @Override | 75 | @Override |
| 70 | public boolean isCellEditable(EventObject event) { | 76 | public boolean isCellEditable(EventObject event) { |
| 71 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); | 77 | return isRenamable && !(event instanceof MouseEvent) && super.isCellEditable(event); |
| 72 | } | 78 | } |
| 73 | }; | 79 | }; |
| 74 | this.setCellEditor(editor); | 80 | this.setCellEditor(editor); |
| 75 | editor.addCellEditorListener(new CellEditorListener() { | 81 | editor.addCellEditorListener(new CellEditorListener() { |
| 76 | @Override | 82 | @Override |
| 77 | public void editingStopped(ChangeEvent e) { | 83 | public void editingStopped(ChangeEvent e) { |
| 78 | String data = editor.getCellEditorValue().toString(); | 84 | String data = editor.getCellEditorValue().toString(); |
| 79 | TreePath path = getSelectionPath(); | 85 | TreePath path = getSelectionPath(); |
| 80 | 86 | ||
| 81 | Object realPath = path.getLastPathComponent(); | 87 | Object realPath = path.getLastPathComponent(); |
| 82 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { | 88 | if (realPath != null && realPath instanceof DefaultMutableTreeNode && data != null) { |
| 83 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; | 89 | DefaultMutableTreeNode node = (DefaultMutableTreeNode) realPath; |
| 84 | TreeNode parentNode = node.getParent(); | 90 | TreeNode parentNode = node.getParent(); |
| 85 | if (parentNode == null) | 91 | if (parentNode == null) |
| 86 | return; | 92 | return; |
| 87 | boolean allowEdit = true; | 93 | boolean allowEdit = true; |
| 88 | for (int i = 0; i < parentNode.getChildCount(); i++) { | 94 | for (int i = 0; i < parentNode.getChildCount(); i++) { |
| 89 | TreeNode childNode = parentNode.getChildAt(i); | 95 | TreeNode childNode = parentNode.getChildAt(i); |
| 90 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { | 96 | if (childNode != null && childNode.toString().equals(data) && childNode != node) { |
| 91 | allowEdit = false; | 97 | allowEdit = false; |
| 92 | break; | 98 | break; |
| 93 | } | ||
| 94 | } | 99 | } |
| 95 | if (allowEdit && renameSelectionListener != null) { | ||
| 96 | Object prevData = node.getUserObject(); | ||
| 97 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; | ||
| 98 | try { | ||
| 99 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | ||
| 100 | node.setUserObject(objectData); // Make sure that it's modified | ||
| 101 | } catch (IllegalNameException ex) { | ||
| 102 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | ||
| 103 | JOptionPane.ERROR_MESSAGE, null, new String[] { "Ok" }, "OK"); | ||
| 104 | editor.cancelCellEditing(); | ||
| 105 | } | ||
| 106 | } else | ||
| 107 | editor.cancelCellEditing(); | ||
| 108 | } | 100 | } |
| 109 | 101 | if (allowEdit && renameSelectionListener != null) { | |
| 102 | Object prevData = node.getUserObject(); | ||
| 103 | Object objectData = node.getUserObject() instanceof ClassEntry ? new ClassEntry(((ClassEntry) prevData).getPackageName() + "/" + data) : data; | ||
| 104 | try { | ||
| 105 | renameSelectionListener.onSelectionRename(node.getUserObject(), objectData, node); | ||
| 106 | node.setUserObject(objectData); // Make sure that it's modified | ||
| 107 | } catch (IllegalNameException ex) { | ||
| 108 | JOptionPane.showOptionDialog(gui.getFrame(), ex.getMessage(), "Enigma - Error", JOptionPane.OK_OPTION, | ||
| 109 | JOptionPane.ERROR_MESSAGE, null, new String[]{"Ok"}, "OK"); | ||
| 110 | editor.cancelCellEditing(); | ||
| 111 | } | ||
| 112 | } else | ||
| 113 | editor.cancelCellEditing(); | ||
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | @Override | 116 | } |
| 113 | public void editingCanceled(ChangeEvent e) { | 117 | |
| 114 | // NOP | 118 | @Override |
| 115 | } | 119 | public void editingCanceled(ChangeEvent e) { |
| 116 | }); | 120 | // NOP |
| 117 | } | 121 | } |
| 122 | }); | ||
| 118 | // init defaults | 123 | // init defaults |
| 119 | this.selectionListener = null; | 124 | this.selectionListener = null; |
| 120 | this.renameSelectionListener = null; | 125 | this.renameSelectionListener = null; |
| @@ -142,16 +147,21 @@ public class ClassSelector extends JTree { | |||
| 142 | } | 147 | } |
| 143 | 148 | ||
| 144 | public void setClasses(Collection<ClassEntry> classEntries) { | 149 | public void setClasses(Collection<ClassEntry> classEntries) { |
| 150 | displayedObfToDeobf.clear(); | ||
| 151 | |||
| 145 | List<StateEntry> state = getExpansionState(this); | 152 | List<StateEntry> state = getExpansionState(this); |
| 146 | if (classEntries == null) { | 153 | if (classEntries == null) { |
| 147 | setModel(null); | 154 | setModel(null); |
| 148 | return; | 155 | return; |
| 149 | } | 156 | } |
| 150 | 157 | ||
| 158 | Translator translator = controller.getDeobfuscator().getMapper().getDeobfuscator(); | ||
| 159 | |||
| 151 | // build the package names | 160 | // build the package names |
| 152 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); | 161 | Map<String, ClassSelectorPackageNode> packages = Maps.newHashMap(); |
| 153 | for (ClassEntry classEntry : classEntries) { | 162 | for (ClassEntry obfClass : classEntries) { |
| 154 | packages.put(classEntry.getPackageName(), null); | 163 | ClassEntry deobfClass = translator.translate(obfClass); |
| 164 | packages.put(deobfClass.getPackageName(), null); | ||
| 155 | } | 165 | } |
| 156 | 166 | ||
| 157 | // sort the packages | 167 | // sort the packages |
| @@ -191,20 +201,24 @@ public class ClassSelector extends JTree { | |||
| 191 | 201 | ||
| 192 | // put the classes into packages | 202 | // put the classes into packages |
| 193 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); | 203 | Multimap<String, ClassEntry> packagedClassEntries = ArrayListMultimap.create(); |
| 194 | for (ClassEntry classEntry : classEntries) { | 204 | for (ClassEntry obfClass : classEntries) { |
| 195 | packagedClassEntries.put(classEntry.getPackageName(), classEntry); | 205 | ClassEntry deobfClass = translator.translate(obfClass); |
| 206 | packagedClassEntries.put(deobfClass.getPackageName(), obfClass); | ||
| 196 | } | 207 | } |
| 197 | 208 | ||
| 198 | // build the class nodes | 209 | // build the class nodes |
| 199 | for (String packageName : packagedClassEntries.keySet()) { | 210 | for (String packageName : packagedClassEntries.keySet()) { |
| 200 | // sort the class entries | 211 | // sort the class entries |
| 201 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); | 212 | List<ClassEntry> classEntriesInPackage = Lists.newArrayList(packagedClassEntries.get(packageName)); |
| 202 | classEntriesInPackage.sort(this.comparator); | 213 | classEntriesInPackage.sort((o1, o2) -> comparator.compare(translator.translate(o1), translator.translate(o2))); |
| 203 | 214 | ||
| 204 | // create the nodes in order | 215 | // create the nodes in order |
| 205 | for (ClassEntry classEntry : classEntriesInPackage) { | 216 | for (ClassEntry obfClass : classEntriesInPackage) { |
| 217 | ClassEntry deobfClass = translator.translate(obfClass); | ||
| 206 | ClassSelectorPackageNode node = packages.get(packageName); | 218 | ClassSelectorPackageNode node = packages.get(packageName); |
| 207 | node.add(new ClassSelectorClassNode(classEntry)); | 219 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfClass, deobfClass); |
| 220 | displayedObfToDeobf.put(obfClass, deobfClass); | ||
| 221 | node.add(classNode); | ||
| 208 | } | 222 | } |
| 209 | } | 223 | } |
| 210 | 224 | ||
| @@ -324,7 +338,7 @@ public class ClassSelector extends JTree { | |||
| 324 | } | 338 | } |
| 325 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 339 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 326 | if (packageNode.getPackageName().equals(packageName)) { | 340 | if (packageNode.getPackageName().equals(packageName)) { |
| 327 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); | 341 | expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); |
| 328 | return; | 342 | return; |
| 329 | } | 343 | } |
| 330 | } | 344 | } |
| @@ -332,14 +346,13 @@ public class ClassSelector extends JTree { | |||
| 332 | 346 | ||
| 333 | public void expandAll() { | 347 | public void expandAll() { |
| 334 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 348 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 335 | expandPath(new TreePath(new Object[] { getModel().getRoot(), packageNode })); | 349 | expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); |
| 336 | } | 350 | } |
| 337 | } | 351 | } |
| 338 | 352 | ||
| 339 | public ClassEntry getFirstClass() { | 353 | public ClassEntry getFirstClass() { |
| 340 | ClassSelectorPackageNode packageNode = packageNodes().get(0); | 354 | ClassSelectorPackageNode packageNode = packageNodes().get(0); |
| 341 | if (packageNode != null) | 355 | if (packageNode != null) { |
| 342 | { | ||
| 343 | ClassSelectorClassNode classNode = classNodes(packageNode).get(0); | 356 | ClassSelectorClassNode classNode = classNodes(packageNode).get(0); |
| 344 | if (classNode != null) { | 357 | if (classNode != null) { |
| 345 | return classNode.getClassEntry(); | 358 | return classNode.getClassEntry(); |
| @@ -350,7 +363,7 @@ public class ClassSelector extends JTree { | |||
| 350 | 363 | ||
| 351 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { | 364 | public ClassSelectorPackageNode getPackageNode(ClassEntry entry) { |
| 352 | String packageName = entry.getPackageName(); | 365 | String packageName = entry.getPackageName(); |
| 353 | if (packageName == null){ | 366 | if (packageName == null) { |
| 354 | packageName = "(none)"; | 367 | packageName = "(none)"; |
| 355 | } | 368 | } |
| 356 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 369 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| @@ -361,6 +374,11 @@ public class ClassSelector extends JTree { | |||
| 361 | return null; | 374 | return null; |
| 362 | } | 375 | } |
| 363 | 376 | ||
| 377 | @Nullable | ||
| 378 | public ClassEntry getDisplayedDeobf(ClassEntry obfEntry) { | ||
| 379 | return displayedObfToDeobf.get(obfEntry); | ||
| 380 | } | ||
| 381 | |||
| 364 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { | 382 | public ClassSelectorPackageNode getPackageNode(ClassSelector selector, ClassEntry entry) { |
| 365 | ClassSelectorPackageNode packageNode = getPackageNode(entry); | 383 | ClassSelectorPackageNode packageNode = getPackageNode(entry); |
| 366 | 384 | ||
| @@ -402,7 +420,7 @@ public class ClassSelector extends JTree { | |||
| 402 | for (ClassSelectorPackageNode packageNode : packageNodes()) { | 420 | for (ClassSelectorPackageNode packageNode : packageNodes()) { |
| 403 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { | 421 | for (ClassSelectorClassNode classNode : classNodes(packageNode)) { |
| 404 | if (classNode.getClassEntry().equals(classEntry)) { | 422 | if (classNode.getClassEntry().equals(classEntry)) { |
| 405 | setSelectionPath(new TreePath(new Object[] { getModel().getRoot(), packageNode, classNode })); | 423 | setSelectionPath(new TreePath(new Object[]{getModel().getRoot(), packageNode, classNode})); |
| 406 | } | 424 | } |
| 407 | } | 425 | } |
| 408 | } | 426 | } |
| @@ -418,6 +436,9 @@ public class ClassSelector extends JTree { | |||
| 418 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); | 436 | DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) packageNode.getChildAt(i); |
| 419 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { | 437 | if (childNode.getUserObject() instanceof ClassEntry && childNode.getUserObject().equals(entry)) { |
| 420 | model.removeNodeFromParent(childNode); | 438 | model.removeNodeFromParent(childNode); |
| 439 | if (childNode instanceof ClassSelectorClassNode) { | ||
| 440 | displayedObfToDeobf.remove(((ClassSelectorClassNode) childNode).getObfEntry()); | ||
| 441 | } | ||
| 421 | break; | 442 | break; |
| 422 | } | 443 | } |
| 423 | } | 444 | } |
| @@ -428,13 +449,25 @@ public class ClassSelector extends JTree { | |||
| 428 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); | 449 | ((DefaultTreeModel) getModel()).removeNodeFromParent(packageNode); |
| 429 | } | 450 | } |
| 430 | 451 | ||
| 431 | public void moveClassTree(ClassEntry oldClassEntry, ClassEntry newClassEntry, ClassSelector otherSelector) { | 452 | public void moveClassIn(ClassEntry classEntry) { |
| 432 | if (otherSelector == null) | 453 | removeEntry(classEntry); |
| 433 | removeNode(getPackageNode(oldClassEntry), oldClassEntry); | 454 | insertNode(classEntry); |
| 434 | insertNode(getOrCreate(newClassEntry), newClassEntry); | ||
| 435 | } | 455 | } |
| 436 | 456 | ||
| 437 | public ClassSelectorPackageNode getOrCreate(ClassEntry entry) { | 457 | public void moveClassOut(ClassEntry classEntry) { |
| 458 | removeEntry(classEntry); | ||
| 459 | } | ||
| 460 | |||
| 461 | private void removeEntry(ClassEntry classEntry) { | ||
| 462 | ClassEntry previousDeobf = displayedObfToDeobf.get(classEntry); | ||
| 463 | if (previousDeobf != null) { | ||
| 464 | ClassSelectorPackageNode packageNode = getPackageNode(previousDeobf); | ||
| 465 | removeNode(packageNode, previousDeobf); | ||
| 466 | removeNodeIfEmpty(packageNode); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | public ClassSelectorPackageNode getOrCreatePackage(ClassEntry entry) { | ||
| 438 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 471 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 439 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); | 472 | ClassSelectorPackageNode newPackageNode = getPackageNode(entry); |
| 440 | if (newPackageNode == null) { | 473 | if (newPackageNode == null) { |
| @@ -444,10 +477,15 @@ public class ClassSelector extends JTree { | |||
| 444 | return newPackageNode; | 477 | return newPackageNode; |
| 445 | } | 478 | } |
| 446 | 479 | ||
| 447 | public void insertNode(ClassSelectorPackageNode packageNode, ClassEntry entry) { | 480 | public void insertNode(ClassEntry obfEntry) { |
| 481 | ClassEntry deobfEntry = controller.getDeobfuscator().deobfuscate(obfEntry); | ||
| 482 | ClassSelectorPackageNode packageNode = getOrCreatePackage(deobfEntry); | ||
| 483 | |||
| 448 | DefaultTreeModel model = (DefaultTreeModel) getModel(); | 484 | DefaultTreeModel model = (DefaultTreeModel) getModel(); |
| 449 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(entry); | 485 | ClassSelectorClassNode classNode = new ClassSelectorClassNode(obfEntry, deobfEntry); |
| 450 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); | 486 | model.insertNodeInto(classNode, packageNode, getPlacementIndex(packageNode, classNode)); |
| 487 | |||
| 488 | displayedObfToDeobf.put(obfEntry, deobfEntry); | ||
| 451 | } | 489 | } |
| 452 | 490 | ||
| 453 | public void reload() { | 491 | public void reload() { |
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java index 08100438..e119640a 100644 --- a/src/main/java/cuchaz/enigma/gui/CodeReader.java +++ b/src/main/java/cuchaz/enigma/gui/CodeReader.java | |||
| @@ -11,58 +11,27 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | ||
| 15 | import cuchaz.enigma.Deobfuscator; | ||
| 16 | import cuchaz.enigma.analysis.EntryReference; | ||
| 17 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 18 | import cuchaz.enigma.analysis.Token; | 14 | import cuchaz.enigma.analysis.Token; |
| 19 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 21 | import de.sciss.syntaxpane.DefaultSyntaxKit; | ||
| 22 | 15 | ||
| 23 | import javax.swing.*; | 16 | import javax.swing.*; |
| 24 | import javax.swing.text.BadLocationException; | 17 | import javax.swing.text.BadLocationException; |
| 18 | import javax.swing.text.Document; | ||
| 25 | import javax.swing.text.Highlighter.HighlightPainter; | 19 | import javax.swing.text.Highlighter.HighlightPainter; |
| 26 | import java.awt.*; | 20 | import java.awt.*; |
| 27 | import java.awt.event.ActionEvent; | 21 | import java.awt.event.ActionEvent; |
| 28 | import java.awt.event.ActionListener; | 22 | import java.awt.event.ActionListener; |
| 29 | 23 | ||
| 30 | public class CodeReader extends JEditorPane { | 24 | public class CodeReader extends JEditorPane { |
| 31 | |||
| 32 | private static final long serialVersionUID = 3673180950485748810L; | 25 | private static final long serialVersionUID = 3673180950485748810L; |
| 33 | 26 | ||
| 34 | private static final Object lock = new Object(); | ||
| 35 | private SourceIndex sourceIndex; | ||
| 36 | private SelectionListener selectionListener; | ||
| 37 | |||
| 38 | public CodeReader() { | ||
| 39 | |||
| 40 | setEditable(false); | ||
| 41 | setContentType("text/java"); | ||
| 42 | |||
| 43 | // turn off token highlighting (it's wrong most of the time anyway...) | ||
| 44 | DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit(); | ||
| 45 | kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker"); | ||
| 46 | |||
| 47 | // hook events | ||
| 48 | addCaretListener(event -> | ||
| 49 | { | ||
| 50 | if (selectionListener != null && sourceIndex != null) { | ||
| 51 | Token token = sourceIndex.getReferenceToken(event.getDot()); | ||
| 52 | if (token != null) { | ||
| 53 | selectionListener.onSelect(sourceIndex.getDeobfReference(token)); | ||
| 54 | } else { | ||
| 55 | selectionListener.onSelect(null); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }); | ||
| 59 | } | ||
| 60 | |||
| 61 | // HACKHACK: someday we can update the main GUI to use this code reader | 27 | // HACKHACK: someday we can update the main GUI to use this code reader |
| 62 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { | 28 | public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) { |
| 63 | 29 | ||
| 64 | // set the caret position to the token | 30 | // set the caret position to the token |
| 65 | editor.setCaretPosition(token.start); | 31 | Document document = editor.getDocument(); |
| 32 | int clampedPosition = Math.min(Math.max(token.start, 0), document.getLength()); | ||
| 33 | |||
| 34 | editor.setCaretPosition(clampedPosition); | ||
| 66 | editor.grabFocus(); | 35 | editor.grabFocus(); |
| 67 | 36 | ||
| 68 | try { | 37 | try { |
| @@ -101,57 +70,4 @@ public class CodeReader extends JEditorPane { | |||
| 101 | }); | 70 | }); |
| 102 | timer.start(); | 71 | timer.start(); |
| 103 | } | 72 | } |
| 104 | |||
| 105 | public void setSelectionListener(SelectionListener val) { | ||
| 106 | selectionListener = val; | ||
| 107 | } | ||
| 108 | |||
| 109 | public void setCode(String code) { | ||
| 110 | // sadly, the java lexer is not thread safe, so we have to serialize all these calls | ||
| 111 | synchronized (lock) { | ||
| 112 | setText(code); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | public SourceIndex getSourceIndex() { | ||
| 117 | return sourceIndex; | ||
| 118 | } | ||
| 119 | |||
| 120 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator) { | ||
| 121 | decompileClass(classEntry, deobfuscator, null); | ||
| 122 | } | ||
| 123 | |||
| 124 | public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) { | ||
| 125 | decompileClass(classEntry, deobfuscator, null, callback); | ||
| 126 | } | ||
| 127 | |||
| 128 | public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) { | ||
| 129 | |||
| 130 | if (classEntry == null) { | ||
| 131 | setCode(null); | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | |||
| 135 | setCode("(decompiling...)"); | ||
| 136 | |||
| 137 | // run decompilation in a separate thread to keep ui responsive | ||
| 138 | new Thread(() -> | ||
| 139 | { | ||
| 140 | |||
| 141 | // decompile it | ||
| 142 | |||
| 143 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getName()); | ||
| 144 | String source = deobfuscator.getSource(sourceTree); | ||
| 145 | setCode(source); | ||
| 146 | sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens); | ||
| 147 | |||
| 148 | if (callback != null) { | ||
| 149 | callback.run(); | ||
| 150 | } | ||
| 151 | }).start(); | ||
| 152 | } | ||
| 153 | |||
| 154 | public interface SelectionListener { | ||
| 155 | void onSelect(EntryReference<Entry<?>, Entry<?>> reference); | ||
| 156 | } | ||
| 157 | } | 73 | } |
diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java new file mode 100644 index 00000000..03f76c90 --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.Deobfuscator; | ||
| 4 | import cuchaz.enigma.analysis.EntryReference; | ||
| 5 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 6 | import cuchaz.enigma.analysis.Token; | ||
| 7 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 8 | import cuchaz.enigma.gui.highlight.TokenHighlightType; | ||
| 9 | import cuchaz.enigma.translation.LocalNameGenerator; | ||
| 10 | import cuchaz.enigma.translation.Translator; | ||
| 11 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; | ||
| 16 | |||
| 17 | import javax.annotation.Nullable; | ||
| 18 | import java.util.*; | ||
| 19 | |||
| 20 | public class DecompiledClassSource { | ||
| 21 | private final ClassEntry classEntry; | ||
| 22 | private final Deobfuscator deobfuscator; | ||
| 23 | |||
| 24 | private final SourceIndex obfuscatedIndex; | ||
| 25 | private SourceIndex remappedIndex; | ||
| 26 | |||
| 27 | private final Map<TokenHighlightType, Collection<Token>> highlightedTokens = new EnumMap<>(TokenHighlightType.class); | ||
| 28 | |||
| 29 | public DecompiledClassSource(ClassEntry classEntry, Deobfuscator deobfuscator, SourceIndex index) { | ||
| 30 | this.classEntry = classEntry; | ||
| 31 | this.deobfuscator = deobfuscator; | ||
| 32 | this.obfuscatedIndex = index; | ||
| 33 | this.remappedIndex = index; | ||
| 34 | } | ||
| 35 | |||
| 36 | public void remapSource(Translator translator) { | ||
| 37 | highlightedTokens.clear(); | ||
| 38 | |||
| 39 | SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens()); | ||
| 40 | |||
| 41 | SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(token, movedToken, translator)); | ||
| 42 | remappedIndex = obfuscatedIndex.remapTo(remapResult); | ||
| 43 | } | ||
| 44 | |||
| 45 | private String remapToken(Token token, Token movedToken, Translator translator) { | ||
| 46 | EntryReference<Entry<?>, Entry<?>> reference = obfuscatedIndex.getReference(token); | ||
| 47 | |||
| 48 | if (deobfuscator.isRenamable(reference)) { | ||
| 49 | Entry<?> entry = reference.getNameableEntry(); | ||
| 50 | Entry<?> translatedEntry = translator.translate(entry); | ||
| 51 | |||
| 52 | if (isDeobfuscated(entry, translatedEntry)) { | ||
| 53 | highlightToken(movedToken, TokenHighlightType.DEOBFUSCATED); | ||
| 54 | return translatedEntry.getSourceRemapName(); | ||
| 55 | } else { | ||
| 56 | String proposedName = proposeName(entry); | ||
| 57 | if (proposedName != null) { | ||
| 58 | highlightToken(movedToken, TokenHighlightType.PROPOSED); | ||
| 59 | return proposedName; | ||
| 60 | } | ||
| 61 | |||
| 62 | highlightToken(movedToken, TokenHighlightType.OBFUSCATED); | ||
| 63 | |||
| 64 | String defaultName = generateDefaultName(translatedEntry); | ||
| 65 | if (defaultName != null) { | ||
| 66 | return defaultName; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | return null; | ||
| 72 | } | ||
| 73 | |||
| 74 | @Nullable | ||
| 75 | private String proposeName(Entry<?> entry) { | ||
| 76 | if (entry instanceof FieldEntry) { | ||
| 77 | for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { | ||
| 78 | String owner = entry.getContainingClass().getFullName(); | ||
| 79 | String proposal = plugin.proposeFieldName(owner, entry.getName(), ((FieldEntry) entry).getDesc().toString()); | ||
| 80 | if (proposal != null) { | ||
| 81 | return proposal; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | return null; | ||
| 86 | } | ||
| 87 | |||
| 88 | @Nullable | ||
| 89 | private String generateDefaultName(Entry<?> entry) { | ||
| 90 | if (entry instanceof LocalVariableDefEntry) { | ||
| 91 | LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry; | ||
| 92 | |||
| 93 | int index = localVariable.getIndex(); | ||
| 94 | if (localVariable.isArgument()) { | ||
| 95 | List<TypeDescriptor> arguments = localVariable.getParent().getDesc().getArgumentDescs(); | ||
| 96 | return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments); | ||
| 97 | } else { | ||
| 98 | return LocalNameGenerator.generateLocalVariableName(index, localVariable.getDesc()); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | return null; | ||
| 103 | } | ||
| 104 | |||
| 105 | private boolean isDeobfuscated(Entry<?> entry, Entry<?> translatedEntry) { | ||
| 106 | return !entry.getName().equals(translatedEntry.getName()); | ||
| 107 | } | ||
| 108 | |||
| 109 | public ClassEntry getEntry() { | ||
| 110 | return classEntry; | ||
| 111 | } | ||
| 112 | |||
| 113 | public SourceIndex getIndex() { | ||
| 114 | return remappedIndex; | ||
| 115 | } | ||
| 116 | |||
| 117 | public Map<TokenHighlightType, Collection<Token>> getHighlightedTokens() { | ||
| 118 | return highlightedTokens; | ||
| 119 | } | ||
| 120 | |||
| 121 | private void highlightToken(Token token, TokenHighlightType highlightType) { | ||
| 122 | highlightedTokens.computeIfAbsent(highlightType, t -> new ArrayList<>()).add(token); | ||
| 123 | } | ||
| 124 | |||
| 125 | @Override | ||
| 126 | public String toString() { | ||
| 127 | return remappedIndex.getSource(); | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index d119735c..a6e20a27 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java | |||
| @@ -24,7 +24,7 @@ import cuchaz.enigma.gui.filechooser.FileChooserAny; | |||
| 24 | import cuchaz.enigma.gui.filechooser.FileChooserFolder; | 24 | import cuchaz.enigma.gui.filechooser.FileChooserFolder; |
| 25 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; | 25 | import cuchaz.enigma.gui.highlight.BoxHighlightPainter; |
| 26 | import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; | 26 | import cuchaz.enigma.gui.highlight.SelectionHighlightPainter; |
| 27 | import cuchaz.enigma.gui.node.ClassSelectorPackageNode; | 27 | import cuchaz.enigma.gui.highlight.TokenHighlightType; |
| 28 | import cuchaz.enigma.gui.panels.PanelDeobf; | 28 | import cuchaz.enigma.gui.panels.PanelDeobf; |
| 29 | import cuchaz.enigma.gui.panels.PanelEditor; | 29 | import cuchaz.enigma.gui.panels.PanelEditor; |
| 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; | 30 | import cuchaz.enigma.gui.panels.PanelIdentifier; |
| @@ -44,10 +44,9 @@ import javax.swing.tree.TreeNode; | |||
| 44 | import javax.swing.tree.TreePath; | 44 | import javax.swing.tree.TreePath; |
| 45 | import java.awt.*; | 45 | import java.awt.*; |
| 46 | import java.awt.event.*; | 46 | import java.awt.event.*; |
| 47 | import java.io.IOException; | ||
| 48 | import java.nio.file.Path; | 47 | import java.nio.file.Path; |
| 49 | import java.util.*; | ||
| 50 | import java.util.List; | 48 | import java.util.List; |
| 49 | import java.util.*; | ||
| 51 | import java.util.function.Function; | 50 | import java.util.function.Function; |
| 52 | 51 | ||
| 53 | public class Gui { | 52 | public class Gui { |
| @@ -71,7 +70,7 @@ public class Gui { | |||
| 71 | private JPanel classesPanel; | 70 | private JPanel classesPanel; |
| 72 | private JSplitPane splitClasses; | 71 | private JSplitPane splitClasses; |
| 73 | private PanelIdentifier infoPanel; | 72 | private PanelIdentifier infoPanel; |
| 74 | public Map<String, BoxHighlightPainter> boxHighlightPainters; | 73 | public Map<TokenHighlightType, BoxHighlightPainter> boxHighlightPainters; |
| 75 | private SelectionHighlightPainter selectionHighlightPainter; | 74 | private SelectionHighlightPainter selectionHighlightPainter; |
| 76 | private JTree inheritanceTree; | 75 | private JTree inheritanceTree; |
| 77 | private JTree implementationsTree; | 76 | private JTree implementationsTree; |
| @@ -320,7 +319,7 @@ public class Gui { | |||
| 320 | this.frame.setTitle(Constants.NAME + " - " + jarName); | 319 | this.frame.setTitle(Constants.NAME + " - " + jarName); |
| 321 | this.classesPanel.removeAll(); | 320 | this.classesPanel.removeAll(); |
| 322 | this.classesPanel.add(splitClasses); | 321 | this.classesPanel.add(splitClasses); |
| 323 | setSource(null); | 322 | setEditorText(null); |
| 324 | 323 | ||
| 325 | // update menu | 324 | // update menu |
| 326 | this.menuBar.closeJarMenu.setEnabled(true); | 325 | this.menuBar.closeJarMenu.setEnabled(true); |
| @@ -342,7 +341,7 @@ public class Gui { | |||
| 342 | this.frame.setTitle(Constants.NAME); | 341 | this.frame.setTitle(Constants.NAME); |
| 343 | setObfClasses(null); | 342 | setObfClasses(null); |
| 344 | setDeobfClasses(null); | 343 | setDeobfClasses(null); |
| 345 | setSource(null); | 344 | setEditorText(null); |
| 346 | this.classesPanel.removeAll(); | 345 | this.classesPanel.removeAll(); |
| 347 | 346 | ||
| 348 | // update menu | 347 | // update menu |
| @@ -373,11 +372,16 @@ public class Gui { | |||
| 373 | this.menuBar.saveMappingsMenu.setEnabled(path != null); | 372 | this.menuBar.saveMappingsMenu.setEnabled(path != null); |
| 374 | } | 373 | } |
| 375 | 374 | ||
| 376 | public void setSource(String source) { | 375 | public void setEditorText(String source) { |
| 377 | this.editor.getHighlighter().removeAllHighlights(); | 376 | this.editor.getHighlighter().removeAllHighlights(); |
| 378 | this.editor.setText(source); | 377 | this.editor.setText(source); |
| 379 | } | 378 | } |
| 380 | 379 | ||
| 380 | public void setSource(DecompiledClassSource source) { | ||
| 381 | editor.setText(source.toString()); | ||
| 382 | setHighlightedTokens(source.getHighlightedTokens()); | ||
| 383 | } | ||
| 384 | |||
| 381 | public void showToken(final Token token) { | 385 | public void showToken(final Token token) { |
| 382 | if (token == null) { | 386 | if (token == null) { |
| 383 | throw new IllegalArgumentException("Token cannot be null!"); | 387 | throw new IllegalArgumentException("Token cannot be null!"); |
| @@ -401,15 +405,15 @@ public class Gui { | |||
| 401 | showToken(sortedTokens.get(0)); | 405 | showToken(sortedTokens.get(0)); |
| 402 | } | 406 | } |
| 403 | 407 | ||
| 404 | public void setHighlightedTokens(Map<String, Iterable<Token>> tokens) { | 408 | public void setHighlightedTokens(Map<TokenHighlightType, Collection<Token>> tokens) { |
| 405 | // remove any old highlighters | 409 | // remove any old highlighters |
| 406 | this.editor.getHighlighter().removeAllHighlights(); | 410 | this.editor.getHighlighter().removeAllHighlights(); |
| 407 | 411 | ||
| 408 | if (boxHighlightPainters != null) { | 412 | if (boxHighlightPainters != null) { |
| 409 | for (String s : tokens.keySet()) { | 413 | for (TokenHighlightType type : tokens.keySet()) { |
| 410 | BoxHighlightPainter painter = boxHighlightPainters.get(s); | 414 | BoxHighlightPainter painter = boxHighlightPainters.get(type); |
| 411 | if (painter != null) { | 415 | if (painter != null) { |
| 412 | setHighlightedTokens(tokens.get(s), painter); | 416 | setHighlightedTokens(tokens.get(type), painter); |
| 413 | } | 417 | } |
| 414 | } | 418 | } |
| 415 | } | 419 | } |
| @@ -435,17 +439,19 @@ public class Gui { | |||
| 435 | 439 | ||
| 436 | this.reference = reference; | 440 | this.reference = reference; |
| 437 | 441 | ||
| 442 | EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); | ||
| 443 | |||
| 438 | infoPanel.removeAll(); | 444 | infoPanel.removeAll(); |
| 439 | if (reference.entry instanceof ClassEntry) { | 445 | if (translatedReference.entry instanceof ClassEntry) { |
| 440 | showClassEntry((ClassEntry) this.reference.entry); | 446 | showClassEntry((ClassEntry) translatedReference.entry); |
| 441 | } else if (this.reference.entry instanceof FieldEntry) { | 447 | } else if (translatedReference.entry instanceof FieldEntry) { |
| 442 | showFieldEntry((FieldEntry) this.reference.entry); | 448 | showFieldEntry((FieldEntry) translatedReference.entry); |
| 443 | } else if (this.reference.entry instanceof MethodEntry) { | 449 | } else if (translatedReference.entry instanceof MethodEntry) { |
| 444 | showMethodEntry((MethodEntry) this.reference.entry); | 450 | showMethodEntry((MethodEntry) translatedReference.entry); |
| 445 | } else if (this.reference.entry instanceof LocalVariableEntry) { | 451 | } else if (translatedReference.entry instanceof LocalVariableEntry) { |
| 446 | showLocalVariableEntry((LocalVariableEntry) this.reference.entry); | 452 | showLocalVariableEntry((LocalVariableEntry) translatedReference.entry); |
| 447 | } else { | 453 | } else { |
| 448 | throw new Error("Unknown entry desc: " + this.reference.entry.getClass().getName()); | 454 | throw new Error("Unknown entry desc: " + translatedReference.entry.getClass().getName()); |
| 449 | } | 455 | } |
| 450 | 456 | ||
| 451 | redraw(); | 457 | redraw(); |
| @@ -519,7 +525,7 @@ public class Gui { | |||
| 519 | Token token = this.controller.getToken(pos); | 525 | Token token = this.controller.getToken(pos); |
| 520 | boolean isToken = token != null; | 526 | boolean isToken = token != null; |
| 521 | 527 | ||
| 522 | reference = this.controller.getDeobfReference(token); | 528 | reference = this.controller.getReference(token); |
| 523 | 529 | ||
| 524 | Entry<?> referenceEntry = reference != null ? reference.entry : null; | 530 | Entry<?> referenceEntry = reference != null ? reference.entry : null; |
| 525 | boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry; | 531 | boolean isClassEntry = isToken && referenceEntry instanceof ClassEntry; |
| @@ -527,7 +533,7 @@ public class Gui { | |||
| 527 | boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); | 533 | boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); |
| 528 | boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); | 534 | boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); |
| 529 | boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); | 535 | boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); |
| 530 | boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); | 536 | boolean isRenameable = isToken && this.controller.getDeobfuscator().isRenamable(reference); |
| 531 | 537 | ||
| 532 | if (isToken) { | 538 | if (isToken) { |
| 533 | showReference(reference); | 539 | showReference(reference); |
| @@ -544,7 +550,7 @@ public class Gui { | |||
| 544 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); | 550 | this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); |
| 545 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); | 551 | this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); |
| 546 | 552 | ||
| 547 | if (isToken && this.controller.entryHasDeobfuscatedName(referenceEntry)) { | 553 | if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) { |
| 548 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); | 554 | this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); |
| 549 | } else { | 555 | } else { |
| 550 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); | 556 | this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); |
| @@ -576,7 +582,10 @@ public class Gui { | |||
| 576 | 582 | ||
| 577 | // init the text box | 583 | // init the text box |
| 578 | final JTextField text = new JTextField(); | 584 | final JTextField text = new JTextField(); |
| 579 | text.setText(reference.getNameableName()); | 585 | |
| 586 | EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); | ||
| 587 | text.setText(translatedReference.getNameableName()); | ||
| 588 | |||
| 580 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); | 589 | text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); |
| 581 | text.addKeyListener(new KeyAdapter() { | 590 | text.addKeyListener(new KeyAdapter() { |
| 582 | @Override | 591 | @Override |
| @@ -603,7 +612,7 @@ public class Gui { | |||
| 603 | 612 | ||
| 604 | int offset = text.getText().lastIndexOf('/') + 1; | 613 | int offset = text.getText().lastIndexOf('/') + 1; |
| 605 | // If it's a class and isn't in the default package, assume that it's deobfuscated. | 614 | // If it's a class and isn't in the default package, assume that it's deobfuscated. |
| 606 | if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) | 615 | if (translatedReference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) |
| 607 | text.select(offset, text.getText().length()); | 616 | text.select(offset, text.getText().length()); |
| 608 | else | 617 | else |
| 609 | text.selectAll(); | 618 | text.selectAll(); |
| @@ -719,7 +728,7 @@ public class Gui { | |||
| 719 | } | 728 | } |
| 720 | 729 | ||
| 721 | public void toggleMapping() { | 730 | public void toggleMapping() { |
| 722 | if (this.controller.entryHasDeobfuscatedName(reference.entry)) { | 731 | if (this.controller.getDeobfuscator().isRemapped(reference.entry)) { |
| 723 | this.controller.removeMapping(reference); | 732 | this.controller.removeMapping(reference); |
| 724 | } else { | 733 | } else { |
| 725 | this.controller.markAsDeobfuscated(reference); | 734 | this.controller.markAsDeobfuscated(reference); |
| @@ -743,7 +752,7 @@ public class Gui { | |||
| 743 | callback.apply(response); | 752 | callback.apply(response); |
| 744 | } | 753 | } |
| 745 | 754 | ||
| 746 | public void saveMapping() throws IOException { | 755 | public void saveMapping() { |
| 747 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) | 756 | if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) |
| 748 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); | 757 | this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile().toPath()); |
| 749 | } | 758 | } |
| @@ -757,13 +766,8 @@ public class Gui { | |||
| 757 | // ask to save before closing | 766 | // ask to save before closing |
| 758 | showDiscardDiag((response) -> { | 767 | showDiscardDiag((response) -> { |
| 759 | if (response == JOptionPane.YES_OPTION) { | 768 | if (response == JOptionPane.YES_OPTION) { |
| 760 | try { | 769 | this.saveMapping(); |
| 761 | this.saveMapping(); | 770 | this.frame.dispose(); |
| 762 | this.frame.dispose(); | ||
| 763 | |||
| 764 | } catch (IOException ex) { | ||
| 765 | throw new Error(ex); | ||
| 766 | } | ||
| 767 | } else if (response == JOptionPane.NO_OPTION) | 771 | } else if (response == JOptionPane.NO_OPTION) |
| 768 | this.frame.dispose(); | 772 | this.frame.dispose(); |
| 769 | 773 | ||
| @@ -796,47 +800,39 @@ public class Gui { | |||
| 796 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); | 800 | this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); |
| 797 | } | 801 | } |
| 798 | 802 | ||
| 799 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName) { | 803 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, String newName) { |
| 800 | String oldEntry = deobfReference.entry.getContainingClass().getPackageName(); | 804 | String oldEntry = obfReference.entry.getContainingClass().getPackageName(); |
| 801 | String newEntry = new ClassEntry(newName).getPackageName(); | 805 | String newEntry = new ClassEntry(newName).getPackageName(); |
| 802 | moveClassTree(deobfReference, newName, oldEntry == null, | 806 | moveClassTree(obfReference, oldEntry == null, newEntry == null); |
| 803 | newEntry == null); | ||
| 804 | } | 807 | } |
| 805 | 808 | ||
| 806 | // TODO: getExpansionState will *not* actually update itself based on name changes! | 809 | // TODO: getExpansionState will *not* actually update itself based on name changes! |
| 807 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean isOldOb, boolean isNewOb) { | 810 | public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, boolean isOldOb, boolean isNewOb) { |
| 808 | ClassEntry oldEntry = deobfReference.entry.getContainingClass(); | 811 | ClassEntry classEntry = obfReference.entry.getContainingClass(); |
| 809 | ClassEntry newEntry = new ClassEntry(newName); | ||
| 810 | 812 | ||
| 811 | // Ob -> deob | 813 | // Ob -> deob |
| 812 | List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses); | 814 | List<ClassSelector.StateEntry> stateDeobf = this.deobfPanel.deobfClasses.getExpansionState(this.deobfPanel.deobfClasses); |
| 813 | List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses); | 815 | List<ClassSelector.StateEntry> stateObf = this.obfPanel.obfClasses.getExpansionState(this.obfPanel.obfClasses); |
| 814 | 816 | ||
| 815 | if (isOldOb && !isNewOb) { | 817 | if (isOldOb && !isNewOb) { |
| 816 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); | 818 | this.deobfPanel.deobfClasses.moveClassIn(classEntry); |
| 817 | ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); | 819 | this.obfPanel.obfClasses.moveClassOut(classEntry); |
| 818 | this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); | ||
| 819 | this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); | ||
| 820 | this.deobfPanel.deobfClasses.reload(); | 820 | this.deobfPanel.deobfClasses.reload(); |
| 821 | this.obfPanel.obfClasses.reload(); | 821 | this.obfPanel.obfClasses.reload(); |
| 822 | } | 822 | } |
| 823 | // Deob -> ob | 823 | // Deob -> ob |
| 824 | else if (isNewOb && !isOldOb) { | 824 | else if (isNewOb && !isOldOb) { |
| 825 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); | 825 | this.obfPanel.obfClasses.moveClassIn(classEntry); |
| 826 | ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); | 826 | this.deobfPanel.deobfClasses.moveClassOut(classEntry); |
| 827 | this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); | ||
| 828 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); | ||
| 829 | this.deobfPanel.deobfClasses.reload(); | 827 | this.deobfPanel.deobfClasses.reload(); |
| 830 | this.obfPanel.obfClasses.reload(); | 828 | this.obfPanel.obfClasses.reload(); |
| 831 | } | 829 | } |
| 832 | // Local move | 830 | // Local move |
| 833 | else if (isOldOb) { | 831 | else if (isOldOb) { |
| 834 | this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); | 832 | this.obfPanel.obfClasses.moveClassIn(classEntry); |
| 835 | this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); | ||
| 836 | this.obfPanel.obfClasses.reload(); | 833 | this.obfPanel.obfClasses.reload(); |
| 837 | } else { | 834 | } else { |
| 838 | this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); | 835 | this.deobfPanel.deobfClasses.moveClassIn(classEntry); |
| 839 | this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); | ||
| 840 | this.deobfPanel.deobfClasses.reload(); | 836 | this.deobfPanel.deobfClasses.reload(); |
| 841 | } | 837 | } |
| 842 | 838 | ||
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index fd9e7f0c..03e17682 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java | |||
| @@ -11,13 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma.gui; | 12 | package cuchaz.enigma.gui; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.ImmutableMap; | ||
| 15 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 16 | import com.google.common.collect.Queues; | 15 | import com.google.common.collect.Queues; |
| 16 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | ||
| 17 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 17 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 18 | import cuchaz.enigma.Deobfuscator; | 18 | import cuchaz.enigma.Deobfuscator; |
| 19 | import cuchaz.enigma.SourceProvider; | ||
| 19 | import cuchaz.enigma.analysis.*; | 20 | import cuchaz.enigma.analysis.*; |
| 20 | import cuchaz.enigma.api.EnigmaPlugin; | ||
| 21 | import cuchaz.enigma.config.Config; | 21 | import cuchaz.enigma.config.Config; |
| 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; | 22 | import cuchaz.enigma.gui.dialog.ProgressDialog; |
| 23 | import cuchaz.enigma.throwables.MappingParseException; | 23 | import cuchaz.enigma.throwables.MappingParseException; |
| @@ -36,16 +36,20 @@ import java.awt.event.ItemEvent; | |||
| 36 | import java.io.File; | 36 | import java.io.File; |
| 37 | import java.io.IOException; | 37 | import java.io.IOException; |
| 38 | import java.nio.file.Path; | 38 | import java.nio.file.Path; |
| 39 | import java.util.*; | 39 | import java.util.Collection; |
| 40 | import java.util.Deque; | ||
| 41 | import java.util.List; | ||
| 42 | import java.util.concurrent.ExecutorService; | ||
| 43 | import java.util.concurrent.Executors; | ||
| 40 | import java.util.jar.JarFile; | 44 | import java.util.jar.JarFile; |
| 41 | import java.util.stream.Collectors; | 45 | import java.util.stream.Collectors; |
| 42 | 46 | ||
| 43 | public class GuiController { | 47 | public class GuiController { |
| 48 | private static final ExecutorService DECOMPILER_SERVICE = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("decompiler-thread").build()); | ||
| 44 | 49 | ||
| 45 | private Deobfuscator deobfuscator; | 50 | private Deobfuscator deobfuscator; |
| 46 | private Gui gui; | 51 | private Gui gui; |
| 47 | private SourceIndex index; | 52 | private DecompiledClassSource currentSource; |
| 48 | private ClassEntry currentObfClass; | ||
| 49 | private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; | 53 | private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; |
| 50 | 54 | ||
| 51 | private Path loadedMappingPath; | 55 | private Path loadedMappingPath; |
| @@ -54,8 +58,7 @@ public class GuiController { | |||
| 54 | public GuiController(Gui gui) { | 58 | public GuiController(Gui gui) { |
| 55 | this.gui = gui; | 59 | this.gui = gui; |
| 56 | this.deobfuscator = null; | 60 | this.deobfuscator = null; |
| 57 | this.index = null; | 61 | this.currentSource = null; |
| 58 | this.currentObfClass = null; | ||
| 59 | this.referenceStack = Queues.newArrayDeque(); | 62 | this.referenceStack = Queues.newArrayDeque(); |
| 60 | } | 63 | } |
| 61 | 64 | ||
| @@ -93,7 +96,7 @@ public class GuiController { | |||
| 93 | public void saveMappings(MappingFormat format, Path path) { | 96 | public void saveMappings(MappingFormat format, Path path) { |
| 94 | EntryRemapper mapper = deobfuscator.getMapper(); | 97 | EntryRemapper mapper = deobfuscator.getMapper(); |
| 95 | 98 | ||
| 96 | MappingDelta delta = mapper.takeMappingDelta(); | 99 | MappingDelta<EntryMapping> delta = mapper.takeMappingDelta(); |
| 97 | boolean saveAll = !path.equals(loadedMappingPath); | 100 | boolean saveAll = !path.equals(loadedMappingPath); |
| 98 | 101 | ||
| 99 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> { | 102 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> { |
| @@ -116,189 +119,167 @@ public class GuiController { | |||
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | public void exportSource(final File dirOut) { | 121 | public void exportSource(final File dirOut) { |
| 119 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut, progress)); | 122 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut.toPath(), progress)); |
| 120 | } | 123 | } |
| 121 | 124 | ||
| 122 | public void exportJar(final File fileOut) { | 125 | public void exportJar(final File fileOut) { |
| 123 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeJar(fileOut, progress)); | 126 | ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeTransformedJar(fileOut, progress)); |
| 124 | } | 127 | } |
| 125 | 128 | ||
| 126 | public Token getToken(int pos) { | 129 | public Token getToken(int pos) { |
| 127 | if (this.index == null) { | 130 | if (this.currentSource == null) { |
| 128 | return null; | 131 | return null; |
| 129 | } | 132 | } |
| 130 | return this.index.getReferenceToken(pos); | 133 | return this.currentSource.getIndex().getReferenceToken(pos); |
| 131 | } | 134 | } |
| 132 | 135 | ||
| 133 | @Nullable | 136 | @Nullable |
| 134 | public EntryReference<Entry<?>, Entry<?>> getDeobfReference(Token token) { | 137 | public EntryReference<Entry<?>, Entry<?>> getReference(Token token) { |
| 135 | if (this.index == null) { | 138 | if (this.currentSource == null) { |
| 136 | return null; | 139 | return null; |
| 137 | } | 140 | } |
| 138 | return this.index.getDeobfReference(token); | 141 | return this.currentSource.getIndex().getReference(token); |
| 139 | } | 142 | } |
| 140 | 143 | ||
| 141 | public ReadableToken getReadableToken(Token token) { | 144 | public ReadableToken getReadableToken(Token token) { |
| 142 | if (this.index == null) { | 145 | if (this.currentSource == null) { |
| 143 | return null; | 146 | return null; |
| 144 | } | 147 | } |
| 148 | SourceIndex index = this.currentSource.getIndex(); | ||
| 145 | return new ReadableToken( | 149 | return new ReadableToken( |
| 146 | this.index.getLineNumber(token.start), | 150 | index.getLineNumber(token.start), |
| 147 | this.index.getColumnNumber(token.start), | 151 | index.getColumnNumber(token.start), |
| 148 | this.index.getColumnNumber(token.end) | 152 | index.getColumnNumber(token.end) |
| 149 | ); | 153 | ); |
| 150 | } | 154 | } |
| 151 | 155 | ||
| 152 | public boolean entryHasDeobfuscatedName(Entry<?> deobfEntry) { | 156 | public boolean entryIsInJar(Entry<?> entry) { |
| 153 | EntryResolver resolver = this.deobfuscator.getMapper().getDeobfResolver(); | 157 | if (entry == null) return false; |
| 154 | Entry<?> resolvedEntry = resolver.resolveFirstEntry(deobfEntry, ResolutionStrategy.RESOLVE_ROOT); | 158 | return this.deobfuscator.isRenamable(entry); |
| 155 | return this.deobfuscator.hasDeobfuscatedName(this.deobfuscator.getMapper().obfuscate(resolvedEntry)); | ||
| 156 | } | 159 | } |
| 157 | 160 | ||
| 158 | public boolean entryIsInJar(Entry<?> deobfEntry) { | 161 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { |
| 159 | if (deobfEntry == null) return false; | ||
| 160 | return this.deobfuscator.isObfuscatedIdentifier(this.deobfuscator.getMapper().obfuscate(deobfEntry)); | ||
| 161 | } | ||
| 162 | |||
| 163 | public boolean referenceIsRenameable(EntryReference<Entry<?>, Entry<?>> deobfReference) { | ||
| 164 | if (deobfReference == null) return false; | ||
| 165 | return this.deobfuscator.isRenameable(this.deobfuscator.getMapper().obfuscate(deobfReference)); | ||
| 166 | } | ||
| 167 | |||
| 168 | public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { | ||
| 169 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 170 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 162 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 171 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, obfClassEntry); | 163 | ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, entry); |
| 172 | return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); | 164 | return ClassInheritanceTreeNode.findNode(rootNode, entry); |
| 173 | } | 165 | } |
| 174 | 166 | ||
| 175 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { | 167 | public ClassImplementationsTreeNode getClassImplementations(ClassEntry entry) { |
| 176 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 177 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 168 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 178 | return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, obfClassEntry); | 169 | return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, entry); |
| 179 | } | 170 | } |
| 180 | 171 | ||
| 181 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { | 172 | public MethodInheritanceTreeNode getMethodInheritance(MethodEntry entry) { |
| 182 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 183 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 173 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 184 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, obfMethodEntry); | 174 | MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, entry); |
| 185 | return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); | 175 | return MethodInheritanceTreeNode.findNode(rootNode, entry); |
| 186 | } | 176 | } |
| 187 | 177 | ||
| 188 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { | 178 | public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { |
| 189 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 190 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 179 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 191 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, obfMethodEntry); | 180 | List<MethodImplementationsTreeNode> rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, entry); |
| 192 | if (rootNodes.isEmpty()) { | 181 | if (rootNodes.isEmpty()) { |
| 193 | return null; | 182 | return null; |
| 194 | } | 183 | } |
| 195 | if (rootNodes.size() > 1) { | 184 | if (rootNodes.size() > 1) { |
| 196 | System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); | 185 | System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); |
| 197 | } | 186 | } |
| 198 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); | 187 | return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); |
| 199 | } | 188 | } |
| 200 | 189 | ||
| 201 | public ClassReferenceTreeNode getClassReferences(ClassEntry deobfClassEntry) { | 190 | public ClassReferenceTreeNode getClassReferences(ClassEntry entry) { |
| 202 | ClassEntry obfClassEntry = this.deobfuscator.getMapper().obfuscate(deobfClassEntry); | ||
| 203 | Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); | 191 | Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 204 | ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, obfClassEntry); | 192 | ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, entry); |
| 205 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 193 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 206 | return rootNode; | 194 | return rootNode; |
| 207 | } | 195 | } |
| 208 | 196 | ||
| 209 | public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { | 197 | public FieldReferenceTreeNode getFieldReferences(FieldEntry entry) { |
| 210 | FieldEntry obfFieldEntry = this.deobfuscator.getMapper().obfuscate(deobfFieldEntry); | ||
| 211 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 198 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 212 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, obfFieldEntry); | 199 | FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, entry); |
| 213 | rootNode.load(this.deobfuscator.getJarIndex(), true); | 200 | rootNode.load(this.deobfuscator.getJarIndex(), true); |
| 214 | return rootNode; | 201 | return rootNode; |
| 215 | } | 202 | } |
| 216 | 203 | ||
| 217 | public MethodReferenceTreeNode getMethodReferences(MethodEntry deobfMethodEntry, boolean recursive) { | 204 | public MethodReferenceTreeNode getMethodReferences(MethodEntry entry, boolean recursive) { |
| 218 | MethodEntry obfMethodEntry = this.deobfuscator.getMapper().obfuscate(deobfMethodEntry); | ||
| 219 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); | 205 | Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); |
| 220 | MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, obfMethodEntry); | 206 | MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, entry); |
| 221 | rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); | 207 | rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); |
| 222 | return rootNode; | 208 | return rootNode; |
| 223 | } | 209 | } |
| 224 | 210 | ||
| 225 | public void rename(EntryReference<Entry<?>, Entry<?>> deobfReference, String newName, boolean refreshClassTree) { | 211 | public void rename(EntryReference<Entry<?>, Entry<?>> reference, String newName, boolean refreshClassTree) { |
| 226 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 212 | this.deobfuscator.rename(reference.getNameableEntry(), newName); |
| 227 | this.deobfuscator.rename(obfReference.getNameableEntry(), newName); | ||
| 228 | |||
| 229 | if (refreshClassTree && deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | ||
| 230 | this.gui.moveClassTree(deobfReference, newName); | ||
| 231 | refreshCurrentClass(obfReference); | ||
| 232 | 213 | ||
| 214 | if (refreshClassTree && reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) | ||
| 215 | this.gui.moveClassTree(reference, newName); | ||
| 216 | refreshCurrentClass(reference); | ||
| 233 | } | 217 | } |
| 234 | 218 | ||
| 235 | public void removeMapping(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 219 | public void removeMapping(EntryReference<Entry<?>, Entry<?>> reference) { |
| 236 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 220 | this.deobfuscator.removeMapping(reference.getNameableEntry()); |
| 237 | this.deobfuscator.removeMapping(obfReference.getNameableEntry()); | 221 | if (reference.entry instanceof ClassEntry) |
| 238 | if (deobfReference.entry instanceof ClassEntry) | 222 | this.gui.moveClassTree(reference, false, true); |
| 239 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), false, true); | 223 | refreshCurrentClass(reference); |
| 240 | refreshCurrentClass(obfReference); | ||
| 241 | } | 224 | } |
| 242 | 225 | ||
| 243 | public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 226 | public void markAsDeobfuscated(EntryReference<Entry<?>, Entry<?>> reference) { |
| 244 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 227 | this.deobfuscator.markAsDeobfuscated(reference.getNameableEntry()); |
| 245 | this.deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); | 228 | if (reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) |
| 246 | if (deobfReference.entry instanceof ClassEntry && !((ClassEntry) deobfReference.entry).isInnerClass()) | 229 | this.gui.moveClassTree(reference, true, false); |
| 247 | this.gui.moveClassTree(deobfReference, obfReference.entry.getName(), true, false); | 230 | refreshCurrentClass(reference); |
| 248 | refreshCurrentClass(obfReference); | ||
| 249 | } | 231 | } |
| 250 | 232 | ||
| 251 | public void openDeclaration(Entry<?> deobfEntry) { | 233 | public void openDeclaration(Entry<?> entry) { |
| 252 | if (deobfEntry == null) { | 234 | if (entry == null) { |
| 253 | throw new IllegalArgumentException("Entry cannot be null!"); | 235 | throw new IllegalArgumentException("Entry cannot be null!"); |
| 254 | } | 236 | } |
| 255 | openReference(new EntryReference<>(deobfEntry, deobfEntry.getName())); | 237 | openReference(new EntryReference<>(entry, entry.getName())); |
| 256 | } | 238 | } |
| 257 | 239 | ||
| 258 | public void openReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 240 | public void openReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 259 | if (deobfReference == null) { | 241 | if (reference == null) { |
| 260 | throw new IllegalArgumentException("Reference cannot be null!"); | 242 | throw new IllegalArgumentException("Reference cannot be null!"); |
| 261 | } | 243 | } |
| 262 | 244 | ||
| 263 | // get the reference target class | 245 | // get the reference target class |
| 264 | EntryReference<Entry<?>, Entry<?>> obfReference = this.deobfuscator.getMapper().obfuscate(deobfReference); | 246 | ClassEntry classEntry = reference.getLocationClassEntry(); |
| 265 | ClassEntry obfClassEntry = obfReference.getLocationClassEntry(); | 247 | if (!this.deobfuscator.isRenamable(classEntry)) { |
| 266 | if (!this.deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { | 248 | throw new IllegalArgumentException("Obfuscated class " + classEntry + " was not found in the jar!"); |
| 267 | throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); | ||
| 268 | } | 249 | } |
| 269 | if (this.currentObfClass == null || !this.currentObfClass.equals(obfClassEntry)) { | 250 | |
| 251 | if (this.currentSource == null || !this.currentSource.getEntry().equals(classEntry)) { | ||
| 270 | // deobfuscate the class, then navigate to the reference | 252 | // deobfuscate the class, then navigate to the reference |
| 271 | this.currentObfClass = obfClassEntry; | 253 | loadClass(classEntry, () -> showReference(reference)); |
| 272 | deobfuscate(this.currentObfClass, obfReference); | ||
| 273 | } else { | 254 | } else { |
| 274 | showReference(obfReference); | 255 | showReference(reference); |
| 275 | } | 256 | } |
| 276 | } | 257 | } |
| 277 | 258 | ||
| 278 | private void showReference(EntryReference<Entry<?>, Entry<?>> obfReference) { | 259 | private void showReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 279 | EntryRemapper mapper = this.deobfuscator.getMapper(); | 260 | EntryRemapper mapper = this.deobfuscator.getMapper(); |
| 280 | 261 | ||
| 281 | Collection<Token> tokens = mapper.getObfResolver().resolveReference(obfReference, ResolutionStrategy.RESOLVE_ROOT) | 262 | SourceIndex index = this.currentSource.getIndex(); |
| 263 | Collection<Token> tokens = mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_ROOT) | ||
| 282 | .stream() | 264 | .stream() |
| 283 | .map(mapper::deobfuscate) | 265 | .flatMap(r -> index.getReferenceTokens(r).stream()) |
| 284 | .flatMap(reference -> index.getReferenceTokens(reference).stream()) | ||
| 285 | .collect(Collectors.toList()); | 266 | .collect(Collectors.toList()); |
| 286 | 267 | ||
| 287 | if (tokens.isEmpty()) { | 268 | if (tokens.isEmpty()) { |
| 288 | // DEBUG | 269 | // DEBUG |
| 289 | System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentObfClass)); | 270 | System.err.println(String.format("WARNING: no tokens found for %s in %s", tokens, this.currentSource.getEntry())); |
| 290 | } else { | 271 | } else { |
| 291 | this.gui.showTokens(tokens); | 272 | this.gui.showTokens(tokens); |
| 292 | } | 273 | } |
| 293 | } | 274 | } |
| 294 | 275 | ||
| 295 | public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> deobfReference) { | 276 | public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> reference) { |
| 296 | this.referenceStack.push(this.deobfuscator.getMapper().obfuscate(deobfReference)); | 277 | this.referenceStack.push(reference); |
| 297 | } | 278 | } |
| 298 | 279 | ||
| 299 | public void openPreviousReference() { | 280 | public void openPreviousReference() { |
| 300 | if (hasPreviousLocation()) { | 281 | if (hasPreviousLocation()) { |
| 301 | openReference(this.deobfuscator.getMapper().deobfuscate(this.referenceStack.pop())); | 282 | openReference(this.referenceStack.pop()); |
| 302 | } | 283 | } |
| 303 | } | 284 | } |
| 304 | 285 | ||
| @@ -318,97 +299,65 @@ public class GuiController { | |||
| 318 | refreshCurrentClass(null); | 299 | refreshCurrentClass(null); |
| 319 | } | 300 | } |
| 320 | 301 | ||
| 321 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> obfReference) { | 302 | private void refreshCurrentClass(EntryReference<Entry<?>, Entry<?>> reference) { |
| 322 | if (this.currentObfClass != null) { | 303 | if (currentSource != null) { |
| 323 | deobfuscate(this.currentObfClass, obfReference); | 304 | loadClass(currentSource.getEntry(), () -> { |
| 305 | if (reference != null) { | ||
| 306 | showReference(reference); | ||
| 307 | } | ||
| 308 | }); | ||
| 324 | } | 309 | } |
| 325 | } | 310 | } |
| 326 | 311 | ||
| 327 | private void deobfuscate(final ClassEntry classEntry, final EntryReference<Entry<?>, Entry<?>> obfReference) { | 312 | private void loadClass(ClassEntry classEntry, Runnable callback) { |
| 313 | ClassEntry targetClass = classEntry.getOutermostClass(); | ||
| 328 | 314 | ||
| 329 | this.gui.setSource("(deobfuscating...)"); | 315 | boolean requiresDecompile = currentSource == null || !currentSource.getEntry().equals(targetClass); |
| 316 | if (requiresDecompile) { | ||
| 317 | gui.setEditorText("(decompiling...)"); | ||
| 318 | } | ||
| 330 | 319 | ||
| 331 | // run the deobfuscator in a separate thread so we don't block the GUI event queue | 320 | DECOMPILER_SERVICE.submit(() -> { |
| 332 | new Thread(() -> | 321 | try { |
| 333 | { | 322 | if (requiresDecompile) { |
| 334 | // decompile,deobfuscate the bytecode | 323 | decompileSource(targetClass, deobfuscator.getObfSourceProvider()); |
| 335 | CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClass().getFullName()); | ||
| 336 | if (sourceTree == null) { | ||
| 337 | // decompilation of this class is not supported | ||
| 338 | gui.setSource("Unable to find class: " + classEntry); | ||
| 339 | return; | ||
| 340 | } | ||
| 341 | String source = deobfuscator.getSource(sourceTree); | ||
| 342 | index = deobfuscator.getSourceIndex(sourceTree, source); | ||
| 343 | |||
| 344 | String sourceString = index.getSource(); | ||
| 345 | |||
| 346 | // set the highlighted tokens | ||
| 347 | List<Token> obfuscatedTokens = Lists.newArrayList(); | ||
| 348 | List<Token> proposedTokens = Lists.newArrayList(); | ||
| 349 | List<Token> deobfuscatedTokens = Lists.newArrayList(); | ||
| 350 | List<Token> otherTokens = Lists.newArrayList(); | ||
| 351 | |||
| 352 | int offset = 0; | ||
| 353 | Map<Token, Token> tokenRemap = new HashMap<>(); | ||
| 354 | boolean remapped = false; | ||
| 355 | |||
| 356 | for (Token inToken : index.referenceTokens()) { | ||
| 357 | Token token = inToken.move(offset); | ||
| 358 | |||
| 359 | EntryReference<Entry<?>, Entry<?>> reference = index.getDeobfReference(inToken); | ||
| 360 | if (referenceIsRenameable(reference)) { | ||
| 361 | boolean added = false; | ||
| 362 | |||
| 363 | if (!entryHasDeobfuscatedName(reference.getNameableEntry())) { | ||
| 364 | Entry<?> obfEntry = deobfuscator.getMapper().obfuscate(reference.getNameableEntry()); | ||
| 365 | if (obfEntry instanceof FieldEntry) { | ||
| 366 | for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { | ||
| 367 | String owner = obfEntry.getContainingClass().getFullName(); | ||
| 368 | String proposal = plugin.proposeFieldName(owner, obfEntry.getName(), ((FieldEntry) obfEntry).getDesc().toString()); | ||
| 369 | if (proposal != null) { | ||
| 370 | proposedTokens.add(token); | ||
| 371 | offset += token.getRenameOffset(proposal); | ||
| 372 | sourceString = token.rename(sourceString, proposal); | ||
| 373 | added = true; | ||
| 374 | remapped = true; | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | if (!added) { | ||
| 382 | if (entryHasDeobfuscatedName(reference.getNameableEntry())) { | ||
| 383 | deobfuscatedTokens.add(token); | ||
| 384 | } else { | ||
| 385 | obfuscatedTokens.add(token); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | } else { | ||
| 389 | otherTokens.add(token); | ||
| 390 | } | 324 | } |
| 391 | 325 | ||
| 392 | tokenRemap.put(inToken, token); | 326 | remapSource(deobfuscator.getMapper().getDeobfuscator()); |
| 327 | callback.run(); | ||
| 328 | } catch (Throwable t) { | ||
| 329 | System.err.println("An exception was thrown while decompiling class " + classEntry.getFullName()); | ||
| 330 | t.printStackTrace(System.err); | ||
| 393 | } | 331 | } |
| 332 | }); | ||
| 333 | } | ||
| 394 | 334 | ||
| 395 | if (remapped) { | 335 | private void decompileSource(ClassEntry targetClass, SourceProvider sourceProvider) { |
| 396 | index.remap(sourceString, tokenRemap); | 336 | CompilationUnit sourceTree = sourceProvider.getSources(targetClass.getFullName()); |
| 397 | } | 337 | if (sourceTree == null) { |
| 338 | gui.setEditorText("Unable to find class: " + targetClass); | ||
| 339 | return; | ||
| 340 | } | ||
| 398 | 341 | ||
| 399 | gui.setSource(sourceString); | 342 | DropImportAstTransform.INSTANCE.run(sourceTree); |
| 400 | if (obfReference != null) { | 343 | |
| 401 | showReference(obfReference); | 344 | String sourceString = sourceProvider.writeSourceToString(sourceTree); |
| 402 | } | 345 | |
| 346 | SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true); | ||
| 347 | index.resolveReferences(deobfuscator.getMapper().getObfResolver()); | ||
| 348 | |||
| 349 | currentSource = new DecompiledClassSource(targetClass, deobfuscator, index); | ||
| 350 | } | ||
| 351 | |||
| 352 | private void remapSource(Translator translator) { | ||
| 353 | if (currentSource == null) { | ||
| 354 | return; | ||
| 355 | } | ||
| 356 | |||
| 357 | currentSource.remapSource(translator); | ||
| 403 | 358 | ||
| 404 | gui.setEditorTheme(Config.getInstance().lookAndFeel); | 359 | gui.setEditorTheme(Config.getInstance().lookAndFeel); |
| 405 | gui.setHighlightedTokens(ImmutableMap.of( | 360 | gui.setSource(currentSource); |
| 406 | "obfuscated", obfuscatedTokens, | ||
| 407 | "proposed", proposedTokens, | ||
| 408 | "deobfuscated", deobfuscatedTokens, | ||
| 409 | "other", otherTokens | ||
| 410 | )); | ||
| 411 | }).start(); | ||
| 412 | } | 361 | } |
| 413 | 362 | ||
| 414 | public Deobfuscator getDeobfuscator() { | 363 | public Deobfuscator getDeobfuscator() { |
diff --git a/src/main/java/cuchaz/enigma/gui/SourceRemapper.java b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java new file mode 100644 index 00000000..f38f44ea --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/SourceRemapper.java | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | package cuchaz.enigma.gui; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.Token; | ||
| 4 | |||
| 5 | import java.util.HashMap; | ||
| 6 | import java.util.Map; | ||
| 7 | |||
| 8 | public class SourceRemapper { | ||
| 9 | private final String source; | ||
| 10 | private final Iterable<Token> tokens; | ||
| 11 | |||
| 12 | public SourceRemapper(String source, Iterable<Token> tokens) { | ||
| 13 | this.source = source; | ||
| 14 | this.tokens = tokens; | ||
| 15 | } | ||
| 16 | |||
| 17 | public Result remap(Remapper remapper) { | ||
| 18 | StringBuffer remappedSource = new StringBuffer(source); | ||
| 19 | Map<Token, Token> remappedTokens = new HashMap<>(); | ||
| 20 | |||
| 21 | int accumulatedOffset = 0; | ||
| 22 | for (Token token : tokens) { | ||
| 23 | Token movedToken = token.move(accumulatedOffset); | ||
| 24 | |||
| 25 | String remappedName = remapper.remap(token, movedToken); | ||
| 26 | if (remappedName != null) { | ||
| 27 | accumulatedOffset += movedToken.getRenameOffset(remappedName); | ||
| 28 | movedToken.rename(remappedSource, remappedName); | ||
| 29 | } | ||
| 30 | |||
| 31 | if (!token.equals(movedToken)) { | ||
| 32 | remappedTokens.put(token, movedToken); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return new Result(remappedSource.toString(), remappedTokens); | ||
| 37 | } | ||
| 38 | |||
| 39 | public static class Result { | ||
| 40 | private final String remappedSource; | ||
| 41 | private final Map<Token, Token> remappedTokens; | ||
| 42 | |||
| 43 | Result(String remappedSource, Map<Token, Token> remappedTokens) { | ||
| 44 | this.remappedSource = remappedSource; | ||
| 45 | this.remappedTokens = remappedTokens; | ||
| 46 | } | ||
| 47 | |||
| 48 | public String getSource() { | ||
| 49 | return remappedSource; | ||
| 50 | } | ||
| 51 | |||
| 52 | public Token getRemappedToken(Token token) { | ||
| 53 | return remappedTokens.getOrDefault(token, token); | ||
| 54 | } | ||
| 55 | |||
| 56 | public boolean isEmpty() { | ||
| 57 | return remappedTokens.isEmpty(); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | public interface Remapper { | ||
| 62 | String remap(Token token, Token movedToken); | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index f4f02776..dfbfa650 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java | |||
| @@ -152,12 +152,8 @@ public class MenuBar extends JMenuBar { | |||
| 152 | if (this.gui.getController().isDirty()) { | 152 | if (this.gui.getController().isDirty()) { |
| 153 | this.gui.showDiscardDiag((response -> { | 153 | this.gui.showDiscardDiag((response -> { |
| 154 | if (response == JOptionPane.YES_OPTION) { | 154 | if (response == JOptionPane.YES_OPTION) { |
| 155 | try { | 155 | gui.saveMapping(); |
| 156 | gui.saveMapping(); | 156 | this.gui.getController().closeMappings(); |
| 157 | this.gui.getController().closeMappings(); | ||
| 158 | } catch (IOException e) { | ||
| 159 | throw new Error(e); | ||
| 160 | } | ||
| 161 | } else if (response == JOptionPane.NO_OPTION) | 157 | } else if (response == JOptionPane.NO_OPTION) |
| 162 | this.gui.getController().closeMappings(); | 158 | this.gui.getController().closeMappings(); |
| 163 | return null; | 159 | return null; |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java index 10366ced..cef64943 100644 --- a/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java +++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java | |||
| @@ -34,7 +34,9 @@ public class BoxHighlightPainter implements Highlighter.HighlightPainter { | |||
| 34 | public static Rectangle getBounds(JTextComponent text, int start, int end) { | 34 | public static Rectangle getBounds(JTextComponent text, int start, int end) { |
| 35 | try { | 35 | try { |
| 36 | // determine the bounds of the text | 36 | // determine the bounds of the text |
| 37 | Rectangle bounds = text.modelToView(start).union(text.modelToView(end)); | 37 | Rectangle startRect = text.modelToView(start); |
| 38 | Rectangle endRect = text.modelToView(end); | ||
| 39 | Rectangle bounds = startRect.union(endRect); | ||
| 38 | 40 | ||
| 39 | // adjust the box so it looks nice | 41 | // adjust the box so it looks nice |
| 40 | bounds.x -= 2; | 42 | bounds.x -= 2; |
diff --git a/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java new file mode 100644 index 00000000..ae23f324 --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/highlight/TokenHighlightType.java | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | package cuchaz.enigma.gui.highlight; | ||
| 2 | |||
| 3 | public enum TokenHighlightType { | ||
| 4 | OBFUSCATED, | ||
| 5 | DEOBFUSCATED, | ||
| 6 | PROPOSED | ||
| 7 | } | ||
diff --git a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index bf6b1788..922f8f24 100644 --- a/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java | |||
| @@ -17,13 +17,19 @@ import javax.swing.tree.DefaultMutableTreeNode; | |||
| 17 | 17 | ||
| 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { | 18 | public class ClassSelectorClassNode extends DefaultMutableTreeNode { |
| 19 | 19 | ||
| 20 | private final ClassEntry obfEntry; | ||
| 20 | private ClassEntry classEntry; | 21 | private ClassEntry classEntry; |
| 21 | 22 | ||
| 22 | public ClassSelectorClassNode(ClassEntry classEntry) { | 23 | public ClassSelectorClassNode(ClassEntry obfEntry, ClassEntry classEntry) { |
| 24 | this.obfEntry = obfEntry; | ||
| 23 | this.classEntry = classEntry; | 25 | this.classEntry = classEntry; |
| 24 | this.setUserObject(classEntry); | 26 | this.setUserObject(classEntry); |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 29 | public ClassEntry getObfEntry() { | ||
| 30 | return obfEntry; | ||
| 31 | } | ||
| 32 | |||
| 27 | public ClassEntry getClassEntry() { | 33 | public ClassEntry getClassEntry() { |
| 28 | return this.classEntry; | 34 | return this.classEntry; |
| 29 | } | 35 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java b/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java new file mode 100644 index 00000000..18c966cd --- /dev/null +++ b/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 4 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 5 | |||
| 6 | import java.util.Collection; | ||
| 7 | import java.util.Locale; | ||
| 8 | |||
| 9 | public class LocalNameGenerator { | ||
| 10 | public static String generateArgumentName(int index, TypeDescriptor desc, Collection<TypeDescriptor> arguments) { | ||
| 11 | boolean uniqueType = arguments.stream().filter(desc::equals).count() <= 1; | ||
| 12 | String translatedName; | ||
| 13 | int nameIndex = index + 1; | ||
| 14 | StringBuilder nameBuilder = new StringBuilder(getTypeName(desc)); | ||
| 15 | if (!uniqueType || NameValidator.isReserved(nameBuilder.toString())) { | ||
| 16 | nameBuilder.append(nameIndex); | ||
| 17 | } | ||
| 18 | translatedName = nameBuilder.toString(); | ||
| 19 | return translatedName; | ||
| 20 | } | ||
| 21 | |||
| 22 | public static String generateLocalVariableName(int index, TypeDescriptor desc) { | ||
| 23 | int nameIndex = index + 1; | ||
| 24 | return getTypeName(desc) + nameIndex; | ||
| 25 | } | ||
| 26 | |||
| 27 | private static String getTypeName(TypeDescriptor desc) { | ||
| 28 | // Unfortunately each of these have different name getters, so they have different code paths | ||
| 29 | if (desc.isPrimitive()) { | ||
| 30 | TypeDescriptor.Primitive argCls = desc.getPrimitive(); | ||
| 31 | return argCls.name().toLowerCase(Locale.ROOT); | ||
| 32 | } else if (desc.isArray()) { | ||
| 33 | // List types would require this whole block again, so just go with aListx | ||
| 34 | return "arr"; | ||
| 35 | } else if (desc.isType()) { | ||
| 36 | String typeName = desc.getTypeEntry().getSimpleName().replace("$", ""); | ||
| 37 | typeName = typeName.substring(0, 1).toLowerCase(Locale.ROOT) + typeName.substring(1); | ||
| 38 | return typeName; | ||
| 39 | } else { | ||
| 40 | System.err.println("Encountered invalid argument type descriptor " + desc.toString()); | ||
| 41 | return "var"; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java index b7d8d17e..1203aba0 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java | |||
| @@ -6,7 +6,6 @@ import cuchaz.enigma.translation.Translatable; | |||
| 6 | import cuchaz.enigma.translation.Translator; | 6 | import cuchaz.enigma.translation.Translator; |
| 7 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | 7 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 9 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | 9 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; |
| 11 | import cuchaz.enigma.translation.representation.entry.Entry; | 10 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 12 | 11 | ||
| @@ -14,59 +13,25 @@ import javax.annotation.Nullable; | |||
| 14 | import java.util.Collection; | 13 | import java.util.Collection; |
| 15 | 14 | ||
| 16 | public class EntryRemapper { | 15 | public class EntryRemapper { |
| 17 | private final EntryTree<EntryMapping> obfToDeobf; | 16 | private final DeltaTrackingTree<EntryMapping> obfToDeobf; |
| 18 | private final DeltaTrackingTree<EntryMapping> deobfToObf; | ||
| 19 | |||
| 20 | private final JarIndex obfIndex; | ||
| 21 | 17 | ||
| 22 | private final EntryResolver obfResolver; | 18 | private final EntryResolver obfResolver; |
| 23 | private EntryResolver deobfResolver; | ||
| 24 | |||
| 25 | private final Translator deobfuscator; | 19 | private final Translator deobfuscator; |
| 26 | private Translator obfuscator; | ||
| 27 | 20 | ||
| 28 | private final MappingValidator validator; | 21 | private final MappingValidator validator; |
| 29 | 22 | ||
| 30 | private EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf, EntryTree<EntryMapping> deobfToObf) { | 23 | public EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf) { |
| 31 | this.obfToDeobf = obfToDeobf; | 24 | this.obfToDeobf = new DeltaTrackingTree<>(obfToDeobf); |
| 32 | this.deobfToObf = new DeltaTrackingTree<>(deobfToObf); | ||
| 33 | 25 | ||
| 34 | this.obfIndex = jarIndex; | ||
| 35 | this.obfResolver = jarIndex.getEntryResolver(); | 26 | this.obfResolver = jarIndex.getEntryResolver(); |
| 36 | 27 | ||
| 37 | this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver); | 28 | this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver); |
| 38 | rebuildDeobfIndex(); | ||
| 39 | 29 | ||
| 40 | this.validator = new MappingValidator(this.deobfToObf, deobfuscator, obfResolver); | 30 | this.validator = new MappingValidator(obfToDeobf, deobfuscator, obfResolver); |
| 41 | } | 31 | } |
| 42 | 32 | ||
| 43 | public EntryRemapper(JarIndex jarIndex) { | 33 | public EntryRemapper(JarIndex jarIndex) { |
| 44 | this(jarIndex, new HashEntryTree<>(), new HashEntryTree<>()); | 34 | this(jarIndex, new HashEntryTree<>()); |
| 45 | } | ||
| 46 | |||
| 47 | public EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> deobfuscationTrees) { | ||
| 48 | this(jarIndex, deobfuscationTrees, inverse(deobfuscationTrees)); | ||
| 49 | } | ||
| 50 | |||
| 51 | private static EntryTree<EntryMapping> inverse(EntryTree<EntryMapping> tree) { | ||
| 52 | Translator translator = new MappingTranslator(tree, VoidEntryResolver.INSTANCE); | ||
| 53 | EntryTree<EntryMapping> inverse = new HashEntryTree<>(); | ||
| 54 | |||
| 55 | // Naive approach, could operate on the nodes of the tree. However, this runs infrequently. | ||
| 56 | Collection<Entry<?>> entries = tree.getAllEntries(); | ||
| 57 | for (Entry<?> sourceEntry : entries) { | ||
| 58 | Entry<?> targetEntry = translator.translate(sourceEntry); | ||
| 59 | inverse.insert(targetEntry, new EntryMapping(sourceEntry.getName())); | ||
| 60 | } | ||
| 61 | |||
| 62 | return inverse; | ||
| 63 | } | ||
| 64 | |||
| 65 | private void rebuildDeobfIndex() { | ||
| 66 | JarIndex deobfIndex = obfIndex.remapped(deobfuscator); | ||
| 67 | |||
| 68 | this.deobfResolver = deobfIndex.getEntryResolver(); | ||
| 69 | this.obfuscator = new MappingTranslator(deobfToObf, deobfResolver); | ||
| 70 | } | 35 | } |
| 71 | 36 | ||
| 72 | public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { | 37 | public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { |
| @@ -76,84 +41,31 @@ public class EntryRemapper { | |||
| 76 | validator.validateRename(resolvedEntry, deobfMapping.getTargetName()); | 41 | validator.validateRename(resolvedEntry, deobfMapping.getTargetName()); |
| 77 | } | 42 | } |
| 78 | 43 | ||
| 79 | setObfToDeobf(resolvedEntry, deobfMapping); | 44 | obfToDeobf.insert(obfuscatedEntry, deobfMapping); |
| 80 | } | 45 | } |
| 81 | |||
| 82 | // Temporary hack, not very performant | ||
| 83 | rebuildDeobfIndex(); | ||
| 84 | } | ||
| 85 | |||
| 86 | public <E extends Entry<?>> void mapFromDeobf(E deobfuscatedEntry, @Nullable EntryMapping deobfMapping) { | ||
| 87 | E obfuscatedEntry = obfuscate(deobfuscatedEntry); | ||
| 88 | mapFromObf(obfuscatedEntry, deobfMapping); | ||
| 89 | } | 46 | } |
| 90 | 47 | ||
| 91 | public void removeByObf(Entry<?> obfuscatedEntry) { | 48 | public void removeByObf(Entry<?> obfuscatedEntry) { |
| 92 | mapFromObf(obfuscatedEntry, null); | 49 | mapFromObf(obfuscatedEntry, null); |
| 93 | } | 50 | } |
| 94 | 51 | ||
| 95 | public void removeByDeobf(Entry<?> deobfuscatedEntry) { | ||
| 96 | mapFromObf(obfuscate(deobfuscatedEntry), null); | ||
| 97 | } | ||
| 98 | |||
| 99 | private <E extends Entry<?>> void setObfToDeobf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { | ||
| 100 | E prevDeobf = deobfuscate(obfuscatedEntry); | ||
| 101 | obfToDeobf.insert(obfuscatedEntry, deobfMapping); | ||
| 102 | |||
| 103 | E newDeobf = deobfuscate(obfuscatedEntry); | ||
| 104 | |||
| 105 | // Reconstruct the children of this node in the deobf -> obf tree with our new mapping | ||
| 106 | // We only need to do this for deobf -> obf because the obf tree is always consistent on the left hand side | ||
| 107 | // We lookup by obf, and the obf never changes. This is not the case for deobf so we need to update the tree. | ||
| 108 | |||
| 109 | EntryTreeNode<EntryMapping> node = deobfToObf.findNode(prevDeobf); | ||
| 110 | if (node != null) { | ||
| 111 | for (EntryTreeNode<EntryMapping> child : node.getNodesRecursively()) { | ||
| 112 | Entry<?> entry = child.getEntry(); | ||
| 113 | EntryMapping mapping = new EntryMapping(obfuscate(entry).getName()); | ||
| 114 | |||
| 115 | deobfToObf.insert(entry.replaceAncestor(prevDeobf, newDeobf), mapping); | ||
| 116 | deobfToObf.remove(entry); | ||
| 117 | } | ||
| 118 | } else { | ||
| 119 | deobfToObf.insert(newDeobf, new EntryMapping(obfuscatedEntry.getName())); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | @Nullable | 52 | @Nullable |
| 124 | public EntryMapping getDeobfMapping(Entry<?> entry) { | 53 | public EntryMapping getDeobfMapping(Entry<?> entry) { |
| 125 | return obfToDeobf.get(entry); | 54 | return obfToDeobf.get(entry); |
| 126 | } | 55 | } |
| 127 | 56 | ||
| 128 | @Nullable | ||
| 129 | public EntryMapping getObfMapping(Entry<?> entry) { | ||
| 130 | return deobfToObf.get(entry); | ||
| 131 | } | ||
| 132 | |||
| 133 | public boolean hasDeobfMapping(Entry<?> obfEntry) { | 57 | public boolean hasDeobfMapping(Entry<?> obfEntry) { |
| 134 | return obfToDeobf.contains(obfEntry); | 58 | return obfToDeobf.contains(obfEntry); |
| 135 | } | 59 | } |
| 136 | 60 | ||
| 137 | public boolean hasObfMapping(Entry<?> deobfEntry) { | ||
| 138 | return deobfToObf.contains(deobfEntry); | ||
| 139 | } | ||
| 140 | |||
| 141 | public <T extends Translatable> T deobfuscate(T translatable) { | 61 | public <T extends Translatable> T deobfuscate(T translatable) { |
| 142 | return deobfuscator.translate(translatable); | 62 | return deobfuscator.translate(translatable); |
| 143 | } | 63 | } |
| 144 | 64 | ||
| 145 | public <T extends Translatable> T obfuscate(T translatable) { | ||
| 146 | return obfuscator.translate(translatable); | ||
| 147 | } | ||
| 148 | |||
| 149 | public Translator getDeobfuscator() { | 65 | public Translator getDeobfuscator() { |
| 150 | return deobfuscator; | 66 | return deobfuscator; |
| 151 | } | 67 | } |
| 152 | 68 | ||
| 153 | public Translator getObfuscator() { | ||
| 154 | return obfuscator; | ||
| 155 | } | ||
| 156 | |||
| 157 | public Collection<Entry<?>> getObfEntries() { | 69 | public Collection<Entry<?>> getObfEntries() { |
| 158 | return obfToDeobf.getAllEntries(); | 70 | return obfToDeobf.getAllEntries(); |
| 159 | } | 71 | } |
| @@ -162,40 +74,23 @@ public class EntryRemapper { | |||
| 162 | return obfToDeobf.getRootEntries(); | 74 | return obfToDeobf.getRootEntries(); |
| 163 | } | 75 | } |
| 164 | 76 | ||
| 165 | public Collection<Entry<?>> getDeobfEntries() { | ||
| 166 | return deobfToObf.getAllEntries(); | ||
| 167 | } | ||
| 168 | |||
| 169 | public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) { | 77 | public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) { |
| 170 | return obfToDeobf.getChildren(obfuscatedEntry); | 78 | return obfToDeobf.getChildren(obfuscatedEntry); |
| 171 | } | 79 | } |
| 172 | 80 | ||
| 173 | public Collection<Entry<?>> getDeobfChildren(Entry<?> deobfuscatedEntry) { | 81 | public DeltaTrackingTree<EntryMapping> getObfToDeobf() { |
| 174 | return deobfToObf.getChildren(deobfuscatedEntry); | ||
| 175 | } | ||
| 176 | |||
| 177 | public EntryTree<EntryMapping> getObfToDeobf() { | ||
| 178 | return obfToDeobf; | 82 | return obfToDeobf; |
| 179 | } | 83 | } |
| 180 | 84 | ||
| 181 | public DeltaTrackingTree<EntryMapping> getDeobfToObf() { | 85 | public MappingDelta<EntryMapping> takeMappingDelta() { |
| 182 | return deobfToObf; | 86 | return obfToDeobf.takeDelta(); |
| 183 | } | ||
| 184 | |||
| 185 | public MappingDelta takeMappingDelta() { | ||
| 186 | MappingDelta delta = deobfToObf.takeDelta(); | ||
| 187 | return delta.translate(obfuscator, VoidEntryResolver.INSTANCE, deobfToObf); | ||
| 188 | } | 87 | } |
| 189 | 88 | ||
| 190 | public boolean isDirty() { | 89 | public boolean isDirty() { |
| 191 | return deobfToObf.isDirty(); | 90 | return obfToDeobf.isDirty(); |
| 192 | } | 91 | } |
| 193 | 92 | ||
| 194 | public EntryResolver getObfResolver() { | 93 | public EntryResolver getObfResolver() { |
| 195 | return obfResolver; | 94 | return obfResolver; |
| 196 | } | 95 | } |
| 197 | |||
| 198 | public EntryResolver getDeobfResolver() { | ||
| 199 | return deobfResolver; | ||
| 200 | } | ||
| 201 | } | 96 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java index 4fba49d5..9f1f468b 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java | |||
| @@ -6,28 +6,35 @@ import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | |||
| 6 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 6 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 7 | import cuchaz.enigma.translation.representation.entry.Entry; | 7 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 8 | 8 | ||
| 9 | public class MappingDelta implements Translatable { | 9 | public class MappingDelta<T> implements Translatable { |
| 10 | public static final Object PLACEHOLDER = new Object(); | 10 | public static final Object PLACEHOLDER = new Object(); |
| 11 | 11 | ||
| 12 | private final EntryTree<T> baseMappings; | ||
| 13 | |||
| 12 | private final EntryTree<Object> additions; | 14 | private final EntryTree<Object> additions; |
| 13 | private final EntryTree<Object> deletions; | 15 | private final EntryTree<Object> deletions; |
| 14 | 16 | ||
| 15 | public MappingDelta(EntryTree<Object> additions, EntryTree<Object> deletions) { | 17 | public MappingDelta(EntryTree<T> baseMappings, EntryTree<Object> additions, EntryTree<Object> deletions) { |
| 18 | this.baseMappings = baseMappings; | ||
| 16 | this.additions = additions; | 19 | this.additions = additions; |
| 17 | this.deletions = deletions; | 20 | this.deletions = deletions; |
| 18 | } | 21 | } |
| 19 | 22 | ||
| 20 | public MappingDelta() { | 23 | public MappingDelta(EntryTree<T> baseMappings) { |
| 21 | this(new HashEntryTree<>(), new HashEntryTree<>()); | 24 | this(baseMappings, new HashEntryTree<>(), new HashEntryTree<>()); |
| 22 | } | 25 | } |
| 23 | 26 | ||
| 24 | public static MappingDelta added(EntryTree<EntryMapping> mappings) { | 27 | public static <T> MappingDelta<T> added(EntryTree<T> mappings) { |
| 25 | EntryTree<Object> additions = new HashEntryTree<>(); | 28 | EntryTree<Object> additions = new HashEntryTree<>(); |
| 26 | for (Entry<?> entry : mappings.getAllEntries()) { | 29 | for (Entry<?> entry : mappings.getAllEntries()) { |
| 27 | additions.insert(entry, PLACEHOLDER); | 30 | additions.insert(entry, PLACEHOLDER); |
| 28 | } | 31 | } |
| 29 | 32 | ||
| 30 | return new MappingDelta(additions, new HashEntryTree<>()); | 33 | return new MappingDelta<>(new HashEntryTree<>(), additions, new HashEntryTree<>()); |
| 34 | } | ||
| 35 | |||
| 36 | public EntryTree<T> getBaseMappings() { | ||
| 37 | return baseMappings; | ||
| 31 | } | 38 | } |
| 32 | 39 | ||
| 33 | public EntryTree<?> getAdditions() { | 40 | public EntryTree<?> getAdditions() { |
| @@ -39,18 +46,11 @@ public class MappingDelta implements Translatable { | |||
| 39 | } | 46 | } |
| 40 | 47 | ||
| 41 | @Override | 48 | @Override |
| 42 | public MappingDelta translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { | 49 | public MappingDelta<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { |
| 43 | return new MappingDelta( | 50 | return new MappingDelta<>( |
| 44 | translate(translator, additions), | 51 | translator.translate(baseMappings), |
| 45 | translate(translator, deletions) | 52 | translator.translate(additions), |
| 53 | translator.translate(deletions) | ||
| 46 | ); | 54 | ); |
| 47 | } | 55 | } |
| 48 | |||
| 49 | private EntryTree<Object> translate(Translator translator, EntryTree<Object> tree) { | ||
| 50 | EntryTree<Object> translatedTree = new HashEntryTree<>(); | ||
| 51 | for (Entry<?> entry : tree.getAllEntries()) { | ||
| 52 | translatedTree.insert(translator.translate(entry), PLACEHOLDER); | ||
| 53 | } | ||
| 54 | return translatedTree; | ||
| 55 | } | ||
| 56 | } | 56 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java index 422bf380..9be48c3a 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java | |||
| @@ -6,14 +6,15 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree; | |||
| 6 | import cuchaz.enigma.translation.representation.entry.Entry; | 6 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 7 | 7 | ||
| 8 | import java.util.Collection; | 8 | import java.util.Collection; |
| 9 | import java.util.stream.Collectors; | ||
| 9 | 10 | ||
| 10 | public class MappingValidator { | 11 | public class MappingValidator { |
| 11 | private final EntryTree<EntryMapping> deobfToObf; | 12 | private final EntryTree<EntryMapping> obfToDeobf; |
| 12 | private final Translator deobfuscator; | 13 | private final Translator deobfuscator; |
| 13 | private final EntryResolver entryResolver; | 14 | private final EntryResolver entryResolver; |
| 14 | 15 | ||
| 15 | public MappingValidator(EntryTree<EntryMapping> deobfToObf, Translator deobfuscator, EntryResolver entryResolver) { | 16 | public MappingValidator(EntryTree<EntryMapping> obfToDeobf, Translator deobfuscator, EntryResolver entryResolver) { |
| 16 | this.deobfToObf = deobfToObf; | 17 | this.obfToDeobf = obfToDeobf; |
| 17 | this.deobfuscator = deobfuscator; | 18 | this.deobfuscator = deobfuscator; |
| 18 | this.entryResolver = entryResolver; | 19 | this.entryResolver = entryResolver; |
| 19 | } | 20 | } |
| @@ -28,8 +29,11 @@ public class MappingValidator { | |||
| 28 | 29 | ||
| 29 | private void validateUnique(Entry<?> entry, String name) { | 30 | private void validateUnique(Entry<?> entry, String name) { |
| 30 | Entry<?> translatedEntry = deobfuscator.translate(entry); | 31 | Entry<?> translatedEntry = deobfuscator.translate(entry); |
| 31 | Collection<Entry<?>> siblings = deobfToObf.getSiblings(translatedEntry); | 32 | Collection<Entry<?>> translatedSiblings = obfToDeobf.getSiblings(entry).stream() |
| 32 | if (!isUnique(translatedEntry, siblings, name)) { | 33 | .map(deobfuscator::translate) |
| 34 | .collect(Collectors.toList()); | ||
| 35 | |||
| 36 | if (!isUnique(translatedEntry, translatedSiblings, name)) { | ||
| 33 | throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!"); | 37 | throw new IllegalNameException(name, "Name is not unique in " + translatedEntry.getParent() + "!"); |
| 34 | } | 38 | } |
| 35 | } | 39 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java index 5acb1da2..1d44b6e8 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java | |||
| @@ -14,10 +14,7 @@ package cuchaz.enigma.translation.mapping.serde; | |||
| 14 | import cuchaz.enigma.ProgressListener; | 14 | import cuchaz.enigma.ProgressListener; |
| 15 | import cuchaz.enigma.translation.MappingTranslator; | 15 | import cuchaz.enigma.translation.MappingTranslator; |
| 16 | import cuchaz.enigma.translation.Translator; | 16 | import cuchaz.enigma.translation.Translator; |
| 17 | import cuchaz.enigma.translation.mapping.AccessModifier; | 17 | import cuchaz.enigma.translation.mapping.*; |
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 20 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 21 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 18 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 22 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | 19 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; |
| 23 | import cuchaz.enigma.translation.representation.entry.*; | 20 | import cuchaz.enigma.translation.representation.entry.*; |
| @@ -37,7 +34,7 @@ import java.util.stream.Collectors; | |||
| 37 | public enum EnigmaMappingsWriter implements MappingsWriter { | 34 | public enum EnigmaMappingsWriter implements MappingsWriter { |
| 38 | FILE { | 35 | FILE { |
| 39 | @Override | 36 | @Override |
| 40 | public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { | 37 | public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) { |
| 41 | Collection<ClassEntry> classes = mappings.getRootEntries().stream() | 38 | Collection<ClassEntry> classes = mappings.getRootEntries().stream() |
| 42 | .filter(entry -> entry instanceof ClassEntry) | 39 | .filter(entry -> entry instanceof ClassEntry) |
| 43 | .map(entry -> (ClassEntry) entry) | 40 | .map(entry -> (ClassEntry) entry) |
| @@ -58,8 +55,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter { | |||
| 58 | }, | 55 | }, |
| 59 | DIRECTORY { | 56 | DIRECTORY { |
| 60 | @Override | 57 | @Override |
| 61 | public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { | 58 | public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) { |
| 62 | applyDeletions(delta.getDeletions(), path); | 59 | applyDeletions(delta.getBaseMappings(), delta.getDeletions(), path); |
| 63 | 60 | ||
| 64 | Collection<ClassEntry> classes = delta.getAdditions().getRootEntries().stream() | 61 | Collection<ClassEntry> classes = delta.getAdditions().getRootEntries().stream() |
| 65 | .filter(entry -> entry instanceof ClassEntry) | 62 | .filter(entry -> entry instanceof ClassEntry) |
| @@ -76,8 +73,8 @@ public enum EnigmaMappingsWriter implements MappingsWriter { | |||
| 76 | 73 | ||
| 77 | try { | 74 | try { |
| 78 | Path classPath = resolve(path, translator.translate(classEntry)); | 75 | Path classPath = resolve(path, translator.translate(classEntry)); |
| 79 | Files.deleteIfExists(classPath); | ||
| 80 | Files.createDirectories(classPath.getParent()); | 76 | Files.createDirectories(classPath.getParent()); |
| 77 | Files.deleteIfExists(classPath); | ||
| 81 | 78 | ||
| 82 | try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) { | 79 | try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) { |
| 83 | writeRoot(writer, mappings, classEntry); | 80 | writeRoot(writer, mappings, classEntry); |
| @@ -89,10 +86,12 @@ public enum EnigmaMappingsWriter implements MappingsWriter { | |||
| 89 | }); | 86 | }); |
| 90 | } | 87 | } |
| 91 | 88 | ||
| 92 | private void applyDeletions(EntryTree<?> deletions, Path root) { | 89 | private void applyDeletions(EntryTree<EntryMapping> baseMappings, EntryTree<?> deletions, Path root) { |
| 90 | Translator oldMappingTranslator = new MappingTranslator(baseMappings, VoidEntryResolver.INSTANCE); | ||
| 91 | |||
| 93 | Collection<ClassEntry> deletedClasses = deletions.getRootEntries().stream() | 92 | Collection<ClassEntry> deletedClasses = deletions.getRootEntries().stream() |
| 94 | .filter(e -> e instanceof ClassEntry) | 93 | .filter(e -> e instanceof ClassEntry) |
| 95 | .map(e -> (ClassEntry) e) | 94 | .map(e -> oldMappingTranslator.translate((ClassEntry) e)) |
| 96 | .collect(Collectors.toList()); | 95 | .collect(Collectors.toList()); |
| 97 | 96 | ||
| 98 | for (ClassEntry classEntry : deletedClasses) { | 97 | for (ClassEntry classEntry : deletedClasses) { |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java index 4db16454..622a0e15 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java | |||
| @@ -28,7 +28,7 @@ public enum MappingFormat { | |||
| 28 | write(mappings, MappingDelta.added(mappings), path, progressListener); | 28 | write(mappings, MappingDelta.added(mappings), path, progressListener); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progressListener) { | 31 | public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progressListener) { |
| 32 | if (writer == null) { | 32 | if (writer == null) { |
| 33 | throw new IllegalStateException(name() + " does not support writing"); | 33 | throw new IllegalStateException(name() + " does not support writing"); |
| 34 | } | 34 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java index b5196681..77f6ee03 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java | |||
| @@ -8,5 +8,9 @@ import cuchaz.enigma.translation.mapping.tree.EntryTree; | |||
| 8 | import java.nio.file.Path; | 8 | import java.nio.file.Path; |
| 9 | 9 | ||
| 10 | public interface MappingsWriter { | 10 | public interface MappingsWriter { |
| 11 | void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress); | 11 | void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress); |
| 12 | |||
| 13 | default void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progress) { | ||
| 14 | write(mappings, MappingDelta.added(mappings), path, progress); | ||
| 15 | } | ||
| 12 | } | 16 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java index 5ff91413..40be136a 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java | |||
| @@ -29,7 +29,7 @@ public enum SrgMappingsWriter implements MappingsWriter { | |||
| 29 | INSTANCE; | 29 | INSTANCE; |
| 30 | 30 | ||
| 31 | @Override | 31 | @Override |
| 32 | public void write(EntryTree<EntryMapping> mappings, MappingDelta delta, Path path, ProgressListener progress) { | 32 | public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress) { |
| 33 | try { | 33 | try { |
| 34 | Files.deleteIfExists(path); | 34 | Files.deleteIfExists(path); |
| 35 | Files.createFile(path); | 35 | Files.createFile(path); |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java index 98a01df2..36be5e1e 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | 1 | package cuchaz.enigma.translation.mapping.tree; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 3 | import cuchaz.enigma.translation.mapping.MappingDelta; | 7 | import cuchaz.enigma.translation.mapping.MappingDelta; |
| 4 | import cuchaz.enigma.translation.representation.entry.Entry; | 8 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 5 | 9 | ||
| @@ -10,11 +14,13 @@ import java.util.Iterator; | |||
| 10 | public class DeltaTrackingTree<T> implements EntryTree<T> { | 14 | public class DeltaTrackingTree<T> implements EntryTree<T> { |
| 11 | private final EntryTree<T> delegate; | 15 | private final EntryTree<T> delegate; |
| 12 | 16 | ||
| 17 | private EntryTree<T> deltaReference; | ||
| 13 | private EntryTree<Object> additions = new HashEntryTree<>(); | 18 | private EntryTree<Object> additions = new HashEntryTree<>(); |
| 14 | private EntryTree<Object> deletions = new HashEntryTree<>(); | 19 | private EntryTree<Object> deletions = new HashEntryTree<>(); |
| 15 | 20 | ||
| 16 | public DeltaTrackingTree(EntryTree<T> delegate) { | 21 | public DeltaTrackingTree(EntryTree<T> delegate) { |
| 17 | this.delegate = delegate; | 22 | this.delegate = delegate; |
| 23 | this.deltaReference = new HashEntryTree<>(delegate); | ||
| 18 | } | 24 | } |
| 19 | 25 | ||
| 20 | public DeltaTrackingTree() { | 26 | public DeltaTrackingTree() { |
| @@ -40,7 +46,7 @@ public class DeltaTrackingTree<T> implements EntryTree<T> { | |||
| 40 | } | 46 | } |
| 41 | 47 | ||
| 42 | public void trackAddition(Entry<?> entry) { | 48 | public void trackAddition(Entry<?> entry) { |
| 43 | deletions.remove(entry); | 49 | deletions.insert(entry, MappingDelta.PLACEHOLDER); |
| 44 | additions.insert(entry, MappingDelta.PLACEHOLDER); | 50 | additions.insert(entry, MappingDelta.PLACEHOLDER); |
| 45 | } | 51 | } |
| 46 | 52 | ||
| @@ -82,6 +88,14 @@ public class DeltaTrackingTree<T> implements EntryTree<T> { | |||
| 82 | } | 88 | } |
| 83 | 89 | ||
| 84 | @Override | 90 | @Override |
| 91 | public DeltaTrackingTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { | ||
| 92 | DeltaTrackingTree<T> translatedTree = new DeltaTrackingTree<>(delegate.translate(translator, resolver, mappings)); | ||
| 93 | translatedTree.additions = additions.translate(translator, resolver, mappings); | ||
| 94 | translatedTree.deletions = deletions.translate(translator, resolver, mappings); | ||
| 95 | return translatedTree; | ||
| 96 | } | ||
| 97 | |||
| 98 | @Override | ||
| 85 | public Collection<Entry<?>> getAllEntries() { | 99 | public Collection<Entry<?>> getAllEntries() { |
| 86 | return delegate.getAllEntries(); | 100 | return delegate.getAllEntries(); |
| 87 | } | 101 | } |
| @@ -96,13 +110,14 @@ public class DeltaTrackingTree<T> implements EntryTree<T> { | |||
| 96 | return delegate.iterator(); | 110 | return delegate.iterator(); |
| 97 | } | 111 | } |
| 98 | 112 | ||
| 99 | public MappingDelta takeDelta() { | 113 | public MappingDelta<T> takeDelta() { |
| 100 | MappingDelta delta = new MappingDelta(additions, deletions); | 114 | MappingDelta<T> delta = new MappingDelta<>(deltaReference, additions, deletions); |
| 101 | resetDelta(); | 115 | resetDelta(); |
| 102 | return delta; | 116 | return delta; |
| 103 | } | 117 | } |
| 104 | 118 | ||
| 105 | private void resetDelta() { | 119 | private void resetDelta() { |
| 120 | deltaReference = new HashEntryTree<>(delegate); | ||
| 106 | additions = new HashEntryTree<>(); | 121 | additions = new HashEntryTree<>(); |
| 107 | deletions = new HashEntryTree<>(); | 122 | deletions = new HashEntryTree<>(); |
| 108 | } | 123 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java index 73fe12d0..4f341f45 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | 1 | package cuchaz.enigma.translation.mapping.tree; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.Translatable; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 3 | import cuchaz.enigma.translation.mapping.EntryMap; | 5 | import cuchaz.enigma.translation.mapping.EntryMap; |
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.Entry; | 8 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 5 | 9 | ||
| 6 | import javax.annotation.Nullable; | 10 | import javax.annotation.Nullable; |
| 7 | import java.util.Collection; | 11 | import java.util.Collection; |
| 8 | 12 | ||
| 9 | public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>> { | 13 | public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>>, Translatable { |
| 10 | Collection<Entry<?>> getChildren(Entry<?> entry); | 14 | Collection<Entry<?>> getChildren(Entry<?> entry); |
| 11 | 15 | ||
| 12 | Collection<Entry<?>> getSiblings(Entry<?> entry); | 16 | Collection<Entry<?>> getSiblings(Entry<?> entry); |
| @@ -17,4 +21,7 @@ public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>> { | |||
| 17 | Collection<EntryTreeNode<T>> getAllNodes(); | 21 | Collection<EntryTreeNode<T>> getAllNodes(); |
| 18 | 22 | ||
| 19 | Collection<Entry<?>> getRootEntries(); | 23 | Collection<Entry<?>> getRootEntries(); |
| 24 | |||
| 25 | @Override | ||
| 26 | EntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings); | ||
| 20 | } | 27 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java index ff88bf94..551fb1c8 100644 --- a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java +++ b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | 1 | package cuchaz.enigma.translation.mapping.tree; |
| 2 | 2 | ||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | 7 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 4 | 8 | ||
| 5 | import javax.annotation.Nullable; | 9 | import javax.annotation.Nullable; |
| @@ -9,6 +13,15 @@ import java.util.stream.Collectors; | |||
| 9 | public class HashEntryTree<T> implements EntryTree<T> { | 13 | public class HashEntryTree<T> implements EntryTree<T> { |
| 10 | private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>(); | 14 | private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>(); |
| 11 | 15 | ||
| 16 | public HashEntryTree() { | ||
| 17 | } | ||
| 18 | |||
| 19 | public HashEntryTree(EntryTree<T> tree) { | ||
| 20 | for (EntryTreeNode<T> node : tree.getAllNodes()) { | ||
| 21 | insert(node.getEntry(), node.getValue()); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 12 | @Override | 25 | @Override |
| 13 | public void insert(Entry<?> entry, T value) { | 26 | public void insert(Entry<?> entry, T value) { |
| 14 | List<HashTreeNode<T>> path = computePath(entry); | 27 | List<HashTreeNode<T>> path = computePath(entry); |
| @@ -156,4 +169,13 @@ public class HashEntryTree<T> implements EntryTree<T> { | |||
| 156 | public boolean isEmpty() { | 169 | public boolean isEmpty() { |
| 157 | return root.isEmpty(); | 170 | return root.isEmpty(); |
| 158 | } | 171 | } |
| 172 | |||
| 173 | @Override | ||
| 174 | public HashEntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) { | ||
| 175 | HashEntryTree<T> translatedTree = new HashEntryTree<>(); | ||
| 176 | for (EntryTreeNode<T> node : getAllNodes()) { | ||
| 177 | translatedTree.insert(translator.translate(node.getEntry()), node.getValue()); | ||
| 178 | } | ||
| 179 | return translatedTree; | ||
| 180 | } | ||
| 159 | } | 181 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java index 9c9fa3d3..a9ec5fac 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java +++ b/src/main/java/cuchaz/enigma/translation/representation/ProcyonEntryFactory.java | |||
| @@ -17,29 +17,23 @@ import com.strobel.assembler.metadata.MethodDefinition; | |||
| 17 | import cuchaz.enigma.translation.representation.entry.*; | 17 | import cuchaz.enigma.translation.representation.entry.*; |
| 18 | 18 | ||
| 19 | public class ProcyonEntryFactory { | 19 | public class ProcyonEntryFactory { |
| 20 | private final ReferencedEntryPool entryPool; | ||
| 21 | |||
| 22 | public ProcyonEntryFactory(ReferencedEntryPool entryPool) { | ||
| 23 | this.entryPool = entryPool; | ||
| 24 | } | ||
| 25 | |||
| 26 | public FieldEntry getFieldEntry(MemberReference def) { | 20 | public FieldEntry getFieldEntry(MemberReference def) { |
| 27 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | 21 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); |
| 28 | return entryPool.getField(classEntry, def.getName(), def.getErasedSignature()); | 22 | return new FieldEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature())); |
| 29 | } | 23 | } |
| 30 | 24 | ||
| 31 | public FieldDefEntry getFieldDefEntry(FieldDefinition def) { | 25 | public FieldDefEntry getFieldDefEntry(FieldDefinition def) { |
| 32 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | 26 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); |
| 33 | return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | 27 | return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers())); |
| 34 | } | 28 | } |
| 35 | 29 | ||
| 36 | public MethodEntry getMethodEntry(MemberReference def) { | 30 | public MethodEntry getMethodEntry(MemberReference def) { |
| 37 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | 31 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); |
| 38 | return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature()); | 32 | return new MethodEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature())); |
| 39 | } | 33 | } |
| 40 | 34 | ||
| 41 | public MethodDefEntry getMethodDefEntry(MethodDefinition def) { | 35 | public MethodDefEntry getMethodDefEntry(MethodDefinition def) { |
| 42 | ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); | 36 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); |
| 43 | return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); | 37 | return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers())); |
| 44 | } | 38 | } |
| 45 | } | 39 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java deleted file mode 100644 index 631b3754..00000000 --- a/src/main/java/cuchaz/enigma/translation/representation/ReferencedEntryPool.java +++ /dev/null | |||
| @@ -1,60 +0,0 @@ | |||
| 1 | /******************************************************************************* | ||
| 2 | * Copyright (c) 2015 Jeff Martin. | ||
| 3 | * All rights reserved. This program and the accompanying materials | ||
| 4 | * are made available under the terms of the GNU Lesser General Public | ||
| 5 | * License v3.0 which accompanies this distribution, and is available at | ||
| 6 | * http://www.gnu.org/licenses/lgpl.html | ||
| 7 | * <p> | ||
| 8 | * Contributors: | ||
| 9 | * Jeff Martin - initial API and implementation | ||
| 10 | ******************************************************************************/ | ||
| 11 | |||
| 12 | package cuchaz.enigma.translation.representation; | ||
| 13 | |||
| 14 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | |||
| 18 | import java.util.HashMap; | ||
| 19 | import java.util.Map; | ||
| 20 | |||
| 21 | public class ReferencedEntryPool { | ||
| 22 | private final Map<String, ClassEntry> classEntries = new HashMap<>(); | ||
| 23 | private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>(); | ||
| 24 | private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>(); | ||
| 25 | |||
| 26 | public ClassEntry getClass(String name) { | ||
| 27 | // TODO: FIXME - I'm a hack! | ||
| 28 | if ("[T".equals(name) || "[[T".equals(name) || "[[[T".equals(name)) { | ||
| 29 | name = name.replaceAll("T", "Ljava/lang/Object;"); | ||
| 30 | } | ||
| 31 | |||
| 32 | final String computeName = name; | ||
| 33 | return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(computeName)); | ||
| 34 | } | ||
| 35 | |||
| 36 | public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) { | ||
| 37 | return getMethod(ownerEntry, name, new MethodDescriptor(desc)); | ||
| 38 | } | ||
| 39 | |||
| 40 | public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) { | ||
| 41 | String key = name + desc.toString(); | ||
| 42 | return getClassMethods(ownerEntry.getFullName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc)); | ||
| 43 | } | ||
| 44 | |||
| 45 | public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) { | ||
| 46 | return getField(ownerEntry, name, new TypeDescriptor(desc)); | ||
| 47 | } | ||
| 48 | |||
| 49 | public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) { | ||
| 50 | return getClassFields(ownerEntry.getFullName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc)); | ||
| 51 | } | ||
| 52 | |||
| 53 | private Map<String, MethodEntry> getClassMethods(String name) { | ||
| 54 | return methodEntries.computeIfAbsent(name, s -> new HashMap<>()); | ||
| 55 | } | ||
| 56 | |||
| 57 | private Map<String, FieldEntry> getClassFields(String name) { | ||
| 58 | return fieldEntries.computeIfAbsent(name, s -> new HashMap<>()); | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/src/main/java/cuchaz/enigma/translation/representation/Signature.java b/src/main/java/cuchaz/enigma/translation/representation/Signature.java index dc241b7a..424088ab 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/Signature.java +++ b/src/main/java/cuchaz/enigma/translation/representation/Signature.java | |||
| @@ -3,9 +3,9 @@ package cuchaz.enigma.translation.representation; | |||
| 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; | 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; |
| 4 | import cuchaz.enigma.translation.Translatable; | 4 | import cuchaz.enigma.translation.Translatable; |
| 5 | import cuchaz.enigma.translation.Translator; | 5 | import cuchaz.enigma.translation.Translator; |
| 6 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | 7 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 7 | import cuchaz.enigma.translation.mapping.EntryResolver; | 8 | import cuchaz.enigma.translation.mapping.EntryResolver; |
| 8 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 9 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 10 | import org.objectweb.asm.signature.SignatureReader; | 10 | import org.objectweb.asm.signature.SignatureReader; |
| 11 | import org.objectweb.asm.signature.SignatureVisitor; | 11 | import org.objectweb.asm.signature.SignatureVisitor; |
| @@ -78,7 +78,12 @@ public class Signature implements Translatable { | |||
| 78 | 78 | ||
| 79 | @Override | 79 | @Override |
| 80 | public int hashCode() { | 80 | public int hashCode() { |
| 81 | return signature.hashCode() | (isType ? 1 : 0) << 16; | 81 | int hash = (isType ? 1 : 0) << 16; |
| 82 | if (signature != null) { | ||
| 83 | hash |= signature.hashCode(); | ||
| 84 | } | ||
| 85 | |||
| 86 | return hash; | ||
| 82 | } | 87 | } |
| 83 | 88 | ||
| 84 | @Override | 89 | @Override |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java index f7ba849e..719d693a 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java +++ b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java | |||
| @@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation; | |||
| 13 | 13 | ||
| 14 | import com.google.common.base.Preconditions; | 14 | import com.google.common.base.Preconditions; |
| 15 | import com.google.common.collect.Maps; | 15 | import com.google.common.collect.Maps; |
| 16 | import com.strobel.assembler.metadata.TypeReference; | ||
| 16 | import cuchaz.enigma.translation.Translatable; | 17 | import cuchaz.enigma.translation.Translatable; |
| 17 | import cuchaz.enigma.translation.Translator; | 18 | import cuchaz.enigma.translation.Translator; |
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | 19 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| @@ -111,6 +112,10 @@ public class TypeDescriptor implements Translatable { | |||
| 111 | return new TypeDescriptor("L" + name + ";"); | 112 | return new TypeDescriptor("L" + name + ";"); |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 115 | public static TypeDescriptor parse(TypeReference type) { | ||
| 116 | return new TypeDescriptor(type.getErasedSignature()); | ||
| 117 | } | ||
| 118 | |||
| 114 | @Override | 119 | @Override |
| 115 | public String toString() { | 120 | public String toString() { |
| 116 | return this.desc; | 121 | return this.desc; |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java index 5904efe6..644658fd 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java +++ b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java | |||
| @@ -17,6 +17,7 @@ import cuchaz.enigma.translation.Translator; | |||
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | 17 | import cuchaz.enigma.translation.mapping.EntryMapping; |
| 18 | import cuchaz.enigma.translation.mapping.NameValidator; | 18 | import cuchaz.enigma.translation.mapping.NameValidator; |
| 19 | 19 | ||
| 20 | import javax.annotation.Nonnull; | ||
| 20 | import javax.annotation.Nullable; | 21 | import javax.annotation.Nullable; |
| 21 | import java.util.List; | 22 | import java.util.List; |
| 22 | import java.util.Objects; | 23 | import java.util.Objects; |
| @@ -125,6 +126,7 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable< | |||
| 125 | return parent; | 126 | return parent; |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 129 | @Nonnull | ||
| 128 | public ClassEntry getOutermostClass() { | 130 | public ClassEntry getOutermostClass() { |
| 129 | if (parent == null) { | 131 | if (parent == null) { |
| 130 | return this; | 132 | return this; |
| @@ -181,6 +183,15 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable< | |||
| 181 | } | 183 | } |
| 182 | 184 | ||
| 183 | @Override | 185 | @Override |
| 186 | public String getSourceRemapName() { | ||
| 187 | ClassEntry outerClass = getOuterClass(); | ||
| 188 | if (outerClass != null) { | ||
| 189 | return outerClass.getSourceRemapName() + "." + name; | ||
| 190 | } | ||
| 191 | return getSimpleName(); | ||
| 192 | } | ||
| 193 | |||
| 194 | @Override | ||
| 184 | public int compareTo(ClassEntry entry) { | 195 | public int compareTo(ClassEntry entry) { |
| 185 | return name.compareTo(entry.name); | 196 | return name.compareTo(entry.name); |
| 186 | } | 197 | } |
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java index 1a2ca785..227400eb 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java +++ b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java | |||
| @@ -22,6 +22,10 @@ import java.util.List; | |||
| 22 | public interface Entry<P extends Entry<?>> extends Translatable { | 22 | public interface Entry<P extends Entry<?>> extends Translatable { |
| 23 | String getName(); | 23 | String getName(); |
| 24 | 24 | ||
| 25 | default String getSourceRemapName() { | ||
| 26 | return getName(); | ||
| 27 | } | ||
| 28 | |||
| 25 | @Nullable | 29 | @Nullable |
| 26 | P getParent(); | 30 | P getParent(); |
| 27 | 31 | ||
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java index df96b599..0c12f1c8 100644 --- a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java +++ b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java | |||
| @@ -17,11 +17,6 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co | |||
| 17 | protected final int index; | 17 | protected final int index; |
| 18 | protected final boolean parameter; | 18 | protected final boolean parameter; |
| 19 | 19 | ||
| 20 | @Deprecated | ||
| 21 | public LocalVariableEntry(MethodEntry parent, int index, String name) { | ||
| 22 | this(parent, index, name, true); | ||
| 23 | } | ||
| 24 | |||
| 25 | public LocalVariableEntry(MethodEntry parent, int index, String name, boolean parameter) { | 20 | public LocalVariableEntry(MethodEntry parent, int index, String name, boolean parameter) { |
| 26 | super(parent, name); | 21 | super(parent, name); |
| 27 | 22 | ||
| @@ -37,7 +32,7 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co | |||
| 37 | return MethodEntry.class; | 32 | return MethodEntry.class; |
| 38 | } | 33 | } |
| 39 | 34 | ||
| 40 | public boolean isParameter() { | 35 | public boolean isArgument() { |
| 41 | return this.parameter; | 36 | return this.parameter; |
| 42 | } | 37 | } |
| 43 | 38 | ||
diff --git a/src/test/java/cuchaz/enigma/TestDeobfed.java b/src/test/java/cuchaz/enigma/TestDeobfed.java index 25cb60c5..14b1418d 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfed.java +++ b/src/test/java/cuchaz/enigma/TestDeobfed.java | |||
| @@ -68,27 +68,28 @@ public class TestDeobfed { | |||
| 68 | public void decompile() | 68 | public void decompile() |
| 69 | throws Exception { | 69 | throws Exception { |
| 70 | Deobfuscator deobfuscator = new Deobfuscator(jar); | 70 | Deobfuscator deobfuscator = new Deobfuscator(jar); |
| 71 | deobfuscator.getSourceTree("a"); | 71 | SourceProvider sourceProvider = deobfuscator.getObfSourceProvider(); |
| 72 | deobfuscator.getSourceTree("b"); | 72 | sourceProvider.getSources("a"); |
| 73 | deobfuscator.getSourceTree("c"); | 73 | sourceProvider.getSources("b"); |
| 74 | deobfuscator.getSourceTree("d"); | 74 | sourceProvider.getSources("c"); |
| 75 | deobfuscator.getSourceTree("d$1"); | 75 | sourceProvider.getSources("d"); |
| 76 | deobfuscator.getSourceTree("e"); | 76 | sourceProvider.getSources("d$1"); |
| 77 | deobfuscator.getSourceTree("f"); | 77 | sourceProvider.getSources("e"); |
| 78 | deobfuscator.getSourceTree("g"); | 78 | sourceProvider.getSources("f"); |
| 79 | deobfuscator.getSourceTree("g$a"); | 79 | sourceProvider.getSources("g"); |
| 80 | deobfuscator.getSourceTree("g$a$a"); | 80 | sourceProvider.getSources("g$a"); |
| 81 | deobfuscator.getSourceTree("g$b"); | 81 | sourceProvider.getSources("g$a$a"); |
| 82 | deobfuscator.getSourceTree("g$b$a"); | 82 | sourceProvider.getSources("g$b"); |
| 83 | deobfuscator.getSourceTree("h"); | 83 | sourceProvider.getSources("g$b$a"); |
| 84 | deobfuscator.getSourceTree("h$a"); | 84 | sourceProvider.getSources("h"); |
| 85 | deobfuscator.getSourceTree("h$a$a"); | 85 | sourceProvider.getSources("h$a"); |
| 86 | deobfuscator.getSourceTree("h$b"); | 86 | sourceProvider.getSources("h$a$a"); |
| 87 | deobfuscator.getSourceTree("h$b$a"); | 87 | sourceProvider.getSources("h$b"); |
| 88 | deobfuscator.getSourceTree("h$b$a$a"); | 88 | sourceProvider.getSources("h$b$a"); |
| 89 | deobfuscator.getSourceTree("h$b$a$b"); | 89 | sourceProvider.getSources("h$b$a$a"); |
| 90 | deobfuscator.getSourceTree("i"); | 90 | sourceProvider.getSources("h$b$a$b"); |
| 91 | deobfuscator.getSourceTree("i$a"); | 91 | sourceProvider.getSources("i"); |
| 92 | deobfuscator.getSourceTree("i$b"); | 92 | sourceProvider.getSources("i$a"); |
| 93 | sourceProvider.getSources("i$b"); | ||
| 93 | } | 94 | } |
| 94 | } | 95 | } |
diff --git a/src/test/java/cuchaz/enigma/TestDeobfuscator.java b/src/test/java/cuchaz/enigma/TestDeobfuscator.java index 5f117213..e070b66f 100644 --- a/src/test/java/cuchaz/enigma/TestDeobfuscator.java +++ b/src/test/java/cuchaz/enigma/TestDeobfuscator.java | |||
| @@ -51,6 +51,7 @@ public class TestDeobfuscator { | |||
| 51 | public void decompileClass() | 51 | public void decompileClass() |
| 52 | throws Exception { | 52 | throws Exception { |
| 53 | Deobfuscator deobfuscator = getDeobfuscator(); | 53 | Deobfuscator deobfuscator = getDeobfuscator(); |
| 54 | deobfuscator.getSource(deobfuscator.getSourceTree("a")); | 54 | SourceProvider sourceProvider = deobfuscator.getObfSourceProvider(); |
| 55 | sourceProvider.writeSourceToString(sourceProvider.getSources("a")); | ||
| 55 | } | 56 | } |
| 56 | } | 57 | } |
diff --git a/src/test/java/cuchaz/enigma/TestInnerClasses.java b/src/test/java/cuchaz/enigma/TestInnerClasses.java index 0319cf61..8738fd79 100644 --- a/src/test/java/cuchaz/enigma/TestInnerClasses.java +++ b/src/test/java/cuchaz/enigma/TestInnerClasses.java | |||
| @@ -79,6 +79,6 @@ public class TestInnerClasses { | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | private void decompile(ClassEntry classEntry) { | 81 | private void decompile(ClassEntry classEntry) { |
| 82 | deobfuscator.getSourceTree(classEntry.getName()); | 82 | deobfuscator.getObfSourceProvider().getSources(classEntry.getName()); |
| 83 | } | 83 | } |
| 84 | } | 84 | } |
diff --git a/src/test/java/cuchaz/enigma/TestSourceIndex.java b/src/test/java/cuchaz/enigma/TestSourceIndex.java index a5f5f712..ce5d6316 100644 --- a/src/test/java/cuchaz/enigma/TestSourceIndex.java +++ b/src/test/java/cuchaz/enigma/TestSourceIndex.java | |||
| @@ -13,6 +13,7 @@ package cuchaz.enigma; | |||
| 13 | 13 | ||
| 14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
| 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 15 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 16 | import cuchaz.enigma.analysis.SourceIndex; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 17 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 17 | import org.junit.Test; | 18 | import org.junit.Test; |
| 18 | 19 | ||
| @@ -50,11 +51,13 @@ public class TestSourceIndex { | |||
| 50 | } | 51 | } |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 54 | SourceProvider sourceProvider = deobfuscator.getObfSourceProvider(); | ||
| 53 | for (ClassEntry obfClassEntry : classEntries) { | 55 | for (ClassEntry obfClassEntry : classEntries) { |
| 54 | try { | 56 | try { |
| 55 | CompilationUnit tree = deobfuscator.getSourceTree(obfClassEntry.getName()); | 57 | CompilationUnit tree = sourceProvider.getSources(obfClassEntry.getName()); |
| 56 | String source = deobfuscator.getSource(tree); | 58 | String source = sourceProvider.writeSourceToString(tree); |
| 57 | deobfuscator.getSourceIndex(tree, source); | 59 | |
| 60 | SourceIndex.buildIndex(source, tree, true); | ||
| 58 | } catch (Throwable t) { | 61 | } catch (Throwable t) { |
| 59 | throw new Error("Unable to index " + obfClassEntry, t); | 62 | throw new Error("Unable to index " + obfClassEntry, t); |
| 60 | } | 63 | } |
diff --git a/src/test/java/cuchaz/enigma/TokenChecker.java b/src/test/java/cuchaz/enigma/TokenChecker.java index aac28662..c4670a20 100644 --- a/src/test/java/cuchaz/enigma/TokenChecker.java +++ b/src/test/java/cuchaz/enigma/TokenChecker.java | |||
| @@ -34,11 +34,12 @@ public class TokenChecker { | |||
| 34 | 34 | ||
| 35 | protected String getDeclarationToken(Entry<?> entry) { | 35 | protected String getDeclarationToken(Entry<?> entry) { |
| 36 | // decompile the class | 36 | // decompile the class |
| 37 | CompilationUnit tree = deobfuscator.getSourceTree(entry.getContainingClass().getFullName()); | 37 | SourceProvider sourceProvider = deobfuscator.getObfSourceProvider(); |
| 38 | CompilationUnit tree = sourceProvider.getSources(entry.getContainingClass().getFullName()); | ||
| 38 | // DEBUG | 39 | // DEBUG |
| 39 | // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); | 40 | // tree.acceptVisitor( new TreeDumpVisitor( new File( "tree." + entry.getClassName().replace( '/', '.' ) + ".txt" ) ), null ); |
| 40 | String source = deobfuscator.getSource(tree); | 41 | String source = sourceProvider.writeSourceToString(tree); |
| 41 | SourceIndex index = deobfuscator.getSourceIndex(tree, source); | 42 | SourceIndex index = SourceIndex.buildIndex(source, tree, true); |
| 42 | 43 | ||
| 43 | // get the token value | 44 | // get the token value |
| 44 | Token token = index.getDeclarationToken(entry); | 45 | Token token = index.getDeclarationToken(entry); |
| @@ -51,9 +52,10 @@ public class TokenChecker { | |||
| 51 | @SuppressWarnings("unchecked") | 52 | @SuppressWarnings("unchecked") |
| 52 | protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) { | 53 | protected Collection<String> getReferenceTokens(EntryReference<? extends Entry<?>, ? extends Entry<?>> reference) { |
| 53 | // decompile the class | 54 | // decompile the class |
| 54 | CompilationUnit tree = deobfuscator.getSourceTree(reference.context.getContainingClass().getFullName()); | 55 | SourceProvider sourceProvider = deobfuscator.getObfSourceProvider(); |
| 55 | String source = deobfuscator.getSource(tree); | 56 | CompilationUnit tree = sourceProvider.getSources(reference.context.getContainingClass().getFullName()); |
| 56 | SourceIndex index = deobfuscator.getSourceIndex(tree, source); | 57 | String source = sourceProvider.writeSourceToString(tree); |
| 58 | SourceIndex index = SourceIndex.buildIndex(source, tree, true); | ||
| 57 | 59 | ||
| 58 | // get the token values | 60 | // get the token values |
| 59 | List<String> values = Lists.newArrayList(); | 61 | List<String> values = Lists.newArrayList(); |