From 9a3dddc19b4ceca5c3c66c4235b08b4d13b0d756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 18 Sep 2016 13:23:56 +0200 Subject: [PATCH] Issue #1254 FLA export - AS3 decoding timeline scripts FLA export - placing AS3 scripts to FLA directory instead of scripts dir Added misc TextWriters (stream and stringbuilder) --- .../flash/helpers/FileTextWriter.java | 111 +--------- .../flash/helpers/StreamTextWriter.java | 137 ++++++++++++ .../helpers/StringBuilderTextWriter.java | 123 +++++++++++ .../decompiler/flash/xfl/XFLConverter.java | 203 ++++++++++++++++-- 4 files changed, 447 insertions(+), 127 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StreamTextWriter.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StringBuilderTextWriter.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FileTextWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FileTextWriter.java index 465ee17cf..82717d804 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FileTextWriter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/FileTextWriter.java @@ -16,122 +16,17 @@ */ package com.jpexs.decompiler.flash.helpers; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; -import java.io.BufferedOutputStream; import java.io.FileOutputStream; -import java.io.IOException; -import java.io.Writer; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Provides methods for highlighting positions of instructions in the text. * * @author JPEXS */ -public class FileTextWriter extends GraphTextWriter implements AutoCloseable { +public class FileTextWriter extends StreamTextWriter implements AutoCloseable { - private final Writer writer; - - private boolean newLine = true; - - private int indent; - - private int writtenBytes; - - public FileTextWriter(CodeFormatting formatting, FileOutputStream fos) { - super(formatting); - this.writer = new Utf8OutputStreamWriter(new BufferedOutputStream(fos)); + public FileTextWriter(CodeFormatting formatting, FileOutputStream os) { + super(formatting, os); } - @Override - public GraphTextWriter hilightSpecial(String text, HighlightSpecialType type, String specialValue, HighlightData data) { - writeToFile(text); - return this; - } - - @Override - public FileTextWriter append(String str) { - writeToFile(str); - return this; - } - - @Override - public GraphTextWriter appendWithData(String str, HighlightData data) { - writeToFile(str); - return this; - } - - @Override - public FileTextWriter append(String str, long offset, long fileOffset) { - writeToFile(str); - return this; - } - - @Override - public FileTextWriter appendNoHilight(int i) { - writeToFile(Integer.toString(i)); - return this; - } - - @Override - public FileTextWriter appendNoHilight(String str) { - writeToFile(str); - return this; - } - - @Override - public FileTextWriter indent() { - indent++; - return this; - } - - @Override - public FileTextWriter unindent() { - indent--; - return this; - } - - @Override - public FileTextWriter newLine() { - writeToFile(formatting.newLineChars); - newLine = true; - return this; - } - - @Override - public int getLength() { - return writtenBytes; - } - - @Override - public int getIndent() { - return indent; - } - - private void writeToFile(String str) { - if (newLine) { - newLine = false; - appendIndent(); - } - try { - writer.write(str); - writtenBytes += str.length(); - } catch (IOException ex) { - Logger.getLogger(FileTextWriter.class.getName()).log(Level.SEVERE, null, ex); - } - } - - private void appendIndent() { - for (int i = 0; i < indent; i++) { - writeToFile(formatting.indentString); - } - } - - @Override - public void close() throws IOException { - writer.close(); - } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StreamTextWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StreamTextWriter.java new file mode 100644 index 000000000..a7d4ae87c --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StreamTextWriter.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010-2016 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.helpers; + +import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Provides methods for highlighting positions of instructions in the text. + * + * @author JPEXS + */ +public class StreamTextWriter extends GraphTextWriter implements AutoCloseable { + + private final Writer writer; + + private boolean newLine = true; + + private int indent; + + private int writtenBytes; + + public StreamTextWriter(CodeFormatting formatting, OutputStream os) { + super(formatting); + this.writer = new Utf8OutputStreamWriter(new BufferedOutputStream(os)); + } + + @Override + public GraphTextWriter hilightSpecial(String text, HighlightSpecialType type, String specialValue, HighlightData data) { + writeToOutputStream(text); + return this; + } + + @Override + public StreamTextWriter append(String str) { + writeToOutputStream(str); + return this; + } + + @Override + public GraphTextWriter appendWithData(String str, HighlightData data) { + writeToOutputStream(str); + return this; + } + + @Override + public StreamTextWriter append(String str, long offset, long fileOffset) { + writeToOutputStream(str); + return this; + } + + @Override + public StreamTextWriter appendNoHilight(int i) { + writeToOutputStream(Integer.toString(i)); + return this; + } + + @Override + public StreamTextWriter appendNoHilight(String str) { + writeToOutputStream(str); + return this; + } + + @Override + public StreamTextWriter indent() { + indent++; + return this; + } + + @Override + public StreamTextWriter unindent() { + indent--; + return this; + } + + @Override + public StreamTextWriter newLine() { + writeToOutputStream(formatting.newLineChars); + newLine = true; + return this; + } + + @Override + public int getLength() { + return writtenBytes; + } + + @Override + public int getIndent() { + return indent; + } + + private void writeToOutputStream(String str) { + if (newLine) { + newLine = false; + appendIndent(); + } + try { + writer.write(str); + writtenBytes += str.length(); + } catch (IOException ex) { + Logger.getLogger(StreamTextWriter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void appendIndent() { + for (int i = 0; i < indent; i++) { + writeToOutputStream(formatting.indentString); + } + } + + @Override + public void close() throws IOException { + writer.close(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StringBuilderTextWriter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StringBuilderTextWriter.java new file mode 100644 index 000000000..5d698e9f0 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/helpers/StringBuilderTextWriter.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010-2016 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.helpers; + +import com.jpexs.decompiler.flash.helpers.hilight.HighlightData; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; + +/** + * Provides methods for highlighting positions of instructions in the text. + * + * @author JPEXS + */ +public class StringBuilderTextWriter extends GraphTextWriter { + + private final StringBuilder writer; + + private boolean newLine = true; + + private int indent; + + private int writtenBytes; + + public StringBuilderTextWriter(CodeFormatting formatting, StringBuilder writer) { + super(formatting); + this.writer = writer; + } + + @Override + public GraphTextWriter hilightSpecial(String text, HighlightSpecialType type, String specialValue, HighlightData data) { + writeToOutputStream(text); + return this; + } + + @Override + public StringBuilderTextWriter append(String str) { + writeToOutputStream(str); + return this; + } + + @Override + public GraphTextWriter appendWithData(String str, HighlightData data) { + writeToOutputStream(str); + return this; + } + + @Override + public StringBuilderTextWriter append(String str, long offset, long fileOffset) { + writeToOutputStream(str); + return this; + } + + @Override + public StringBuilderTextWriter appendNoHilight(int i) { + writeToOutputStream(Integer.toString(i)); + return this; + } + + @Override + public StringBuilderTextWriter appendNoHilight(String str) { + writeToOutputStream(str); + return this; + } + + @Override + public StringBuilderTextWriter indent() { + indent++; + return this; + } + + @Override + public StringBuilderTextWriter unindent() { + indent--; + return this; + } + + @Override + public StringBuilderTextWriter newLine() { + writeToOutputStream(formatting.newLineChars); + newLine = true; + return this; + } + + @Override + public int getLength() { + return writtenBytes; + } + + @Override + public int getIndent() { + return indent; + } + + private void writeToOutputStream(String str) { + if (newLine) { + newLine = false; + appendIndent(); + } + writer.append(str); + writtenBytes += str.length(); + + } + + private void appendIndent() { + for (int i = 0; i < indent; i++) { + writeToOutputStream(formatting.indentString); + } + } + +} 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 c8072c922..ab2908583 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 @@ -22,6 +22,21 @@ import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFCompression; import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.model.CallPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThisAVM2Item; +import com.jpexs.decompiler.flash.abc.types.ConvertData; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType; @@ -33,7 +48,10 @@ import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +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.CSMTextSettingsTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; @@ -109,6 +127,8 @@ 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.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; import com.jpexs.helpers.SerializableImage; @@ -473,8 +493,8 @@ public class XFLConverter { int hintedY = 0; int correctX = 0; int correctY = 0; - int lastCorrectX = 0; - int lastCorrectY = 0; + int lastCorrectX; + int lastCorrectY; for (SHAPERECORD rec : shapeRecords) { SHAPERECORD ch = rec.clone(); lastCorrectX = correctX; @@ -1331,7 +1351,7 @@ public class XFLConverter { if (tag instanceof DefineButtonTag) { writer.writeStartElement("Actionscript"); writer.writeStartElement("script"); - writer.writeCData("on(press){\r\n" + convertActionScript(new ButtonAction((DefineButtonTag) tag)) + "}"); + writer.writeCData("on(press){\r\n" + convertActionScript12(new ButtonAction((DefineButtonTag) tag)) + "}"); writer.writeEndElement(); writer.writeEndElement(); } @@ -1342,7 +1362,7 @@ public class XFLConverter { writer.writeStartElement("script"); StringBuilder sbActions = new StringBuilder(); for (BUTTONCONDACTION bca : db2.actions) { - sbActions.append(convertActionScript(bca)); + sbActions.append(convertActionScript12(bca)); } writer.writeCData(sbActions.toString()); writer.writeEndElement(); @@ -1354,7 +1374,7 @@ public class XFLConverter { writer.writeStartElement("script"); StringBuilder sbActions = new StringBuilder(); for (CLIPACTIONRECORD rec : clipActions.clipActionRecords) { - sbActions.append(convertActionScript(rec)); + sbActions.append(convertActionScript12(rec)); } writer.writeCData(sbActions.toString()); writer.writeEndElement(); @@ -1411,7 +1431,7 @@ public class XFLConverter { writer.writeEndElement(); } - private static String convertActionScript(ASMSource as) { + private static String convertActionScript12(ASMSource as) { HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), false); try { Action.actionsToSource(as, as.getActions(), as.toString()/*FIXME?*/, writer); @@ -1426,15 +1446,15 @@ public class XFLConverter { return date.getTime() / 1000; } - private void convertLibrary(SWF swf, Map characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { + private void convertLibrary(SWF swf, Map characterVariables, Map characterClasses, Map characterScriptPacks, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { //TODO: Imported assets //linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf" convertMedia(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer); - convertSymbols(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer); + convertSymbols(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer); } - private void convertSymbols(SWF swf, Map characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { + private void convertSymbols(SWF swf, Map characterVariables, Map characterClasses, Map characterScriptPacks, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { boolean hasSymbol = false; for (int ch : characters.keySet()) { CharacterTag symbol = characters.get(ch); @@ -1590,7 +1610,9 @@ public class XFLConverter { if (sprite.getTags().isEmpty()) { //probably AS2 class continue; } - convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr); + final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite.spriteId) ? characterScriptPacks.get(sprite.spriteId) : null; + convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack); + } else if (symbol instanceof ShapeTag) { itemIcon = "1"; ShapeTag shape = (ShapeTag) symbol; @@ -2402,7 +2424,113 @@ public class XFLConverter { writer.writeEndElement(); } - private boolean convertActionScriptLayer(int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor, XFLXmlWriter writer) throws XMLStreamException { + private static int getPackMainClassId(ScriptPack pack) { + ABC abc = pack.abc; + ScriptInfo script = abc.script_info.get(pack.scriptIndex); + for (int traitIndex : pack.traitIndices) { + Trait trait = script.traits.traits.get(traitIndex); + if (trait instanceof TraitClass) { + TraitClass tc = (TraitClass) trait; + Namespace traitNameNamespace = abc.constants.getNamespace(trait.getName(abc).namespace_index); + if (traitNameNamespace.kind == Namespace.KIND_PACKAGE) { //its public class + //assuming the one public class in the pack is the class we are looking for + return tc.class_info; + } + } + } + return -1; + } + + private static Map getFrameScriptsFromPack(ScriptPack pack) { + Map ret = new HashMap<>(); + int classIndex = getPackMainClassId(pack); + if (classIndex > -1) { + ABC abc = pack.abc; + InstanceInfo instanceInfo = abc.instance_info.get(classIndex); + int constructorMethodIndex = instanceInfo.iinit_index; + MethodBody constructorBody = abc.findBody(constructorMethodIndex); + try { + if (constructorBody.convertedItems == null) { + constructorBody.convert(new ConvertData(), "??", ScriptExportMode.AS, true, constructorMethodIndex, pack.scriptIndex, classIndex, abc, null, new ScopeStack(), GraphTextWriter.TRAIT_INSTANCE_INITIALIZER, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); + } + + Map frameToTraitMultiname = new HashMap<>(); + + //find all addFrameScript(xx,this.method) in constructor + /* + It looks like this: + CallPropertyAVM2Item + ->propertyName == FullMultinameAVM2Item + -> resolvedMultinameName (String) "addFrameScript" + ->arguments + ->0 IntegerValueAVM2Item + ->value (Long) 0 - zero based + ->1 GetPropertyAVM2Item + ->object (ThisAVM2Item) + ->propertyName (FullMultinameAvm2Item) + ->multinameIndex + ->resolvedMultinameName (String) "frame1" + */ + if (constructorBody.convertedItems != null) { + for (GraphTargetItem ti : constructorBody.convertedItems) { + if (ti instanceof CallPropertyAVM2Item) { + CallPropertyAVM2Item callProp = (CallPropertyAVM2Item) ti; + if (callProp.propertyName instanceof FullMultinameAVM2Item) { + FullMultinameAVM2Item propName = (FullMultinameAVM2Item) callProp.propertyName; + if ("addFrameScript".equals(propName.resolvedMultinameName)) { + if (callProp.arguments.size() == 2) { + if (callProp.arguments.get(0) instanceof IntegerValueAVM2Item) { + IntegerValueAVM2Item frameItem = (IntegerValueAVM2Item) callProp.arguments.get(0); + int frame = frameItem.intValue(); + if (callProp.arguments.get(1) instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item getProp = (GetPropertyAVM2Item) callProp.arguments.get(1); + if (getProp.object instanceof ThisAVM2Item) { + if (getProp.propertyName instanceof FullMultinameAVM2Item) { + FullMultinameAVM2Item framePropName = (FullMultinameAVM2Item) getProp.propertyName; + int multinameIndex = framePropName.multinameIndex; + frameToTraitMultiname.put(frame, multinameIndex); + } + } + } + } + } + } + } + } + } + } + Map multinameToMethodTrait = new HashMap<>(); + for (Trait trait : instanceInfo.instance_traits.traits) { + if (trait instanceof TraitMethodGetterSetter) { + multinameToMethodTrait.put(trait.name_index, (TraitMethodGetterSetter) trait); + } + } + + for (int frame : frameToTraitMultiname.keySet()) { + int multiName = frameToTraitMultiname.get(frame); + if (multinameToMethodTrait.containsKey(multiName)) { + TraitMethodGetterSetter methodTrait = multinameToMethodTrait.get(multiName); + int methodIndex = methodTrait.method_info; + MethodBody frameBody = abc.findBody(methodIndex); + + StringBuilder scriptBuilder = new StringBuilder(); + frameBody.convert(new ConvertData(), "??", ScriptExportMode.AS, false, methodIndex, pack.scriptIndex, classIndex, abc, methodTrait, new ScopeStack(), 0, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); + StringBuilderTextWriter writer = new StringBuilderTextWriter(Configuration.getCodeFormatting(), scriptBuilder); + frameBody.toString("??", ScriptExportMode.AS, abc, methodTrait, writer, new ArrayList<>()); + + String script = scriptBuilder.toString(); + ret.put(frame, script); + } + } + + } catch (InterruptedException ex) { + //ignore + } + } + return ret; + } + + private boolean convertActionScriptLayer(int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor, XFLXmlWriter writer, ScriptPack scriptPack) throws XMLStreamException { boolean hasScript = false; String script = ""; @@ -2412,17 +2540,28 @@ public class XFLConverter { if (t instanceof DoInitActionTag) { DoInitActionTag dia = (DoInitActionTag) t; if (dia.spriteId == spriteId) { - script += convertActionScript(dia); + script += convertActionScript12(dia); } } } if (!script.isEmpty()) { script = "#initclip\r\n" + script + "#endinitclip\r\n"; } + + Map frameToScriptMap = new HashMap<>(); + + if (scriptPack != null) { + frameToScriptMap = getFrameScriptsFromPack(scriptPack); + } + for (Tag t : timeLineTags) { if (t instanceof DoActionTag) { DoActionTag da = (DoActionTag) t; - script += convertActionScript(da); + script += convertActionScript12(da); + } + if (frameToScriptMap.containsKey(frame)) { + script += frameToScriptMap.get(frame); + frameToScriptMap.remove(frame); } if (t instanceof ShowFrameTag) { @@ -2617,12 +2756,12 @@ public class XFLConverter { return outlineColor.toHexRGB(); } - private void convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer) throws XMLStreamException { + private void convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer, ScriptPack scriptPack) throws XMLStreamException { writer.writeStartElement("DOMTimeline", new String[]{"name", name}); writer.writeStartElement("layers"); boolean hasLabel = convertLabelsLayer(spriteId, tags, timelineTags, backgroundColor, writer); - boolean hasScript = convertActionScriptLayer(spriteId, tags, timelineTags, backgroundColor, writer); + boolean hasScript = convertActionScriptLayer(spriteId, tags, timelineTags, backgroundColor, writer, scriptPack); int index = 0; @@ -2719,6 +2858,30 @@ public class XFLConverter { }, handler).run(); } + private static Map getCharacterScriptPacks(SWF swf, Map characterClasses) { + Map ret = new HashMap<>(); + + Map classToId = new HashMap<>(); + for (int id : characterClasses.keySet()) { + classToId.put(characterClasses.get(id), id); + } + + List allClasses = new ArrayList<>(characterClasses.values()); + List packs = new ArrayList<>(); + try { + packs = swf.getScriptPacksByClassNames(allClasses); + } catch (Exception ex) { + //ignore + } + for (ScriptPack pack : packs) { + String packClass = pack.getClassPath().toRawString(); + if (classToId.containsKey(packClass)) { + ret.put(classToId.get(packClass), pack); + } + } + return ret; + } + private static Map getCharacterClasses(ReadOnlyTagList tags) { Map ret = new HashMap<>(); for (Tag t : tags) { @@ -3152,6 +3315,7 @@ public class XFLConverter { HashMap characters = getCharacters(swf.getTags()); List nonLibraryShapes = getNonLibraryShapes(swf.getTags(), characters); Map characterClasses = getCharacterClasses(swf.getTags()); + Map characterScriptPacks = getCharacterScriptPacks(swf, characterClasses); Map characterVariables = getCharacterVariables(swf.getTags()); boolean hasAmfMetadata = hasAmfMetadata(swf); @@ -3194,10 +3358,11 @@ public class XFLConverter { } convertFonts(swf.getTags(), domDocument); - convertLibrary(swf, characterVariables, characterClasses, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument); + convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument); domDocument.writeStartElement("timelines"); - convertTimeline(0, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument); + ScriptPack documentScriptPack = characterScriptPacks.containsKey(0) ? characterScriptPacks.get(0) : null; + convertTimeline(0, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack); domDocument.writeEndElement(); if (hasAmfMetadata) { @@ -3228,7 +3393,7 @@ public class XFLConverter { if (characters.get(chid) instanceof DefineSpriteTag) { DefineSpriteTag sprite = (DefineSpriteTag) characters.get(chid); if (sprite.getTags().isEmpty()) { - String data = convertActionScript(dia); + String data = convertActionScript12(dia); String expName = dia.getSwf().getExportName(dia.spriteId); expName = expName != null ? expName : "_unk_"; String expPath = expName; @@ -3541,7 +3706,7 @@ public class XFLConverter { if (useAS3 && settings.exportScript) { try { ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false); - swf.exportActionScript(handler, Path.combine(outDir.getAbsolutePath(), "scripts"), scriptExportSettings, parallel, null); + swf.exportActionScript(handler, outDir.getAbsolutePath(), scriptExportSettings, parallel, null); } catch (Exception ex) { logger.log(Level.SEVERE, "Error during ActionScript3 export", ex); }