From e00865ed6f8fccb9064341b8878e30f46135be82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 29 Oct 2023 16:21:40 +0100 Subject: [PATCH] Fixed #1866 FLA export - multiple usage of morphshapes --- CHANGELOG.md | 1 + .../decompiler/flash/xfl/XFLConverter.java | 370 +++++++++++++----- .../morphshape_multiusage.html | 49 +++ .../morphshape_multiusage.swf | Bin 0 -> 996 bytes .../morphshape_multiusage/DOMDocument.xml | 61 +++ .../morphshape_multiusage/LIBRARY/MyShape.xml | 73 ++++ .../META-INF/metadata.xml | 55 +++ .../morphshape_multiusage/MobileSettings.xml | 0 .../morphshape_multiusage/PublishSettings.xml | 206 ++++++++++ .../morphshape_multiusage/bin/SymDepend.cache | Bin 0 -> 43 bytes .../morphshape_multiusage.xfl | 1 + 11 files changed, 724 insertions(+), 92 deletions(-) create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.html create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.swf create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/DOMDocument.xml create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/LIBRARY/MyShape.xml create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/META-INF/metadata.xml create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/MobileSettings.xml create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/PublishSettings.xml create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/bin/SymDepend.cache create mode 100644 libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/morphshape_multiusage.xfl diff --git a/CHANGELOG.md b/CHANGELOG.md index b6c745ee2..3cb8a0490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ All notable changes to this project will be documented in this file. - [#2031] FLA export - morphshapes with duplicated strokes, timelines with multiple shape tweens - [#1866] FLA export - multilevel clipping handling - [#1866] FLA export - morphshape rounding fix +- [#1866] FLA export - multiple usage of morphshapes ### Changed - Basic tag info panel always visible even when nothing to display (to avoid flickering) 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 0f5fd53e6..e57fdb127 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 @@ -169,13 +169,11 @@ import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.Stack; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -231,7 +229,7 @@ public class XFLConverter { /** * Adds "(depht xxx)" to layer name */ - private final boolean DEBUG_EXPORT_LAYER_DEPTHS = false; + private final boolean DEBUG_EXPORT_LAYER_DEPTHS = true; private static String formatEdgeDouble(double value, boolean curved, boolean morphshape) { if (value % 1 == 0) { @@ -1147,7 +1145,62 @@ public class XFLConverter { } } } + + private static void walkMorphShapeUsages(ReadOnlyTagList timeLineTags, Map characters, Map usages) { + Map depthMap = new HashMap<>(); + for (Tag t : timeLineTags) { + if (t instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) t; + walkMorphShapeUsages(sprite.getTags(), characters, usages); + } + if (t instanceof RemoveTag) { + depthMap.remove(((RemoveTag) t).getDepth()); + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int d = po.getDepth(); + if (po.flagMove() || !depthMap.containsKey(d)) { + int ch = po.getCharacterId(); + if (ch == -1) { + if (depthMap.containsKey(d)) { + ch = depthMap.get(d); + } + } else { + depthMap.put(d, ch); + } + if (ch == -1) { + continue; + } + + CharacterTag ct = characters.get(ch); + if (ct instanceof MorphShapeTag) { + if (!usages.containsKey(ch)) { + usages.put(ch, 0); + } + int usageCount = usages.get(ch); + if (po.getRatio() <= 0) { + usageCount++; + usages.put(ch, usageCount); + } + } + } + } + } + } + + private static List getMultiUsageMorphShapes(ReadOnlyTagList tags, HashMap characters) { + List ret = new ArrayList<>(); + Map usages = new TreeMap<>(); + walkMorphShapeUsages(tags, characters, usages); + for (int ch : usages.keySet()) { + if (usages.get(ch) > 1) { + ret.add(ch); + } + } + return ret; + } + private static List getNonLibraryShapes(ReadOnlyTagList tags, HashMap characters) { HashMap usages = new HashMap<>(); walkShapeUsages(tags, characters, usages); @@ -1392,7 +1445,10 @@ public class XFLConverter { if (blendModeStr != null) { writer.writeAttribute("blendMode", blendModeStr); } - if (tag instanceof ShapeTag) { + if (tag instanceof MorphShapeTag) { //multiple usage instance + writer.writeAttribute("symbolType", "graphic"); + writer.writeAttribute("loop", "loop"); + } else if (tag instanceof ShapeTag) { writer.writeAttribute("symbolType", "graphic"); writer.writeAttribute("loop", "loop"); } else if (tag instanceof DefineSpriteTag) { @@ -1566,15 +1622,15 @@ public class XFLConverter { return date.getTime() / 1000; } - 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, Map placeToMaskedSymbol) 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, Map placeToMaskedSymbol, List multiUsageMorphShapes) 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, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, placeToMaskedSymbol); + convertSymbols(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, characters, files, datfiles, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes); } - 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, Map placeToMaskedSymbol) 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, Map placeToMaskedSymbol, List multiUsageMorphShapes) throws XMLStreamException { boolean hasSymbol = false; Reference nextClipId = new Reference<>(-1); writer.writeStartElement("symbols"); @@ -1787,9 +1843,9 @@ public class XFLConverter { } final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite.spriteId) ? characterScriptPacks.get(sprite.spriteId) : null; - extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol); + extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes); - convertTimeline(swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol); + convertTimeline(swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes); } else if (symbol instanceof ShapeTag) { symbolStr.writeStartElement("timeline"); @@ -1827,12 +1883,15 @@ public class XFLConverter { } } - extractMultilevelClips(swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol); + extractMultilevelClips(swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes); + extractMultiUsageMorphShapes(writer, swf, nonLibraryShapes, backgroundColor, characters, flaVersion, files, multiUsageMorphShapes); /*if (hasSymbol) { }*/ writer.writeEndElement(); } + + private void convertMedia(SWF swf, Map characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion, XFLXmlWriter writer) throws XMLStreamException { boolean hasMedia = false; @@ -2297,7 +2356,7 @@ public class XFLConverter { writer.writeEndElement(); // SoundEnvelope } } - + private static void convertFrame(boolean shapeTween, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files, XFLXmlWriter writer) throws XMLStreamException { DefineSoundTag sound = null; if (startSound != null) { @@ -2375,7 +2434,7 @@ public class XFLConverter { writer.writeEndElement(); } - private static void convertFrames(List onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer) throws XMLStreamException { + private static void convertFrames(List onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer, List multiUsageMorphShapes) throws XMLStreamException { boolean lastIn = true; XFLXmlWriter writer2 = new XFLXmlWriter(); prevStr += ""; @@ -2419,6 +2478,8 @@ public class XFLConverter { timTags.add(new ShowFrameTag(swf)); } + MorphShapeTag standaloneShapeTweener = null; + MATRIX standaloneShapeTweenerMatrix = null; for (Tag t : timTags) { if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; @@ -2490,6 +2551,18 @@ public class XFLConverter { } } } + + /*if (t instanceof ShowFrameTag) { + if (t == timTags.get(timTags.size() - 1)) { + if (shapeTween && character != null && (character instanceof MorphShapeTag)) { + MorphShapeTag m = (MorphShapeTag) character; + shapeTweener = m; + shapeTween = false; + lastTweenRatio = ratio; + character = null; + } + } + }*/ if (t instanceof RemoveTag) { RemoveTag rt = (RemoveTag) t; @@ -2515,7 +2588,14 @@ public class XFLConverter { } } + if (t instanceof ShowFrameTag) { + /*if (prevWasShapeTween) { + prevWasShapeTween = false; + continue; + }*/ + + boolean shapeTweenNow = false; if (frame + 1 >= startFrame && (onlyFrames == null || onlyFrames.contains(frame + 1))) { XFLXmlWriter elementsWriter = new XFLXmlWriter(); @@ -2526,53 +2606,70 @@ public class XFLConverter { MorphShapeTag m = shapeTweener; XFLXmlWriter addLastWriter = new XFLXmlWriter(); SHAPEWITHSTYLE endShape = m.getShapeAtRatio(65535); //lastTweenRatio); - convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter); - duration--; + convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter); + //duration--; convertFrame(true, null, null, frame - duration, duration, "", lastElements, files, writer2); duration = 1; lastElements = addLastWriter.toString(); shapeTweener = null; + shapeTweenNow = true; } - if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) { // || shapeTweener != null)) { - ShapeTag shape = (ShapeTag) character; - convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter); - shapeTween = false; - shapeTweener = null; - } else if (character != null) { - if (character instanceof MorphShapeTag) { - MorphShapeTag m = (MorphShapeTag) character; - convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter); - shapeTween = true; - } else { + if (!shapeTweenNow) { + if (character instanceof ShapeTag && standaloneShapeTweener != null) { + convertSymbolInstance(instanceName, standaloneShapeTweenerMatrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, standaloneShapeTweener, characters, tags, flaVersion, elementsWriter); + standaloneShapeTweener = null; + } else if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId))) { // || shapeTweener != null)) { + ShapeTag shape = (ShapeTag) character; + convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false, elementsWriter); + shapeTween = false; - if (character instanceof TextTag) { - convertText(instanceName, (TextTag) character, matrix, filters, clipActions, elementsWriter); - } else if (character instanceof DefineVideoStreamTag) { - convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions, elementsWriter); - } else if (character instanceof ImageTag) { - convertImageInstance(instanceName, matrix, (ImageTag) character, clipActions, elementsWriter); + shapeTweener = null; + } else if (character != null) { + if (character instanceof MorphShapeTag) { + MorphShapeTag m = (MorphShapeTag) character; + if (multiUsageMorphShapes.contains(m.getCharacterId())) { + shapeTween = false; + shapeTweener = null; + standaloneShapeTweener = m; + standaloneShapeTweenerMatrix = matrix; + convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, character, characters, tags, flaVersion, elementsWriter); + } else { + convertShape(characters, matrix, m.getShapeNum() == 1 ? 3 : 4, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false, elementsWriter); + shapeTween = true; + } } else { - convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, character, characters, tags, flaVersion, elementsWriter); + shapeTween = false; + if (character instanceof TextTag) { + convertText(instanceName, (TextTag) character, matrix, filters, clipActions, elementsWriter); + } else if (character instanceof DefineVideoStreamTag) { + convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions, elementsWriter); + } else if (character instanceof ImageTag) { + convertImageInstance(instanceName, matrix, (ImageTag) character, clipActions, elementsWriter); + } else { + convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, metadata, character, characters, tags, flaVersion, elementsWriter); + } } } + } + } + if (!shapeTweenNow) { + frame++; + String elements = elementsWriter.toString(); + if (!elements.equals(lastElements) && frame > 0) { + convertFrame(false, null, null, frame - duration, duration, "", lastElements, files, writer2); + duration = 1; + } else if (frame == 0) { + duration = 1; + } else { + duration++; } - } - frame++; - String elements = elementsWriter.toString(); - if (!elements.equals(lastElements) && frame > 0) { - convertFrame(false, null, null, frame - duration, duration, "", lastElements, files, writer2); - duration = 1; - } else if (frame == 0) { - duration = 1; - } else { - duration++; - } - - lastElements = elements; - if (frame > endFrame) { - if (lastIn) { - lastElements = ""; - lastIn = false; + + lastElements = elements; + if (frame > endFrame) { + if (lastIn) { + lastElements = ""; + lastIn = false; + } } } } else { @@ -3090,7 +3187,8 @@ public class XFLConverter { HashMap characters, FLAVersion flaVersion, HashMap files, - Map placeToMaskedSymbol + Map placeToMaskedSymbol, + List multiUsageMorphShapes ) throws XMLStreamException { XFLXmlWriter symbolStr = new XFLXmlWriter(); @@ -3110,7 +3208,7 @@ public class XFLConverter { "lastModified", Long.toString(getTimestamp(swf))}); symbolStr.writeAttribute("symbolType", "graphic"); - convertTimeline(swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol); + convertTimeline(swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes); symbolStr.writeEndElement(); // DOMSymbolItem @@ -3127,13 +3225,126 @@ public class XFLConverter { } writer.writeEndElement(); - extractMultilevelClips(timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol); + extractMultilevelClips(timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes); } private String generateMaskedSymbolName(int symbolId) { return (DEBUG_EXPORT_LAYER_DEPTHS ? "MaskedSymbol " : "Symbol ") + symbolId; } + + private boolean getMorphshapeTimeline(int morphShapeId, ReadOnlyTagList tags, List outTimelineTags) { + int morphDepth = -2; + boolean onTrack = false; + boolean wasOnTrack = false; + int lastRatio = -1; + for (Tag t : tags) { + if (t instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) t; + if (getMorphshapeTimeline(morphShapeId, sprite.getTags(), outTimelineTags)) { + return true; + } + } + } + for (Tag t : tags) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag place = (PlaceObjectTypeTag) t; + if (morphDepth == place.getDepth() + && place.getCharacterId() != -1 + && place.getCharacterId() != morphShapeId + ) { + outTimelineTags.add(t); + onTrack = false; + } else if ((morphDepth == -2 && place.getCharacterId() == morphShapeId) || morphDepth == place.getDepth()) { + morphDepth = place.getDepth(); + if (onTrack && place.getRatio() < lastRatio) { + onTrack = false; + } else { + onTrack = true; + wasOnTrack = true; + outTimelineTags.add(t); + lastRatio = place.getRatio(); + } + } + } + if (t instanceof RemoveTag) { + RemoveTag rem = (RemoveTag) t; + if (rem.getDepth() == morphDepth) { + onTrack = false; + } + } + if (t instanceof ShowFrameTag) { + if (onTrack) { + outTimelineTags.add(t); + } + if (wasOnTrack && !onTrack) { + outTimelineTags.add(t); + break; + } + } + } + + if (!outTimelineTags.isEmpty()) { + return true; + } + return false; + } + + private void extractMultiUsageMorphShapes( + XFLXmlWriter writer, + SWF swf, + List nonLibraryShapes, + String backgroundColor, + HashMap characters, + FLAVersion flaVersion, + HashMap files, + List multiUsageMorphShapes + ) throws XMLStreamException { + + for (int objectId : multiUsageMorphShapes) { + XFLXmlWriter symbolStr = new XFLXmlWriter(); + symbolStr.writeStartElement("DOMSymbolItem", new String[]{ + "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance", + "xmlns", "http://ns.adobe.com/xfl/2008/", + "name", "Symbol " + objectId, + "lastModified", Long.toString(getTimestamp(swf))}); + symbolStr.writeAttribute("symbolType", "graphic"); + + List timelineTags = new ArrayList<>(); + + getMorphshapeTimeline(objectId, swf.getTags(), timelineTags); + + timelineTags = Helper.deepCopy(timelineTags); + + for (Tag t : timelineTags) { + t.setSwf(swf); + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag place = (PlaceObjectTypeTag) t; + if (place.getMatrix() != null) { + place.setMatrix(new MATRIX()); + } + } + } + + convertTimeline(swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>()); + + + symbolStr.writeEndElement(); // DOMSymbolItem + String symbolStr2 = prettyFormatXML(symbolStr.toString()); + String symbolFile = "Symbol " + objectId + ".xml"; + files.put(symbolFile, Utf8Helper.getBytes(symbolStr2)); + + writer.writeStartElement("Include", new String[]{"href", symbolFile}); + writer.writeAttribute("itemIcon", "1"); + writer.writeAttribute("loadImmediate", false); + if (flaVersion.ordinal() >= FLAVersion.CS5_5.ordinal()) { + writer.writeAttribute("lastModified", getTimestamp(swf)); + //TODO: itemID="518de416-00000341" + } + writer.writeEndElement(); + } + } + private void extractMultilevelClips(ReadOnlyTagList timelineTags, XFLXmlWriter writer, SWF swf, @@ -3143,7 +3354,8 @@ public class XFLConverter { HashMap characters, FLAVersion flaVersion, HashMap files, - Map placeToMaskedSymbol + Map placeToMaskedSymbol, + List multiUsageMorphShapes ) throws XMLStreamException { int f = 0; @@ -3252,9 +3464,6 @@ public class XFLConverter { lastTag = null; Map depthStates = new HashMap<>(); - if (nextClipId.getVal() == 134) { - System.err.println("xxx"); - } for (Tag t : timelineTags) { if (f < fr) { if (t instanceof PlaceObjectTypeTag) { @@ -3412,14 +3621,14 @@ public class XFLConverter { } } System.err.println("clip repeats in " + found);*/ - addExtractedClip(new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol); + addExtractedClip(new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes); placeToMaskedSymbol.put(secondPlace, new MultiLevelClip(secondPlace, nextClipId.getVal(), numFrames)); } } } } - private void convertTimeline(AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer, ScriptPack scriptPack, Map placeToMaskedSymbol) throws XMLStreamException { + private void convertTimeline(AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer, ScriptPack scriptPack, Map placeToMaskedSymbol, List multiUsageMorphShapes) throws XMLStreamException { List classNames = new ArrayList<>(); //Searches for Object.registerClass("linkageIdentifier",mypkg.MyClass); @@ -3601,9 +3810,6 @@ public class XFLConverter { Map>> frameToDepthToClips = new TreeMap<>(); - /*if (spriteId == 116) { - System.err.println("xxx"); - }*/ for (f = 0; f < frameCount; f++) { for (int d = 0; d < maxDepth; d++) { for (int p = 0; p < clipPlaces.size() - 1; p++) { @@ -3694,13 +3900,12 @@ public class XFLConverter { "color", randomOutlineColor(), "layerType", "mask", "locked", "true"}); - convertFrames(depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer); + convertFrames(depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes); writer.writeEndElement(); int parentIndex = index; index++; - //Set processedClips = new HashSet<>(); for (int fx = clipFrame; fx <= lastFrame; fx++) { for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) { if (!depthToFramesList.containsKey(nd) || !depthToFramesList.get(nd).contains(fx)) { @@ -3746,9 +3951,6 @@ public class XFLConverter { - if (mlc.symbol == 135) { - System.err.println("xxx"); - } writer.writeStartElement("DOMFrame", new String[]{ "index", "" + clipFrame2, "duration", "" + mlc.numFrames, @@ -3787,10 +3989,10 @@ public class XFLConverter { } } } - } + } for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) { - boolean nonEmpty = writeLayer(index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files); + boolean nonEmpty = writeLayer(index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes); for (int i = clipFrame; i <= lastFrame; i++) { depthToFramesList.get(nd).remove((Integer) i); } @@ -3804,22 +4006,7 @@ public class XFLConverter { } } - /*boolean hasClipDepth = false; - for (Tag t : timelineTags) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getDepth() == d) { - if (po.getClipDepth() != -1) { - hasClipDepth = true; - break; - } - } - } - } - if (hasClipDepth) { - continue; - }*/ - boolean nonEmpty = writeLayer(index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files); + boolean nonEmpty = writeLayer(index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes); if (nonEmpty) { index++; } @@ -3857,7 +4044,7 @@ public class XFLConverter { writer.writeEndElement(); //DOMLayer } - private boolean writeLayer(int index, List onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, FLAVersion flaVersion, HashMap files) throws XMLStreamException { + private boolean writeLayer(int index, List onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, FLAVersion flaVersion, HashMap files, List multiUsageMorphShapes) throws XMLStreamException { XFLXmlWriter layerPrev = new XFLXmlWriter(); layerPrev.writeStartElement("DOMLayer", new String[]{ "name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + d + ")" : ""), @@ -3868,15 +4055,13 @@ public class XFLConverter { layerPrev.writeAttribute("isSelected", true); } if (parentLayer != -1) { - if (parentLayer != d) { - layerPrev.writeAttribute("parentLayerIndex", parentLayer); - layerPrev.writeAttribute("locked", true); - } + layerPrev.writeAttribute("parentLayerIndex", parentLayer); + layerPrev.writeAttribute("locked", true); } layerPrev.writeCharacters(""); // todo honfika: hack to close start tag String layerAfter = ""; int prevLength = writer.length(); - convertFrames(onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer); + convertFrames(onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes); return writer.length() != prevLength; } @@ -4363,6 +4548,7 @@ public class XFLConverter { final HashMap files = new HashMap<>(); final HashMap datfiles = new HashMap<>(); HashMap characters = getCharacters(swf.getTags()); + List multiUsageMorphShapes = getMultiUsageMorphShapes(swf.getTags(), characters); List nonLibraryShapes = getNonLibraryShapes(swf.getTags(), characters); Map characterClasses = getCharacterClasses(swf.getTags()); Map characterScriptPacks = getCharacterScriptPacks(swf, characterClasses); @@ -4410,11 +4596,11 @@ public class XFLConverter { Map placeToMaskedSymbol = new HashMap<>(); convertFonts(characterClasses, swf.getTags(), domDocument); - convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument, placeToMaskedSymbol); + convertLibrary(swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), characters, files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes); //domDocument.writeStartElement("timelines"); ScriptPack documentScriptPack = characterScriptPacks.containsKey(0) ? characterScriptPacks.get(0) : null; - convertTimeline(swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol); + convertTimeline(swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes); //domDocument.writeEndElement(); if (hasAmfMetadata) { diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.html b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.html new file mode 100644 index 000000000..76ae6029b --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.html @@ -0,0 +1,49 @@ + + + + morphshape_multiusage + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + Get Adobe Flash player + + + + + +
+ + diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.swf b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage.swf new file mode 100644 index 0000000000000000000000000000000000000000..fd83bd16ad745d972ddba72c925a0b1c29aec79a GIT binary patch literal 996 zcmV?s|7=QhXZXl*u-Q`oe%^MH$5h7(UXCMLuCmfm&bEygy+(DDoEVztYe8$^bOG@?ekEihh|1eKTG$q z6y+g%S(2>tR+JTMTr@a=Gq_FW0891NJ2wbzuOdjvnR`3eWq6RdE5rLsRp4u(s#z^v zJ}H4G#I%?ZWkW~7QsrO;o=qxRhBHE(s-`8Lvw%e2qo)-9)f_i1dXiSiL1JC_pe>=D z#pBx_btXep)w+=oliREH(HT`F5~8kTHC{yWPJ2}1adueN4Nm68;b;im@Jf=v4o2u` zq$k|lGuRs#8lou1NBJ0v?DhF$fiTJV%GqzUR%ua%&sw?JG~r*+>a11TK~0*J(6DYn zjSZ1uIvnnyquW!rK-78_HECKDVw#dEpMuJ1x@aZU@2*B=#Z=Chl{Uw8?-n*$t2E(3 zHnhoFr78awS!sDY3q?D?8jdShls;8&1af?=+TJ5)bJmLkLFK}H$b6KFu?@t z1Ez_!gZ2NY>r!Fi#-Ahlxyy6MS8k1z;N8YX4EbOH96K>6wwypR2c%#(4m+-ZI(MuK z`*3oprwlLC&>MrgS6laQ3GY$u=9|W6-#j-vKIb}rHPNwWXZ(V3?OGFn^Vs#9?MIqd z-0lFt+`zqt34 z-6xiuuU}Y#_pJsU&~fD37v=}xXk!}&T8CY%^x4JOm|ZMS*#&dbF24N2E;^R&;^eRP zW+9IHZ>Vk3QO?Tx!CgOeJ#;<+?SU4?U;29XAUOTEVf;JgK+DCFV=)2USMOY0J@t}& z7M$I!u>bJv@Er!nB9&$vs?8SRam3pJ>glH9dutPD0SlfqO|-!G#gETUA6PiII(Pi} zTZ1LkSp$ZomCpXZJ#TFAeip4H)rmIZgqcgL%b%__pDE9R39YW*KN!0GTH>!8DeuqW S>BrV8)8w?K3E*$@nqYN#gZn)I literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/DOMDocument.xml b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/DOMDocument.xml new file mode 100644 index 000000000..80ac50dae --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/DOMDocument.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/LIBRARY/MyShape.xml b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/LIBRARY/MyShape.xml new file mode 100644 index 000000000..2c066c9d2 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/LIBRARY/MyShape.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/META-INF/metadata.xml new file mode 100644 index 000000000..4b998bfe2 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/META-INF/metadata.xml @@ -0,0 +1,55 @@ + + + + + Adobe Flash Professional CS6 - build 481 + 2023-10-29T04:28:11-07:00 + 2023-10-29T04:28:43-07:00 + 2023-10-29T04:28:43-07:00 + + + application/vnd.adobe.fla + + + xmp.iid:FE0A2AA52D76EE119313910734F6A093 + xmp.did:FE0A2AA52D76EE119313910734F6A093 + xmp.did:FE0A2AA52D76EE119313910734F6A093 + + + + created + xmp.iid:FE0A2AA52D76EE119313910734F6A093 + 2023-10-29T04:28:11-07:00 + Adobe Flash Professional CS6 - build 481 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/MobileSettings.xml b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/MobileSettings.xml new file mode 100644 index 000000000..e69de29bb diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/PublishSettings.xml b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/PublishSettings.xml new file mode 100644 index 000000000..096fa7648 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/PublishSettings.xml @@ -0,0 +1,206 @@ + + + + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + morphshape_multiusage.swf + morphshape_multiusage.exe + morphshape_multiusage.app + morphshape_multiusage.html + morphshape_multiusage.gif + morphshape_multiusage.jpg + morphshape_multiusage.png + morphshape_multiusage.mov + morphshape_multiusage.smil + morphshape_multiusage.swc + + + 0 + 12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1; + 1 + 1 + morphshape_multiusage.xfl_content.html + morphshape_multiusage.xfl_alternate.html + 0 + + 550 + 400 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 4 + 0 + 0 + 1 + 0 + C:\Users\MyUser\AppData\Local\Adobe\Flash CS6\en_US\Configuration\HTML\Default.html + 1 + + + + + 0 + 0 + 0 + 80 + 0 + 0 + 7 + 0 + 7 + 0 + 15 + FlashPlayer11.2 + 2 + 1 + + . + CONFIG::FLASH_AUTHORING="true"; + 0 + + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + 2 + 4 + 4096 + AS3 + 1 + 1 + 0 + 15 + 1 + 0 + 4102 + rsl + wrap + $(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf + + + $(AppConfig)/ActionScript 3.0/libs + merge + + + $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc + rsl + http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz + http://fpdownload.adobe.com/pub/swz/crossdomain.xml + textLayout_2.0.0.232.swz + + + + + $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc + + http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz + http://fpdownload.adobe.com/pub/swz/crossdomain.xml + textLayout_2.0.0.232.swz + + + + + 550 + 400 + 0 + 4718592 + 0 + 80 + 1 + + + 1 + 0 + 1 + 0 + 0 + 100000 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + + 550 + 400 + 0 + 1 + 1 + + 1 + 0 + 1 + 0 + 0 + + 128 + + + 255 + + + + 550 + 400 + 1 + 0 + 0 + 1 + 0 + 0 + 1 + + + + 24-bit with Alpha + 255 + + + + 550 + 400 + 1 + 0 + + + 00000000 + 0 + 0 + 0 + 0 + 1 + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/bin/SymDepend.cache new file mode 100644 index 0000000000000000000000000000000000000000..59545be119272d309a7ef5e3fddb311090077fdc GIT binary patch literal 43 rcmYdiU|@L8&IKf88Q2;47z}|dMj+;7@U0BaNGwQYsMxm_$Oi%dci#qQ literal 0 HcmV?d00001 diff --git a/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/morphshape_multiusage.xfl b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/morphshape_multiusage.xfl new file mode 100644 index 000000000..860a820ec --- /dev/null +++ b/libsrc/ffdec_lib/testdata/morphshape_multiusage/morphshape_multiusage/morphshape_multiusage.xfl @@ -0,0 +1 @@ +PROXY-CS5 \ No newline at end of file