fix(plugins): use loaded mapping tree on export (#1732)
This commit is contained in:
@@ -2,8 +2,20 @@ package jadx.api.plugins.input.data.attributes;
|
||||
|
||||
/**
|
||||
* Marker interface for attribute type.
|
||||
* Similar to enumeration but extensible.
|
||||
* <p>
|
||||
* Used for attach attribute instance class information (T).
|
||||
* T - class of attribute instance
|
||||
* <p>
|
||||
* To create new one define static field like this:
|
||||
* {@code
|
||||
* static final IJadxAttrType<AttrTypeClass> ATTR_TYPE = IJadxAttrType.create();
|
||||
* }
|
||||
*/
|
||||
public interface IJadxAttrType<T extends IJadxAttribute> {
|
||||
|
||||
static <A extends IJadxAttribute> IJadxAttrType<A> create() {
|
||||
return new IJadxAttrType<>() {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ClassInfo, ClassNode> clsMap = new HashMap<>();
|
||||
private final Map<String, ClassNode> rawClsMap = new HashMap<>();
|
||||
@@ -701,6 +703,10 @@ public class RootNode {
|
||||
return typeUtils;
|
||||
}
|
||||
|
||||
public AttributeStorage getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public boolean isProto() {
|
||||
return isProto;
|
||||
}
|
||||
|
||||
+5
-2
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Path> 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<Path> 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<Path> 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<TaskStatus> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Path> 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<Path> 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<Path> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,6 @@ public class RenameDialog extends JDialog {
|
||||
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
|
||||
}
|
||||
node.reload(mainWindow);
|
||||
mainWindow.renamesChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+38
@@ -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<RenameMappingsData> 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<RenameMappingsData> getAttrType() {
|
||||
return DATA;
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -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)));
|
||||
|
||||
+4
-8
@@ -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));
|
||||
}
|
||||
|
||||
+4
-7
@@ -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<String, ClassMapping> 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));
|
||||
}
|
||||
|
||||
+3
-8
@@ -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) {
|
||||
|
||||
+17
-4
@@ -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<SimpleEntry<VarNode, Integer>> 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);
|
||||
|
||||
Reference in New Issue
Block a user