From e27d5967029f4f3da8889dd673ba516dcd9f3ac8 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sun, 16 Jun 2019 23:49:25 +0200 Subject: Plugin rework along with API rework: Enigma split from EnigmaProject; plugins now provide services configurable via a profile --- src/main/java/cuchaz/enigma/gui/ClassSelector.java | 4 +- .../cuchaz/enigma/gui/DecompiledClassSource.java | 38 ++- src/main/java/cuchaz/enigma/gui/Gui.java | 45 +-- src/main/java/cuchaz/enigma/gui/GuiController.java | 343 +++++++++++++-------- .../cuchaz/enigma/gui/dialog/ProgressDialog.java | 3 +- .../cuchaz/enigma/gui/dialog/SearchDialog.java | 2 +- .../java/cuchaz/enigma/gui/elements/MenuBar.java | 33 +- .../java/cuchaz/enigma/gui/panels/PanelEditor.java | 6 +- 8 files changed, 283 insertions(+), 191 deletions(-) (limited to 'src/main/java/cuchaz/enigma/gui') diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 39d0333..5051032 100644 --- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -155,7 +155,7 @@ public class ClassSelector extends JTree { return; } - Translator translator = controller.getDeobfuscator().getMapper().getDeobfuscator(); + Translator translator = controller.project.getMapper().getDeobfuscator(); // build the package names Map packages = Maps.newHashMap(); @@ -478,7 +478,7 @@ public class ClassSelector extends JTree { } public void insertNode(ClassEntry obfEntry) { - ClassEntry deobfEntry = controller.getDeobfuscator().deobfuscate(obfEntry); + ClassEntry deobfEntry = controller.project.getMapper().deobfuscate(obfEntry); ClassSelectorPackageNode packageNode = getOrCreatePackage(deobfEntry); DefaultTreeModel model = (DefaultTreeModel) getModel(); diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java index d7c981a..44f70f8 100644 --- a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java +++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java @@ -1,12 +1,17 @@ package cuchaz.enigma.gui; -import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.EnigmaServices; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.analysis.SourceIndex; import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.api.service.NameProposalService; import cuchaz.enigma.gui.highlight.TokenHighlightType; import cuchaz.enigma.translation.LocalNameGenerator; import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.mapping.EntryRemapper; +import cuchaz.enigma.translation.mapping.EntryResolver; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.TypeDescriptor; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; @@ -34,27 +39,27 @@ public class DecompiledClassSource { return new DecompiledClassSource(classEntry, new SourceIndex(text)); } - public void remapSource(Deobfuscator deobfuscator, Translator translator) { + public void remapSource(EnigmaProject project, Translator translator) { highlightedTokens.clear(); SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens()); - SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(deobfuscator, token, movedToken, translator)); + SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(project, token, movedToken, translator)); remappedIndex = obfuscatedIndex.remapTo(remapResult); } - private String remapToken(Deobfuscator deobfuscator, Token token, Token movedToken, Translator translator) { + private String remapToken(EnigmaProject project, Token token, Token movedToken, Translator translator) { EntryReference, Entry> reference = obfuscatedIndex.getReference(token); Entry entry = reference.getNameableEntry(); Entry translatedEntry = translator.translate(entry); - if (deobfuscator.isRenamable(reference)) { + if (project.isRenamable(reference)) { if (isDeobfuscated(entry, translatedEntry)) { highlightToken(movedToken, TokenHighlightType.DEOBFUSCATED); return translatedEntry.getSourceRemapName(); } else { - Optional proposedName = proposeName(deobfuscator, entry); + Optional proposedName = proposeName(project, entry); if (proposedName.isPresent()) { highlightToken(movedToken, TokenHighlightType.PROPOSED); return proposedName.get(); @@ -72,13 +77,22 @@ public class DecompiledClassSource { return null; } - private Optional proposeName(Deobfuscator deobfuscator, Entry entry) { - Stream proposals = deobfuscator.getNameProposers() - .map(plugin -> plugin.proposeName(entry, deobfuscator.getMapper())) - .filter(Optional::isPresent) - .map(Optional::get); + private Optional proposeName(EnigmaProject project, Entry entry) { + EnigmaServices services = project.getEnigma().getServices(); - return proposals.findFirst(); + return services.get(NameProposalService.TYPE).flatMap(nameProposalService -> { + EntryResolver resolver = project.getMapper().getObfResolver(); + + Collection> resolved = resolver.resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT); + EntryRemapper mapper = project.getMapper(); + + Stream proposals = resolved.stream() + .map(e -> nameProposalService.proposeName(e, mapper)) + .filter(Optional::isPresent) + .map(Optional::get); + + return proposals.findFirst(); + }); } @Nullable diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index a61f4dd..f5dd8a0 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java @@ -31,9 +31,7 @@ import cuchaz.enigma.gui.panels.PanelIdentifier; import cuchaz.enigma.gui.panels.PanelObf; import cuchaz.enigma.gui.util.History; import cuchaz.enigma.throwables.IllegalNameException; -import cuchaz.enigma.translation.mapping.AccessModifier; -import cuchaz.enigma.translation.mapping.EntryResolver; -import cuchaz.enigma.translation.mapping.ResolutionStrategy; +import cuchaz.enigma.translation.mapping.*; import cuchaz.enigma.translation.representation.entry.*; import cuchaz.enigma.utils.Utils; import de.sciss.syntaxpane.DefaultSyntaxKit; @@ -312,13 +310,8 @@ public class Gui { return this.controller; } - public void onStartOpenJar(String message) { + public void onStartOpenJar() { this.classesPanel.removeAll(); - JPanel panel = new JPanel(); - panel.setLayout(new FlowLayout()); - panel.add(new JLabel(message)); - this.classesPanel.add(panel); - redraw(); } @@ -447,7 +440,7 @@ public class Gui { this.cursorReference = reference; - EntryReference, Entry> translatedReference = controller.getDeobfuscator().deobfuscate(reference); + EntryReference, Entry> translatedReference = controller.project.getMapper().deobfuscate(reference); infoPanel.removeAll(); if (translatedReference.entry instanceof ClassEntry) { @@ -509,7 +502,7 @@ public class Gui { } private JComboBox addModifierComboBox(JPanel container, String name, Entry entry) { - if (!getController().entryIsInJar(entry)) + if (!getController().project.isRenamable(entry)) return null; JPanel panel = new JPanel(); panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); @@ -519,8 +512,16 @@ public class Gui { JComboBox combo = new JComboBox<>(AccessModifier.values()); ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); - combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); - combo.addItemListener(getController()::modifierChange); + + EntryMapping mapping = controller.project.getMapper().getDeobfMapping(entry); + if (mapping != null) { + combo.setSelectedIndex(mapping.getAccessModifier().ordinal()); + } else { + combo.setSelectedIndex(AccessModifier.UNCHANGED.ordinal()); + } + + combo.addItemListener(controller::modifierChange); + panel.add(combo); container.add(panel); @@ -529,6 +530,8 @@ public class Gui { } public void onCaretMove(int pos) { + EntryRemapper mapper = controller.project.getMapper(); + Token token = this.controller.getToken(pos); boolean isToken = token != null; @@ -539,7 +542,7 @@ public class Gui { shouldNavigateOnClick = false; Entry navigationEntry = referenceEntry; if (cursorReference.context == null) { - EntryResolver resolver = controller.getDeobfuscator().getMapper().getObfResolver(); + EntryResolver resolver = mapper.getObfResolver(); navigationEntry = resolver.resolveFirstEntry(referenceEntry, ResolutionStrategy.RESOLVE_ROOT); } controller.navigateTo(navigationEntry); @@ -550,8 +553,7 @@ public class Gui { boolean isFieldEntry = isToken && referenceEntry instanceof FieldEntry; boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); - boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); - boolean isRenamable = isToken && this.controller.getDeobfuscator().isRenamable(cursorReference); + boolean isRenamable = isToken && this.controller.project.isRenamable(cursorReference); if (isToken) { showCursorReference(cursorReference); @@ -564,12 +566,12 @@ public class Gui { this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); this.popupMenu.showCallsSpecificMenu.setEnabled(isMethodEntry); - this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); + this.popupMenu.openEntryMenu.setEnabled(isRenamable && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousReference()); this.popupMenu.openNextMenu.setEnabled(this.controller.hasNextReference()); this.popupMenu.toggleMappingMenu.setEnabled(isRenamable); - if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) { + if (isToken && !Objects.equals(referenceEntry, mapper.deobfuscate(referenceEntry))) { this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); } else { this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); @@ -581,7 +583,7 @@ public class Gui { // init the text box renameTextField = new JTextField(); - EntryReference, Entry> translatedReference = controller.getDeobfuscator().deobfuscate(cursorReference); + EntryReference, Entry> translatedReference = controller.project.getMapper().deobfuscate(cursorReference); renameTextField.setText(translatedReference.getNameableName()); renameTextField.setPreferredSize(new Dimension(360, renameTextField.getPreferredSize().height)); @@ -728,7 +730,10 @@ public class Gui { } public void toggleMapping() { - if (this.controller.getDeobfuscator().isRemapped(cursorReference.entry)) { + Entry obfEntry = cursorReference.entry; + Entry deobfEntry = controller.project.getMapper().deobfuscate(obfEntry); + + if (!Objects.equals(obfEntry, deobfEntry)) { this.controller.removeMapping(cursorReference); } else { this.controller.markAsDeobfuscated(cursorReference); diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index 1683333..a55d2cd 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -14,9 +14,13 @@ package cuchaz.enigma.gui; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.strobel.decompiler.languages.java.ast.CompilationUnit; -import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.CompiledSourceTypeLoader; +import cuchaz.enigma.Enigma; +import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.SourceProvider; import cuchaz.enigma.analysis.*; +import cuchaz.enigma.api.service.ObfuscationTestService; +import cuchaz.enigma.bytecode.translators.SourceFixVisitor; import cuchaz.enigma.config.Config; import cuchaz.enigma.gui.dialog.ProgressDialog; import cuchaz.enigma.gui.util.History; @@ -30,114 +34,150 @@ import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.FieldEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.ReadableToken; +import org.objectweb.asm.Opcodes; import javax.annotation.Nullable; import javax.swing.*; import java.awt.event.ItemEvent; -import java.io.File; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.file.Path; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.jar.JarFile; import java.util.stream.Collectors; +import java.util.stream.Stream; public class GuiController { - private static final ExecutorService DECOMPILER_SERVICE = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("decompiler-thread").build()); + private static final ExecutorService DECOMPILER_SERVICE = Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("decompiler-thread") + .build() + ); private final Gui gui; - private Deobfuscator deobfuscator; - private DecompiledClassSource currentSource; + public final Enigma enigma; + public EnigmaProject project; + private SourceProvider sourceProvider; + private IndexTreeBuilder indexTreeBuilder; private Path loadedMappingPath; private MappingFormat loadedMappingFormat; + private DecompiledClassSource currentSource; + public GuiController(Gui gui) { this.gui = gui; + // TODO: load and set profile + this.enigma = Enigma.create(); } public boolean isDirty() { - if (deobfuscator == null) { - return false; - } - return deobfuscator.getMapper().isDirty(); + return project != null && project.getMapper().isDirty(); } - public void openJar(final JarFile jar) throws IOException { - this.gui.onStartOpenJar("Loading JAR..."); - this.deobfuscator = new Deobfuscator(jar, this.gui::onStartOpenJar); - this.gui.onFinishOpenJar(jar.getName()); - refreshClasses(); + public void openJar(final Path jarPath) { + this.gui.onStartOpenJar(); + + ProgressDialog.runOffThread(gui.getFrame(), progress -> { + project = enigma.openJar(jarPath, progress); + + indexTreeBuilder = new IndexTreeBuilder(project.getJarIndex()); + + CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(project.getClassCache()); + typeLoader.addVisitor(visitor -> new SourceFixVisitor(Opcodes.ASM5, visitor, project.getJarIndex())); + sourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader); + + gui.onFinishOpenJar(jarPath.getFileName().toString()); + + refreshClasses(); + }); } public void closeJar() { - this.deobfuscator = null; + this.project = null; this.gui.onCloseJar(); } public void openMappings(MappingFormat format, Path path) { - if (deobfuscator == null) return; - ProgressDialog.runInThread(this.gui.getFrame(), progress -> { + if (project == null) return; + + gui.setMappingsFile(path); + + ProgressDialog.runOffThread(gui.getFrame(), progress -> { try { EntryTree mappings = format.read(path, progress); - deobfuscator.setMappings(mappings, progress); + project.setMappings(mappings); - gui.setMappingsFile(path); loadedMappingFormat = format; loadedMappingPath = path; refreshClasses(); refreshCurrentClass(); } catch (MappingParseException e) { - JOptionPane.showMessageDialog(this.gui.getFrame(), e.getMessage()); + JOptionPane.showMessageDialog(gui.getFrame(), e.getMessage()); } }); } public void saveMappings(Path path) { - saveMappings(loadedMappingFormat, path); + if (project == null) return; + + saveMappings(path, loadedMappingFormat); } - public void saveMappings(MappingFormat format, Path path) { - if (deobfuscator == null) return; - EntryRemapper mapper = deobfuscator.getMapper(); + public void saveMappings(Path path, MappingFormat format) { + if (project == null) return; + + ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { + EntryRemapper mapper = project.getMapper(); - MappingDelta delta = mapper.takeMappingDelta(); - boolean saveAll = !path.equals(loadedMappingPath); + MappingDelta delta = mapper.takeMappingDelta(); + boolean saveAll = !path.equals(loadedMappingPath); + + loadedMappingFormat = format; + loadedMappingPath = path; - ProgressDialog.runInThread(this.gui.getFrame(), progress -> { if (saveAll) { format.write(mapper.getObfToDeobf(), path, progress); } else { format.write(mapper.getObfToDeobf(), delta, path, progress); } }); - - loadedMappingFormat = format; - loadedMappingPath = path; } public void closeMappings() { - if (deobfuscator == null) return; - this.deobfuscator.setMappings(null); + if (project == null) return; + + project.setMappings(null); + this.gui.setMappingsFile(null); refreshClasses(); refreshCurrentClass(); } - public void exportSource(final File dirOut) { - if (deobfuscator == null) return; - ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeSources(dirOut.toPath(), progress)); + public void exportSource(final Path path) { + if (project == null) return; + + ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { + EnigmaProject.JarExport jar = project.exportRemappedJar(progress); + EnigmaProject.SourceExport source = jar.decompile(progress); + + source.write(path, progress); + }); } - public void exportJar(final File fileOut) { - if (deobfuscator == null) return; - ProgressDialog.runInThread(this.gui.getFrame(), progress -> this.deobfuscator.writeTransformedJar(fileOut, progress)); + public void exportJar(final Path path) { + if (project == null) return; + + ProgressDialog.runOffThread(this.gui.getFrame(), progress -> { + EnigmaProject.JarExport jar = project.exportRemappedJar(progress); + jar.write(path, progress); + }); } public Token getToken(int pos) { @@ -167,85 +207,9 @@ public class GuiController { ); } - public boolean entryIsInJar(Entry entry) { - if (entry == null || deobfuscator == null) return false; - return this.deobfuscator.isRenamable(entry); - } - - public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - ClassInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildClassInheritance(translator, entry); - return ClassInheritanceTreeNode.findNode(rootNode, entry); - } - - public ClassImplementationsTreeNode getClassImplementations(ClassEntry entry) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - return this.deobfuscator.getIndexTreeBuilder().buildClassImplementations(translator, entry); - } - - public MethodInheritanceTreeNode getMethodInheritance(MethodEntry entry) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - MethodInheritanceTreeNode rootNode = this.deobfuscator.getIndexTreeBuilder().buildMethodInheritance(translator, entry); - return MethodInheritanceTreeNode.findNode(rootNode, entry); - } - - public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - List rootNodes = this.deobfuscator.getIndexTreeBuilder().buildMethodImplementations(translator, entry); - if (rootNodes.isEmpty()) { - return null; - } - if (rootNodes.size() > 1) { - System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); - } - return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); - } - - public ClassReferenceTreeNode getClassReferences(ClassEntry entry) { - Translator deobfuscator = this.deobfuscator.getMapper().getDeobfuscator(); - ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, entry); - rootNode.load(this.deobfuscator.getJarIndex(), true); - return rootNode; - } - - public FieldReferenceTreeNode getFieldReferences(FieldEntry entry) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, entry); - rootNode.load(this.deobfuscator.getJarIndex(), true); - return rootNode; - } - - public MethodReferenceTreeNode getMethodReferences(MethodEntry entry, boolean recursive) { - Translator translator = this.deobfuscator.getMapper().getDeobfuscator(); - MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, entry); - rootNode.load(this.deobfuscator.getJarIndex(), true, recursive); - return rootNode; - } - - public void rename(EntryReference, Entry> reference, String newName, boolean refreshClassTree) { - this.deobfuscator.rename(reference.getNameableEntry(), newName); - - if (refreshClassTree && reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) - this.gui.moveClassTree(reference, newName); - refreshCurrentClass(reference); - } - - public void removeMapping(EntryReference, Entry> reference) { - this.deobfuscator.removeMapping(reference.getNameableEntry()); - if (reference.entry instanceof ClassEntry) - this.gui.moveClassTree(reference, false, true); - refreshCurrentClass(reference); - } - - public void markAsDeobfuscated(EntryReference, Entry> reference) { - this.deobfuscator.markAsDeobfuscated(reference.getNameableEntry()); - if (reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) - this.gui.moveClassTree(reference, true, false); - refreshCurrentClass(reference); - } - /** * Navigates to the declaration with respect to navigation history + * * @param entry the entry whose declaration will be navigated to */ public void openDeclaration(Entry entry) { @@ -257,6 +221,7 @@ public class GuiController { /** * Navigates to the reference with respect to navigation history + * * @param reference the reference */ public void openReference(EntryReference, Entry> reference) { @@ -275,12 +240,13 @@ public class GuiController { /** * Navigates to the reference without modifying history. If the class is not currently loaded, it will be loaded. + * * @param reference the reference */ private void setReference(EntryReference, Entry> reference) { // get the reference target class ClassEntry classEntry = reference.getLocationClassEntry(); - if (!this.deobfuscator.isRenamable(classEntry)) { + if (!project.isRenamable(classEntry)) { throw new IllegalArgumentException("Obfuscated class " + classEntry + " was not found in the jar!"); } @@ -294,6 +260,7 @@ public class GuiController { /** * Navigates to the reference without modifying history. Assumes the class is loaded. + * * @param reference */ private void showReference(EntryReference, Entry> reference) { @@ -307,7 +274,7 @@ public class GuiController { } public Collection getTokensForReference(EntryReference, Entry> reference) { - EntryRemapper mapper = this.deobfuscator.getMapper(); + EntryRemapper mapper = this.project.getMapper(); SourceIndex index = this.currentSource.getIndex(); return mapper.getObfResolver().resolveReference(reference, ResolutionStrategy.RESOLVE_CLOSEST) @@ -337,7 +304,7 @@ public class GuiController { } public void navigateTo(Entry entry) { - if (!entryIsInJar(entry)) { + if (!project.isRenamable(entry)) { // entry is not in the jar. Ignore it return; } @@ -345,7 +312,7 @@ public class GuiController { } public void navigateTo(EntryReference, Entry> reference) { - if (!entryIsInJar(reference.getLocationClassEntry())) { + if (!project.isRenamable(reference.getLocationClassEntry())) { return; } openReference(reference); @@ -354,11 +321,38 @@ public class GuiController { private void refreshClasses() { List obfClasses = Lists.newArrayList(); List deobfClasses = Lists.newArrayList(); - this.deobfuscator.getSeparatedClasses(obfClasses, deobfClasses); + this.addSeparatedClasses(obfClasses, deobfClasses); this.gui.setObfClasses(obfClasses); this.gui.setDeobfClasses(deobfClasses); } + public void addSeparatedClasses(List obfClasses, List deobfClasses) { + EntryRemapper mapper = project.getMapper(); + + Collection classes = project.getJarIndex().getEntryIndex().getClasses(); + Stream visibleClasses = classes.stream() + .filter(entry -> !entry.isInnerClass()); + + visibleClasses.forEach(entry -> { + ClassEntry deobfEntry = mapper.deobfuscate(entry); + + Optional obfService = enigma.getServices().get(ObfuscationTestService.TYPE); + boolean obfuscated = deobfEntry.equals(entry); + + if (obfuscated && obfService.isPresent()) { + if (obfService.get().testDeobfuscated(entry)) { + obfuscated = false; + } + } + + if (obfuscated) { + obfClasses.add(entry); + } else { + deobfClasses.add(entry); + } + }); + } + public void refreshCurrentClass() { refreshCurrentClass(null); } @@ -384,10 +378,10 @@ public class GuiController { DECOMPILER_SERVICE.submit(() -> { try { if (requiresDecompile) { - currentSource = decompileSource(targetClass, deobfuscator.getObfSourceProvider()); + currentSource = decompileSource(targetClass); } - remapSource(deobfuscator.getMapper().getDeobfuscator()); + remapSource(project.getMapper().getDeobfuscator()); callback.run(); } catch (Throwable t) { System.err.println("An exception was thrown while decompiling class " + classEntry.getFullName()); @@ -396,7 +390,7 @@ public class GuiController { }); } - private DecompiledClassSource decompileSource(ClassEntry targetClass, SourceProvider sourceProvider) { + private DecompiledClassSource decompileSource(ClassEntry targetClass) { try { CompilationUnit sourceTree = sourceProvider.getSources(targetClass.getFullName()); if (sourceTree == null) { @@ -410,7 +404,7 @@ public class GuiController { String sourceString = sourceProvider.writeSourceToString(sourceTree); SourceIndex index = SourceIndex.buildIndex(sourceString, sourceTree, true); - index.resolveReferences(deobfuscator.getMapper().getObfResolver()); + index.resolveReferences(project.getMapper().getObfResolver()); return new DecompiledClassSource(targetClass, index); } catch (Throwable t) { @@ -426,20 +420,105 @@ public class GuiController { return; } - currentSource.remapSource(deobfuscator, translator); + currentSource.remapSource(project, translator); gui.setEditorTheme(Config.getInstance().lookAndFeel); gui.setSource(currentSource); } - public Deobfuscator getDeobfuscator() { - return deobfuscator; - } - public void modifierChange(ItemEvent event) { if (event.getStateChange() == ItemEvent.SELECTED) { - deobfuscator.changeModifier(gui.cursorReference.entry, (AccessModifier) event.getItem()); + EntryRemapper mapper = project.getMapper(); + Entry entry = gui.cursorReference.entry; + AccessModifier modifier = (AccessModifier) event.getItem(); + + EntryMapping mapping = mapper.getDeobfMapping(entry); + if (mapping != null) { + mapper.mapFromObf(entry, new EntryMapping(mapping.getTargetName(), modifier)); + } else { + mapper.mapFromObf(entry, new EntryMapping(entry.getName(), modifier)); + } + refreshCurrentClass(); } } + + public ClassInheritanceTreeNode getClassInheritance(ClassEntry entry) { + Translator translator = project.getMapper().getDeobfuscator(); + ClassInheritanceTreeNode rootNode = indexTreeBuilder.buildClassInheritance(translator, entry); + return ClassInheritanceTreeNode.findNode(rootNode, entry); + } + + public ClassImplementationsTreeNode getClassImplementations(ClassEntry entry) { + Translator translator = project.getMapper().getDeobfuscator(); + return this.indexTreeBuilder.buildClassImplementations(translator, entry); + } + + public MethodInheritanceTreeNode getMethodInheritance(MethodEntry entry) { + Translator translator = project.getMapper().getDeobfuscator(); + MethodInheritanceTreeNode rootNode = indexTreeBuilder.buildMethodInheritance(translator, entry); + return MethodInheritanceTreeNode.findNode(rootNode, entry); + } + + public MethodImplementationsTreeNode getMethodImplementations(MethodEntry entry) { + Translator translator = project.getMapper().getDeobfuscator(); + List rootNodes = indexTreeBuilder.buildMethodImplementations(translator, entry); + if (rootNodes.isEmpty()) { + return null; + } + if (rootNodes.size() > 1) { + System.err.println("WARNING: Method " + entry + " implements multiple interfaces. Only showing first one."); + } + return MethodImplementationsTreeNode.findNode(rootNodes.get(0), entry); + } + + public ClassReferenceTreeNode getClassReferences(ClassEntry entry) { + Translator deobfuscator = project.getMapper().getDeobfuscator(); + ClassReferenceTreeNode rootNode = new ClassReferenceTreeNode(deobfuscator, entry); + rootNode.load(project.getJarIndex(), true); + return rootNode; + } + + public FieldReferenceTreeNode getFieldReferences(FieldEntry entry) { + Translator translator = project.getMapper().getDeobfuscator(); + FieldReferenceTreeNode rootNode = new FieldReferenceTreeNode(translator, entry); + rootNode.load(project.getJarIndex(), true); + return rootNode; + } + + public MethodReferenceTreeNode getMethodReferences(MethodEntry entry, boolean recursive) { + Translator translator = project.getMapper().getDeobfuscator(); + MethodReferenceTreeNode rootNode = new MethodReferenceTreeNode(translator, entry); + rootNode.load(project.getJarIndex(), true, recursive); + return rootNode; + } + + public void rename(EntryReference, Entry> reference, String newName, boolean refreshClassTree) { + Entry entry = reference.getNameableEntry(); + project.getMapper().mapFromObf(entry, new EntryMapping(newName)); + + if (refreshClassTree && reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) + this.gui.moveClassTree(reference, newName); + + refreshCurrentClass(reference); + } + + public void removeMapping(EntryReference, Entry> reference) { + project.getMapper().removeByObf(reference.getNameableEntry()); + + if (reference.entry instanceof ClassEntry) + this.gui.moveClassTree(reference, false, true); + refreshCurrentClass(reference); + } + + public void markAsDeobfuscated(EntryReference, Entry> reference) { + EntryRemapper mapper = project.getMapper(); + Entry entry = reference.getNameableEntry(); + mapper.mapFromObf(entry, new EntryMapping(mapper.deobfuscate(entry).getName())); + + if (reference.entry instanceof ClassEntry && !((ClassEntry) reference.entry).isInnerClass()) + this.gui.moveClassTree(reference, true, false); + + refreshCurrentClass(reference); + } } diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java index 84fe7c8..c135d03 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java @@ -57,7 +57,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); } - public static void runInThread(final JFrame parent, final ProgressRunnable runnable) { + public static void runOffThread(final JFrame parent, final ProgressRunnable runnable) { new Thread(() -> { try (ProgressDialog progress = new ProgressDialog(parent)) { @@ -68,6 +68,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable { }).start(); } + @Override public void close() { this.frame.dispose(); } diff --git a/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java index a122bd8..1657d7b 100644 --- a/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java +++ b/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java @@ -40,7 +40,7 @@ public class SearchDialog { this.parent = parent; deobfClasses = Lists.newArrayList(); - this.parent.getController().getDeobfuscator().getSeparatedClasses(Lists.newArrayList(), deobfClasses); + this.parent.getController().addSeparatedClasses(Lists.newArrayList(), deobfClasses); deobfClasses.removeIf(ClassEntry::isInnerClass); } diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java index 98275b4..5578325 100644 --- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java +++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java @@ -13,10 +13,11 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; -import java.util.jar.JarFile; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; public class MenuBar extends JMenuBar { @@ -43,17 +44,9 @@ public class MenuBar extends JMenuBar { menu.add(item); item.addActionListener(event -> { this.gui.jarFileChooser.setVisible(true); - File file = new File(this.gui.jarFileChooser.getDirectory() + File.separator + this.gui.jarFileChooser.getFile()); - if (file.exists()) { - // load the jar in a separate thread - new Thread(() -> - { - try { - gui.getController().openJar(new JarFile(file)); - } catch (IOException ex) { - throw new Error(ex); - } - }).start(); + Path path = Paths.get(this.gui.jarFileChooser.getDirectory()).resolve(this.gui.jarFileChooser.getFile()); + if (Files.exists(path)) { + gui.getController().openJar(path); } }); } @@ -106,7 +99,7 @@ public class MenuBar extends JMenuBar { item.addActionListener(event -> { // TODO: Use a specific file chooser for it if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { - this.gui.getController().saveMappings(MappingFormat.ENIGMA_FILE, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath()); + this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath(), MappingFormat.ENIGMA_FILE); this.saveMappingsMenu.setEnabled(true); } }); @@ -118,7 +111,7 @@ public class MenuBar extends JMenuBar { item.addActionListener(event -> { // TODO: Use a specific file chooser for it if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { - this.gui.getController().saveMappings(MappingFormat.ENIGMA_DIRECTORY, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath()); + this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath(), MappingFormat.ENIGMA_DIRECTORY); this.saveMappingsMenu.setEnabled(true); } }); @@ -131,7 +124,7 @@ public class MenuBar extends JMenuBar { item.addActionListener(event -> { // TODO: Use a specific file chooser for it if (this.gui.enigmaMappingsFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { - this.gui.getController().saveMappings(MappingFormat.SRG_FILE, this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath()); + this.gui.getController().saveMappings(this.gui.enigmaMappingsFileChooser.getSelectedFile().toPath(), MappingFormat.SRG_FILE); this.saveMappingsMenu.setEnabled(true); } }); @@ -162,7 +155,7 @@ public class MenuBar extends JMenuBar { menu.add(item); item.addActionListener(event -> { if (this.gui.exportSourceFileChooser.showSaveDialog(this.gui.getFrame()) == JFileChooser.APPROVE_OPTION) { - this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile()); + this.gui.getController().exportSource(this.gui.exportSourceFileChooser.getSelectedFile().toPath()); } }); this.exportSourceMenu = item; @@ -173,8 +166,8 @@ public class MenuBar extends JMenuBar { item.addActionListener(event -> { this.gui.exportJarFileChooser.setVisible(true); if (this.gui.exportJarFileChooser.getFile() != null) { - File file = new File(this.gui.exportJarFileChooser.getDirectory() + File.separator + this.gui.exportJarFileChooser.getFile()); - this.gui.getController().exportJar(file); + Path path = Paths.get(this.gui.exportJarFileChooser.getDirectory(), this.gui.exportJarFileChooser.getFile()); + this.gui.getController().exportJar(path); } }); this.exportJarMenu = item; @@ -202,7 +195,7 @@ public class MenuBar extends JMenuBar { search.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_MASK)); menu.add(search); search.addActionListener(event -> { - if (this.gui.getController().getDeobfuscator() != null) { + if (this.gui.getController().project != null) { new SearchDialog(this.gui).show(); } }); diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java index ff84648..e8a9cad 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java @@ -1,6 +1,6 @@ package cuchaz.enigma.gui.panels; -import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.analysis.EntryReference; import cuchaz.enigma.config.Config; import cuchaz.enigma.gui.BrowserCaret; @@ -94,8 +94,8 @@ public class PanelEditor extends JEditorPane { if (!gui.popupMenu.renameMenu.isEnabled()) return; if (!event.isControlDown() && !event.isAltDown()) { - Deobfuscator deobfuscator = gui.getController().getDeobfuscator(); - EntryReference, Entry> reference = deobfuscator.deobfuscate(gui.cursorReference); + EnigmaProject project = gui.getController().project; + EntryReference, Entry> reference = project.getMapper().deobfuscate(gui.cursorReference); Entry entry = reference.getNameableEntry(); String name = String.valueOf(event.getKeyChar()); -- cgit v1.2.3