From e3f452250e51b7271f3989c7dfd12e4422934942 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 21 May 2015 23:30:00 +0100 Subject: Support Gradle alongside SSJB This makes builds faster, simpler and better automated but still keeps Cuchaz happy. :) --- src/cuchaz/enigma/gui/GuiController.java | 358 +++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 src/cuchaz/enigma/gui/GuiController.java (limited to 'src/cuchaz/enigma/gui/GuiController.java') diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java new file mode 100644 index 0000000..6690622 --- /dev/null +++ b/src/cuchaz/enigma/gui/GuiController.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma.gui; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.jar.JarFile; + +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import com.strobel.decompiler.languages.java.ast.CompilationUnit; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.Deobfuscator.ProgressListener; +import cuchaz.enigma.analysis.BehaviorReferenceTreeNode; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.FieldReferenceTreeNode; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.gui.ProgressDialog.ProgressRunnable; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.Entry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MappingParseException; +import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.TranslationDirection; + +public class GuiController { + + private Deobfuscator m_deobfuscator; + private Gui m_gui; + private SourceIndex m_index; + private ClassEntry m_currentObfClass; + private boolean m_isDirty; + private Deque> m_referenceStack; + + public GuiController(Gui gui) { + m_gui = gui; + m_deobfuscator = null; + m_index = null; + m_currentObfClass = null; + m_isDirty = false; + m_referenceStack = Queues.newArrayDeque(); + } + + public boolean isDirty() { + return m_isDirty; + } + + public void openJar(final JarFile jar) throws IOException { + m_gui.onStartOpenJar(); + m_deobfuscator = new Deobfuscator(jar); + m_gui.onFinishOpenJar(m_deobfuscator.getJarName()); + refreshClasses(); + } + + public void closeJar() { + m_deobfuscator = null; + m_gui.onCloseJar(); + } + + public void openMappings(File file) throws IOException, MappingParseException { + FileReader in = new FileReader(file); + m_deobfuscator.setMappings(new MappingsReader().read(in)); + in.close(); + m_isDirty = false; + m_gui.setMappingsFile(file); + refreshClasses(); + refreshCurrentClass(); + } + + public void saveMappings(File file) throws IOException { + FileWriter out = new FileWriter(file); + new MappingsWriter().write(out, m_deobfuscator.getMappings()); + out.close(); + m_isDirty = false; + } + + public void closeMappings() { + m_deobfuscator.setMappings(null); + m_gui.setMappingsFile(null); + refreshClasses(); + refreshCurrentClass(); + } + + public void exportSource(final File dirOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) throws Exception { + m_deobfuscator.writeSources(dirOut, progress); + } + }); + } + + public void exportJar(final File fileOut) { + ProgressDialog.runInThread(m_gui.getFrame(), new ProgressRunnable() { + @Override + public void run(ProgressListener progress) { + m_deobfuscator.writeJar(fileOut, progress); + } + }); + } + + public Token getToken(int pos) { + if (m_index == null) { + return null; + } + return m_index.getReferenceToken(pos); + } + + public EntryReference getDeobfReference(Token token) { + if (m_index == null) { + return null; + } + return m_index.getDeobfReference(token); + } + + public ReadableToken getReadableToken(Token token) { + if (m_index == null) { + return null; + } + return new ReadableToken( + m_index.getLineNumber(token.start), + m_index.getColumnNumber(token.start), + m_index.getColumnNumber(token.end) + ); + } + + public boolean entryHasDeobfuscatedName(Entry deobfEntry) { + return m_deobfuscator.hasDeobfuscatedName(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean entryIsInJar(Entry deobfEntry) { + return m_deobfuscator.isObfuscatedIdentifier(m_deobfuscator.obfuscateEntry(deobfEntry)); + } + + public boolean referenceIsRenameable(EntryReference deobfReference) { + return m_deobfuscator.isRenameable(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public ClassInheritanceTreeNode getClassInheritance(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + ClassInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getClassInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + return ClassInheritanceTreeNode.findNode(rootNode, obfClassEntry); + } + + public ClassImplementationsTreeNode getClassImplementations(ClassEntry deobfClassEntry) { + ClassEntry obfClassEntry = m_deobfuscator.obfuscateEntry(deobfClassEntry); + return m_deobfuscator.getJarIndex().getClassImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfClassEntry + ); + } + + public MethodInheritanceTreeNode getMethodInheritance(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + MethodInheritanceTreeNode rootNode = m_deobfuscator.getJarIndex().getMethodInheritance( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + return MethodInheritanceTreeNode.findNode(rootNode, obfMethodEntry); + } + + public MethodImplementationsTreeNode getMethodImplementations(MethodEntry deobfMethodEntry) { + MethodEntry obfMethodEntry = m_deobfuscator.obfuscateEntry(deobfMethodEntry); + List rootNodes = m_deobfuscator.getJarIndex().getMethodImplementations( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfMethodEntry + ); + if (rootNodes.isEmpty()) { + return null; + } + if (rootNodes.size() > 1) { + System.err.println("WARNING: Method " + deobfMethodEntry + " implements multiple interfaces. Only showing first one."); + } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), obfMethodEntry); + } + + public FieldReferenceTreeNode getFieldReferences(FieldEntry deobfFieldEntry) { + FieldEntry obfFieldEntry = m_deobfuscator.obfuscateEntry(deobfFieldEntry); + FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfFieldEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public BehaviorReferenceTreeNode getMethodReferences(BehaviorEntry deobfBehaviorEntry) { + BehaviorEntry obfBehaviorEntry = m_deobfuscator.obfuscateEntry(deobfBehaviorEntry); + BehaviorReferenceTreeNode rootNode = new BehaviorReferenceTreeNode( + m_deobfuscator.getTranslator(TranslationDirection.Deobfuscating), + obfBehaviorEntry + ); + rootNode.load(m_deobfuscator.getJarIndex(), true); + return rootNode; + } + + public void rename(EntryReference deobfReference, String newName) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.rename(obfReference.getNameableEntry(), newName); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void removeMapping(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.removeMapping(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void markAsDeobfuscated(EntryReference deobfReference) { + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + m_deobfuscator.markAsDeobfuscated(obfReference.getNameableEntry()); + m_isDirty = true; + refreshClasses(); + refreshCurrentClass(obfReference); + } + + public void openDeclaration(Entry deobfEntry) { + if (deobfEntry == null) { + throw new IllegalArgumentException("Entry cannot be null!"); + } + openReference(new EntryReference(deobfEntry, deobfEntry.getName())); + } + + public void openReference(EntryReference deobfReference) { + if (deobfReference == null) { + throw new IllegalArgumentException("Reference cannot be null!"); + } + + // get the reference target class + EntryReference obfReference = m_deobfuscator.obfuscateReference(deobfReference); + ClassEntry obfClassEntry = obfReference.getLocationClassEntry().getOutermostClassEntry(); + if (!m_deobfuscator.isObfuscatedIdentifier(obfClassEntry)) { + throw new IllegalArgumentException("Obfuscated class " + obfClassEntry + " was not found in the jar!"); + } + if (m_currentObfClass == null || !m_currentObfClass.equals(obfClassEntry)) { + // deobfuscate the class, then navigate to the reference + m_currentObfClass = obfClassEntry; + deobfuscate(m_currentObfClass, obfReference); + } else { + showReference(obfReference); + } + } + + private void showReference(EntryReference obfReference) { + EntryReference deobfReference = m_deobfuscator.deobfuscateReference(obfReference); + Collection tokens = m_index.getReferenceTokens(deobfReference); + if (tokens.isEmpty()) { + // DEBUG + System.err.println(String.format("WARNING: no tokens found for %s in %s", deobfReference, m_currentObfClass)); + } else { + m_gui.showTokens(tokens); + } + } + + public void savePreviousReference(EntryReference deobfReference) { + m_referenceStack.push(m_deobfuscator.obfuscateReference(deobfReference)); + } + + public void openPreviousReference() { + if (hasPreviousLocation()) { + openReference(m_deobfuscator.deobfuscateReference(m_referenceStack.pop())); + } + } + + public boolean hasPreviousLocation() { + return !m_referenceStack.isEmpty(); + } + + private void refreshClasses() { + List obfClasses = Lists.newArrayList(); + List deobfClasses = Lists.newArrayList(); + m_deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + m_gui.setObfClasses(obfClasses); + m_gui.setDeobfClasses(deobfClasses); + } + + private void refreshCurrentClass() { + refreshCurrentClass(null); + } + + private void refreshCurrentClass(EntryReference obfReference) { + if (m_currentObfClass != null) { + deobfuscate(m_currentObfClass, obfReference); + } + } + + private void deobfuscate(final ClassEntry classEntry, final EntryReference obfReference) { + + m_gui.setSource("(deobfuscating...)"); + + // run the deobfuscator in a separate thread so we don't block the GUI event queue + new Thread() { + @Override + public void run() { + // decompile,deobfuscate the bytecode + CompilationUnit sourceTree = m_deobfuscator.getSourceTree(classEntry.getClassName()); + if (sourceTree == null) { + // decompilation of this class is not supported + m_gui.setSource("Unable to find class: " + classEntry); + return; + } + String source = m_deobfuscator.getSource(sourceTree); + m_index = m_deobfuscator.getSourceIndex(sourceTree, source); + m_gui.setSource(m_index.getSource()); + if (obfReference != null) { + showReference(obfReference); + } + + // set the highlighted tokens + List obfuscatedTokens = Lists.newArrayList(); + List deobfuscatedTokens = Lists.newArrayList(); + List otherTokens = Lists.newArrayList(); + for (Token token : m_index.referenceTokens()) { + EntryReference reference = m_index.getDeobfReference(token); + if (referenceIsRenameable(reference)) { + if (entryHasDeobfuscatedName(reference.getNameableEntry())) { + deobfuscatedTokens.add(token); + } else { + obfuscatedTokens.add(token); + } + } else { + otherTokens.add(token); + } + } + m_gui.setHighlightedTokens(obfuscatedTokens, deobfuscatedTokens, otherTokens); + } + }.start(); + } +} -- cgit v1.2.3