From 6aea1a3083ac1872564b97cf1a81e3b93b7c98c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 18 Jun 2025 18:07:22 +0200 Subject: [PATCH] Changed: SWF to XML export in GUI dialog selects a XML file instead of directory (and directory when multiple SWFs are selected) --- CHANGELOG.md | 2 + .../flash/exporters/swf/SwfXmlExporter.java | 17 ++-- .../jpexs/decompiler/flash/gui/MainPanel.java | 93 +++++++++++++++++-- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c38aba1..6af66cf10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ All notable changes to this project will be documented in this file. This may break backwards compatibility. For importing scripts from older versions of FFDec, you should move the scripts from `` to main scripts folder. +- SWF to XML export in GUI dialog selects a XML file instead of directory + (and directory when multiple SWFs are selected) ### Fixed - [#2456] FLA export - NullPointer exception while exporting to CS4 or lower via commandline diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java index bcbbc0abe..b5711193f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java @@ -36,7 +36,6 @@ import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -70,12 +69,12 @@ public class SwfXmlExporter { /** * Exports SWF to XML. + * * @param swf SWF to export * @param outFile Target file to save to - * @return List of exported files * @throws IOException On I/O error */ - public List exportXml(SWF swf, File outFile) throws IOException { + public void exportXml(SWF swf, File outFile) throws IOException { try { File tmp = File.createTempFile("FFDEC", "XML"); @@ -92,21 +91,23 @@ public class SwfXmlExporter { xmlWriter.close(); } + //Test write to raise IOException + try (FileOutputStream fos = new FileOutputStream(outFile)) { + fos.write(1); + } + if (!new XmlPrettyFormat().prettyFormat(tmp, outFile, 2, true)) { logger.log(Level.SEVERE, "Cannot prettyformat XML"); } tmp.delete(); - } catch (Exception ex) { + } catch (XMLStreamException ex) { logger.log(Level.SEVERE, null, ex); } - - List ret = new ArrayList<>(); - ret.add(outFile); - return ret; } /** * Exports SWF to XML. + * * @param swf SWF to export * @param writer XML writer * @throws IOException On I/O error diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 8cd6cdef2..af09053d8 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.DecompilerPool; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.OpenableSourceInfo; import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.RenameType; @@ -4538,38 +4539,114 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void exportSwfXml(List items) { View.checkAccess(); - Set swfs = new LinkedHashSet<>(); + Set usedOpenables = new LinkedHashSet<>(); + Set usedOpenableLists = new HashSet<>(); for (TreeItem item : items) { if (item instanceof OpenableList) { OpenableList list = (OpenableList) item; + usedOpenableLists.add(list); for (Openable openable : list) { if (openable instanceof SWF) { - swfs.add((SWF) openable); + usedOpenables.add((SWF) openable); } } } else { Openable openable = item.getOpenable(); if (openable instanceof SWF) { - swfs.add((SWF) openable); + usedOpenables.add((SWF) openable); } } } - for (SWF swf : swfs) { - final String selFile = selectExportDir("exportxml"); + Map> usedSwfIdsInBundles = new HashMap<>(); + Map usedSwfsIds = new HashMap<>(); + + String selFile; + if (usedOpenables.size() > 1) { + selFile = selectExportDir("exportxml"); + if (selFile == null) { + return; + } + } else { + JFileChooser fc = View.getFileChooserWithIcon("exportxml"); + fc.setDialogTitle(AppStrings.translate("menu.file.export.xml")); + String selDir = Configuration.lastExportDir.get(); + fc.setCurrentDirectory(new File(selDir)); + if (!selDir.endsWith(File.separator)) { + selDir += File.separator; + } + SWF swf = usedOpenables.iterator().next(); + String swfFileName = swf.getTitleOrShortFileName(); + String xmlFileName = swfFileName + ".xml"; + if (swfFileName.toLowerCase(Locale.ENGLISH).endsWith(".swf") + || swfFileName.toLowerCase(Locale.ENGLISH).endsWith(".gfx")) { + xmlFileName = swfFileName.substring(0, swfFileName.lastIndexOf(".")) + ".xml"; + } + fc.setSelectedFile(new File(selDir + xmlFileName)); + fc.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || f.getName().toLowerCase(Locale.ENGLISH).endsWith(".xml"); + } + + @Override + public String getDescription() { + return AppStrings.translate("filter.xml"); + } + }); + if (fc.showSaveDialog(Main.getDefaultMessagesComponent()) != JFileChooser.APPROVE_OPTION) { + return; + } + selFile = Helper.fixDialogFile(fc.getSelectedFile()).getAbsolutePath(); + if (!selFile.toLowerCase(Locale.ENGLISH).endsWith(".xml")) { + selFile = selFile + ".xml"; + } + } + + AbortRetryIgnoreHandler handler = new GuiAbortRetryIgnoreHandler(); + for (SWF openable : usedOpenables) { if (selFile != null) { Main.startWork(translate("work.exporting") + "...", null); + String selFile2; + if (usedOpenables.size() > 1) { + String swfFileName = openable.getTitleOrShortFileName(); + String fileNameNoExt = swfFileName; + if (swfFileName.toLowerCase(Locale.ENGLISH).endsWith(".swf") + || swfFileName.toLowerCase(Locale.ENGLISH).endsWith(".gfx")) { + fileNameNoExt = swfFileName.substring(0, swfFileName.lastIndexOf(".")); + } + + if (usedOpenableLists.size() > 1 && openable.getOpenableList() != null && openable.getOpenableList().isBundle()) { + if (!usedSwfIdsInBundles.containsKey(openable.getOpenableList())) { + usedSwfIdsInBundles.put(openable.getOpenableList(), new HashMap<>()); + } + File parentDir = new File(selFile + File.separator + openable.getOpenableList().name); + parentDir.mkdirs(); + selFile2 = selFile + File.separator + openable.getOpenableList().name + File.separator + Helper.getNextId(fileNameNoExt, usedSwfIdsInBundles.get(openable.getOpenableList())) + ".xml"; + } else { + selFile2 = selFile + File.separator + Helper.getNextId(fileNameNoExt, usedSwfsIds) + ".xml"; + } + } else { + selFile2 = selFile; + } + try { - File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); - new SwfXmlExporter().exportXml(swf, outFile); - Main.stopWork(); + new RetryTask(() -> { + File outFile = new File(selFile2); + new SwfXmlExporter().exportXml(openable, outFile); + }, handler).run(); + } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); + break; + } catch (InterruptedException ex) { + break; } } } + Main.stopWork(); } public void importSwfXml(List items) {