From bf8cfbe17ac073dfa6dd7aaf0cf1a1f8b9746af0 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Tue, 14 Feb 2023 22:25:15 -0600 Subject: [PATCH 01/20] initial really broken impl --- .../java/cuchaz/enigma/gui/ClassSelector.java | 12 +- .../java/cuchaz/enigma/gui/util/GuiUtil.java | 16 +++ .../src/main/resources/icons/deobfuscated.svg | 26 +++++ .../src/main/resources/icons/obfuscated.svg | 12 ++ .../icons/partially_deobfuscated.svg | 42 +++++++ .../java/cuchaz/enigma/EnigmaProject.java | 103 ++++++++++++++++++ 6 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 enigma-swing/src/main/resources/icons/deobfuscated.svg create mode 100644 enigma-swing/src/main/resources/icons/obfuscated.svg create mode 100644 enigma-swing/src/main/resources/icons/partially_deobfuscated.svg diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index c38c4d91e..b21b7c114 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -88,7 +88,17 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (gui.getController().project != null && leaf && value instanceof ClassSelectorClassNode node) { - this.setIcon(GuiUtil.getClassIcon(gui, node.getObfEntry())); + // todo! + JPanel panel = new JPanel(); + panel.setOpaque(false); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(new JLabel(GuiUtil.getClassIcon(gui, node.getObfEntry()))); + JLabel textLabel = new JLabel(node.getObfEntry().getSimpleName()); + + textLabel.setIcon(GuiUtil.getDeobfuscationIcon(gui.getController().project, node.getObfEntry())); + panel.add(textLabel); + + return panel; } return this; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 066d1b620..eb2550179 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -23,10 +23,12 @@ import com.formdev.flatlaf.extras.FlatSVGIcon; +import cuchaz.enigma.EnigmaProject; import cuchaz.enigma.analysis.index.EntryIndex; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.Os; @@ -40,6 +42,10 @@ public class GuiUtil { public static final Icon FIELD_ICON = loadIcon("field"); public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); + public static final Icon OBFUSCATED_ICON = loadIcon("obfuscated"); + public static final Icon PARTIALLY_DEOBFUSCATED_ICON = loadIcon("partially_deobfuscated"); + public static final Icon DEOBFUSCATED_ICON = loadIcon("deobfuscated"); + public static void openUrl(String url) { try { if (Objects.requireNonNull(Os.getOs()) == Os.LINUX) { @@ -142,6 +148,16 @@ public static Icon getClassIcon(Gui gui, ClassEntry entry) { return CLASS_ICON; } + public static Icon getDeobfuscationIcon(EnigmaProject project, Entry entry) { + if (project.isFullyDeobfuscated(entry)) { + return DEOBFUSCATED_ICON; + } else if (project.isAtLeastPartiallyDeobfuscated(entry)) { + return PARTIALLY_DEOBFUSCATED_ICON; + } else { + return OBFUSCATED_ICON; + } + } + public static Icon getMethodIcon(MethodEntry entry) { if (entry.isConstructor()) { return CONSTRUCTOR_ICON; diff --git a/enigma-swing/src/main/resources/icons/deobfuscated.svg b/enigma-swing/src/main/resources/icons/deobfuscated.svg new file mode 100644 index 000000000..84a629b66 --- /dev/null +++ b/enigma-swing/src/main/resources/icons/deobfuscated.svg @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/enigma-swing/src/main/resources/icons/obfuscated.svg b/enigma-swing/src/main/resources/icons/obfuscated.svg new file mode 100644 index 000000000..82c9b63b8 --- /dev/null +++ b/enigma-swing/src/main/resources/icons/obfuscated.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg b/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg new file mode 100644 index 000000000..1dbf1797e --- /dev/null +++ b/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 32ad668fa..90cdb5d86 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -23,7 +23,10 @@ import cuchaz.enigma.analysis.index.EnclosingMethodIndex; import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; +import cuchaz.enigma.translation.TranslateResult; +import cuchaz.enigma.translation.mapping.ResolutionStrategy; import cuchaz.enigma.translation.representation.entry.ClassDefEntry; +import cuchaz.enigma.translation.representation.entry.ParentedEntry; import cuchaz.enigma.utils.Pair; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; @@ -192,6 +195,106 @@ public boolean isRenamable(EntryReference, Entry> obfReference) { return obfReference.isNamed() && this.isRenamable(obfReference.getNameableEntry()); } + /** + * Checks whether the provided entry or some of its potential children + * are deobfuscated. Local variables are not being considered. + */ + public boolean isAtLeastPartiallyDeobfuscated(Entry entry) { + // todo! + return testDeobfuscated(entry, false); + } + + /** + * Checks whether the provided entry and all of its potential children + * are deobfuscated. Local variables are not being considered. + */ + public boolean isFullyDeobfuscated(Entry entry) { + // todo! + return testDeobfuscated(entry, true); + } + + private boolean testDeobfuscated(Entry entry, boolean mustBeFullyDeobf) { + // todo! + boolean obfuscationDetected = false; + + // Target name check + TranslateResult> targetName = mapper.extendedDeobfuscate(entry); + + if (targetName != null && !targetName.isObfuscated()) { + if (!mustBeFullyDeobf) { + return true; + } + } else { + obfuscationDetected = true; + + if (mustBeFullyDeobf) { + return false; + } + } + + // Name Proposal check + List nameProposalServices = this.getEnigma().getServices().get(NameProposalService.TYPE); + + if (!mustBeFullyDeobf && !nameProposalServices.isEmpty()) { + for (NameProposalService service : nameProposalServices) { + if (service.proposeName(entry, mapper).isPresent()) { + return true; + } + } + } + + // Obfuscation Test Services check + List obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE); + + if (!obfuscationTestServices.isEmpty()) { + for (ObfuscationTestService service : obfuscationTestServices) { + if (service.testDeobfuscated(entry)) { + if (!mustBeFullyDeobf) { + return true; + } + } else { + obfuscationDetected = true; + } + + if (mustBeFullyDeobf && obfuscationDetected) { + return false; + } + } + } + + // Obfuscated children check + List> renamableChildren = jarIndex.getChildrenByClass().entries() + .stream() + .filter(item -> item.getKey().equals(entry)) + .map(Map.Entry::getValue) + .filter(item -> isRenamable(item) + // No constructors + && !item.getName().equals("") + && !item.getName().equals("") + // Don't check inherited members + && jarIndex.getEntryResolver().resolveFirstEntry(item, ResolutionStrategy.RESOLVE_ROOT).getParent().equals(entry)) + .toList(); + for (ParentedEntry child : renamableChildren) { + if (isAtLeastPartiallyDeobfuscated(child)) { + if (!mustBeFullyDeobf) { + return true; + } + } else { + obfuscationDetected = true; + } + + if (mustBeFullyDeobf && obfuscationDetected) { + return false; + } + } + + if (mustBeFullyDeobf) { + return !obfuscationDetected; + } else { + return false; + } + } + public boolean isObfuscated(Entry entry) { String name = entry.getName(); From 8e802fdd6e0a6ce86f078ce8428cb92747a762af Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 16 Feb 2023 21:06:27 -0600 Subject: [PATCH 02/20] temporarily remove shadowing warning --- .../translation/mapping/MappingValidator.java | 24 ------------------- .../enigma/utils/validation/Message.java | 3 --- 2 files changed, 27 deletions(-) diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java index 1e4bb0571..f12165b88 100644 --- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java +++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java @@ -14,8 +14,6 @@ import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; -import javax.annotation.Nullable; - public class MappingValidator { private final EntryTree obfToDeobf; private final Translator deobfuscator; @@ -42,7 +40,6 @@ private boolean validateUnique(ValidationContext vc, Entry entry, String name Collection relatedClasses = this.getRelatedClasses(containingClass); boolean error = false; - Entry shadowedEntry; for (ClassEntry relatedClass : relatedClasses) { if (this.isStatic(entry) && relatedClass != containingClass) { @@ -66,13 +63,6 @@ private boolean validateUnique(ValidationContext vc, Entry entry, String name vc.raise(Message.NONUNIQUE_NAME, name); } error = true; - } else if ((shadowedEntry = this.getShadowedEntry(translatedEntry, translatedSiblings, name)) != null) { - Entry parent = shadowedEntry.getParent(); - if (parent != null) { - vc.raise(Message.SHADOWED_NAME_CLASS, name, parent); - } else { - vc.raise(Message.SHADOWED_NAME, name); - } } } @@ -103,20 +93,6 @@ private boolean canConflict(Entry entry, Entry sibling) { return entry.canConflictWith(sibling); } - @Nullable - private Entry getShadowedEntry(Entry entry, List> siblings, String name) { - for (Entry sibling : siblings) { - if (this.canShadow(entry, sibling) && sibling.getName().equals(name)) { - return sibling; - } - } - return null; - } - - private boolean canShadow(Entry entry, Entry sibling) { - return entry.canShadow(sibling); - } - private boolean isStatic(Entry entry) { AccessFlags accessFlags = this.index.getEntryIndex().getEntryAccess(entry); return accessFlags != null && accessFlags.isStatic(); diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java index 846687102..2b7acb7b2 100644 --- a/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java +++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/Message.java @@ -15,9 +15,6 @@ public class Message { public static final Message ILLEGAL_DOC_COMMENT_END = create(Type.ERROR, "illegal_doc_comment_end"); public static final Message UNKNOWN_RECORD_GETTER = create(Type.ERROR, "unknown_record_getter"); - public static final Message SHADOWED_NAME_CLASS = create(Type.WARNING, "shadowed_name_class"); - public static final Message SHADOWED_NAME = create(Type.WARNING, "shadowed_name"); - public static final Message SERVER_STARTED = create(Type.INFO, "server_started"); public static final Message CONNECTED_TO_SERVER = create(Type.INFO, "connected_to_server"); public static final Message LEFT_SERVER = create(Type.INFO, "left_server"); From dc17d84e7ac7602beca94eb53a7c7fb1fd33157e Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 Feb 2023 20:34:38 -0600 Subject: [PATCH 03/20] better icons and fix names --- .../java/cuchaz/enigma/gui/ClassSelector.java | 6 +- .../java/cuchaz/enigma/gui/util/GuiUtil.java | 10 +-- .../src/main/resources/icons/deobfuscated.svg | 71 ++++++++++------ .../src/main/resources/icons/obfuscated.svg | 77 ++++++++++++++--- .../icons/partially_deobfuscated.svg | 84 +++++++++---------- 5 files changed, 155 insertions(+), 93 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index b21b7c114..01ac4b37e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -88,15 +88,13 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (gui.getController().project != null && leaf && value instanceof ClassSelectorClassNode node) { - // todo! JPanel panel = new JPanel(); panel.setOpaque(false); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(new JLabel(GuiUtil.getClassIcon(gui, node.getObfEntry()))); - JLabel textLabel = new JLabel(node.getObfEntry().getSimpleName()); - textLabel.setIcon(GuiUtil.getDeobfuscationIcon(gui.getController().project, node.getObfEntry())); - panel.add(textLabel); + this.setIcon(GuiUtil.getDeobfuscationIcon(gui.getController().project, node.getObfEntry())); + panel.add(this); return panel; } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index eb2550179..5bfb19ac4 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -42,6 +42,7 @@ public class GuiUtil { public static final Icon FIELD_ICON = loadIcon("field"); public static final Icon CONSTRUCTOR_ICON = loadIcon("constructor"); + // icons sourced from https://github.com/primer/octicons public static final Icon OBFUSCATED_ICON = loadIcon("obfuscated"); public static final Icon PARTIALLY_DEOBFUSCATED_ICON = loadIcon("partially_deobfuscated"); public static final Icon DEOBFUSCATED_ICON = loadIcon("deobfuscated"); @@ -95,15 +96,6 @@ public static void showPopup(JComponent component, String text, int x, int y) { t.start(); } - public static void showToolTipNow(JComponent component) { - // HACKHACK: trick the tooltip manager into showing the tooltip right now - ToolTipManager manager = ToolTipManager.sharedInstance(); - int oldDelay = manager.getInitialDelay(); - manager.setInitialDelay(0); - manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false)); - manager.setInitialDelay(oldDelay); - } - public static JLabel createLink(String text, Runnable action) { JLabel link = new JLabel(text); link.setForeground(Color.BLUE.darker()); diff --git a/enigma-swing/src/main/resources/icons/deobfuscated.svg b/enigma-swing/src/main/resources/icons/deobfuscated.svg index 84a629b66..f6c49f84a 100644 --- a/enigma-swing/src/main/resources/icons/deobfuscated.svg +++ b/enigma-swing/src/main/resources/icons/deobfuscated.svg @@ -1,26 +1,45 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/enigma-swing/src/main/resources/icons/obfuscated.svg b/enigma-swing/src/main/resources/icons/obfuscated.svg index 82c9b63b8..ba331bc0f 100644 --- a/enigma-swing/src/main/resources/icons/obfuscated.svg +++ b/enigma-swing/src/main/resources/icons/obfuscated.svg @@ -1,12 +1,65 @@ - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg b/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg index 1dbf1797e..b9f24f43d 100644 --- a/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg +++ b/enigma-swing/src/main/resources/icons/partially_deobfuscated.svg @@ -1,42 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + From 156fb01bb54229c450b2ee3bc54a61f19db0e37a Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 Feb 2023 20:50:48 -0600 Subject: [PATCH 04/20] real time updates --- .../main/java/cuchaz/enigma/gui/ClassSelector.java | 11 +++++++++++ enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | 10 +++++++--- .../main/java/cuchaz/enigma/gui/GuiController.java | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 01ac4b37e..0226fc467 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -274,6 +274,17 @@ public void removeEntry(ClassEntry classEntry) { this.packageManager.removeClassNode(classEntry); } + public void reloadEntry(ClassEntry classEntry) { + this.removeEntry(classEntry); + this.moveClassIn(classEntry); + this.reload(classEntry); + } + + public void reload(ClassEntry classEntry) { + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + model.reload(this.packageManager.getClassNode(classEntry)); + } + public void reload() { DefaultTreeModel model = (DefaultTreeModel) this.getModel(); model.reload(this.packageManager.getRoot()); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index ef0799919..3d4d380ec 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -594,14 +594,18 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { deobfuscatedClassSelector.reload(); } - allClassesClassSelector.removeEntry(classEntry); - allClassesClassSelector.moveClassIn(classEntry); - allClassesClassSelector.reload(); + allClassesClassSelector.reloadEntry(classEntry); deobfuscatedClassSelector.restoreExpansionState(deobfuscatedPanelExpansionState); obfuscatedClassSelector.restoreExpansionState(obfuscatedPanelExpansionState); } + public void reloadClassEntry(ClassEntry classEntry) { + Docker.getDocker(DeobfuscatedClassesDocker.class).getClassSelector().reloadEntry(classEntry); + Docker.getDocker(ObfuscatedClassesDocker.class).getClassSelector().reloadEntry(classEntry); + Docker.getDocker(AllClassesDocker.class).getClassSelector().reloadEntry(classEntry); + } + public SearchDialog getSearchDialog() { if (this.searchDialog == null) { this.searchDialog = new SearchDialog(this); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index 6650bd964..1d7a5840c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -522,6 +522,7 @@ private void applyChange0(ValidationContext vc, EntryChange change) { } this.gui.updateStructure(this.gui.getActiveEditor()); + this.gui.reloadClassEntry(change.getTarget().getTopLevelClass()); } public void openStats(Set includedMembers, String topLevelPackage, boolean includeSynthetic) { From 1ca552b8c84e90d46eefe5652adf8adf58fd1074 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 Feb 2023 20:54:48 -0600 Subject: [PATCH 05/20] cleanup --- .../java/cuchaz/enigma/gui/util/GuiUtil.java | 2 +- .../java/cuchaz/enigma/EnigmaProject.java | 26 ++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 5bfb19ac4..72b3be858 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -143,7 +143,7 @@ public static Icon getClassIcon(Gui gui, ClassEntry entry) { public static Icon getDeobfuscationIcon(EnigmaProject project, Entry entry) { if (project.isFullyDeobfuscated(entry)) { return DEOBFUSCATED_ICON; - } else if (project.isAtLeastPartiallyDeobfuscated(entry)) { + } else if (project.isPartiallyDeobfuscated(entry)) { return PARTIALLY_DEOBFUSCATED_ICON; } else { return OBFUSCATED_ICON; diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 90cdb5d86..75da0613c 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -197,24 +197,24 @@ public boolean isRenamable(EntryReference, Entry> obfReference) { /** * Checks whether the provided entry or some of its potential children - * are deobfuscated. Local variables are not being considered. + * are deobfuscated. Local variables are not considered. */ - public boolean isAtLeastPartiallyDeobfuscated(Entry entry) { - // todo! - return testDeobfuscated(entry, false); + public boolean isPartiallyDeobfuscated(Entry entry) { + return testDeobfuscation(entry, false); } /** * Checks whether the provided entry and all of its potential children - * are deobfuscated. Local variables are not being considered. + * are deobfuscated. Local variables are not considered. */ public boolean isFullyDeobfuscated(Entry entry) { - // todo! - return testDeobfuscated(entry, true); + return testDeobfuscation(entry, true); } - private boolean testDeobfuscated(Entry entry, boolean mustBeFullyDeobf) { - // todo! + /** + * @author NebelNidas + */ + private boolean testDeobfuscation(Entry entry, boolean mustBeFullyDeobf) { boolean obfuscationDetected = false; // Target name check @@ -275,7 +275,7 @@ private boolean testDeobfuscated(Entry entry, boolean mustBeFullyDeobf) { && jarIndex.getEntryResolver().resolveFirstEntry(item, ResolutionStrategy.RESOLVE_ROOT).getParent().equals(entry)) .toList(); for (ParentedEntry child : renamableChildren) { - if (isAtLeastPartiallyDeobfuscated(child)) { + if (isPartiallyDeobfuscated(child)) { if (!mustBeFullyDeobf) { return true; } @@ -288,11 +288,7 @@ private boolean testDeobfuscated(Entry entry, boolean mustBeFullyDeobf) { } } - if (mustBeFullyDeobf) { - return !obfuscationDetected; - } else { - return false; - } + return mustBeFullyDeobf; } public boolean isObfuscated(Entry entry) { From 1e3f0cb53602c0632e8dcfbc37c17d1442a8f6cc Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sat, 18 Feb 2023 22:25:11 -0600 Subject: [PATCH 06/20] use StatsGenerator instead of @NebelNidas ' code --- .../java/cuchaz/enigma/gui/ClassSelector.java | 2 +- .../enigma/gui/stats/StatsGenerator.java | 37 ++++- .../cuchaz/enigma/gui/stats/StatsResult.java | 4 +- .../java/cuchaz/enigma/gui/util/GuiUtil.java | 61 ++++--- .../java/cuchaz/enigma/EnigmaProject.java | 155 ++++-------------- 5 files changed, 104 insertions(+), 155 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 0226fc467..52d49ce29 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -93,7 +93,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(new JLabel(GuiUtil.getClassIcon(gui, node.getObfEntry()))); - this.setIcon(GuiUtil.getDeobfuscationIcon(gui.getController().project, node.getObfEntry())); + this.setIcon(GuiUtil.getDeobfuscationIcon(gui, node.getObfEntry())); panel.add(this); return panel; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 55f71bd02..14a0470ee 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -34,7 +34,15 @@ public StatsGenerator(EnigmaProject project) { this.entryResolver = project.getJarIndex().getEntryResolver(); } + public StatsResult generate(ProgressListener progress, ClassEntry entry, boolean includeSynthetic) { + return generate(progress, EnumSet.allOf(StatsMember.class), entry.getFullName(), true, includeSynthetic); + } + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean includeSynthetic) { + return generate(progress, includedMembers, topLevelPackage, false, includeSynthetic); + } + + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean singleClass, boolean includeSynthetic) { includedMembers = EnumSet.copyOf(includedMembers); int totalWork = 0; int totalMappable = 0; @@ -61,6 +69,12 @@ public StatsResult generate(ProgressListener progress, Set included if (includedMembers.contains(StatsMember.METHODS) || includedMembers.contains(StatsMember.PARAMETERS)) { for (MethodEntry method : this.entryIndex.getMethods()) { progress.step(numDone++, I18n.translate("type.methods")); + + // we don't want constructors to show as a mapped method! + if (method.isConstructor()) { + continue; + } + MethodEntry root = this.entryResolver .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) .stream() @@ -68,9 +82,8 @@ public StatsResult generate(ProgressListener progress, Set included .orElseThrow(AssertionError::new); ClassEntry clazz = root.getParent(); - String deobfuscatedPackageName = this.mapper.deobfuscate(clazz).getPackageName(); - if (root == method && (topLevelPackageSlash.isBlank() || (deobfuscatedPackageName != null && deobfuscatedPackageName.startsWith(topLevelPackageSlash)))) { + if (root == method && checkPackage(clazz, topLevelPackageSlash, singleClass)) { if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { this.update(counts, method); totalMappable++; @@ -92,9 +105,8 @@ public StatsResult generate(ProgressListener progress, Set included for (FieldEntry field : this.entryIndex.getFields()) { progress.step(numDone++, I18n.translate("type.fields")); ClassEntry clazz = field.getParent(); - String deobfuscatedPackageName = this.mapper.deobfuscate(clazz).getPackageName(); - if (!((FieldDefEntry) field).getAccess().isSynthetic() && (topLevelPackageSlash.isBlank() || (deobfuscatedPackageName != null && deobfuscatedPackageName.startsWith(topLevelPackageSlash)))) { + if (!((FieldDefEntry) field).getAccess().isSynthetic() && checkPackage(clazz, topLevelPackageSlash, singleClass)) { this.update(counts, field); totalMappable++; } @@ -105,9 +117,7 @@ public StatsResult generate(ProgressListener progress, Set included for (ClassEntry clazz : this.entryIndex.getClasses()) { progress.step(numDone++, I18n.translate("type.classes")); - String deobfuscatedPackageName = this.mapper.deobfuscate(clazz).getPackageName(); - - if (topLevelPackageSlash.isBlank() || (deobfuscatedPackageName != null && deobfuscatedPackageName.startsWith(topLevelPackageSlash))) { + if (checkPackage(clazz, topLevelPackageSlash, singleClass)) { this.update(counts, clazz); totalMappable++; } @@ -128,10 +138,23 @@ public StatsResult generate(ProgressListener progress, Set included return new StatsResult(totalMappable, counts.values().stream().mapToInt(i -> i).sum(), tree); } + private boolean checkPackage(ClassEntry clazz, String topLevelPackage, boolean singleClass) { + String deobfuscatedName = this.mapper.deobfuscate(clazz).getPackageName(); + if (singleClass) { + return (deobfuscatedName != null && deobfuscatedName.equals(topLevelPackage)) || clazz.getFullName().equals(topLevelPackage); + } + + return topLevelPackage.isBlank() || (deobfuscatedName != null && deobfuscatedName.startsWith(topLevelPackage)); + } + private void update(Map counts, Entry entry) { if (this.project.isObfuscated(entry) && this.project.isRenamable(entry) && !this.project.isSynthetic(entry)) { + System.out.println("unmapped! " + entry); String parent = this.mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); counts.put(parent, counts.getOrDefault(parent, 0) + 1); + return; } + + System.out.println("mapped! " + entry); } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java index 491c027cb..579e2a66a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java @@ -27,7 +27,7 @@ public int getUnmapped() { } public int getMapped() { - return this.total - this.unmapped; + return this.getTotal() - this.getUnmapped(); } public double getPercentage() { @@ -51,7 +51,7 @@ public static class Node { public String name; public T value; public List> children = new ArrayList<>(); - private final transient Map> namedChildren = new HashMap<>(); + private final Map> namedChildren = new HashMap<>(); public Node(String name, T value) { this.name = name; diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index 72b3be858..a15204dd7 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -1,10 +1,43 @@ package cuchaz.enigma.gui.util; -import java.awt.*; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import cuchaz.enigma.EnigmaProject; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.analysis.index.EntryIndex; +import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.stats.StatsGenerator; +import cuchaz.enigma.gui.stats.StatsResult; +import cuchaz.enigma.translation.representation.AccessFlags; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.MethodEntry; +import cuchaz.enigma.utils.Os; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JToolTip; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.Timer; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.event.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import java.awt.font.TextAttribute; import java.io.IOException; import java.net.URI; @@ -17,21 +50,6 @@ import java.util.Objects; import java.util.function.Consumer; -import javax.swing.*; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import com.formdev.flatlaf.extras.FlatSVGIcon; - -import cuchaz.enigma.EnigmaProject; -import cuchaz.enigma.analysis.index.EntryIndex; -import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.translation.representation.AccessFlags; -import cuchaz.enigma.translation.representation.entry.ClassEntry; -import cuchaz.enigma.translation.representation.entry.Entry; -import cuchaz.enigma.translation.representation.entry.MethodEntry; -import cuchaz.enigma.utils.Os; - public class GuiUtil { public static final Icon CLASS_ICON = loadIcon("class"); public static final Icon INTERFACE_ICON = loadIcon("interface"); @@ -140,10 +158,13 @@ public static Icon getClassIcon(Gui gui, ClassEntry entry) { return CLASS_ICON; } - public static Icon getDeobfuscationIcon(EnigmaProject project, Entry entry) { - if (project.isFullyDeobfuscated(entry)) { + public static Icon getDeobfuscationIcon(Gui gui, ClassEntry entry) { + EnigmaProject project = gui.getController().project; + StatsResult result = new StatsGenerator(project).generate(ProgressListener.none(), entry, false); + + if (result.getPercentage() == 100d) { return DEOBFUSCATED_ICON; - } else if (project.isPartiallyDeobfuscated(entry)) { + } else if (result.getPercentage() > 0) { return PARTIALLY_DEOBFUSCATED_ICON; } else { return OBFUSCATED_ICON; diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 75da0613c..99603126e 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -1,41 +1,15 @@ package cuchaz.enigma; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import com.google.common.base.Functions; import com.google.common.base.Preconditions; -import cuchaz.enigma.analysis.index.EnclosingMethodIndex; -import cuchaz.enigma.api.service.ObfuscationTestService; -import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; -import cuchaz.enigma.translation.TranslateResult; -import cuchaz.enigma.translation.mapping.ResolutionStrategy; -import cuchaz.enigma.translation.representation.entry.ClassDefEntry; -import cuchaz.enigma.translation.representation.entry.ParentedEntry; -import cuchaz.enigma.utils.Pair; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.tree.ClassNode; - import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.index.EnclosingMethodIndex; import cuchaz.enigma.analysis.index.JarIndex; import cuchaz.enigma.api.service.NameProposalService; +import cuchaz.enigma.api.service.ObfuscationTestService; import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; import cuchaz.enigma.classprovider.ClassProvider; +import cuchaz.enigma.classprovider.ObfuscationFixClassProvider; import cuchaz.enigma.source.Decompiler; import cuchaz.enigma.source.DecompilerService; import cuchaz.enigma.source.SourceSettings; @@ -46,13 +20,35 @@ import cuchaz.enigma.translation.mapping.MappingsChecker; import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.translation.representation.entry.ClassDefEntry; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.translation.representation.entry.Entry; import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; import cuchaz.enigma.translation.representation.entry.MethodEntry; import cuchaz.enigma.utils.I18n; +import cuchaz.enigma.utils.Pair; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; import org.tinylog.Logger; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class EnigmaProject { private static final List> NON_RENAMABLE_METHODS = new ArrayList<>(); @@ -164,6 +160,11 @@ public boolean isNavigable(Entry obfEntry) { public boolean isRenamable(Entry obfEntry) { if (obfEntry instanceof MethodEntry obfMethodEntry) { + // constructors are not renamable! + if (obfMethodEntry.isConstructor()) { + return false; + } + // HACKHACK: Object methods are not obfuscated identifiers String name = obfMethodEntry.getName(); String sig = obfMethodEntry.getDesc().toString(); @@ -195,102 +196,6 @@ public boolean isRenamable(EntryReference, Entry> obfReference) { return obfReference.isNamed() && this.isRenamable(obfReference.getNameableEntry()); } - /** - * Checks whether the provided entry or some of its potential children - * are deobfuscated. Local variables are not considered. - */ - public boolean isPartiallyDeobfuscated(Entry entry) { - return testDeobfuscation(entry, false); - } - - /** - * Checks whether the provided entry and all of its potential children - * are deobfuscated. Local variables are not considered. - */ - public boolean isFullyDeobfuscated(Entry entry) { - return testDeobfuscation(entry, true); - } - - /** - * @author NebelNidas - */ - private boolean testDeobfuscation(Entry entry, boolean mustBeFullyDeobf) { - boolean obfuscationDetected = false; - - // Target name check - TranslateResult> targetName = mapper.extendedDeobfuscate(entry); - - if (targetName != null && !targetName.isObfuscated()) { - if (!mustBeFullyDeobf) { - return true; - } - } else { - obfuscationDetected = true; - - if (mustBeFullyDeobf) { - return false; - } - } - - // Name Proposal check - List nameProposalServices = this.getEnigma().getServices().get(NameProposalService.TYPE); - - if (!mustBeFullyDeobf && !nameProposalServices.isEmpty()) { - for (NameProposalService service : nameProposalServices) { - if (service.proposeName(entry, mapper).isPresent()) { - return true; - } - } - } - - // Obfuscation Test Services check - List obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE); - - if (!obfuscationTestServices.isEmpty()) { - for (ObfuscationTestService service : obfuscationTestServices) { - if (service.testDeobfuscated(entry)) { - if (!mustBeFullyDeobf) { - return true; - } - } else { - obfuscationDetected = true; - } - - if (mustBeFullyDeobf && obfuscationDetected) { - return false; - } - } - } - - // Obfuscated children check - List> renamableChildren = jarIndex.getChildrenByClass().entries() - .stream() - .filter(item -> item.getKey().equals(entry)) - .map(Map.Entry::getValue) - .filter(item -> isRenamable(item) - // No constructors - && !item.getName().equals("") - && !item.getName().equals("") - // Don't check inherited members - && jarIndex.getEntryResolver().resolveFirstEntry(item, ResolutionStrategy.RESOLVE_ROOT).getParent().equals(entry)) - .toList(); - for (ParentedEntry child : renamableChildren) { - if (isPartiallyDeobfuscated(child)) { - if (!mustBeFullyDeobf) { - return true; - } - } else { - obfuscationDetected = true; - } - - if (mustBeFullyDeobf && obfuscationDetected) { - return false; - } - } - - return mustBeFullyDeobf; - } - public boolean isObfuscated(Entry entry) { String name = entry.getName(); From 82476a1b3813e831213f548a762581768bd7d05f Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 19 Feb 2023 13:44:46 -0600 Subject: [PATCH 07/20] fix StatsGenerator --- .../enigma/gui/stats/StatsGenerator.java | 32 ++++++++----------- .../cuchaz/enigma/gui/stats/StatsResult.java | 6 ++++ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 14a0470ee..f26835525 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -70,11 +70,6 @@ public StatsResult generate(ProgressListener progress, Set included for (MethodEntry method : this.entryIndex.getMethods()) { progress.step(numDone++, I18n.translate("type.methods")); - // we don't want constructors to show as a mapped method! - if (method.isConstructor()) { - continue; - } - MethodEntry root = this.entryResolver .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) .stream() @@ -85,16 +80,14 @@ public StatsResult generate(ProgressListener progress, Set included if (root == method && checkPackage(clazz, topLevelPackageSlash, singleClass)) { if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { - this.update(counts, method); - totalMappable++; + totalMappable += this.update(counts, method); } if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { - this.update(counts, new LocalVariableEntry(method, index, "", true, null)); + totalMappable += this.update(counts, new LocalVariableEntry(method, index, "", true, null)); index += argument.getSize(); - totalMappable++; } } } @@ -107,8 +100,7 @@ public StatsResult generate(ProgressListener progress, Set included ClassEntry clazz = field.getParent(); if (!((FieldDefEntry) field).getAccess().isSynthetic() && checkPackage(clazz, topLevelPackageSlash, singleClass)) { - this.update(counts, field); - totalMappable++; + totalMappable += this.update(counts, field); } } } @@ -118,8 +110,7 @@ public StatsResult generate(ProgressListener progress, Set included progress.step(numDone++, I18n.translate("type.classes")); if (checkPackage(clazz, topLevelPackageSlash, singleClass)) { - this.update(counts, clazz); - totalMappable++; + totalMappable += this.update(counts, clazz); } } } @@ -141,20 +132,25 @@ public StatsResult generate(ProgressListener progress, Set included private boolean checkPackage(ClassEntry clazz, String topLevelPackage, boolean singleClass) { String deobfuscatedName = this.mapper.deobfuscate(clazz).getPackageName(); if (singleClass) { - return (deobfuscatedName != null && deobfuscatedName.equals(topLevelPackage)) || clazz.getFullName().equals(topLevelPackage); + return (deobfuscatedName != null && deobfuscatedName.startsWith(topLevelPackage)) || clazz.getFullName().startsWith(topLevelPackage); } return topLevelPackage.isBlank() || (deobfuscatedName != null && deobfuscatedName.startsWith(topLevelPackage)); } - private void update(Map counts, Entry entry) { - if (this.project.isObfuscated(entry) && this.project.isRenamable(entry) && !this.project.isSynthetic(entry)) { + private int update(Map counts, Entry entry) { + boolean renamable = this.project.isRenamable(entry); + + if (this.project.isObfuscated(entry) && renamable && !this.project.isSynthetic(entry)) { System.out.println("unmapped! " + entry); String parent = this.mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); counts.put(parent, counts.getOrDefault(parent, 0) + 1); - return; } - System.out.println("mapped! " + entry); + if (renamable) { + return 1; + } else { + return 0; + } } } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java index 579e2a66a..c7bb2f2bf 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsResult.java @@ -31,6 +31,12 @@ public int getMapped() { } public double getPercentage() { + // avoid showing "Nan%" when there are no entries to map + // if there are none, you've mapped them all! + if (this.total == 0) { + return 100.0f; + } + return (this.getMapped() * 100.0f) / this.total; } From 349b700ce7c201fe9f93e75dc5ee5fbfa25ed688 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 19 Feb 2023 13:45:42 -0600 Subject: [PATCH 08/20] remove debug message --- .../src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index f26835525..9ecd7ce2a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -142,7 +142,6 @@ private int update(Map counts, Entry entry) { boolean renamable = this.project.isRenamable(entry); if (this.project.isObfuscated(entry) && renamable && !this.project.isSynthetic(entry)) { - System.out.println("unmapped! " + entry); String parent = this.mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); counts.put(parent, counts.getOrDefault(parent, 0) + 1); } From 480585de702a882e550f2a5ffa0f6e6a628c4efe Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 19 Feb 2023 13:49:00 -0600 Subject: [PATCH 09/20] fix all classes docker not properly reloading when classes are moved to new packages --- enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | 1 + 1 file changed, 1 insertion(+) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 3d4d380ec..978812271 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -595,6 +595,7 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { } allClassesClassSelector.reloadEntry(classEntry); + allClassesClassSelector.reload(); deobfuscatedClassSelector.restoreExpansionState(deobfuscatedPanelExpansionState); obfuscatedClassSelector.restoreExpansionState(obfuscatedPanelExpansionState); From 79bc517f5a70559d2c67eba1f326319c869133ac Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 19 Feb 2023 14:00:17 -0600 Subject: [PATCH 10/20] helpful comment --- enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | 1 + 1 file changed, 1 insertion(+) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 978812271..4c27fbf84 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -594,6 +594,7 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { deobfuscatedClassSelector.reload(); } + // all classes docker contains every class, so it always has to reload allClassesClassSelector.reloadEntry(classEntry); allClassesClassSelector.reload(); From d59601a10ca1b971522e31b0cf4a138c35aa9642 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 Feb 2023 20:33:59 -0600 Subject: [PATCH 11/20] fix massive lag --- .../java/cuchaz/enigma/gui/ClassSelector.java | 41 ++++++++++++++++--- .../gui/node/ClassSelectorClassNode.java | 25 +++++++++-- .../java/cuchaz/enigma/gui/util/GuiUtil.java | 10 ++--- .../main/resources/icons/pending_status.svg | 1 + 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 enigma-swing/src/main/resources/icons/pending_status.svg diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 52d49ce29..3d1048c93 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -17,20 +17,34 @@ import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.validation.ValidationContext; -import javax.swing.*; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTree; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; -import javax.swing.tree.*; -import java.awt.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellEditor; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.Component; import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EventObject; import java.util.List; -import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; public class ClassSelector extends JTree { public static final Comparator DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); private final Comparator comparator; private final GuiController controller; + private final Executor statusGenerator; private NestedPackages packageManager; private ClassSelectionListener selectionListener; @@ -78,6 +92,8 @@ public ClassSelector(Gui gui, Comparator comparator, boolean isRenam } })); + this.statusGenerator = Executors.newSingleThreadExecutor(); + final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() { { this.setLeafIcon(GuiUtil.CLASS_ICON); @@ -93,9 +109,19 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(new JLabel(GuiUtil.getClassIcon(gui, node.getObfEntry()))); - this.setIcon(GuiUtil.getDeobfuscationIcon(gui, node.getObfEntry())); - panel.add(this); + if (node.getStats() == null) { + // calculate stats on a separate thread for performance reasons + this.setIcon(GuiUtil.PENDING_STATUS_ICON); + ClassSelector.this.statusGenerator.execute(() -> { + node.updateStats(gui); + this.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); + }); + } else { + this.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); + } + panel.add(this); + // todo some sort of repainting here! return panel; } @@ -277,6 +303,9 @@ public void removeEntry(ClassEntry classEntry) { public void reloadEntry(ClassEntry classEntry) { this.removeEntry(classEntry); this.moveClassIn(classEntry); + ClassSelectorClassNode node = this.packageManager.getClassNode(classEntry); + node.updateStats(controller.getGui()); + this.reload(classEntry); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 37c56330f..a3a874ed3 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java @@ -11,6 +11,10 @@ package cuchaz.enigma.gui.node; +import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.stats.StatsGenerator; +import cuchaz.enigma.gui.stats.StatsResult; import cuchaz.enigma.translation.representation.entry.ClassEntry; import javax.swing.tree.DefaultMutableTreeNode; @@ -18,10 +22,12 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode { private final ClassEntry obfEntry; private ClassEntry classEntry; + private StatsResult stats; public ClassSelectorClassNode(ClassEntry obfEntry, ClassEntry classEntry) { this.obfEntry = obfEntry; this.classEntry = classEntry; + this.stats = null; this.setUserObject(classEntry); } @@ -33,6 +39,19 @@ public ClassEntry getClassEntry() { return this.classEntry; } + public StatsResult getStats() { + return this.stats; + } + + public void setStats(StatsResult stats) { + this.stats = stats; + } + + public void updateStats(Gui gui) { + StatsResult newStats = new StatsGenerator(gui.getController().project).generate(ProgressListener.none(), this.getObfEntry(), false); + this.setStats(newStats); + } + @Override public String toString() { return this.classEntry.getSimpleName(); @@ -40,7 +59,7 @@ public String toString() { @Override public boolean equals(Object other) { - return other instanceof ClassSelectorClassNode && this.equals((ClassSelectorClassNode) other); + return other instanceof ClassSelectorClassNode node && this.equals(node); } @Override @@ -60,8 +79,8 @@ public void setUserObject(Object userObject) { packageName = this.classEntry.getPackageName() + "/"; if (userObject instanceof String) this.classEntry = new ClassEntry(packageName + userObject); - else if (userObject instanceof ClassEntry) - this.classEntry = (ClassEntry) userObject; + else if (userObject instanceof ClassEntry entry) + this.classEntry = entry; super.setUserObject(this.classEntry); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java index a15204dd7..eea1168e1 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/GuiUtil.java @@ -64,6 +64,7 @@ public class GuiUtil { public static final Icon OBFUSCATED_ICON = loadIcon("obfuscated"); public static final Icon PARTIALLY_DEOBFUSCATED_ICON = loadIcon("partially_deobfuscated"); public static final Icon DEOBFUSCATED_ICON = loadIcon("deobfuscated"); + public static final Icon PENDING_STATUS_ICON = loadIcon("pending_status"); public static void openUrl(String url) { try { @@ -158,13 +159,10 @@ public static Icon getClassIcon(Gui gui, ClassEntry entry) { return CLASS_ICON; } - public static Icon getDeobfuscationIcon(Gui gui, ClassEntry entry) { - EnigmaProject project = gui.getController().project; - StatsResult result = new StatsGenerator(project).generate(ProgressListener.none(), entry, false); - - if (result.getPercentage() == 100d) { + public static Icon getDeobfuscationIcon(StatsResult stats) { + if (stats.getPercentage() == 100d) { return DEOBFUSCATED_ICON; - } else if (result.getPercentage() > 0) { + } else if (stats.getPercentage() > 0) { return PARTIALLY_DEOBFUSCATED_ICON; } else { return OBFUSCATED_ICON; diff --git a/enigma-swing/src/main/resources/icons/pending_status.svg b/enigma-swing/src/main/resources/icons/pending_status.svg new file mode 100644 index 000000000..fb9467d45 --- /dev/null +++ b/enigma-swing/src/main/resources/icons/pending_status.svg @@ -0,0 +1 @@ + \ No newline at end of file From 6261f44559e64a62e2c8906b2a1fbdcbac21a529 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 Feb 2023 20:39:43 -0600 Subject: [PATCH 12/20] fix some issues with StatsGenerator that I seem to have caused for no good reason --- .../enigma/gui/stats/StatsGenerator.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 9ecd7ce2a..2b6151e3e 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -70,6 +70,11 @@ public StatsResult generate(ProgressListener progress, Set included for (MethodEntry method : this.entryIndex.getMethods()) { progress.step(numDone++, I18n.translate("type.methods")); + // we don't want constructors or otherwise non-mappable things to show as a mapped method! + if (!project.isRenamable(method)) { + continue; + } + MethodEntry root = this.entryResolver .resolveEntry(method, ResolutionStrategy.RESOLVE_ROOT) .stream() @@ -80,14 +85,16 @@ public StatsResult generate(ProgressListener progress, Set included if (root == method && checkPackage(clazz, topLevelPackageSlash, singleClass)) { if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { - totalMappable += this.update(counts, method); + this.update(counts, method); + totalMappable++; } if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { - totalMappable += this.update(counts, new LocalVariableEntry(method, index, "", true, null)); + this.update(counts, new LocalVariableEntry(method, index, "", true, null)); index += argument.getSize(); + totalMappable++; } } } @@ -100,7 +107,8 @@ public StatsResult generate(ProgressListener progress, Set included ClassEntry clazz = field.getParent(); if (!((FieldDefEntry) field).getAccess().isSynthetic() && checkPackage(clazz, topLevelPackageSlash, singleClass)) { - totalMappable += this.update(counts, field); + this.update(counts, field); + totalMappable++; } } } @@ -110,7 +118,8 @@ public StatsResult generate(ProgressListener progress, Set included progress.step(numDone++, I18n.translate("type.classes")); if (checkPackage(clazz, topLevelPackageSlash, singleClass)) { - totalMappable += this.update(counts, clazz); + this.update(counts, clazz); + totalMappable++; } } } @@ -138,18 +147,10 @@ private boolean checkPackage(ClassEntry clazz, String topLevelPackage, boolean s return topLevelPackage.isBlank() || (deobfuscatedName != null && deobfuscatedName.startsWith(topLevelPackage)); } - private int update(Map counts, Entry entry) { - boolean renamable = this.project.isRenamable(entry); - - if (this.project.isObfuscated(entry) && renamable && !this.project.isSynthetic(entry)) { + private void update(Map counts, Entry entry) { + if (this.project.isObfuscated(entry) && this.project.isRenamable(entry) && !this.project.isSynthetic(entry)) { String parent = this.mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); counts.put(parent, counts.getOrDefault(parent, 0) + 1); } - - if (renamable) { - return 1; - } else { - return 0; - } } } From 5764ce9468ab840559323d480ad17aed797c37eb Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 Feb 2023 21:40:34 -0600 Subject: [PATCH 13/20] automatically reload icons --- .../java/cuchaz/enigma/gui/ClassSelector.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 3d1048c93..0e5f2b2ed 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -21,6 +21,7 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTree; +import javax.swing.SwingWorker; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.tree.DefaultMutableTreeNode; @@ -36,15 +37,12 @@ import java.util.Comparator; import java.util.EventObject; import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; public class ClassSelector extends JTree { public static final Comparator DEOBF_CLASS_COMPARATOR = Comparator.comparing(ClassEntry::getFullName); private final Comparator comparator; private final GuiController controller; - private final Executor statusGenerator; private NestedPackages packageManager; private ClassSelectionListener selectionListener; @@ -92,8 +90,6 @@ public ClassSelector(Gui gui, Comparator comparator, boolean isRenam } })); - this.statusGenerator = Executors.newSingleThreadExecutor(); - final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() { { this.setLeafIcon(GuiUtil.CLASS_ICON); @@ -112,16 +108,28 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean if (node.getStats() == null) { // calculate stats on a separate thread for performance reasons this.setIcon(GuiUtil.PENDING_STATUS_ICON); - ClassSelector.this.statusGenerator.execute(() -> { - node.updateStats(gui); - this.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); - }); + DefaultTreeCellRenderer thisComponent = this; + + SwingWorker iconUpdateWorker = new SwingWorker<>() { + @Override + protected ClassSelectorClassNode doInBackground() { + node.updateStats(gui); + return node; + } + + @Override + public void done() { + thisComponent.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); + ClassSelector.this.reload(node); + } + }; + + iconUpdateWorker.execute(); } else { this.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); } panel.add(this); - // todo some sort of repainting here! return panel; } @@ -310,13 +318,16 @@ public void reloadEntry(ClassEntry classEntry) { } public void reload(ClassEntry classEntry) { + this.reload(this.packageManager.getClassNode(classEntry)); + } + + public void reload(TreeNode node) { DefaultTreeModel model = (DefaultTreeModel) this.getModel(); - model.reload(this.packageManager.getClassNode(classEntry)); + model.reload(node); } public void reload() { - DefaultTreeModel model = (DefaultTreeModel) this.getModel(); - model.reload(this.packageManager.getRoot()); + this.reload(this.packageManager.getRoot()); } public interface ClassSelectionListener { From d1a08e442f6e58ebb8e4719828e8503287388465 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Wed, 22 Feb 2023 22:15:57 -0600 Subject: [PATCH 14/20] fix nodes calculating their stats repeatedly --- .../src/main/java/cuchaz/enigma/gui/ClassSelector.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 0e5f2b2ed..99ebbca2a 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -108,18 +108,20 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean if (node.getStats() == null) { // calculate stats on a separate thread for performance reasons this.setIcon(GuiUtil.PENDING_STATUS_ICON); - DefaultTreeCellRenderer thisComponent = this; + DefaultTreeCellRenderer renderer = this; SwingWorker iconUpdateWorker = new SwingWorker<>() { @Override protected ClassSelectorClassNode doInBackground() { - node.updateStats(gui); + if (node.getStats() == null) { + node.updateStats(gui); + } return node; } @Override public void done() { - thisComponent.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); + renderer.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); ClassSelector.this.reload(node); } }; From f055eb12a7bc147343f81d1df9f7eb6f636d7ade Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 Feb 2023 16:50:16 -0600 Subject: [PATCH 15/20] run icon reloading off thread to prevent lag when renaming an entry --- .../java/cuchaz/enigma/gui/ClassSelector.java | 28 +++------------- .../gui/node/ClassSelectorClassNode.java | 33 +++++++++++++++++-- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 99ebbca2a..664542f71 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -21,7 +21,6 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTree; -import javax.swing.SwingWorker; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.tree.DefaultMutableTreeNode; @@ -108,25 +107,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean if (node.getStats() == null) { // calculate stats on a separate thread for performance reasons this.setIcon(GuiUtil.PENDING_STATUS_ICON); - DefaultTreeCellRenderer renderer = this; - - SwingWorker iconUpdateWorker = new SwingWorker<>() { - @Override - protected ClassSelectorClassNode doInBackground() { - if (node.getStats() == null) { - node.updateStats(gui); - } - return node; - } - - @Override - public void done() { - renderer.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); - ClassSelector.this.reload(node); - } - }; - - iconUpdateWorker.execute(); + node.reloadStats(gui, ClassSelector.this, false); } else { this.setIcon(GuiUtil.getDeobfuscationIcon(node.getStats())); } @@ -155,8 +136,7 @@ public void editingStopped(ChangeEvent e) { String data = editor.getCellEditorValue().toString(); TreePath path = ClassSelector.this.getSelectionPath(); - Object realPath = path.getLastPathComponent(); - if (realPath instanceof DefaultMutableTreeNode node && data != null) { + if (path != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode node && data != null) { TreeNode parentNode = node.getParent(); if (parentNode == null) return; @@ -224,7 +204,7 @@ public void setClasses(Collection classEntries) { } public ClassEntry getSelectedClass() { - if (!this.isSelectionEmpty()) { + if (!this.isSelectionEmpty() && this.getSelectionPath() != null) { Object selectedNode = this.getSelectionPath().getLastPathComponent(); if (selectedNode instanceof ClassSelectorClassNode classNode) { @@ -314,7 +294,7 @@ public void reloadEntry(ClassEntry classEntry) { this.removeEntry(classEntry); this.moveClassIn(classEntry); ClassSelectorClassNode node = this.packageManager.getClassNode(classEntry); - node.updateStats(controller.getGui()); + node.reloadStats(controller.getGui(), this, true); this.reload(classEntry); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index a3a874ed3..38e4ca8ee 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java @@ -12,12 +12,16 @@ package cuchaz.enigma.gui.node; import cuchaz.enigma.ProgressListener; +import cuchaz.enigma.gui.ClassSelector; import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.stats.StatsGenerator; import cuchaz.enigma.gui.stats.StatsResult; +import cuchaz.enigma.gui.util.GuiUtil; import cuchaz.enigma.translation.representation.entry.ClassEntry; +import javax.swing.SwingWorker; import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; public class ClassSelectorClassNode extends DefaultMutableTreeNode { private final ClassEntry obfEntry; @@ -47,9 +51,32 @@ public void setStats(StatsResult stats) { this.stats = stats; } - public void updateStats(Gui gui) { - StatsResult newStats = new StatsGenerator(gui.getController().project).generate(ProgressListener.none(), this.getObfEntry(), false); - this.setStats(newStats); + /** + * Reloads the stats for this class node and updates the icon in the provided class selector. + * @param gui the current gui instance + * @param selector the class selector to reload on + * @param updateIfPresent whether to update the stats if they have already been generated for this node + */ + public void reloadStats(Gui gui, ClassSelector selector, boolean updateIfPresent) { + SwingWorker iconUpdateWorker = new SwingWorker<>() { + @Override + protected ClassSelectorClassNode doInBackground() { + if (ClassSelectorClassNode.this.getStats() == null || updateIfPresent) { + StatsResult newStats = new StatsGenerator(gui.getController().project).generate(ProgressListener.none(), ClassSelectorClassNode.this.getObfEntry(), false); + ClassSelectorClassNode.this.setStats(newStats); + } + + return ClassSelectorClassNode.this; + } + + @Override + public void done() { + ((DefaultTreeCellRenderer) selector.getCellRenderer()).setIcon(GuiUtil.getDeobfuscationIcon(ClassSelectorClassNode.this.getStats())); + selector.reload(ClassSelectorClassNode.this); + } + }; + + iconUpdateWorker.execute(); } @Override From ab45fbf10051794a114e82962d7a0a77125fe9d4 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 Feb 2023 17:10:32 -0600 Subject: [PATCH 16/20] fix isObfuscated in EnigmaProject --- enigma/src/main/java/cuchaz/enigma/EnigmaProject.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java index 99603126e..bfe324a42 100644 --- a/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java +++ b/enigma/src/main/java/cuchaz/enigma/EnigmaProject.java @@ -197,8 +197,6 @@ public boolean isRenamable(EntryReference, Entry> obfReference) { } public boolean isObfuscated(Entry entry) { - String name = entry.getName(); - List obfuscationTestServices = this.getEnigma().getServices().get(ObfuscationTestService.TYPE); if (!obfuscationTestServices.isEmpty()) { for (ObfuscationTestService service : obfuscationTestServices) { @@ -217,8 +215,8 @@ public boolean isObfuscated(Entry entry) { } } - String mappedName = this.mapper.deobfuscate(entry).getName(); - return mappedName == null || mappedName.isEmpty() || mappedName.equals(name); + EntryMapping mapping = this.mapper.getDeobfMapping(entry); + return mapping.targetName() == null; } public boolean isSynthetic(Entry entry) { From d475a9b5398294ff6ff83156f992d45b69f93e35 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 Feb 2023 17:12:50 -0600 Subject: [PATCH 17/20] remove superfluous reload in ClassSelector --- .../src/main/java/cuchaz/enigma/gui/ClassSelector.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java index 664542f71..ceaa524ef 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/ClassSelector.java @@ -295,12 +295,6 @@ public void reloadEntry(ClassEntry classEntry) { this.moveClassIn(classEntry); ClassSelectorClassNode node = this.packageManager.getClassNode(classEntry); node.reloadStats(controller.getGui(), this, true); - - this.reload(classEntry); - } - - public void reload(ClassEntry classEntry) { - this.reload(this.packageManager.getClassNode(classEntry)); } public void reload(TreeNode node) { From 7e15e41e29d44bc59f3a3395e139f3dfb9bf42c2 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Thu, 23 Feb 2023 17:56:58 -0600 Subject: [PATCH 18/20] fix all classes docker losing its expansion state on rename (I already caused this exact bug once, we stay silly :iea:) --- .../src/main/java/cuchaz/enigma/gui/Gui.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 4c27fbf84..51ec425dd 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -571,7 +571,6 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { ClassSelector deobfuscatedClassSelector = Docker.getDocker(DeobfuscatedClassesDocker.class).getClassSelector(); ClassSelector obfuscatedClassSelector = Docker.getDocker(ObfuscatedClassesDocker.class).getClassSelector(); - ClassSelector allClassesClassSelector = Docker.getDocker(AllClassesDocker.class).getClassSelector(); List deobfuscatedPanelExpansionState = deobfuscatedClassSelector.getExpansionState(); List obfuscatedPanelExpansionState = obfuscatedClassSelector.getExpansionState(); @@ -594,10 +593,6 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { deobfuscatedClassSelector.reload(); } - // all classes docker contains every class, so it always has to reload - allClassesClassSelector.reloadEntry(classEntry); - allClassesClassSelector.reload(); - deobfuscatedClassSelector.restoreExpansionState(deobfuscatedPanelExpansionState); obfuscatedClassSelector.restoreExpansionState(obfuscatedPanelExpansionState); } @@ -605,7 +600,12 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { public void reloadClassEntry(ClassEntry classEntry) { Docker.getDocker(DeobfuscatedClassesDocker.class).getClassSelector().reloadEntry(classEntry); Docker.getDocker(ObfuscatedClassesDocker.class).getClassSelector().reloadEntry(classEntry); - Docker.getDocker(AllClassesDocker.class).getClassSelector().reloadEntry(classEntry); + + ClassSelector allClassesClassSelector = Docker.getDocker(AllClassesDocker.class).getClassSelector(); + List expansionState = allClassesClassSelector.getExpansionState(); + allClassesClassSelector.reloadEntry(classEntry); + allClassesClassSelector.reload(); + allClassesClassSelector.restoreExpansionState(expansionState); } public SearchDialog getSearchDialog() { From a002680551a05b4b8372f6c4fc95cd20230cb778 Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 26 Feb 2023 19:45:36 -0600 Subject: [PATCH 19/20] StatsGenerator fix for anonymous classes always showing as a mapped entry --- .../gui/node/ClassSelectorClassNode.java | 2 +- .../enigma/gui/stats/StatsGenerator.java | 49 +++++++++++++------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java index 38e4ca8ee..f6eddb50b 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java @@ -62,7 +62,7 @@ public void reloadStats(Gui gui, ClassSelector selector, boolean updateIfPresent @Override protected ClassSelectorClassNode doInBackground() { if (ClassSelectorClassNode.this.getStats() == null || updateIfPresent) { - StatsResult newStats = new StatsGenerator(gui.getController().project).generate(ProgressListener.none(), ClassSelectorClassNode.this.getObfEntry(), false); + StatsResult newStats = new StatsGenerator(gui.getController().project).generateForClassTree(ProgressListener.none(), ClassSelectorClassNode.this.getObfEntry(), false); ClassSelectorClassNode.this.setStats(newStats); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java index 2b6151e3e..d794b8aec 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/stats/StatsGenerator.java @@ -34,7 +34,7 @@ public StatsGenerator(EnigmaProject project) { this.entryResolver = project.getJarIndex().getEntryResolver(); } - public StatsResult generate(ProgressListener progress, ClassEntry entry, boolean includeSynthetic) { + public StatsResult generateForClassTree(ProgressListener progress, ClassEntry entry, boolean includeSynthetic) { return generate(progress, EnumSet.allOf(StatsMember.class), entry.getFullName(), true, includeSynthetic); } @@ -42,7 +42,16 @@ public StatsResult generate(ProgressListener progress, Set included return generate(progress, includedMembers, topLevelPackage, false, includeSynthetic); } - public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean singleClass, boolean includeSynthetic) { + /** + * Generates stats for the given package or class. + * @param progress a listener to update with current progress + * @param includedMembers the types of entry to include in the stats + * @param topLevelPackage the package or class to generate stats for + * @param forClassTree if true, the stats will be generated for the class tree - this means that non-mappable obfuscated entries will be ignored for correctness + * @param includeSynthetic whether to include synthetic methods + * @return the generated {@link StatsResult} for the provided class or package. + */ + public StatsResult generate(ProgressListener progress, Set includedMembers, String topLevelPackage, boolean forClassTree, boolean includeSynthetic) { includedMembers = EnumSet.copyOf(includedMembers); int totalWork = 0; int totalMappable = 0; @@ -83,18 +92,16 @@ public StatsResult generate(ProgressListener progress, Set included ClassEntry clazz = root.getParent(); - if (root == method && checkPackage(clazz, topLevelPackageSlash, singleClass)) { + if (root == method && checkPackage(clazz, topLevelPackageSlash, forClassTree)) { if (includedMembers.contains(StatsMember.METHODS) && !((MethodDefEntry) method).getAccess().isSynthetic()) { - this.update(counts, method); - totalMappable++; + totalMappable += this.update(counts, method, forClassTree); } if (includedMembers.contains(StatsMember.PARAMETERS) && (!((MethodDefEntry) method).getAccess().isSynthetic() || includeSynthetic)) { int index = ((MethodDefEntry) method).getAccess().isStatic() ? 0 : 1; for (TypeDescriptor argument : method.getDesc().getArgumentDescs()) { - this.update(counts, new LocalVariableEntry(method, index, "", true, null)); + totalMappable += this.update(counts, new LocalVariableEntry(method, index, "", true, null), forClassTree); index += argument.getSize(); - totalMappable++; } } } @@ -106,9 +113,8 @@ public StatsResult generate(ProgressListener progress, Set included progress.step(numDone++, I18n.translate("type.fields")); ClassEntry clazz = field.getParent(); - if (!((FieldDefEntry) field).getAccess().isSynthetic() && checkPackage(clazz, topLevelPackageSlash, singleClass)) { - this.update(counts, field); - totalMappable++; + if (!((FieldDefEntry) field).getAccess().isSynthetic() && checkPackage(clazz, topLevelPackageSlash, forClassTree)) { + totalMappable += this.update(counts, field, forClassTree); } } } @@ -117,9 +123,8 @@ public StatsResult generate(ProgressListener progress, Set included for (ClassEntry clazz : this.entryIndex.getClasses()) { progress.step(numDone++, I18n.translate("type.classes")); - if (checkPackage(clazz, topLevelPackageSlash, singleClass)) { - this.update(counts, clazz); - totalMappable++; + if (checkPackage(clazz, topLevelPackageSlash, forClassTree)) { + totalMappable += this.update(counts, clazz, forClassTree); } } } @@ -147,10 +152,24 @@ private boolean checkPackage(ClassEntry clazz, String topLevelPackage, boolean s return topLevelPackage.isBlank() || (deobfuscatedName != null && deobfuscatedName.startsWith(topLevelPackage)); } - private void update(Map counts, Entry entry) { - if (this.project.isObfuscated(entry) && this.project.isRenamable(entry) && !this.project.isSynthetic(entry)) { + /** + * @return whether to increment the total mappable entry count - 0 if no, 1 if yes + */ + private int update(Map counts, Entry entry, boolean forClassTree) { + boolean obfuscated = this.project.isObfuscated(entry); + boolean renamable = this.project.isRenamable(entry); + boolean synthetic = this.project.isSynthetic(entry); + + if (forClassTree && obfuscated && !renamable) { + return 0; + } + + if (obfuscated && renamable && !synthetic) { String parent = this.mapper.deobfuscate(entry.getAncestry().get(0)).getName().replace('/', '.'); counts.put(parent, counts.getOrDefault(parent, 0) + 1); + return 1; } + + return 1; } } From 101921644da53417eac25895719d10c7b0e0546b Mon Sep 17 00:00:00 2001 From: ix0rai Date: Sun, 26 Feb 2023 20:00:08 -0600 Subject: [PATCH 20/20] fix class selector exploding sometimes --- enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java | 2 ++ .../main/java/cuchaz/enigma/gui/GuiController.java | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java index 51ec425dd..21e6fb94c 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/Gui.java @@ -593,6 +593,8 @@ public void moveClassTree(Entry obfEntry, boolean isOldOb, boolean isNewOb) { deobfuscatedClassSelector.reload(); } + this.reloadClassEntry(classEntry); + deobfuscatedClassSelector.restoreExpansionState(deobfuscatedPanelExpansionState); obfuscatedClassSelector.restoreExpansionState(obfuscatedPanelExpansionState); } diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java index b1553de6b..b14a11a12 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -510,10 +510,7 @@ private void applyChange0(ValidationContext vc, EntryChange change) { EntryMapping mapping = EntryUtil.applyChange(vc, this.project.getMapper(), change); boolean renamed = !change.getDeobfName().isUnchanged(); - - if (renamed && target instanceof ClassEntry classEntry && !classEntry.isInnerClass()) { - this.gui.moveClassTree(target, prev.targetName() == null, mapping.targetName() == null); - } + this.gui.updateStructure(this.gui.getActiveEditor()); if (!Objects.equals(prev.targetName(), mapping.targetName())) { this.chp.invalidateMapped(); @@ -523,7 +520,11 @@ private void applyChange0(ValidationContext vc, EntryChange change) { this.chp.invalidateJavadoc(target.getTopLevelClass()); } - this.gui.updateStructure(this.gui.getActiveEditor()); + if (renamed && target instanceof ClassEntry classEntry && !classEntry.isInnerClass()) { + this.gui.moveClassTree(target, prev.targetName() == null, mapping.targetName() == null); + return; + } + this.gui.reloadClassEntry(change.getTarget().getTopLevelClass()); }