diff --git a/jadx-core/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java b/jadx-core/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java index 96c3c2aa1..eff978f3e 100644 --- a/jadx-core/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java +++ b/jadx-core/src/main/java/jadx/api/plugins/input/data/attributes/IJadxAttrType.java @@ -2,8 +2,20 @@ package jadx.api.plugins.input.data.attributes; /** * Marker interface for attribute type. + * Similar to enumeration but extensible. + *

* Used for attach attribute instance class information (T). * T - class of attribute instance + *

+ * To create new one define static field like this: + * {@code + * static final IJadxAttrType ATTR_TYPE = IJadxAttrType.create(); + * } */ public interface IJadxAttrType { + + static IJadxAttrType create() { + return new IJadxAttrType<>() { + }; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 751c9653d..5d34a610f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -32,6 +32,7 @@ import jadx.api.plugins.pass.types.JadxPreparePass; import jadx.core.Jadx; import jadx.core.ProcessClass; import jadx.core.clsp.ClspGraph; +import jadx.core.dex.attributes.AttributeStorage; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ConstStorage; import jadx.core.dex.info.FieldInfo; @@ -75,6 +76,7 @@ public class RootNode { private final TypeUpdate typeUpdate; private final MethodUtils methodUtils; private final TypeUtils typeUtils; + private final AttributeStorage attributes = new AttributeStorage(); private final Map clsMap = new HashMap<>(); private final Map rawClsMap = new HashMap<>(); @@ -701,6 +703,10 @@ public class RootNode { return typeUtils; } + public AttributeStorage getAttributes() { + return attributes; + } + public boolean isProto() { return isProto; } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JInputMapping.java b/jadx-gui/src/main/java/jadx/gui/plugins/mappings/JInputMapping.java similarity index 90% rename from jadx-gui/src/main/java/jadx/gui/treemodel/JInputMapping.java rename to jadx-gui/src/main/java/jadx/gui/plugins/mappings/JInputMapping.java index 776a15eb9..480bcfa7e 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JInputMapping.java +++ b/jadx-gui/src/main/java/jadx/gui/plugins/mappings/JInputMapping.java @@ -1,4 +1,4 @@ -package jadx.gui.treemodel; +package jadx.gui.plugins.mappings; import java.nio.file.Path; @@ -15,6 +15,8 @@ import jadx.api.ICodeInfo; import jadx.api.impl.SimpleCodeInfo; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; +import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JEditableNode; import jadx.gui.ui.MainWindow; import jadx.gui.ui.TabbedPane; import jadx.gui.ui.codearea.CodeContentPanel; @@ -63,7 +65,8 @@ public class JInputMapping extends JEditableNode { @Override public JPopupMenu onTreePopupMenu(MainWindow mainWindow) { JPopupMenu menu = new JPopupMenu(); - menu.add(new SimpleMenuItem(NLS.str("popup.remove"), mainWindow::closeMappingsAndRemoveFromProject)); + menu.add(new SimpleMenuItem(NLS.str("popup.remove"), + () -> mainWindow.getRenameMappings().closeMappingsAndRemoveFromProject())); return menu; } diff --git a/jadx-gui/src/main/java/jadx/gui/plugins/mappings/RenameMappingsGui.java b/jadx-gui/src/main/java/jadx/gui/plugins/mappings/RenameMappingsGui.java new file mode 100644 index 000000000..8057507ce --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/plugins/mappings/RenameMappingsGui.java @@ -0,0 +1,231 @@ +package jadx.gui.plugins.mappings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import javax.swing.Action; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JOptionPane; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.format.MappingFormat; + +import jadx.api.args.UserRenamesMappingsMode; +import jadx.api.plugins.utils.CommonFileUtils; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.Utils; +import jadx.gui.jobs.TaskStatus; +import jadx.gui.settings.JadxProject; +import jadx.gui.settings.JadxSettings; +import jadx.gui.ui.MainWindow; +import jadx.gui.ui.filedialog.FileDialogWrapper; +import jadx.gui.ui.filedialog.FileOpenMode; +import jadx.gui.utils.NLS; +import jadx.gui.utils.UiUtils; +import jadx.gui.utils.ui.ActionHandler; +import jadx.plugins.mappings.RenameMappingsOptions; +import jadx.plugins.mappings.save.MappingExporter; + +public class RenameMappingsGui { + private static final Logger LOG = LoggerFactory.getLogger(RenameMappingsGui.class); + + private final MainWindow mainWindow; + + // private MappingFormat currentMappingFormat; + private boolean renamesChanged = false; + + private transient JMenu openMappingsMenu; + private transient Action saveMappingsAction; + private transient JMenu saveMappingsAsMenu; + private transient Action closeMappingsAction; + + public RenameMappingsGui(MainWindow mainWindow) { + this.mainWindow = mainWindow; + mainWindow.addLoadListener(this::onLoad); + } + + public void addMenuActions(JMenu menu) { + openMappingsMenu = new JMenu(NLS.str("file.open_mappings")); + openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, true)).withNameAndDesc("Proguard (inverted)")); + openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, false)).withNameAndDesc("Proguard")); + + saveMappingsAction = new ActionHandler(this::saveMappings).withNameAndDesc(NLS.str("file.save_mappings")); + + saveMappingsAsMenu = new JMenu(NLS.str("file.save_mappings_as")); + + for (MappingFormat mappingFormat : MappingFormat.values()) { + if (mappingFormat != MappingFormat.PROGUARD) { + openMappingsMenu.add(new ActionHandler(ev -> openMappings(mappingFormat, false)) + .withNameAndDesc(mappingFormat.name)); + } + saveMappingsAsMenu.add(new ActionHandler(ev -> saveMappingsAs(mappingFormat)) + .withNameAndDesc(mappingFormat.name)); + } + + closeMappingsAction = new ActionHandler(ev -> closeMappingsAndRemoveFromProject()) + .withNameAndDesc(NLS.str("file.close_mappings")); + + menu.addSeparator(); + menu.add(openMappingsMenu); + menu.add(saveMappingsAction); + menu.add(saveMappingsAsMenu); + menu.add(closeMappingsAction); + } + + private boolean onLoad(boolean loaded) { + renamesChanged = false; + if (loaded) { + RootNode rootNode = mainWindow.getWrapper().getRootNode(); + rootNode.registerCodeDataUpdateListener(codeData -> onRename()); + } else { + // project or window close + JadxProject project = mainWindow.getProject(); + JadxSettings settings = mainWindow.getSettings(); + if (project.getMappingsPath() != null + && settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) { + saveMappings(); + } + } + return false; + } + + private void onRename() { + JadxProject project = mainWindow.getProject(); + JadxSettings settings = mainWindow.getSettings(); + if (project.getMappingsPath() != null + && settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) { + saveMappings(); + } else { + renamesChanged = true; + UiUtils.uiRun(mainWindow::update); + } + } + + public void onUpdate(boolean loaded) { + JadxProject project = mainWindow.getProject(); + openMappingsMenu.setEnabled(loaded); + saveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null); + saveMappingsAsMenu.setEnabled(loaded); + closeMappingsAction.setEnabled(project.getMappingsPath() != null); + } + + private void openMappings(MappingFormat mappingFormat, boolean inverted) { + FileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_OPEN); + fileDialog.setTitle(NLS.str("file.open_mappings")); + if (mappingFormat.hasSingleFile()) { + fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt)); + fileDialog.setSelectionMode(JFileChooser.FILES_ONLY); + } else { + fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY); + } + List selectedPaths = fileDialog.show(); + if (selectedPaths.size() != 1) { + return; + } + Path filePath = selectedPaths.get(0); + LOG.info("Loading mappings from: {}", filePath.toAbsolutePath()); + JadxProject project = mainWindow.getProject(); + project.setMappingsPath(filePath); + project.updatePluginOptions(options -> { + options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name()); + options.put(RenameMappingsOptions.INVERT_OPT, inverted ? "yes" : "no"); + }); + mainWindow.reopen(); + } + + public void closeMappingsAndRemoveFromProject() { + mainWindow.getProject().setMappingsPath(null); + mainWindow.reopen(); + } + + private void saveMappings() { + renamesChanged = false; + saveInBackground(getCurrentMappingFormat(), + mainWindow.getProject().getMappingsPath(), + s -> mainWindow.update()); + } + + private void saveMappingsAs(MappingFormat mappingFormat) { + FileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_SAVE); + fileDialog.setTitle(NLS.str("file.save_mappings_as")); + if (mappingFormat.hasSingleFile()) { + Path currentDir = Utils.getOrElse(fileDialog.getCurrentDir(), CommonFileUtils.CWD_PATH); + fileDialog.setSelectedFile(currentDir.resolve("mappings." + mappingFormat.fileExt)); + fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt)); + fileDialog.setSelectionMode(JFileChooser.FILES_ONLY); + } else { + fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY); + } + List selectedPaths = fileDialog.show(); + if (selectedPaths.size() != 1) { + return; + } + Path selectedPath = selectedPaths.get(0); + Path savePath; + // Append file extension if missing + if (mappingFormat.hasSingleFile() + && !selectedPath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(mappingFormat.fileExt)) { + savePath = selectedPath.resolveSibling(selectedPath.getFileName() + "." + mappingFormat.fileExt); + } else { + savePath = selectedPath; + } + // If the target file already exists (and it's not an empty directory), show an overwrite + // confirmation + if (Files.exists(savePath)) { + boolean emptyDir = false; + try (Stream entries = Files.list(savePath)) { + emptyDir = entries.findFirst().isEmpty(); + } catch (IOException ignored) { + } + if (!emptyDir) { + int res = JOptionPane.showConfirmDialog( + mainWindow, + NLS.str("confirm.save_as_message", savePath.getFileName()), + NLS.str("confirm.save_as_title"), + JOptionPane.YES_NO_OPTION); + if (res == JOptionPane.NO_OPTION) { + return; + } + } + } + LOG.info("Saving mappings to: {}", savePath.toAbsolutePath()); + JadxProject project = mainWindow.getProject(); + project.setMappingsPath(savePath); + project.updatePluginOptions(options -> { + options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name()); + options.put(RenameMappingsOptions.INVERT_OPT, "no"); + }); + saveInBackground(mappingFormat, savePath, s -> mainWindow.reopen()); + } + + private void saveInBackground(MappingFormat mappingFormat, Path savePath, Consumer onFinishUiRunnable) { + mainWindow.getBackgroundExecutor().execute(NLS.str("progress.save_mappings"), + () -> new MappingExporter(mainWindow.getWrapper().getRootNode()) + .exportMappings(savePath, mainWindow.getProject().getCodeData(), mappingFormat), + onFinishUiRunnable); + } + + private MappingFormat getCurrentMappingFormat() { + JadxProject project = mainWindow.getProject(); + String fmtStr = project.getPluginOption(RenameMappingsOptions.FORMAT_OPT); + if (fmtStr != null) { + return MappingFormat.valueOf(fmtStr); + } + Path mappingsPath = project.getMappingsPath(); + try { + return MappingReader.detectFormat(mappingsPath); + } catch (IOException e) { + throw new RuntimeException("Failed to detect mapping format for: " + mappingsPath); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java b/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java index 39fb3456b..82227600b 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java @@ -5,10 +5,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.JavaClass; +import jadx.gui.plugins.mappings.JInputMapping; import jadx.gui.settings.data.TabViewState; import jadx.gui.settings.data.ViewPoint; import jadx.gui.treemodel.JClass; -import jadx.gui.treemodel.JInputMapping; import jadx.gui.treemodel.JInputScript; import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JResource; diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java index 22d9d10b0..a95e4688a 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java @@ -11,6 +11,7 @@ import javax.swing.ImageIcon; import jadx.core.utils.files.FileUtils; import jadx.gui.JadxWrapper; +import jadx.gui.plugins.mappings.JInputMapping; import jadx.gui.settings.JadxProject; import jadx.gui.utils.NLS; import jadx.gui.utils.UiUtils; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 183672dd1..9bac6f8c2 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -24,7 +24,6 @@ import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.AffineTransform; -import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; @@ -35,11 +34,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.Action; @@ -78,19 +75,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Level; -import net.fabricmc.mappingio.MappingReader; -import net.fabricmc.mappingio.format.MappingFormat; import jadx.api.JadxArgs; import jadx.api.JavaNode; import jadx.api.ResourceFile; -import jadx.api.args.UserRenamesMappingsMode; import jadx.api.plugins.utils.CommonFileUtils; import jadx.core.Jadx; import jadx.core.export.TemplateFile; import jadx.core.utils.ListUtils; import jadx.core.utils.StringUtils; -import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; import jadx.gui.JadxWrapper; @@ -102,6 +95,7 @@ import jadx.gui.jobs.TaskStatus; import jadx.gui.logs.LogCollector; import jadx.gui.logs.LogOptions; import jadx.gui.logs.LogPanel; +import jadx.gui.plugins.mappings.RenameMappingsGui; import jadx.gui.plugins.quark.QuarkDialog; import jadx.gui.settings.JadxProject; import jadx.gui.settings.JadxSettings; @@ -145,8 +139,6 @@ import jadx.gui.utils.UiUtils; import jadx.gui.utils.fileswatcher.LiveReloadWorker; import jadx.gui.utils.ui.ActionHandler; import jadx.gui.utils.ui.NodeLabel; -import jadx.plugins.mappings.RenameMappingsOptions; -import jadx.plugins.mappings.save.MappingExporter; import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE; import static javax.swing.KeyStroke.getKeyStroke; @@ -188,12 +180,6 @@ public class MainWindow extends JFrame { private transient Action newProjectAction; private transient Action saveProjectAction; - private transient JMenu openMappingsMenu; - private transient Action saveMappingsAction; - private transient JMenu saveMappingsAsMenu; - private transient Action closeMappingsAction; - private MappingFormat currentMappingFormat; - private boolean renamesChanged = false; private transient JPanel mainPanel; private transient JSplitPane treeSplitPane; @@ -230,12 +216,15 @@ public class MainWindow extends JFrame { private JMenu pluginsMenu; + private final transient RenameMappingsGui renameMappings; + public MainWindow(JadxSettings settings) { this.settings = settings; this.cacheObject = new CacheObject(); this.project = new JadxProject(this); this.wrapper = new JadxWrapper(this); this.liveReloadWorker = new LiveReloadWorker(this); + this.renameMappings = new RenameMappingsGui(this); resetCache(); FontUtils.registerBundledFonts(); @@ -390,100 +379,6 @@ public class MainWindow extends JFrame { update(); } - private void openMappings(MappingFormat mappingFormat, boolean inverted) { - FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_OPEN); - fileDialog.setTitle(NLS.str("file.open_mappings")); - if (mappingFormat.hasSingleFile()) { - fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt)); - fileDialog.setSelectionMode(JFileChooser.FILES_ONLY); - } else { - fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY); - } - List selectedPaths = fileDialog.show(); - if (selectedPaths.size() != 1) { - return; - } - settings.setLastOpenFilePath(fileDialog.getCurrentDir()); - Path filePath = selectedPaths.get(0); - LOG.info("Loading mappings from: {}", filePath.toAbsolutePath()); - project.setMappingsPath(filePath); - currentMappingFormat = mappingFormat; - project.updatePluginOptions(options -> { - options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name()); - options.put(RenameMappingsOptions.INVERT_OPT, inverted ? "yes" : "no"); - }); - reopen(); - } - - public void closeMappingsAndRemoveFromProject() { - project.setMappingsPath(null); - currentMappingFormat = null; - reopen(); - } - - private void saveMappings() { - Path savePath = project.getMappingsPath(); - Objects.requireNonNull(savePath, "expect mapping path to be set"); - if (currentMappingFormat == null) { - try { - currentMappingFormat = MappingReader.detectFormat(savePath); - } catch (IOException e) { - throw new JadxRuntimeException("Failed to save mappings", e); - } - } - renamesChanged = false; - backgroundExecutor.execute(NLS.str("progress.save_mappings"), - () -> new MappingExporter(wrapper.getDecompiler().getRoot()) - .exportMappings(savePath, project.getCodeData(), currentMappingFormat), - s -> update()); - } - - private void saveMappingsAs(MappingFormat mappingFormat) { - FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE); - fileDialog.setTitle(NLS.str("file.save_mappings_as")); - if (mappingFormat.hasSingleFile()) { - Path currentDir = Utils.getOrElse(fileDialog.getCurrentDir(), CommonFileUtils.CWD_PATH); - fileDialog.setSelectedFile(currentDir.resolve("mappings." + mappingFormat.fileExt)); - fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt)); - fileDialog.setSelectionMode(JFileChooser.FILES_ONLY); - } else { - fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY); - } - List selectedPaths = fileDialog.show(); - if (selectedPaths.size() != 1) { - return; - } - settings.setLastSaveFilePath(fileDialog.getCurrentDir()); - Path savePath = selectedPaths.get(0); - // Append file extension if missing - if (mappingFormat.hasSingleFile() && !savePath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(mappingFormat.fileExt)) { - savePath = savePath.resolveSibling(savePath.getFileName() + "." + mappingFormat.fileExt); - } - // If the target file already exists (and it's not an empty directory), show an overwrite - // confirmation - if (Files.exists(savePath)) { - boolean emptyDir = false; - try (Stream entries = Files.list(savePath)) { - emptyDir = !entries.findFirst().isPresent(); - } catch (IOException ignored) { - } - if (!emptyDir) { - int res = JOptionPane.showConfirmDialog( - this, - NLS.str("confirm.save_as_message", savePath.getFileName()), - NLS.str("confirm.save_as_title"), - JOptionPane.YES_NO_OPTION); - if (res == JOptionPane.NO_OPTION) { - return; - } - } - } - LOG.info("Saving mappings to: {}", savePath.toAbsolutePath()); - project.setMappingsPath(savePath); - currentMappingFormat = mappingFormat; - saveMappings(); - } - public void addNewScript() { FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE); fileDialog.setTitle(NLS.str("file.save")); @@ -625,7 +520,6 @@ public class MainWindow extends JFrame { private void closeAll() { notifyLoadListeners(false); - renamesChanged = false; cancelBackgroundJobs(); clearTree(); resetCache(); @@ -694,10 +588,6 @@ public class MainWindow extends JFrame { private boolean ensureProjectIsSaved() { if (!project.isSaved() && !project.isInitial()) { - if (project.getMappingsPath() != null - && settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) { - saveMappings(); - } int res = JOptionPane.showConfirmDialog( this, NLS.str("confirm.not_saved_message"), @@ -718,15 +608,12 @@ public class MainWindow extends JFrame { update(); } - private void update() { + public void update() { UiUtils.uiThreadGuard(); newProjectAction.setEnabled(!project.isInitial()); saveProjectAction.setEnabled(loaded && !project.isSaved()); - openMappingsMenu.setEnabled(loaded); - saveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null); - saveMappingsAsMenu.setEnabled(loaded && !project.getCodeData().isEmpty()); - closeMappingsAction.setEnabled(project.getMappingsPath() != null); deobfToggleBtn.setSelected(settings.isDeobfuscationOn()); + renameMappings.onUpdate(loaded); Path projectPath = project.getProjectPath(); String pathString; @@ -739,16 +626,6 @@ public class MainWindow extends JFrame { + project.getName() + pathString + " - " + DEFAULT_TITLE); } - public void renamesChanged() { - if (project.getMappingsPath() != null - && settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) { - saveMappings(); - } else { - renamesChanged = true; - update(); - } - } - protected void resetCache() { cacheObject.reset(); } @@ -1018,26 +895,6 @@ public class MainWindow extends JFrame { liveReloadMenuItem = new JCheckBoxMenuItem(liveReload); liveReloadMenuItem.setState(project.isEnableLiveReload()); - openMappingsMenu = new JMenu(NLS.str("file.open_mappings")); - openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, true)).withNameAndDesc("Proguard (inverted)")); - openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, false)).withNameAndDesc("Proguard")); - - saveMappingsAction = new ActionHandler(this::saveMappings).withNameAndDesc(NLS.str("file.save_mappings")); - - saveMappingsAsMenu = new JMenu(NLS.str("file.save_mappings_as")); - - for (MappingFormat mappingFormat : MappingFormat.values()) { - if (mappingFormat != MappingFormat.PROGUARD) { - openMappingsMenu.add(new ActionHandler(ev -> openMappings(mappingFormat, false)) - .withNameAndDesc(mappingFormat.name)); - } - saveMappingsAsMenu.add(new ActionHandler(ev -> saveMappingsAs(mappingFormat)) - .withNameAndDesc(mappingFormat.name)); - } - - closeMappingsAction = new ActionHandler(ev -> closeMappingsAndRemoveFromProject()) - .withNameAndDesc(NLS.str("file.close_mappings")); - Action saveAllAction = new AbstractAction(NLS.str("file.save_all"), Icons.SAVE_ALL) { @Override public void actionPerformed(ActionEvent e) { @@ -1230,11 +1087,7 @@ public class MainWindow extends JFrame { file.addSeparator(); file.add(reload); file.add(liveReloadMenuItem); - file.addSeparator(); - file.add(openMappingsMenu); - file.add(saveMappingsAction); - file.add(saveMappingsAsMenu); - file.add(closeMappingsAction); + renameMappings.addMenuActions(file); file.addSeparator(); file.add(saveAllAction); file.add(exportAction); @@ -1770,4 +1623,8 @@ public class MainWindow extends JFrame { public JMenu getPluginsMenu() { return pluginsMenu; } + + public RenameMappingsGui getRenameMappings() { + return renameMappings; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java index e8fb9b0b1..2fc018e3a 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java @@ -176,7 +176,6 @@ public class RenameDialog extends JDialog { UiUtils.errorMessage(this, NLS.str("message.memoryLow")); } node.reload(mainWindow); - mainWindow.renamesChanged(); }); } diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsData.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsData.java new file mode 100644 index 000000000..d7761d57a --- /dev/null +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsData.java @@ -0,0 +1,38 @@ +package jadx.plugins.mappings; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.mappingio.tree.MappingTree; + +import jadx.api.plugins.input.data.attributes.IJadxAttrType; +import jadx.api.plugins.input.data.attributes.IJadxAttribute; +import jadx.core.dex.nodes.RootNode; + +public class RenameMappingsData implements IJadxAttribute { + + private static final IJadxAttrType DATA = IJadxAttrType.create(); + + public static @Nullable RenameMappingsData getData(RootNode root) { + return root.getAttributes().get(DATA); + } + + public static @Nullable MappingTree getTree(RootNode root) { + RenameMappingsData data = getData(root); + return data == null ? null : data.getMappings(); + } + + private final MappingTree mappings; + + public RenameMappingsData(MappingTree mappings) { + this.mappings = mappings; + } + + public MappingTree getMappings() { + return mappings; + } + + @Override + public IJadxAttrType getAttrType() { + return DATA; + } +} diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java index 02d8377cb..7330be86f 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/RenameMappingsPlugin.java @@ -34,10 +34,9 @@ public class RenameMappingsPlugin implements JadxPlugin { if (mappingsPath == null || !Files.isReadable(mappingsPath)) { return; } - LoadMappingsPass loadPass = new LoadMappingsPass(options); - context.addPass(loadPass); - context.addPass(new ApplyMappingsPass(loadPass)); - context.addPass(new CodeMappingsPass(loadPass)); + context.addPass(new LoadMappingsPass(options)); + context.addPass(new ApplyMappingsPass()); + context.addPass(new CodeMappingsPass()); // use mapping file time modification to check for changes context.registerInputsHashSupplier(() -> FileUtils.md5Sum(getInputsHashString(mappingsPath))); diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/ApplyMappingsPass.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/ApplyMappingsPass.java index 06559ce94..e0bdf1f49 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/ApplyMappingsPass.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/ApplyMappingsPass.java @@ -15,15 +15,10 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.plugins.mappings.RenameMappingsData; public class ApplyMappingsPass implements JadxPreparePass { - private final LoadMappingsPass loadPass; - - public ApplyMappingsPass(LoadMappingsPass loadPass) { - this.loadPass = loadPass; - } - @Override public JadxPassInfo getInfo() { return new OrderedJadxPassInfo( @@ -35,10 +30,11 @@ public class ApplyMappingsPass implements JadxPreparePass { @Override public void init(RootNode root) { - MappingTree mappingTree = loadPass.getMappings(); - if (mappingTree == null) { + RenameMappingsData data = RenameMappingsData.getData(root); + if (data == null) { return; } + MappingTree mappingTree = data.getMappings(); process(root, mappingTree); root.registerCodeDataUpdateListener(codeData -> process(root, mappingTree)); } diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/CodeMappingsPass.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/CodeMappingsPass.java index 7ac2c80ca..bd1c24a92 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/CodeMappingsPass.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/CodeMappingsPass.java @@ -16,16 +16,12 @@ import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.plugins.mappings.RenameMappingsData; import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils; public class CodeMappingsPass implements JadxDecompilePass { - private final LoadMappingsPass loadPass; private Map clsRenamesMap; - public CodeMappingsPass(LoadMappingsPass loadPass) { - this.loadPass = loadPass; - } - @Override public JadxPassInfo getInfo() { return new OrderedJadxPassInfo( @@ -36,10 +32,11 @@ public class CodeMappingsPass implements JadxDecompilePass { @Override public void init(RootNode root) { - MappingTree mappingTree = loadPass.getMappings(); - if (mappingTree == null) { + RenameMappingsData data = RenameMappingsData.getData(root); + if (data == null) { return; } + MappingTree mappingTree = data.getMappings(); updateMappingsMap(mappingTree); root.registerCodeDataUpdateListener(codeData -> updateMappingsMap(mappingTree)); } diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/LoadMappingsPass.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/LoadMappingsPass.java index a4b62e493..47be5dcfd 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/LoadMappingsPass.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/load/LoadMappingsPass.java @@ -3,8 +3,6 @@ package jadx.plugins.mappings.load; import java.nio.file.Path; import java.util.Collections; -import org.jetbrains.annotations.Nullable; - import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; @@ -17,12 +15,12 @@ import jadx.api.plugins.pass.impl.SimpleJadxPassInfo; import jadx.api.plugins.pass.types.JadxPreparePass; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.plugins.mappings.RenameMappingsData; import jadx.plugins.mappings.RenameMappingsOptions; public class LoadMappingsPass implements JadxPreparePass { private final RenameMappingsOptions options; - private MappingTree mappings; public LoadMappingsPass(RenameMappingsOptions options) { this.options = options; @@ -35,11 +33,8 @@ public class LoadMappingsPass implements JadxPreparePass { @Override public void init(RootNode root) { - mappings = loadMapping(root.getArgs()); - } - - public @Nullable MappingTree getMappings() { - return mappings; + MappingTree mappings = loadMapping(root.getArgs()); + root.getAttributes().add(new RenameMappingsData(mappings)); } private MappingTree loadMapping(JadxArgs args) { diff --git a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java index b2113c15c..953e6daf1 100644 --- a/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java +++ b/jadx-plugins/jadx-rename-mappings/src/main/java/jadx/plugins/mappings/save/MappingExporter.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +21,7 @@ import net.fabricmc.mappingio.MappedElementKind; import net.fabricmc.mappingio.MappingUtil; import net.fabricmc.mappingio.MappingWriter; import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; import jadx.api.ICodeInfo; @@ -43,14 +45,18 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.files.FileUtils; +import jadx.plugins.mappings.RenameMappingsData; import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils; public class MappingExporter { private static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class); - private final RootNode root; - public MappingExporter(RootNode rootNode) { - this.root = rootNode; + private final RootNode root; + private final @Nullable MappingTree loadedMappingTree; + + public MappingExporter(RootNode root) { + this.root = root; + this.loadedMappingTree = RenameMappingsData.getTree(this.root); } private List> collectMethodVars(MethodNode methodNode) { @@ -138,7 +144,10 @@ public class MappingExporter { String srcNamespace = MappingUtil.NS_SOURCE_FALLBACK; String dstNamespace = MappingUtil.NS_TARGET_FALLBACK; - + if (loadedMappingTree != null && loadedMappingTree.getDstNamespaces() != null) { + srcNamespace = loadedMappingTree.getSrcNamespace(); + dstNamespace = loadedMappingTree.getDstNamespaces().get(0); + } mappingTree.visitHeader(); mappingTree.visitNamespaces(srcNamespace, Collections.singletonList(dstNamespace)); mappingTree.visitContent(); @@ -234,6 +243,10 @@ public class MappingExporter { } } } + // Copy mappings from potentially imported mappings file + if (loadedMappingTree != null && loadedMappingTree.getDstNamespaces() != null) { + loadedMappingTree.accept(mappingTree); + } // Write file MappingWriter writer = MappingWriter.create(path, mappingFormat); mappingTree.accept(writer);