From 4eabf892910c4e1b2b99fbc2085c7bc20bbc4858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 24 Sep 2023 19:38:42 +0200 Subject: [PATCH] Added AS3 - Show `Embed` tag over asset classes (readonly) Added AS3 - Checkbox for exporting assets embedded using `Embed` (-exportembed in commandline) Added FLA export - AS3 - Using `Embed` tag for DefineBinaryData, images not extending BitmapData --- CHANGELOG.md | 3 + .../decompiler/flash/abc/ScriptPack.java | 2 + .../abc/avm2/parser/script/AbcIndexing.java | 17 ++ .../parser/script/ActionScript3Parser.java | 8 +- .../flash/abc/types/ConvertData.java | 4 + .../flash/abc/types/InstanceInfo.java | 108 +++++++++++- .../flash/abc/types/traits/TraitClass.java | 89 +++++++--- .../flash/configuration/Configuration.java | 4 + .../flash/exporters/PreviewExporter.java | 5 +- .../exporters/script/AS3ScriptExporter.java | 165 +++++++++++++++++- .../settings/ScriptExportSettings.java | 14 +- .../flash/flexsdk/MxmlcAs3ScriptReplacer.java | 2 +- .../flash/importers/AS3ScriptImporter.java | 2 +- .../decompiler/flash/xfl/XFLConverter.java | 22 ++- .../jpexs/decompiler/flash/ExportTest.java | 2 +- .../console/CommandLineArgumentParser.java | 16 +- .../decompiler/flash/gui/ExportDialog.java | 41 ++++- .../jpexs/decompiler/flash/gui/MainPanel.java | 6 +- .../locales/AdvancedSettingsDialog.properties | 5 + .../AdvancedSettingsDialog_cs.properties | 4 + .../flash/gui/locales/ExportDialog.properties | 2 + .../gui/locales/ExportDialog_cs.properties | 6 +- 22 files changed, 479 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 322febf57..d81d84109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ All notable changes to this project will be documented in this file. - [#2057] Show all assigned AS linkage classes in the item name (instead just one) - Exporting ByteArrayRange in the raw editor with the Export button - Export DefineFont4 to OpenType CFF file +- AS3 - Show `Embed` tag over asset classes (readonly) +- AS3 - Checkbox for exporting assets embedded using `Embed` (-exportembed in commandline) +- FLA export - AS3 - Using `Embed` tag for DefineBinaryData, images not extending BitmapData ### Fixed - [#2043] StartSound2 tag handling 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 c603377c5..c8be6a411 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 @@ -326,6 +326,8 @@ public class ScriptPack extends AS3ClassTreeItem { FileTextWriter writer2 = exportSettings.singleFile ? exportSettings.singleFileWriter : writer; ConvertData convertData = new ConvertData(); convertData.ignoreFrameScripts = exportSettings.ignoreFrameScripts; + convertData.exportEmbed = exportSettings.exportEmbed; + convertData.exportEmbedFlaMode = exportSettings.exportEmbedFlaMode; 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/avm2/parser/script/AbcIndexing.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java index bcca9319c..bf015ba32 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java @@ -872,4 +872,21 @@ public final class AbcIndexing { } return null; } + + public boolean isInstanceOf(ABC abc, int classIndex, DottedChain searchClassName) { + DottedChain clsName = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false); + if (searchClassName.equals(clsName)) { + return true; + } + if (abc.instance_info.get(classIndex).super_index == 0) { + return false; + } + DottedChain parentClassName = abc.constants.getMultiname(abc.instance_info.get(classIndex).super_index).getNameWithNamespace(abc.constants, false); + + AbcIndexing.ClassIndex ci = findClass(new TypeItem(parentClassName), abc, null); + if (ci == null) { + return false; + } + return isInstanceOf(ci.abc, ci.index, searchClassName); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScript3Parser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScript3Parser.java index b5f9c734f..9f05bf5aa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScript3Parser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScript3Parser.java @@ -611,9 +611,15 @@ public class ActionScript3Parser { expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE); s = lex(); } - metadata.add(en); expected(s, lexer.yyline(), SymbolType.BRACKET_CLOSE); s = lex(); + + /** + * Skip Embed metadata - these are loaded automatically by the assignment in SymbolClass tag + */ + if (!"Embed".equals(name)) { + metadata.add(en); + } } lexer.pushback(s); return metadata; 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 a788ee213..30e6da1d8 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 @@ -34,6 +34,10 @@ public class ConvertData { public boolean thisHasDefaultToPrimitive; public boolean ignoreFrameScripts; + + public boolean exportEmbed; + + public boolean exportEmbedFlaMode; 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 16cb4a397..efe489b36 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 @@ -21,7 +21,17 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; import java.util.List; @@ -80,7 +90,101 @@ 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) { + public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames, boolean allowPrivate, boolean allowEmbed) { + + final String ASSETS_DIR = "/_assets/"; + if (allowEmbed) { + if (abc.getSwf() != null) { + String className = getName(abc.constants).getNameWithNamespace(abc.constants, false).toRawString(); + CharacterTag ct = abc.getSwf().getCharacterByClass(className); + if (ct != null) { + if (ct instanceof DefineBinaryDataTag) { + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".bin\", mimeType=\"application/octet-stream\")]").newLine(); + } + if (ct instanceof ImageTag) { + ImageTag it = (ImageTag) ct; + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ((ImageTag) ct).getImageFormat().getExtension() + "\")]").newLine(); + } + if (ct instanceof DefineSpriteTag) { + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + "assets.swf\", symbol=\"" + className + "\")]").newLine(); + } + if (ct instanceof DefineSoundTag) { + //should be mp3, otherwise it won't work. Should we convert this? + DefineSoundTag st = (DefineSoundTag) ct; + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + "." + (st.getSoundFormat().formatId == SoundFormat.FORMAT_MP3 ? "mp3" : "wav") + "\")]").newLine(); + } + if (ct instanceof FontTag) { + FontTag ft = (FontTag) ct; + + boolean hasFontAlignZones = false; + List sameIdTags = ft.getSwf().getCharacterIdTags(ft.getFontId()); + for (CharacterIdTag sit : sameIdTags) { + if (sit instanceof DefineFontAlignZonesTag) { + hasFontAlignZones = true; + break; + } + } + + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".ttf\",").newLine(); + writer.appendNoHilight("fontName=\"" + ft.getFontName() + "\",").newLine(); + writer.appendNoHilight("mimeType=\"application/x-font\",").newLine(); + writer.appendNoHilight("fontWeight=\"" + (ft.isBold() ? "bold" : "normal") + "\",").newLine(); + writer.appendNoHilight("fontStyle=\"" + (ft.isItalic() ? "italic" : "normal") + "\",").newLine(); + String fontChars = ft.getCharacters(); + if (!fontChars.isEmpty()) { + Character firstC = null; + Character lastC = null; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fontChars.length(); i++) { + char c = fontChars.charAt(i); + if (firstC == null) { + firstC = c; + lastC = c; + continue; + } + + if (lastC + 1 != c) { + if (!sb.isEmpty()) { + sb.append(","); + } + if (firstC == lastC) { + sb.append(String.format("U+%04X", (int) firstC)); + } else { + sb.append(String.format("U+%04X-%04X", (int) firstC, (int) lastC)); + } + firstC = c; + } + lastC = c; + } + if (!sb.isEmpty()) { + sb.append(","); + } + if (firstC == lastC) { + sb.append(String.format("U+%04X", (int) firstC)); + } else { + sb.append(String.format("U+%04X-%04X", (int) firstC, (int) lastC)); + } + writer.appendNoHilight("unicodeRange=\"").appendNoHilight(sb.toString()).appendNoHilight("\",").newLine(); + } + writer.appendNoHilight("advancedAntiAliasing=\"" + (hasFontAlignZones ? "true" : "false") + "\",").newLine(); + writer.appendNoHilight("embedAsCFF=\"false\"").newLine(); + writer.appendNoHilight(")]").newLine(); + } + + if (ct instanceof DefineFont4Tag) { + DefineFont4Tag ft4 = (DefineFont4Tag)ct; + writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".cff\",").newLine(); + writer.appendNoHilight("fontName=\"" + ft4.fontName + "\",").newLine(); + writer.appendNoHilight("mimeType=\"application/x-font\",").newLine(); + writer.appendNoHilight("fontWeight=\"" + (ft4.fontFlagsBold ? "bold" : "normal") + "\",").newLine(); + writer.appendNoHilight("fontStyle=\"" + (ft4.fontFlagsItalic ? "italic" : "normal") + "\",").newLine(); + writer.appendNoHilight("embedAsCFF=\"true\"").newLine(); + writer.appendNoHilight(")]").newLine(); + } + } + } + } + String modifiers; Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); modifiers = ns.getPrefix(abc); @@ -104,7 +208,7 @@ public class InstanceInfo { writer.appendNoHilight(modifiers + objType); String classTypeName = abc.constants.getMultiname(name_index).getNameWithNamespace(abc.constants, true).toRawString(); - + writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, null/* No full names here*/, false, true), HighlightSpecialType.CLASS_NAME, classTypeName); if (super_index > 0) { 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 9b1a6e400..2a8200f6b 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 @@ -40,6 +40,10 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.search.MethodId; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; @@ -109,7 +113,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); + abc.instance_info.get(class_info).getClassHeaderStr(writer, abc, fullyQualifiedNames, false, false /*??*/); return writer; } @@ -117,13 +121,15 @@ public class TraitClass extends Trait implements TraitWithSlot { public void convertHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { } + + @Override public GraphTextWriter toString(AbcIndexing abcIndex, Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel, boolean insideInterface) throws InterruptedException { InstanceInfo instanceInfo = abc.instance_info.get(class_info); - - boolean isInterface = instanceInfo.isInterface(); - + + boolean isInterface = instanceInfo.isInterface(); + Multiname instanceInfoMultiname = instanceInfo.getName(abc.constants); DottedChain packageName = instanceInfoMultiname.getNamespace(abc.constants).getName(abc.constants); //assume not null name @@ -132,11 +138,42 @@ public class TraitClass extends Trait implements TraitWithSlot { String instanceInfoName = instanceInfoMultiname.getName(abc.constants, fullyQualifiedNames, false, true); - getMetaData(parent, convertData, abc, writer); + + boolean allowEmbed = true; + + if (convertData.exportEmbedFlaMode) { + allowEmbed = false; + if (abc.getSwf() != null) { + CharacterTag ct = abc.getSwf().getCharacterByClass(instanceInfoMultiname.getNameWithNamespace(abc.constants, false).toRawString()); + if (ct == null) { + allowEmbed = false; + } else { + if (ct instanceof DefineBinaryDataTag) { + allowEmbed = true; + } + + if (ct instanceof ImageTag) { + allowEmbed = true; + if (abcIndex.isInstanceOf(abc, class_info, DottedChain.parseNoSuffix("flash.display.BitmapData"))) { + allowEmbed = false; + } + } + + if (ct instanceof DefineFont4Tag) { + allowEmbed = true; + } + + if (ct.getClassNames().size() > 1) { + allowEmbed = true; + } + } + } + } + //class header - instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false); - writer.endTrait(); + instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false, allowEmbed); + writer.endTrait(); writer.startBlock(); writer.startClass(class_info); @@ -177,7 +214,7 @@ public class TraitClass extends Trait implements TraitWithSlot { //instance initializer - constructor if (!instanceInfo.isInterface()) { String modifier = "public "; - Multiname m = abc.constants.getMultiname(instanceInfo.name_index); + Multiname m = abc.constants.getMultiname(instanceInfo.name_index); writer.newLine(); writer.startTrait(GraphTextWriter.TRAIT_INSTANCE_INITIALIZER); @@ -209,7 +246,7 @@ public class TraitClass extends Trait implements TraitWithSlot { //instance methods instanceInfo.instance_traits.toString(abcIndex, new Class[]{TraitClass.class, TraitFunction.class, TraitMethodGetterSetter.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel, convertData.ignoreFrameScripts ? frameTraitNames : new ArrayList<>(), isInterface); - writer.endClass(); + writer.endClass(); writer.endBlock(); // class writer.newLine(); return writer; @@ -236,8 +273,8 @@ public class TraitClass extends Trait implements TraitWithSlot { } } else { convertData.thisHasDefaultToPrimitive = true; - } - ScopeStack newScopeStack = (ScopeStack)scopeStack.clone(); + } + ScopeStack newScopeStack = (ScopeStack) scopeStack.clone(); //class initializer int bodyIndex = abc.findBodyIndex(classInfo.cinit_index); if (bodyIndex != -1) { @@ -245,27 +282,27 @@ public class TraitClass extends Trait implements TraitWithSlot { List ts = new ArrayList<>(); ts.add(classInfo.static_traits); List callStack = new ArrayList<>(); - callStack.add(abc.bodies.get(bodyIndex)); - + callStack.add(abc.bodies.get(bodyIndex)); + if (!abc.instance_info.get(class_info).isInterface()) { AbcIndexing.ClassIndex cls = abcIndex.findClass(AbcIndexing.multinameToType(abc.instance_info.get(class_info).name_index, abc.constants), abc, scriptIndex); List clsList = new ArrayList<>(); cls = cls.parent; - while(cls != null) { + while (cls != null) { clsList.add(0, cls); cls = cls.parent; } - for (AbcIndexing.ClassIndex cls2: clsList) { + for (AbcIndexing.ClassIndex cls2 : clsList) { newScopeStack.push(new ClassAVM2Item(cls2.abc.instance_info.get(cls2.index).getName(cls2.abc.constants).getNameWithNamespace(cls2.abc.constants, true))); - } - } - + } + } + abc.bodies.get(bodyIndex).convert(callStack, abcIndex, convertData, path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, true, classInfo.cinit_index, scriptIndex, class_info, abc, this, newScopeStack, GraphTextWriter.TRAIT_CLASS_INITIALIZER, writer, fullyQualifiedNames, ts, true, new HashSet<>()); - - newScopeStack.push(new ClassAVM2Item(abc.instance_info.get(class_info).getName(abc.constants))); + + newScopeStack.push(new ClassAVM2Item(abc.instance_info.get(class_info).getName(abc.constants))); classInitializerIsEmpty = !writer.getMark(); - } - + } + //constructor - instance initializer if (!instanceInfo.isInterface()) { bodyIndex = abc.findBodyIndex(instanceInfo.iinit_index); @@ -274,7 +311,7 @@ public class TraitClass extends Trait implements TraitWithSlot { ts.add(instanceInfo.instance_traits); MethodBody constructorBody = abc.bodies.get(bodyIndex); List callStack = new ArrayList<>(); - callStack.add(constructorBody); + callStack.add(constructorBody); constructorBody.convert(callStack, abcIndex, convertData, path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, false, instanceInfo.iinit_index, scriptIndex, class_info, abc, this, new ScopeStack(), GraphTextWriter.TRAIT_INSTANCE_INITIALIZER, writer, fullyQualifiedNames, ts, true, new HashSet<>()); if (convertData.ignoreFrameScripts) { @@ -372,7 +409,7 @@ public class TraitClass extends Trait implements TraitWithSlot { writer.appendNoHilight("instance ").hilightSpecial(abc.constants.multinameToString(ii.name_index), HighlightSpecialType.INSTANCE_NAME).newLine(); writer.indent(); writer.appendNoHilight("extends ").hilightSpecial(abc.constants.multinameToString(ii.super_index), HighlightSpecialType.EXTENDS).newLine(); - for(int iface : ii.interfaces) { + for (int iface : ii.interfaces) { writer.appendNoHilight("implements ").hilightSpecial(abc.constants.multinameToString(iface), HighlightSpecialType.IMPLEMENTS).newLine(); } if ((ii.flags & InstanceInfo.CLASS_SEALED) == InstanceInfo.CLASS_SEALED) { @@ -386,7 +423,7 @@ public class TraitClass extends Trait implements TraitWithSlot { } if ((ii.flags & InstanceInfo.CLASS_PROTECTEDNS) == InstanceInfo.CLASS_PROTECTEDNS) { writer.appendNoHilight("flag PROTECTEDNS").newLine(); - } + } if ((ii.flags & InstanceInfo.CLASS_NON_NULLABLE) == InstanceInfo.CLASS_NON_NULLABLE) { writer.appendNoHilight("flag NON_NULLABLE").newLine(); } @@ -396,7 +433,7 @@ public class TraitClass extends Trait implements TraitWithSlot { writer.unindent(); writer.appendNoHilight("end ; instance").newLine(); writer.unindent(); - writer.appendNoHilight("end ; class").newLine(); + writer.appendNoHilight("end ; class").newLine(); return writer; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 4d1363afd..4ef823e10 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -915,6 +915,10 @@ public final class Configuration { @ConfigurationName("warning.cannotencrypt") public static ConfigurationItem warningCannotEncrypt = null; + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("export") + public static ConfigurationItem lastExportEnableEmbed = null; + private enum OSId { WINDOWS, OSX, UNIX } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java index 46cd33c05..1d365f540 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java @@ -83,6 +83,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -342,7 +343,7 @@ public class PreviewExporter { Frame fn = (Frame) treeItem; Timelined parent = fn.timeline.timelined; - Set doneCharacters = new HashSet<>(); + Set doneCharacters = new LinkedHashSet<>(); for (Tag t : parent.getTags()) { if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { continue; @@ -353,7 +354,7 @@ public class PreviewExporter { continue; } - Set needed = new HashSet<>(); + Set needed = new LinkedHashSet<>(); t.getNeededCharactersDeep(needed); for (int n : needed) { if (!doneCharacters.contains(n)) { 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 1ef577168..136af561b 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 @@ -18,7 +18,10 @@ package com.jpexs.decompiler.flash.exporters.script; import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.avm2.model.CallPropertyAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; @@ -41,9 +44,42 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.Font4Exporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.ImageExporter; +import com.jpexs.decompiler.flash.exporters.SoundExporter; +import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; +import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.Font4ExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +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.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.helpers.CancellableWorker; @@ -51,10 +87,14 @@ import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; import com.jpexs.helpers.XmlPrettyFormat; import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -115,7 +155,7 @@ public class AS3ScriptExporter { int name2 = ((FullMultinameAVM2Item) cap.propertyName).multinameIndex; for (Trait ct : pack.abc.instance_info.get(cindex).instance_traits.traits) { if (ct.name_index == name2 && (ct instanceof TraitMethodGetterSetter)) { - tagContent.append(handleMxmlMethod(abcIndex,namespaces, pack, cindex, (TraitMethodGetterSetter) ct)); + tagContent.append(handleMxmlMethod(abcIndex, namespaces, pack, cindex, (TraitMethodGetterSetter) ct)); } } } @@ -433,6 +473,129 @@ public class AS3ScriptExporter { } } + if (exportSettings.exportEmbedFlaMode || exportSettings.exportEmbed) { + final String ASSETS_DIR = outdir + "/_assets/"; + List exportTagList = new ArrayList<>(); + List spriteTagList = new ArrayList<>(); + + for (ScriptPack item : packs) { + if (!item.isSimple && Configuration.ignoreCLikePackages.get()) { + continue; + } + String className = item.getClassPath().toRawString(); + CharacterTag ct = swf.getCharacterByClass(className); + if (ct == null) { + continue; + } + if (ct instanceof DefineBinaryDataTag) { + exportTagList.add(ct); + } + if (ct instanceof ImageTag) { + int classIndex = item.abc.findClassByName(className); + if (exportSettings.exportEmbedFlaMode && classIndex != -1 && swf.getAbcIndex().isInstanceOf(item.abc, classIndex, DottedChain.parseNoSuffix("flash.display.BitmapData"))) { + continue; + } + exportTagList.add(ct); + } + if (!exportSettings.exportEmbedFlaMode) { + if (ct instanceof DefineSpriteTag) { + spriteTagList.add((DefineSpriteTag) ct); + } + if (ct instanceof DefineSoundTag) { + exportTagList.add(ct); + } + if (ct instanceof FontTag) { + exportTagList.add(ct); + } + } + if (ct instanceof DefineFont4Tag) { + exportTagList.add(ct); + } + } + ReadOnlyTagList rttl = new ReadOnlyTagList(exportTagList); + try { + BinaryDataExporter bde = new BinaryDataExporter(); + bde.exportBinaryData(handler, ASSETS_DIR, rttl, new BinaryDataExportSettings(BinaryDataExportMode.RAW), evl); + ImageExporter ie = new ImageExporter(); + ie.exportImages(handler, ASSETS_DIR, rttl, new ImageExportSettings(ImageExportMode.PNG_GIF_JPEG), evl); + SoundExporter se = new SoundExporter(); + se.exportSounds(handler, ASSETS_DIR, rttl, new SoundExportSettings(SoundExportMode.MP3_WAV), evl); + FontExporter fe = new FontExporter(); + fe.exportFonts(handler, ASSETS_DIR, rttl, new FontExportSettings(FontExportMode.TTF), evl); + Font4Exporter f4e = new Font4Exporter(); + f4e.exportFonts(handler, ASSETS_DIR, rttl, new Font4ExportSettings(Font4ExportMode.CFF), evl); + + if (!spriteTagList.isEmpty()) { + new RetryTask(() -> { + try (FileOutputStream fos = new FileOutputStream(ASSETS_DIR + "/assets.swf")) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(baos, swf.version, swf.getCharset()); + sos2.writeRECT(swf.displayRect); + sos2.writeFIXED8(swf.frameRate); + sos2.writeUI16(1); + FileAttributesTag fa = swf.getFileAttributes(); + if (fa != null) { + fa.writeTag(sos2); + } + SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + if (setBgColorTag != null) { + setBgColorTag.writeTag(sos2); + } + + Set neededCharacters = new LinkedHashSet<>(); + List symbolClassIds = new ArrayList<>(); + List symbolClassNames = new ArrayList<>(); + for (DefineSpriteTag st : spriteTagList) { + st.getNeededCharactersDeep(neededCharacters); + neededCharacters.add(st.spriteId); + } + for (int n : neededCharacters) { + CharacterTag ct = (CharacterTag) swf.getCharacter(n); + if (ct == null) { + continue; + } + ct.writeTag(sos2); + for (String cls : ct.getClassNames()) { + symbolClassIds.add(ct.getCharacterId()); + symbolClassNames.add(cls); + } + List cidTags = swf.getCharacterIdTags(n); + for (CharacterIdTag t : cidTags) { + if (t instanceof PlaceObjectTypeTag) { + continue; + } + if (t instanceof RemoveTag) { + continue; + } + ((Tag) t).writeTag(sos2); + } + } + + SymbolClassTag sc = new SymbolClassTag(swf); + sc.names = symbolClassNames; + sc.tags = symbolClassIds; + sc.writeTag(sos2); + + new ShowFrameTag(swf).writeTag(sos2); + new EndTag(swf).writeTag(sos2); + + SWFOutputStream sos = new SWFOutputStream(fos, swf.version, swf.getCharset()); + sos.write("FWS".getBytes()); + sos.write(swf.version); + byte data[] = baos.toByteArray(); + long fileSize = sos.getPos() + data.length + 4; + sos.writeUI32(fileSize); + sos.write(data); + } + }, handler).run(); + } + } catch (IOException ex) { + Logger.getLogger(AS3ScriptExporter.class.getName()).log(Level.SEVERE, null, ex); + } catch (InterruptedException ex) { + Logger.getLogger(AS3ScriptExporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + return ret; } } 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 63e244f7a..aba32f2bd 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 @@ -34,11 +34,23 @@ public class ScriptExportSettings { public FileTextWriter singleFileWriter; public boolean ignoreFrameScripts; + + public boolean exportEmbed; + + public boolean exportEmbedFlaMode; - public ScriptExportSettings(ScriptExportMode mode, boolean singleFile, boolean ignoreFrameScripts) { + public ScriptExportSettings( + ScriptExportMode mode, + boolean singleFile, + boolean ignoreFrameScripts, + boolean exportEmbed, + boolean exportEmbedFlaMode + ) { this.mode = mode; this.singleFile = singleFile; this.ignoreFrameScripts = ignoreFrameScripts; + this.exportEmbed = exportEmbed; + this.exportEmbedFlaMode = exportEmbedFlaMode; } public String getFileExtension() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java index 7aa18f82f..dbd0ff753 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/flexsdk/MxmlcAs3ScriptReplacer.java @@ -297,7 +297,7 @@ public class MxmlcAs3ScriptReplacer extends MxmlcRunner implements As3ScriptRepl //This compiled code won't be used at all in original SWF, //it is used only by Flex to properly compile current script AS3ScriptExporter ex = new AS3ScriptExporter(); - ex.exportActionScript3(swfCopy, null, tempDir.getAbsolutePath(), removedPacks, new ScriptExportSettings(ScriptExportMode.AS_METHOD_STUBS, false, false), false, null); + ex.exportActionScript3(swfCopy, null, tempDir.getAbsolutePath(), removedPacks, new ScriptExportSettings(ScriptExportMode.AS_METHOD_STUBS, false, false, false /* ??? FIXME */, false), false, null); //now really remove the classes from SWF copy for (ABC a : modAbcs) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java index 2dcbf52a6..560d31a7c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS3ScriptImporter.java @@ -52,7 +52,7 @@ public class AS3ScriptImporter { return importCount; } try { - File file = pack.getExportFile(scriptsFolder, new ScriptExportSettings(ScriptExportMode.AS, false, false)); + File file = pack.getExportFile(scriptsFolder, new ScriptExportSettings(ScriptExportMode.AS, false, false, false, false)); if (file.exists()) { Openable openable = pack.getOpenable(); SWF swf = (openable instanceof SWF) ? (SWF) openable : ((ABC)openable).getSwf(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index e30f4d55f..a2fdb8de4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -71,6 +71,7 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.helpers.NulWriter; import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; @@ -148,6 +149,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; import com.jpexs.decompiler.flash.types.sound.MP3FRAME; import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.Graph; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; @@ -1871,8 +1873,22 @@ public class XFLConverter { break; } if (characterClasses.containsKey(symbol.getCharacterId())) { - writer.writeAttribute("linkageExportForAS", true); - writer.writeAttribute("linkageClassName", characterClasses.get(symbol.getCharacterId())); + String className = characterClasses.get(symbol.getCharacterId()); + boolean isBitmapData = false; + for (ABCContainerTag c : swf.getAbcList()) { + int classIndex = c.getABC().findClassByName(className); + if (classIndex != -1) { + if (swf.getAbcIndex().isInstanceOf(c.getABC(), classIndex, DottedChain.parseNoSuffix("flash.display.BitmapData"))) { + isBitmapData = true; + } + break; + } + } + if (isBitmapData) { + writer.writeAttribute("linkageExportForAS", true); + writer.writeAttribute("linkageClassName", characterClasses.get(symbol.getCharacterId())); + } + //if it's not BitmapData, then it should use Embed } writer.writeAttribute("quality", 50); writer.writeAttribute("href", symbolFile); @@ -4152,7 +4168,7 @@ public class XFLConverter { } if (useAS3 && settings.exportScript) { try { - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true, false, true); swf.exportActionScript(handler, scriptsDir.getAbsolutePath(), scriptExportSettings, parallel, null); } catch (Exception ex) { logger.log(Level.SEVERE, "Error during ActionScript3 export", ex); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java index f6247cecc..3e2b18c15 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ExportTest.java @@ -108,7 +108,7 @@ public class ExportTest extends FileTestBase { return this; } - }, fdir.getAbsolutePath(), new ScriptExportSettings(exportMode, false, false), false, null); + }, fdir.getAbsolutePath(), new ScriptExportSettings(exportMode, false, false, false, false), false, null); } catch (Exception ex) { fail("Exception during decompilation: " + filePath + " " + ex.getMessage()); } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 34c23df2a..6f774b1ff 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -450,6 +450,12 @@ public class CommandLineArgumentParser { out.println(" DO NOT PUT space between comma (,) and next class."); } + if (filter == null || filter.equals("exportembed")) { + out.println(" " + (cnt++) + ") -exportembed"); + out.println(" ...Allows exporting embedded assets via [Embed tag]"); + out.println(" Use in combination with -export -format script:as"); + } + if (filter == null || filter.equals("dumpswf")) { out.println(" " + (cnt++) + ") -dumpSWF "); out.println(" ...dumps list of SWF tags to console"); @@ -864,6 +870,7 @@ public class CommandLineArgumentParser { String charset = Charset.defaultCharset().name(); boolean cliMode = false; boolean air = false; + boolean exportEmbed = false; Selection selection = new Selection(); Selection selectionIds = new Selection(); List selectionClasses = null; @@ -893,6 +900,9 @@ public class CommandLineArgumentParser { case "-selectclass": selectionClasses = parseSelectClass(args); break; + case "-exportembed": + exportEmbed = true; + break; case "-zoom": zoom = parseZoom(args); break; @@ -1004,7 +1014,7 @@ public class CommandLineArgumentParser { } else if (command.equals("proxy")) { parseProxy(args); } else if (command.equals("export")) { - parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset); + parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed); System.exit(0); } else if (command.equals("compress")) { parseCompress(args); @@ -2250,7 +2260,7 @@ public class CommandLineArgumentParser { } - private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset) { + private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset, boolean exportEmbed) { if (args.size() < 3) { badArguments("export"); } @@ -2514,7 +2524,7 @@ public class CommandLineArgumentParser { singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false, exportEmbed, false); boolean exportAllScript = exportAll || exportFormats.contains("script"); boolean exportAs2Script = exportAllScript || exportFormats.contains("script_as2"); boolean exportAs3Script = exportAllScript || exportFormats.contains("script_as3"); diff --git a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java index 11cfcaedb..aac6697dc 100644 --- a/src/com/jpexs/decompiler/flash/gui/ExportDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/ExportDialog.java @@ -16,6 +16,7 @@ */ package com.jpexs.decompiler.flash.gui; +import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; @@ -46,8 +47,10 @@ import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.timeline.AS3Package; import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.awt.BorderLayout; import java.awt.Container; @@ -144,6 +147,8 @@ public class ExportDialog extends AppDialog { private JTextField zoomTextField = new JTextField(); + private JCheckBox embedCheckBox; + public E getValue(Class option) { for (int i = 0; i < optionClasses.length; i++) { if (option == optionClasses[i]) { @@ -165,6 +170,10 @@ public class ExportDialog extends AppDialog { return false; } + public boolean isEmbedEnabled() { + return embedCheckBox.isSelected(); + } + public double getZoom() { return Double.parseDouble(zoomTextField.getText()) / 100; } @@ -318,6 +327,36 @@ public class ExportDialog extends AppDialog { top += combos[i].getHeight(); } + embedCheckBox = new JCheckBox(translate("embed")); + + boolean hasAs3 = false; + if (exportables == null) { + hasAs3 = true; //?? + } else { + for (TreeItem ti : exportables) { + if (ti instanceof AS3ClassTreeItem) { + hasAs3 = true; + break; + } + } + } + + int w = 10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10; + + if (hasAs3 && Arrays.asList(optionClasses).contains(ScriptExportMode.class)) { + top += 2; + embedCheckBox.setBounds(10, top, embedCheckBox.getPreferredSize().width, embedCheckBox.getPreferredSize().height); + comboPanel.add(embedCheckBox); + top += embedCheckBox.getHeight(); + + if (embedCheckBox.getWidth() + 10 > w) { + w = embedCheckBox.getWidth() + 10; + } + if (Configuration.lastExportEnableEmbed.get()) { + embedCheckBox.setSelected(true); + } + } + int zoomWidth = 50; if (zoomable) { top += 2; @@ -333,7 +372,7 @@ public class ExportDialog extends AppDialog { top += zoomTextField.getHeight(); } - Dimension dim = new Dimension(10 + labWidth + 10 + checkBoxWidth + 10 + comboWidth + 10, top + 10); + Dimension dim = new Dimension(w, top + 10); comboPanel.setMinimumSize(dim); comboPanel.setPreferredSize(dim); cnt.add(comboPanel, BorderLayout.CENTER); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 53de28950..707033b26 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -2065,7 +2065,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false); String singleFileName = Path.combine(scriptsFolder, openable.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; @@ -2172,7 +2172,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile, false, export.isEmbedEnabled(), false); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; @@ -2289,7 +2289,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se singleScriptFile = false; } - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile, false); + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile, false, export.isEmbedEnabled(), false); String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { scriptExportSettings.singleFileWriter = writer; diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 0a3b8bae4..2a851c5ad 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -694,3 +694,8 @@ config.description.maxCachedNum = Maximum number of cached items before older it config.name.warning.cannotencrypt = Warn when cannot save encrypted config.description.warning.cannotencrypt = Show warning when cannot save SWF file which was encrypted using HARMAN Air encryption. + +#after 18.5.0 +config.name.lastExportEnableEmbed = Last setting of export embedded assets +config.description.lastExportEnableEmbed = Last setting of exporting embedded assets via [Embed] metadata. + diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties index 31356da17..f2fb410d7 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties @@ -684,3 +684,7 @@ config.description.maxCachedNum = Maxim\u00e1ln\u00ed po\u010det cachovan\u00fdc config.name.warning.cannotencrypt = Varovat kdy\u017e nelze ulo\u017eit za\u0161ifrovan\u00e9 config.description.warning.cannotencrypt = Zobrazit varov\u00e1n\u00ed kdy\u017e nelze ulo\u017eit SWF soubor kter\u00fd byl \u0161ifrov\u00e1n pomoc\u00ed HARMAN Air \u0161ifrov\u00e1n\u00ed. + +#after 18.5.0 +config.name.lastExportEnableEmbed = Posledn\u00ed nastaven\u00ed exportu vlo\u017een\u00fdch zdroj\u016f +config.description.lastExportEnableEmbed = Posledn\u00ed nastaven\u00ed exportov\u00e1n\u00ed vlo\u017een\u00fdch zdroj\u016f skrze [Embed] metadata. \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 58ca520cf..3862e0dbf 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -105,3 +105,5 @@ images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha #after 18.5.0 fonts4 = DefineFont4 fonts4.cff = CFF + +embed = Export embedded assets via [Embed] diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties index d69681878..78c884de4 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties @@ -45,7 +45,7 @@ scripts.pcode = P-k\u00f3d scripts.pcode_hex = P-k\u00f3d s hex scripts.hex = Hex scripts.constants = Konstanty -scripts.as_method_stubs = Konstanty +scripts.as_method_stubs = Z\u00e1klady ActionScript metod scripts.pcode_graphviz = P-k\u00f3d GraphViz binaryData = Bin\u00e1rn\u00ed data @@ -99,4 +99,6 @@ symbolclass = Mapov\u00e1n\u00ed symbol\u016f na t\u0159\u00eddy symbolclass.csv = CSV #after 18.0.0 -images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha \ No newline at end of file +images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha + +embed = Exportovat vlo\u017een\u00e9 zdroje skrze [Embed] \ No newline at end of file