diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java index 224c3bbe92e8b259a514a8a89d2d6fe9f8c603ef..c7bb46cb64c1e66732755108a29c840c88f66803 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java @@ -31,7 +31,7 @@ import jadx.gui.settings.data.ProjectData; import jadx.gui.settings.data.TabViewState; import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorViewState; -import jadx.gui.utils.PathTypeAdapter; +import jadx.gui.utils.RelativePathTypeAdapter; public class JadxProject { private static final Logger LOG = LoggerFactory.getLogger(JadxProject.class); @@ -39,15 +39,6 @@ public class JadxProject { private static final int CURRENT_PROJECT_VERSION = 1; public static final String PROJECT_EXTENSION = "jadx"; - private static final Gson GSON = new GsonBuilder() - .registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()) - .registerTypeAdapter(ICodeComment.class, GsonUtils.interfaceReplace(JadxCodeComment.class)) - .registerTypeAdapter(ICodeRename.class, GsonUtils.interfaceReplace(JadxCodeRename.class)) - .registerTypeAdapter(IJavaNodeRef.class, GsonUtils.interfaceReplace(JadxNodeRef.class)) - .registerTypeAdapter(IJavaCodeRef.class, GsonUtils.interfaceReplace(JadxCodeRef.class)) - .setPrettyPrinting() - .create(); - private transient MainWindow mainWindow; private transient JadxSettings settings; @@ -179,9 +170,11 @@ public class JadxProject { } public void save() { - if (getProjectPath() != null) { - try (Writer writer = Files.newBufferedWriter(getProjectPath(), StandardCharsets.UTF_8)) { - GSON.toJson(data, writer); + Path savePath = getProjectPath(); + if (savePath != null) { + Path basePath = savePath.toAbsolutePath().getParent(); + try (Writer writer = Files.newBufferedWriter(savePath, StandardCharsets.UTF_8)) { + buildGson(basePath).toJson(data, writer); saved = true; } catch (Exception e) { LOG.error("Error saving project", e); @@ -190,9 +183,10 @@ public class JadxProject { } public static JadxProject from(Path path) { + Path basePath = path.toAbsolutePath().getParent(); try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { JadxProject project = new JadxProject(); - project.data = GSON.fromJson(reader, ProjectData.class); + project.data = buildGson(basePath).fromJson(reader, ProjectData.class); project.saved = true; project.setProjectPath(path); project.upgrade(); @@ -203,6 +197,17 @@ public class JadxProject { } } + private static Gson buildGson(Path basePath) { + return new GsonBuilder() + .registerTypeHierarchyAdapter(Path.class, new RelativePathTypeAdapter(basePath)) + .registerTypeAdapter(ICodeComment.class, GsonUtils.interfaceReplace(JadxCodeComment.class)) + .registerTypeAdapter(ICodeRename.class, GsonUtils.interfaceReplace(JadxCodeRename.class)) + .registerTypeAdapter(IJavaNodeRef.class, GsonUtils.interfaceReplace(JadxNodeRef.class)) + .registerTypeAdapter(IJavaCodeRef.class, GsonUtils.interfaceReplace(JadxCodeRef.class)) + .setPrettyPrinting() + .create(); + } + private void upgrade() { int fromVersion = data.getProjectVersion(); LOG.debug("upgrade settings from version: {} to {}", fromVersion, CURRENT_PROJECT_VERSION); 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 ceff56fa9f63a34397f5fcf2a7e4ecdedffdaa44..dd5e2e233d66d42dddcf73dde84f8eb7bb1b4591 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -361,6 +361,12 @@ public class MainWindow extends JFrame { if (currentDirectory != null) { fileChooser.setCurrentDirectory(currentDirectory.toFile()); } + if (this.project.getFilePaths().size() == 1) { + // If there is only one file loaded we suggest saving the jadx project file next to the loaded file + Path loadedFile = this.project.getFilePaths().get(0); + String fileName = loadedFile.getFileName() + "." + JadxProject.PROJECT_EXTENSION; + fileChooser.setSelectedFile(loadedFile.resolveSibling(fileName).toFile()); + } int ret = fileChooser.showSaveDialog(mainPanel); if (ret == JFileChooser.APPROVE_OPTION) { settings.setLastSaveProjectPath(fileChooser.getCurrentDirectory().toPath()); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/RelativePathTypeAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/RelativePathTypeAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..96f653f14e218ee26068f40685a85ccfdb4c6d94 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/RelativePathTypeAdapter.java @@ -0,0 +1,44 @@ +package jadx.gui.utils; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class RelativePathTypeAdapter extends TypeAdapter { + private final Path basePath; + + public RelativePathTypeAdapter(Path basePath) { + this.basePath = Objects.requireNonNull(basePath); + } + + @Override + public void write(JsonWriter out, Path value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + value = value.toAbsolutePath().normalize(); + String relativePath = basePath.relativize(value).toString(); + out.value(relativePath); + } + } + + @Override + public Path read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + Path p = Paths.get(in.nextString()); + if (p.isAbsolute()) { + return p; + } + return basePath.resolve(p); + } + +}