diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dcbd2559..101d44d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ All notable changes to this project will be documented in this file. - [#2260] GFX - Configure path resolving dialog for file paths that use prefixes like `data:` - [#2263] Expand one level more (`+` sign) for needed/dependent characters in tag info panel to show full tag name as in tree +- [#1290] Export to FlashDevelop project ### Fixed - Debugger - getting children of top level variables @@ -81,6 +82,7 @@ All notable changes to this project will be documented in this file. - [#2234] AS1/2 postincrement/decrement inside DefineFunction2 - AS3 PCode - pushbyte operand docs - signed byte - [#2226] Incorrect decompilation of continue statements in some cases +- AS3 Embedded assets export - assets.swf not working, incorrect binary data extension for swfs ### Changed - [#2185] MochiCrypt no longer offered for auto decrypt, user needs to choose variant from "Use unpacker" menu @@ -3456,6 +3458,7 @@ Major version of SWF to XML export changed to 2. [#2215]: https://www.free-decompiler.com/flash/issues/2215 [#2243]: https://www.free-decompiler.com/flash/issues/2243 [#2260]: https://www.free-decompiler.com/flash/issues/2260 +[#1290]: https://www.free-decompiler.com/flash/issues/1290 [#2149]: https://www.free-decompiler.com/flash/issues/2149 [#2172]: https://www.free-decompiler.com/flash/issues/2172 [#2174]: https://www.free-decompiler.com/flash/issues/2174 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java index f67a96945..455d9f3d9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/ScriptPack.java @@ -327,6 +327,7 @@ public class ScriptPack extends AS3ClassTreeItem { convertData.ignoreFrameScripts = exportSettings.ignoreFrameScripts; convertData.exportEmbed = exportSettings.exportEmbed; convertData.exportEmbedFlaMode = exportSettings.exportEmbedFlaMode; + convertData.assetsDir = exportSettings.assetsDir; toSource(abcIndex, writer2, abc.script_info.get(scriptIndex).traits.traits, convertData, exportSettings.mode, parallel, exportSettings.ignoreFrameScripts); } catch (FileNotFoundException ex) { logger.log(Level.SEVERE, "The file path is probably too long", ex); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ConvertData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ConvertData.java index 2f09c3bfa..6a1184c7b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ConvertData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/ConvertData.java @@ -38,6 +38,8 @@ public class ConvertData { public boolean exportEmbed; public boolean exportEmbedFlaMode; + + public String assetsDir = "/_assets/"; public ConvertData() { deobfuscationMode = Configuration.autoDeobfuscate.get() ? 1 : 0; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java index abe0d3848..00914c4df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java @@ -91,9 +91,9 @@ public class InstanceInfo { return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames); } - public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames, boolean allowPrivate, boolean allowEmbed) { + public GraphTextWriter getClassHeaderStr(String assetsDir, GraphTextWriter writer, ABC abc, List fullyQualifiedNames, boolean allowPrivate, boolean allowEmbed) { - final String ASSETS_DIR = "/_assets/"; + final String ASSETS_DIR = assetsDir; // "/_assets/"; if (allowEmbed) { if (abc.getSwf() != null) { String className = getName(abc.constants).getNameWithNamespace(abc.constants, false).toRawString(); @@ -106,14 +106,15 @@ public class InstanceInfo { fileName = Helper.makeFileName(fileName); if (ct instanceof DefineBinaryDataTag) { - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + fileName + ".bin\", mimeType=\"application/octet-stream\")]").newLine(); + DefineBinaryDataTag db = (DefineBinaryDataTag) ct; + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + fileName + (db.innerSwf != null ? ".swf" : ".bin")+ "\", mimeType=\"application/octet-stream\")]").newLine(); } if (ct instanceof ImageTag) { ImageTag it = (ImageTag) ct; writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + fileName + ((ImageTag) ct).getImageFormat().getExtension() + "\")]").newLine(); } if (ct instanceof DefineSpriteTag) { - writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + "assets.swf\", symbol=\"" + Helper.escapeActionScriptString(className) + "\")]").newLine(); + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + "assets.swf\", symbol=\"" + "symbol" + ct.getCharacterId() + "\")]").newLine(); } if (ct instanceof DefineSoundTag) { //should be mp3, otherwise it won't work. Should we convert this? diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index 0f7e9acbd..203ddfd9a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -108,7 +108,7 @@ public class TraitClass extends Trait implements TraitWithSlot { @Override public GraphTextWriter toStringHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel, boolean insideInterface) { - abc.instance_info.get(class_info).getClassHeaderStr(writer, abc, fullyQualifiedNames, false, false /*??*/); + abc.instance_info.get(class_info).getClassHeaderStr(convertData.assetsDir, writer, abc, fullyQualifiedNames, false, false /*??*/); return writer; } @@ -170,7 +170,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } //class header - instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false, allowEmbed); + instanceInfo.getClassHeaderStr(convertData.assetsDir, writer, abc, fullyQualifiedNames, false, allowEmbed); writer.endTrait(); writer.startBlock(); writer.startClass(class_info); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java index 1477dcfda..9a90f3942 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/script/AS3ScriptExporter.java @@ -70,6 +70,7 @@ import com.jpexs.decompiler.flash.tags.DefineFont4Tag; import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; import com.jpexs.decompiler.flash.tags.FileAttributesTag; import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; import com.jpexs.decompiler.flash.tags.ShowFrameTag; @@ -485,7 +486,7 @@ public class AS3ScriptExporter { return ret; } - final String ASSETS_DIR = outdir + "/_assets/"; + final String ASSETS_DIR = outdir + exportSettings.assetsDir; List exportTagList = new ArrayList<>(); List spriteTagList = new ArrayList<>(); @@ -592,11 +593,9 @@ public class AS3ScriptExporter { dbj2.writeTag(sos2); } else { ct.writeTag(sos2); - } - for (String cls : ct.getClassNames()) { - symbolClassIds.add(ct.getCharacterId()); - symbolClassNames.add(cls); - } + } + symbolClassIds.add(ct.getCharacterId()); + symbolClassNames.add("symbol" + ct.getCharacterId()); List cidTags = swf.getCharacterIdTags(n); for (CharacterIdTag t : cidTags) { if (t instanceof PlaceObjectTypeTag) { @@ -609,10 +608,10 @@ public class AS3ScriptExporter { } } - SymbolClassTag sc = new SymbolClassTag(swf); - sc.names = symbolClassNames; - sc.tags = symbolClassIds; - sc.writeTag(sos2); + ExportAssetsTag ea = new ExportAssetsTag(swf); + ea.names = symbolClassNames; + ea.tags = symbolClassIds; + ea.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); new EndTag(swf).writeTag(sos2); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java index ddc35a5e4..500909643 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ScriptExportSettings.java @@ -40,7 +40,9 @@ public class ScriptExportSettings { public boolean exportEmbedFlaMode; public boolean resampleWav; - + + public String assetsDir; + public ScriptExportSettings( ScriptExportMode mode, boolean singleFile, @@ -48,6 +50,20 @@ public class ScriptExportSettings { boolean exportEmbed, boolean exportEmbedFlaMode, boolean resampleWav + ) { + this(mode, singleFile, ignoreFrameScripts, exportEmbed, exportEmbedFlaMode, resampleWav, "/_assets/"); + } + + + + public ScriptExportSettings( + ScriptExportMode mode, + boolean singleFile, + boolean ignoreFrameScripts, + boolean exportEmbed, + boolean exportEmbedFlaMode, + boolean resampleWav, + String assetsDir ) { this.mode = mode; this.singleFile = singleFile; @@ -55,6 +71,7 @@ public class ScriptExportSettings { this.exportEmbed = exportEmbed; this.exportEmbedFlaMode = exportEmbedFlaMode; this.resampleWav = resampleWav; + this.assetsDir = assetsDir; } public String getFileExtension() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfFlashDevelopExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfFlashDevelopExporter.java new file mode 100644 index 000000000..1af5a4623 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfFlashDevelopExporter.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010-2024 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.exporters.swf; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.xfl.XFLXmlWriter; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.XmlPrettyFormat; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.xml.stream.XMLStreamException; + +/** + * + * @author JPEXS + */ +public class SwfFlashDevelopExporter { + + private static String doubleToString(double d) { + String ds = "" + d; + if (ds.endsWith(".0")) { + ds = ds.substring(0, ds.length() - 2); + } + return ds; + } + + public void exportFlashDevelopProject(SWF swf, File outFile, AbortRetryIgnoreHandler handler) throws IOException { + exportFlashDevelopProject(swf, outFile, handler, null); + } + + public void exportFlashDevelopProject(SWF swf, File outFile, AbortRetryIgnoreHandler handler, EventListener eventListener) throws IOException { + if (!swf.isAS3()) { + throw new IllegalArgumentException("SWF must be AS3"); + } + + String simpleName = outFile.getName(); + if (simpleName.contains(".")) { + simpleName = simpleName.substring(0, simpleName.lastIndexOf(".")); + } + + SetBackgroundColorTag bgColorTag = swf.getBackgroundColor(); + + String documentClass = swf.getDocumentClass(); + + String srcPath = "src"; + String project = "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + (documentClass == null + ? " \n" + : "\n") + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + try (FileOutputStream fos = new FileOutputStream(outFile)) { + fos.write(Utf8Helper.getBytes(project)); + } + + boolean parallel = Configuration.parallelSpeedUp.get(); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, false, true, false, false); + swf.exportActionScript(handler, outFile.toPath().getParent().resolve(srcPath).toFile().getAbsolutePath(), scriptExportSettings, parallel, eventListener); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLXmlWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLXmlWriter.java index 5023695d6..fcf1a3619 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLXmlWriter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLXmlWriter.java @@ -305,14 +305,17 @@ public class XFLXmlWriter implements XMLStreamWriter { @Override public void writeStartDocument() throws XMLStreamException { + writeStartDocument("utf-8", "1.0"); } @Override public void writeStartDocument(String version) throws XMLStreamException { + writeStartDocument(version, "utf-8"); } @Override public void writeStartDocument(String encoding, String version) throws XMLStreamException { + append(""); } public void writeCharactersRaw(String text) throws XMLStreamException { diff --git a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.air.swf b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.air.swf index 2fccc50f4..708d45659 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.air.swf and b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.flex.swf b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.flex.swf index 352db5365..cf123abd2 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.flex.swf and b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_attrib.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.air.swf b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.air.swf index 70c8c7e8a..1008139b7 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.air.swf and b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.flex.swf b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.flex.swf index 0cbe3630a..893b2f462 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.flex.swf and b/libsrc/ffdec_lib/testdata/as3_embed/bin/as3_embed_classes.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesAir.as b/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesAir.as index 9a2c80ad7..27be64a10 100644 --- a/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesAir.as +++ b/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesAir.as @@ -11,8 +11,6 @@ package public class MainAttributesAir extends Sprite { - TestEmbedAir; - public function MainAttributesAir() { if (stage) init(); @@ -22,7 +20,8 @@ package private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); - // entry point + + new TestEmbedAir(); } } diff --git a/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesFlex.as b/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesFlex.as index bcf963cb4..63f3dd103 100644 --- a/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesFlex.as +++ b/libsrc/ffdec_lib/testdata/as3_embed/src/MainAttributesFlex.as @@ -9,9 +9,7 @@ package * @author JPEXS */ public class MainAttributesFlex extends Sprite - { - - TestEmbedFlex; + { public function MainAttributesFlex() { @@ -22,7 +20,8 @@ package private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); - // entry point + + new TestEmbedFlex(); } } diff --git a/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesAir.as b/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesAir.as index fc4642e93..2aed75974 100644 --- a/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesAir.as +++ b/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesAir.as @@ -9,17 +9,7 @@ package * @author JPEXS */ public class MainClassesAir extends Sprite - { - TestBinaryData; - TestFont; - TestFontCFF; - TestImage; - TestImageGrid; - //TestImageSvg; - TestSameImage; - TestSound; - TestSwfSymbol; - + { public function MainClassesAir() { if (stage) init(); @@ -29,7 +19,16 @@ package private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); - // entry point + + new TestBinaryData(); + new TestFont(); + new TestFontCFF(); + new TestImage(); + new TestImageGrid(); + //new TestImageSvg(); + new TestSameImage(); + new TestSound(); + new TestSwfSymbol(); } } diff --git a/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesFlex.as b/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesFlex.as index 6b12f0c2e..a34db1777 100644 --- a/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesFlex.as +++ b/libsrc/ffdec_lib/testdata/as3_embed/src/MainClassesFlex.as @@ -9,17 +9,7 @@ package * @author JPEXS */ public class MainClassesFlex extends Sprite - { - TestBinaryData; - TestFont; - //TestFontCFF; - TestImage; - TestImageGrid; - TestImageSvg; - TestSameImage; - TestSound; - TestSwfSymbol; - + { public function MainClassesFlex() { if (stage) init(); @@ -29,7 +19,17 @@ package private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); - // entry point + + + new TestBinaryData(); + new TestFont(); + //new TestFontCFF(); + new TestImage(); + new TestImageGrid(); + new TestImageSvg(); + new TestSameImage(); + new TestSound(); + new TestSwfSymbol(); } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index dc0a710de..66d707519 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -452,6 +452,17 @@ public abstract class MainFrameMenu implements MenuBuilder { return false; } + + protected void exportFlashDevelopActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + if (mainFrame.getPanel().checkEdited()) { + return; + } + + mainFrame.getPanel().exportFlashDevelopProject((SWF) openable); + } protected void exportFlaActionPerformed(ActionEvent evt) { if (Main.isWorking()) { @@ -1031,6 +1042,8 @@ public abstract class MainFrameMenu implements MenuBuilder { setMenuEnabled("/file/export/exportAll", openableSelected && !isWorking); setMenuEnabled("_/exportFla", swfSelected && !isWorking); setMenuEnabled("/file/export/exportFla", allSameSwf && openableSelected && !isWorking); + setMenuEnabled("_/exportFlashDevelop", swfSelected && !isWorking); + setMenuEnabled("/file/export/exportFlashDevelop", allSameSwf && openableSelected && isAs3 && !isWorking); setMenuEnabled("_/exportSelected", openableSelected && !isWorking); setMenuEnabled("/file/export/exportSelected", openableSelected && !isWorking); setMenuEnabled("/file/export/exportXml", swfSelected && !isWorking); @@ -1128,6 +1141,7 @@ public abstract class MainFrameMenu implements MenuBuilder { addMenuItem("_/saveAs", translate("menu.file.saveas"), "saveas32", this::saveAsActionPerformed, PRIORITY_TOP, null, true, null, false); addSeparator("_"); addMenuItem("_/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/exportFlashDevelop", translate("menu.file.export.flashDevelop"), "exportflashdevelop32", this::exportFlashDevelopActionPerformed, PRIORITY_TOP, null, true, null, false); addMenuItem("_/exportAll", translate("menu.file.export.all"), "export32", this::exportAllActionPerformed, PRIORITY_TOP, null, true, null, false); addMenuItem("_/exportSelected", translate("menu.file.export.selection"), "exportsel32", this::exportSelectedActionPerformed, PRIORITY_TOP, null, true, null, false); addSeparator("_"); @@ -1160,6 +1174,7 @@ public abstract class MainFrameMenu implements MenuBuilder { addMenuItem("/file/export", translate("menu.export"), null, null, 0, null, false, null, false); addMenuItem("/file/export/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/export/exportFlashDevelop", translate("menu.file.export.flashDevelop"), "exportflashdevelop32", this::exportFlashDevelopActionPerformed, PRIORITY_TOP, null, true, null, false); addMenuItem("/file/export/exportXml", translate("menu.file.export.xml"), "exportxml32", this::exportXmlActionPerformed, PRIORITY_MEDIUM, null, true, null, false); addMenuItem("/file/export/exportAll", translate("menu.file.export.all"), "export16", this::exportAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+E"), false); addMenuItem("/file/export/exportSelected", translate("menu.file.export.selection"), "exportsel16", this::exportSelectedActionPerformed, PRIORITY_MEDIUM, null, true, null, false); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 5f20570e3..2d3461251 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -88,6 +88,7 @@ import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; import com.jpexs.decompiler.flash.exporters.settings.SymbolClassExportSettings; import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.exporters.swf.SwfFlashDevelopExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer; @@ -217,6 +218,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import com.jpexs.decompiler.flash.types.sound.SoundFormat; import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.decompiler.flash.xfl.XFLXmlWriter; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.CancellableWorker; @@ -225,6 +227,7 @@ import com.jpexs.helpers.Path; import com.jpexs.helpers.ProgressListener; import com.jpexs.helpers.Reference; import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.XmlPrettyFormat; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; @@ -3306,6 +3309,116 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } } + + + public void exportFlashDevelopProject(final SWF swf) { + if (swf == null) { + return; + } + + JFileChooser fc = new JFileChooser(); + String selDir = Configuration.lastOpenDir.get(); + fc.setCurrentDirectory(new File(selDir)); + if (!selDir.endsWith(File.separator)) { + selDir += File.separator; + } + String swfShortName = swf.getShortFileName(); + if ("".equals(swfShortName)) { + swfShortName = "untitled.swf"; + } + String fileName; + if (swfShortName.contains(".")) { + fileName = swfShortName.substring(0, swfShortName.lastIndexOf(".")) + ".as3proj"; + } else { + fileName = swfShortName + ".as3proj"; + } + + FileFilter f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".as3proj")); + } + + @Override + public String getDescription() { + return translate("filter.as3proj"); + } + }; + fc.setFileFilter(f); + fc.setAcceptAllFileFilterUsed(false); + fc.setSelectedFile(new File(selDir + fileName)); + + if (fc.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { + return; + } + + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File sf = Helper.fixDialogFile(fc.getSelectedFile()); + SwfFlashDevelopExporter exporter = new SwfFlashDevelopExporter(); + + String path = sf.getAbsolutePath(); + if (path.endsWith(".as3proj")) { + path = path.substring(0, path.length() - ".as3proj".length()); + } + path += ".as3proj"; + + final String fpath = path; + + long timeBefore = System.currentTimeMillis(); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + Helper.freeMem(); + + CancellableWorker w = this; + + ProgressListener prog = new ProgressListener() { + @Override + public void progress(int p) { + } + + @Override + public void status(String status) { + Main.startWork(translate("work.exporting.flashDevelop") + "..." + status, w); + } + }; + EventListener evl = swf.getExportEventListener(); + try { + AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + exporter.exportFlashDevelopProject(swf, new File(fpath), errorHandler, evl); + } catch (Exception ex) { + logger.log(Level.SEVERE, "FlashDevelop export error", ex); + ViewMessages.showMessageDialog(MainPanel.this, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); + } + Helper.freeMem(); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.exporting.flashDevelop") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + long timeAfter = System.currentTimeMillis(); + final long timeMs = timeAfter - timeBefore; + + View.execInEventDispatch(() -> { + setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + }); + + if (Configuration.openFolderAfterFlaExport.get()) { + try { + Desktop.getDesktop().open(new File(fpath).getAbsoluteFile().getParentFile()); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + }.execute(); + } public void exportFla(final SWF swf) { if (swf == null) { @@ -3855,6 +3968,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se return r; } + + public void importScript(final Openable openable) { As3ScriptReplacerInterface as3ScriptReplacer = getAs3ScriptReplacer(Main.isSwfAir(openable)); if (as3ScriptReplacer == null) { diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop16.png b/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop16.png new file mode 100644 index 000000000..bafdccf8e Binary files /dev/null and b/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop16.png differ diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop32.png b/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop32.png new file mode 100644 index 000000000..fe7b958d9 Binary files /dev/null and b/src/com/jpexs/decompiler/flash/gui/graphics/exportflashdevelop32.png differ diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 5e95fcab9..ce108e933 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1273,4 +1273,12 @@ tagInfo.idType = Type of the id contextmenu.configurePathResolving = Configure path resolving... -contextmenu.setAsLinkage = Set AS linkage \ No newline at end of file +contextmenu.setAsLinkage = Set AS linkage + +contextmenu.exportFlashDevelopProject = Export FlashDevelop project + +filter.as3proj = FlashDevelop AS3 projects (*.as3proj) + +work.exporting.flashDevelop = Exporting FlashDevelop project + +menu.file.export.flashDevelop = Export FD project \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 6ed0ae388..0113a9090 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1248,4 +1248,13 @@ tagInfo.idType = Typ id contextmenu.configurePathResolving = Nastavit resolvov\u00e1n\u00ed cest... -contextmenu.setAsLinkage = Nastavit AS vazbu \ No newline at end of file +contextmenu.setAsLinkage = Nastavit AS vazbu + + +contextmenu.exportFlashDevelopProject = Exportovat FlashDevelop projekt + +filter.as3proj = FlashDevelop AS3 projekty (*.as3proj) + +work.exporting.flashDevelop = Exportov\u00e1n\u00ed FlashDevelop projektu + +menu.file.export.flashDevelop = Exportovat FD projekt \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index cabe64909..00099606e 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -206,6 +206,8 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem jumpToCharacterMenuItem; private JMenuItem exportJavaSourceMenuItem; + + private JMenuItem exportFlashDevelopProjectMenuItem; private JMenuItem exportSwfXmlMenuItem; @@ -483,6 +485,11 @@ public class TagTreeContextMenu extends JPopupMenu { jumpToCharacterMenuItem.setIcon(View.getIcon("jumpto16")); add(jumpToCharacterMenuItem); + exportFlashDevelopProjectMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportFlashDevelopProject")); + exportFlashDevelopProjectMenuItem.addActionListener(this::exportFlashDevelopProjectActionPerformed); + exportFlashDevelopProjectMenuItem.setIcon(View.getIcon("exportflashdevelop16")); + add(exportFlashDevelopProjectMenuItem); + exportJavaSourceMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportJavaSource")); exportJavaSourceMenuItem.addActionListener(new ActionListener() { @Override @@ -1158,6 +1165,7 @@ public class TagTreeContextMenu extends JPopupMenu { cleanAbcMenuItem.setVisible(false); rawEditMenuItem.setVisible(false); jumpToCharacterMenuItem.setVisible(false); + exportFlashDevelopProjectMenuItem.setVisible(false); exportJavaSourceMenuItem.setVisible(allSelectedIsSwf); exportSwfXmlMenuItem.setVisible(allSelectedIsSwf); @@ -1393,6 +1401,13 @@ public class TagTreeContextMenu extends JPopupMenu { collapseRecursiveMenuItem.setVisible(true); } + if (firstItem instanceof SWF) { + SWF swf = (SWF) firstItem; + if (swf.isAS3()) { + exportFlashDevelopProjectMenuItem.setVisible(true); + } + } + if (firstItem instanceof HasCharacterId && !(firstItem instanceof CharacterTag)) { jumpToCharacterMenuItem.setVisible(true); } @@ -5405,6 +5420,11 @@ public class TagTreeContextMenu extends JPopupMenu { } } + public void exportFlashDevelopProjectActionPerformed(ActionEvent evt) { + SWF swf = (SWF) getCurrentItem().getOpenable(); + mainPanel.exportFlashDevelopProject(swf); + } + public void importScriptsActionPerformed(ActionEvent evt) { Openable openable = getCurrentItem().getOpenable(); SWF swf = (openable instanceof SWF) ? (SWF) openable : ((ABC) openable).getSwf();