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);