Changed: SWF to XML export in GUI dialog selects a XML file instead of directory

(and directory when multiple SWFs are selected)
This commit is contained in:
Jindra Petřík
2025-06-18 18:07:22 +02:00
parent a60a516366
commit 6aea1a3083
3 changed files with 96 additions and 16 deletions
+2
View File
@@ -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 This may break backwards compatibility. For importing scripts from older
versions of FFDec, you should move the scripts from `<default package>` versions of FFDec, you should move the scripts from `<default package>`
to main scripts folder. 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 ### Fixed
- [#2456] FLA export - NullPointer exception while exporting to CS4 or lower via commandline - [#2456] FLA export - NullPointer exception while exporting to CS4 or lower via commandline
@@ -36,7 +36,6 @@ import java.io.Writer;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -70,12 +69,12 @@ public class SwfXmlExporter {
/** /**
* Exports SWF to XML. * Exports SWF to XML.
*
* @param swf SWF to export * @param swf SWF to export
* @param outFile Target file to save to * @param outFile Target file to save to
* @return List of exported files
* @throws IOException On I/O error * @throws IOException On I/O error
*/ */
public List<File> exportXml(SWF swf, File outFile) throws IOException { public void exportXml(SWF swf, File outFile) throws IOException {
try { try {
File tmp = File.createTempFile("FFDEC", "XML"); File tmp = File.createTempFile("FFDEC", "XML");
@@ -92,21 +91,23 @@ public class SwfXmlExporter {
xmlWriter.close(); xmlWriter.close();
} }
//Test write to raise IOException
try (FileOutputStream fos = new FileOutputStream(outFile)) {
fos.write(1);
}
if (!new XmlPrettyFormat().prettyFormat(tmp, outFile, 2, true)) { if (!new XmlPrettyFormat().prettyFormat(tmp, outFile, 2, true)) {
logger.log(Level.SEVERE, "Cannot prettyformat XML"); logger.log(Level.SEVERE, "Cannot prettyformat XML");
} }
tmp.delete(); tmp.delete();
} catch (Exception ex) { } catch (XMLStreamException ex) {
logger.log(Level.SEVERE, null, ex); logger.log(Level.SEVERE, null, ex);
} }
List<File> ret = new ArrayList<>();
ret.add(outFile);
return ret;
} }
/** /**
* Exports SWF to XML. * Exports SWF to XML.
*
* @param swf SWF to export * @param swf SWF to export
* @param writer XML writer * @param writer XML writer
* @throws IOException On I/O error * @throws IOException On I/O error
@@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.DecompilerPool;
import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.OpenableSourceInfo; import com.jpexs.decompiler.flash.OpenableSourceInfo;
import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.RetryTask;
import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.RenameType;
@@ -4538,38 +4539,114 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
public void exportSwfXml(List<TreeItem> items) { public void exportSwfXml(List<TreeItem> items) {
View.checkAccess(); View.checkAccess();
Set<SWF> swfs = new LinkedHashSet<>(); Set<SWF> usedOpenables = new LinkedHashSet<>();
Set<OpenableList> usedOpenableLists = new HashSet<>();
for (TreeItem item : items) { for (TreeItem item : items) {
if (item instanceof OpenableList) { if (item instanceof OpenableList) {
OpenableList list = (OpenableList) item; OpenableList list = (OpenableList) item;
usedOpenableLists.add(list);
for (Openable openable : list) { for (Openable openable : list) {
if (openable instanceof SWF) { if (openable instanceof SWF) {
swfs.add((SWF) openable); usedOpenables.add((SWF) openable);
} }
} }
} else { } else {
Openable openable = item.getOpenable(); Openable openable = item.getOpenable();
if (openable instanceof SWF) { if (openable instanceof SWF) {
swfs.add((SWF) openable); usedOpenables.add((SWF) openable);
} }
} }
} }
for (SWF swf : swfs) { Map<OpenableList, Map<String, Integer>> usedSwfIdsInBundles = new HashMap<>();
final String selFile = selectExportDir("exportxml"); Map<String, Integer> 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) { if (selFile != null) {
Main.startWork(translate("work.exporting") + "...", 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 { try {
File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); new RetryTask(() -> {
new SwfXmlExporter().exportXml(swf, outFile); File outFile = new File(selFile2);
Main.stopWork(); new SwfXmlExporter().exportXml(openable, outFile);
}, handler).run();
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, null, ex); logger.log(Level.SEVERE, null, ex);
break;
} catch (InterruptedException ex) {
break;
} }
} }
} }
Main.stopWork();
} }
public void importSwfXml(List<TreeItem> items) { public void importSwfXml(List<TreeItem> items) {