From d5ea4fc8f9f79095bc852d14a6ea68a692f265ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sat, 2 Dec 2023 16:07:48 +0100 Subject: [PATCH] Added #2134 FLA Export - split main timeline into scenes when DefineSceneAndFrameLabelData tag is present --- CHANGELOG.md | 2 + .../flash/tags/ExportAssetsTag.java | 2 +- .../decompiler/flash/xfl/XFLConverter.java | 652 ++++++++++-------- libsrc/ffdec_lib/testdata/scenes/scenes.swf | Bin 2482 -> 2701 bytes .../testdata/scenes/scenes/DOMDocument.xml | 32 +- .../scenes/scenes/META-INF/metadata.xml | 18 +- .../scenes/scenes/bin/SymDepend.cache | Bin 54 -> 54 bytes 7 files changed, 420 insertions(+), 286 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3238dfd2..25daf13c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. - Remembering breakpoints - [#2131] Breakpoint list dialog - ExportAssets tag - show first item as description in the tree when there is only single item +- [#2134] FLA Export - split main timeline into scenes when DefineSceneAndFrameLabelData tag is present ### Fixed - [#2021], [#2000] Caret position in editors when using tabs and / or unicode @@ -3322,6 +3323,7 @@ Major version of SWF to XML export changed to 2. [#2129]: https://www.free-decompiler.com/flash/issues/2129 [#2131]: https://www.free-decompiler.com/flash/issues/2131 [#2124]: https://www.free-decompiler.com/flash/issues/2124 +[#2134]: https://www.free-decompiler.com/flash/issues/2134 [#2021]: https://www.free-decompiler.com/flash/issues/2021 [#2000]: https://www.free-decompiler.com/flash/issues/2000 [#2116]: https://www.free-decompiler.com/flash/issues/2116 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/ExportAssetsTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/ExportAssetsTag.java index 00f03ca38..2b7e6d40e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/ExportAssetsTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/ExportAssetsTag.java @@ -158,7 +158,7 @@ public class ExportAssetsTag extends SymbolClassTypeTag { @Override public String toString() { if (names.size() == 1) { - return super.toString() + " (" + tags.get(0) + ": " + names.get(0)+")"; + return super.toString() + " (" + tags.get(0) + ": " + names.get(0) + ")"; } return super.toString(); } 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 6fd0f15ee..5fc0374bf 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 @@ -69,6 +69,7 @@ import com.jpexs.decompiler.flash.tags.DefineButtonTag; import com.jpexs.decompiler.flash.tags.DefineEditTextTag; import com.jpexs.decompiler.flash.tags.DefineFontNameTag; import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; +import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag; import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineText2Tag; @@ -172,6 +173,7 @@ 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; @@ -1895,7 +1897,7 @@ public class XFLConverter { extractMultilevelClips(sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, characters, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); - convertTimeline(swf, swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); + convertTimelines(swf, swf.getAbcIndex(), sprite.spriteId, characterVariables.get(sprite.spriteId), nonLibraryShapes, backgroundColor, tags, sprite.getTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); } else if (symbol instanceof ShapeTag) { symbolStr.writeStartElement("timeline"); @@ -2998,7 +3000,7 @@ public class XFLConverter { return ret; } - private boolean convertActionScriptLayer(String initClipScript, AbcIndexing abcIndex, int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor, XFLXmlWriter writer, ScriptPack scriptPack) throws XMLStreamException { + private boolean convertActionScriptLayer(Scene scene, String initClipScript, AbcIndexing abcIndex, int spriteId, ReadOnlyTagList tags, ReadOnlyTagList timeLineTags, String backgroundColor, XFLXmlWriter writer, ScriptPack scriptPack) throws XMLStreamException { boolean hasScript = false; String script = initClipScript; @@ -3019,9 +3021,9 @@ public class XFLConverter { DoActionTag da = (DoActionTag) t; script += convertActionScript12(da); } - if (frameToScriptMap.containsKey(frame)) { - script += frameToScriptMap.get(frame); - frameToScriptMap.remove(frame); + if (frameToScriptMap.containsKey(scene.startFrame + frame)) { + script += frameToScriptMap.get(scene.startFrame + frame); + frameToScriptMap.remove(scene.startFrame + frame); } if (t instanceof ShowFrameTag) { @@ -3108,7 +3110,7 @@ public class XFLConverter { duration++; } else { if (duration > 0) { - writer.writeStartElement("DOMFrame", new String[]{"index", Integer.toString(frame - duration)}); + writer.writeStartElement("DOMFrame", new String[]{"index", Integer.toString(i - duration)}); if (duration > 1) { writer.writeAttribute("duration", duration); } @@ -3116,7 +3118,7 @@ public class XFLConverter { writer.writeElementValue("elements", ""); writer.writeEndElement(); } - writer.writeStartElement("DOMFrame", new String[]{"index", Integer.toString(frame)}); + writer.writeStartElement("DOMFrame", new String[]{"index", Integer.toString(i)}); writer.writeAttribute("keyMode", KEY_MODE_NORMAL); writer.writeAttribute("name", frameLabel.name); if (frameLabel.namedAnchor) { @@ -3287,7 +3289,7 @@ public class XFLConverter { "lastModified", Long.toString(getTimestamp(swf))}); symbolStr.writeAttribute("symbolType", "graphic"); - convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); + convertTimelines(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, timelineTags, timelineTags, characters, generateMaskedSymbolName(objectId), flaVersion, files, symbolStr, null, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); symbolStr.writeEndElement(); // DOMSymbolItem String symbolStr2 = prettyFormatXML(symbolStr.toString()); @@ -3405,7 +3407,7 @@ public class XFLConverter { } } - convertTimeline(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>(), statusStack); + convertTimelines(swf, swf.getAbcIndex(), objectId, "", nonLibraryShapes, backgroundColor, swf.getTags(), new ReadOnlyTagList(timelineTags), characters, "Symbol " + objectId, flaVersion, files, symbolStr, null, new HashMap<>(), new ArrayList<>(), statusStack); symbolStr.writeEndElement(); // DOMSymbolItem String symbolStr2 = prettyFormatXML(symbolStr.toString()); @@ -3707,8 +3709,21 @@ public class XFLConverter { } } } + + private class Scene { + public int startFrame; + public int endFrame; + public String name; + public ReadOnlyTagList timelineSubTags; - private void convertTimeline(SWF swf, 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, StatusStack statusStack) throws XMLStreamException { + public Scene(int startFrame, int endFrame, String name) { + this.startFrame = startFrame; + this.endFrame = endFrame; + this.name = name; + } + } + + private void convertTimelines(SWF swf, AbcIndexing abcIndex, int spriteId, String linkageIdentifier, List nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap characters, String spriteName, FLAVersion flaVersion, HashMap files, XFLXmlWriter writer, ScriptPack scriptPack, Map placeToMaskedSymbol, List multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException { List classNames = new ArrayList<>(); //Searches for Object.registerClass("linkageIdentifier",mypkg.MyClass); @@ -3783,318 +3798,403 @@ public class XFLConverter { } else { writer.writeStartElement("timeline"); } - writer.writeStartElement("DOMTimeline", new String[]{"name", name}); - writer.writeStartElement("layers"); - - boolean hasLabel = convertLabelsLayer(tags, timelineTags, backgroundColor, writer); - boolean hasScript = convertActionScriptLayer(initClipScript, abcIndex, spriteId, tags, timelineTags, backgroundColor, writer, scriptPack); - - int index = 0; - - if (hasLabel) { - index++; - } - - if (hasScript) { - index++; - } - - int maxDepth = getMaxDepth(timelineTags); - - List clipFrameSplitters = new ArrayList<>(); - List clipPlaces = new ArrayList<>(); - int f = 0; - - Map depthToClipPlace = new HashMap<>(); - Map clipFinishFrames = new HashMap<>(); - Map clipStartFrames = new HashMap<>(); - Map placeToFirstCharacterDepth = new HashMap<>(); - Tag lastTag = null; - int tpos = 0; + + List scenes = new ArrayList<>(); + + DefineSceneAndFrameLabelDataTag sceneLabelTag = null; + int fc = 0; + boolean lastShowFrame = true; for (Tag t : timelineTags) { + if (t instanceof DefineSceneAndFrameLabelDataTag) { + sceneLabelTag = (DefineSceneAndFrameLabelDataTag) t; + } + if (t instanceof ShowFrameTag) { + lastShowFrame = true; + fc++; + } else { + lastShowFrame = false; + } + } + if (!lastShowFrame) { + fc++; + } + if (spriteId == -1) { + if (sceneLabelTag != null) { + for (int i = 0; i < sceneLabelTag.sceneOffsets.length; i++) { + scenes.add(new Scene( + (int) sceneLabelTag.sceneOffsets[i], + sceneLabelTag.sceneOffsets.length - 1 == i + ? fc - 1 : (int) sceneLabelTag.sceneOffsets[i + 1] - 1, + sceneLabelTag.sceneNames[i])); + } + + if (!scenes.isEmpty()) { + int sceneId = 0; + int f = 0; + List sceneTags = new ArrayList<>(); + + //TODO: The sound stream heads needs better handling, like splitting them into two or something... + + SoundStreamHeadTypeTag soundStreamHead = null; + SoundStreamHeadTypeTag soundStreamHeadThisScene = null; + for (Tag t : timelineTags) { + sceneTags.add(t); + if (t instanceof SoundStreamHeadTypeTag) { + soundStreamHeadThisScene = (SoundStreamHeadTypeTag) t; + soundStreamHead = soundStreamHeadThisScene; + } + if (t instanceof ShowFrameTag) { + f++; + if (f > scenes.get(sceneId).endFrame) { + if (soundStreamHead != null && soundStreamHeadThisScene == null) { + sceneTags.add(0, soundStreamHead); + } + scenes.get(sceneId).timelineSubTags = new ReadOnlyTagList(sceneTags); + sceneTags = new ArrayList<>(); + sceneId++; + soundStreamHeadThisScene = null; + } + } + } + if (!sceneTags.isEmpty()) { + if (soundStreamHead != null && soundStreamHeadThisScene == null) { + sceneTags.add(0, soundStreamHead); + } + scenes.get(sceneId).timelineSubTags = new ReadOnlyTagList(sceneTags); + } + } + + } + + if (scenes.isEmpty()) { + Scene scene = new Scene(0, fc - 1, "Scene 1"); + scene.timelineSubTags = timelineTags; + scenes.add(scene); + } + } else { + Scene scene = new Scene(0, fc - 1, spriteName); + scene.timelineSubTags = timelineTags; + scenes.add(scene); + } + + + for (Scene scene : scenes) { + writer.writeStartElement("DOMTimeline", new String[]{"name", scene.name}); + writer.writeStartElement("layers"); + + ReadOnlyTagList sceneTimelineTags = scene.timelineSubTags; + + boolean hasLabel = convertLabelsLayer(tags, sceneTimelineTags, backgroundColor, writer); + boolean hasScript = convertActionScriptLayer(scene, initClipScript, abcIndex, spriteId, tags, sceneTimelineTags, backgroundColor, writer, scriptPack); + + int index = 0; + + if (hasLabel) { + index++; + } + + if (hasScript) { + index++; + } + + int maxDepth = getMaxDepth(sceneTimelineTags); + + List clipFrameSplitters = new ArrayList<>(); + List clipPlaces = new ArrayList<>(); + int f = 0; + + Map depthToClipPlace = new HashMap<>(); + Map clipFinishFrames = new HashMap<>(); + Map clipStartFrames = new HashMap<>(); + Map placeToFirstCharacterDepth = new HashMap<>(); + Tag lastTag = null; + int tpos = 0; + for (Tag t : sceneTimelineTags) { + if (t instanceof ShowFrameTag) { + f++; + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + if (po.getClipDepth() > -1) { + clipFrameSplitters.add(f); + clipStartFrames.put(po, f); + clipPlaces.add(po); + + if (depthToClipPlace.containsKey(po.getDepth())) { + clipFinishFrames.put(depthToClipPlace.get(po.getDepth()), f - 1); + } + + depthToClipPlace.put(po.getDepth(), po); + for (int j = tpos + 1; j <= sceneTimelineTags.size(); j++) { + Tag t2 = sceneTimelineTags.get(j); + if (t2 instanceof PlaceObject2Tag) { + PlaceObject2Tag pl = (PlaceObject2Tag) t2; + int d = pl.getDepth(); + if (d >= po.getDepth() && d <= po.getClipDepth()) { + placeToFirstCharacterDepth.put(po, d); + } + } + if (t2 instanceof ShowFrameTag) { + break; + } + + } + } else { + if (!po.flagMove() && depthToClipPlace.containsKey(po.getDepth())) { + clipFinishFrames.put(depthToClipPlace.get(po.getDepth()), f - 1); + depthToClipPlace.remove(po.getDepth()); + } + } + } + if (t instanceof RemoveTag) { + RemoveTag re = (RemoveTag) t; + if (depthToClipPlace.containsKey(re.getDepth())) { + clipFinishFrames.put(depthToClipPlace.get(re.getDepth()), f - 1); + depthToClipPlace.remove(re.getDepth()); + } + } + lastTag = t; + tpos++; + } + + //Some sprites do not end with ShowFrame: + if (lastTag != null && !(lastTag instanceof ShowFrameTag)) { f++; } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getClipDepth() > -1) { - clipFrameSplitters.add(f); - clipStartFrames.put(po, f); - clipPlaces.add(po); - if (depthToClipPlace.containsKey(po.getDepth())) { - clipFinishFrames.put(depthToClipPlace.get(po.getDepth()), f - 1); - } + int frameCount = f; - depthToClipPlace.put(po.getDepth(), po); - for (int j = tpos + 1; j <= timelineTags.size(); j++) { - Tag t2 = timelineTags.get(j); - if (t2 instanceof PlaceObject2Tag) { - PlaceObject2Tag pl = (PlaceObject2Tag) t2; - int d = pl.getDepth(); - if (d >= po.getDepth() && d <= po.getClipDepth()) { - placeToFirstCharacterDepth.put(po, d); - } - } - if (t2 instanceof ShowFrameTag) { - break; - } - - } - } else { - if (!po.flagMove() && depthToClipPlace.containsKey(po.getDepth())) { - clipFinishFrames.put(depthToClipPlace.get(po.getDepth()), f - 1); - depthToClipPlace.remove(po.getDepth()); - } + if (!depthToClipPlace.isEmpty()) { + for (PlaceObjectTypeTag po : depthToClipPlace.values()) { + clipFinishFrames.put(po, frameCount - 1); } } - if (t instanceof RemoveTag) { - RemoveTag re = (RemoveTag) t; - if (depthToClipPlace.containsKey(re.getDepth())) { - clipFinishFrames.put(depthToClipPlace.get(re.getDepth()), f - 1); - depthToClipPlace.remove(re.getDepth()); + + if (clipFrameSplitters.isEmpty() || clipFrameSplitters.get(0) != 0) { + clipFrameSplitters.add(0, 0); + clipPlaces.add(0, null); + } + + clipFrameSplitters.add(frameCount); + clipPlaces.add(null); + + Map> depthToFramesList = new HashMap<>(); + for (int d = maxDepth; d >= 0; d--) { + depthToFramesList.put(d, new ArrayList<>()); + for (int i = 0; i < frameCount; i++) { + depthToFramesList.get(d).add(i); } } - lastTag = t; - tpos++; - } - //Some sprites do not end with ShowFrame: - if (lastTag != null && !(lastTag instanceof ShowFrameTag)) { - f++; - } + Map>> frameToDepthToClips = new TreeMap<>(); - int frameCount = f; - - if (!depthToClipPlace.isEmpty()) { - for (PlaceObjectTypeTag po : depthToClipPlace.values()) { - clipFinishFrames.put(po, frameCount - 1); - } - } - - if (clipFrameSplitters.isEmpty() || clipFrameSplitters.get(0) != 0) { - clipFrameSplitters.add(0, 0); - clipPlaces.add(0, null); - } - - clipFrameSplitters.add(frameCount); - clipPlaces.add(null); - - Map> depthToFramesList = new HashMap<>(); - for (int d = maxDepth; d >= 0; d--) { - depthToFramesList.put(d, new ArrayList<>()); - for (int i = 0; i < frameCount; i++) { - depthToFramesList.get(d).add(i); - } - } - - Map>> frameToDepthToClips = new TreeMap<>(); - - for (f = 0; f < frameCount; f++) { - for (int d = 0; d < maxDepth; d++) { - for (int p = 0; p < clipPlaces.size() - 1; p++) { - PlaceObjectTypeTag po = clipPlaces.get(p); - if (po == null) { - continue; - } - int startFrame = clipStartFrames.get(po); - int finishFrame = clipFinishFrames.get(po); - if (f >= startFrame && f <= finishFrame) { - if (d >= po.getDepth() && d <= po.getClipDepth()) { - if (!frameToDepthToClips.containsKey(f)) { - frameToDepthToClips.put(f, new TreeMap<>()); - } - if (!frameToDepthToClips.get(f).containsKey(d)) { - frameToDepthToClips.get(f).put(d, new ArrayList<>()); - } - frameToDepthToClips.get(f).get(d).add(po); - } - } - } - } - } - - Set multiLevelsPlaces = new HashSet<>(); - - Set secondLevelPlaces = new HashSet<>(); - Map secondToFirstLevelPlace = new HashMap<>(); - for (int fr : frameToDepthToClips.keySet()) { - for (int d : frameToDepthToClips.get(fr).keySet()) { - List places = frameToDepthToClips.get(fr).get(d); - if (places.size() > 1) { - //depthToFramesList.get(d).remove((Integer) fr); - for (int i = 1; i < places.size(); i++) { - multiLevelsPlaces.add(places.get(i)); - } - secondLevelPlaces.add(places.get(1)); - secondToFirstLevelPlace.put(places.get(1), places.get(0)); - } - } - } - - Set handledClips = new HashSet<>(); - for (int d = maxDepth; d >= 0; d--) { - loopp: - for (int p = 0; p < clipPlaces.size() - 1; p++) { - PlaceObjectTypeTag po = clipPlaces.get(p); - /* if (po != null && po.getClipDepth() == d && secondLevelPlaces.contains(po)) { - int clipFrame = clipFrameSplitters.get(p); - int nextFrame = clipFinishFrames.get(po); - - - continue; - }*/ - if (po != null && multiLevelsPlaces.contains(po)) { - continue; - } - if (po != null && handledClips.contains(po)) { - continue; - } - if (po != null && po.getClipDepth() == d) { - int clipFrame = clipFrameSplitters.get(p); - int nextFrame = clipFinishFrames.get(po); - handledClips.add(po); - - int lastFrame = nextFrame; - for (int p2 = 0; p2 < clipPlaces.size() - 1; p2++) { - PlaceObjectTypeTag po2 = clipPlaces.get(p2); - if (po2 == null) { + for (f = 0; f < frameCount; f++) { + for (int d = 0; d < maxDepth; d++) { + for (int p = 0; p < clipPlaces.size() - 1; p++) { + PlaceObjectTypeTag po = clipPlaces.get(p); + if (po == null) { continue; } - int clipFrame2 = clipFrameSplitters.get(p2); - int nextFrame2 = clipFinishFrames.get(po2); - if (lastFrame + 1 == clipFrame2 - && po.getDepth() == po2.getDepth() - && po.getClipDepth() == po2.getClipDepth() - && !multiLevelsPlaces.contains(po2)) { - lastFrame = nextFrame2; - handledClips.add(po2); + int startFrame = clipStartFrames.get(po); + int finishFrame = clipFinishFrames.get(po); + if (f >= startFrame && f <= finishFrame) { + if (d >= po.getDepth() && d <= po.getClipDepth()) { + if (!frameToDepthToClips.containsKey(f)) { + frameToDepthToClips.put(f, new TreeMap<>()); + } + if (!frameToDepthToClips.get(f).containsKey(d)) { + frameToDepthToClips.get(f).put(d, new ArrayList<>()); + } + frameToDepthToClips.get(f).get(d).add(po); + } } } + } + } - writer.writeStartElement("DOMLayer", new String[]{ - "name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + po.getDepth() + " clipdepth:" + po.getClipDepth() + ")" : ""), - "color", randomOutlineColor(), - "layerType", "mask", - "locked", "true"}); - convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, timelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes, statusStack); - writer.writeEndElement(); + Set multiLevelsPlaces = new HashSet<>(); - int parentIndex = index; - index++; + Set secondLevelPlaces = new HashSet<>(); + Map secondToFirstLevelPlace = new HashMap<>(); + for (int fr : frameToDepthToClips.keySet()) { + for (int d : frameToDepthToClips.get(fr).keySet()) { + List places = frameToDepthToClips.get(fr).get(d); + if (places.size() > 1) { + //depthToFramesList.get(d).remove((Integer) fr); + for (int i = 1; i < places.size(); i++) { + multiLevelsPlaces.add(places.get(i)); + } + secondLevelPlaces.add(places.get(1)); + secondToFirstLevelPlace.put(places.get(1), places.get(0)); + } + } + } - 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)) { + Set handledClips = new HashSet<>(); + for (int d = maxDepth; d >= 0; d--) { + loopp: + for (int p = 0; p < clipPlaces.size() - 1; p++) { + PlaceObjectTypeTag po = clipPlaces.get(p); + /* if (po != null && po.getClipDepth() == d && secondLevelPlaces.contains(po)) { + int clipFrame = clipFrameSplitters.get(p); + int nextFrame = clipFinishFrames.get(po); + + + continue; + }*/ + if (po != null && multiLevelsPlaces.contains(po)) { + continue; + } + if (po != null && handledClips.contains(po)) { + continue; + } + if (po != null && po.getClipDepth() == d) { + int clipFrame = clipFrameSplitters.get(p); + int nextFrame = clipFinishFrames.get(po); + handledClips.add(po); + + int lastFrame = nextFrame; + for (int p2 = 0; p2 < clipPlaces.size() - 1; p2++) { + PlaceObjectTypeTag po2 = clipPlaces.get(p2); + if (po2 == null) { continue; } - if (frameToDepthToClips.containsKey(fx) - && frameToDepthToClips.get(fx).containsKey(nd)) { - List clips = frameToDepthToClips.get(fx).get(nd); - if (clips.size() > 1) { - PlaceObjectTypeTag po2 = clips.get(1); - if (handledClips.contains(po2)) { - continue; - } - handledClips.add(po2); + int clipFrame2 = clipFrameSplitters.get(p2); + int nextFrame2 = clipFinishFrames.get(po2); + if (lastFrame + 1 == clipFrame2 + && po.getDepth() == po2.getDepth() + && po.getClipDepth() == po2.getClipDepth() + && !multiLevelsPlaces.contains(po2)) { + lastFrame = nextFrame2; + handledClips.add(po2); + } + } - MultiLevelClip mlc = placeToMaskedSymbol.get(po2); + writer.writeStartElement("DOMLayer", new String[]{ + "name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + po.getDepth() + " clipdepth:" + po.getClipDepth() + ")" : ""), + "color", randomOutlineColor(), + "layerType", "mask", + "locked", "true"}); + convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, sceneTimelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes, statusStack); + writer.writeEndElement(); - writer.writeStartElement("DOMLayer", new String[]{ - "name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + po2.getDepth() + " clipdepth:" + po2.getClipDepth() + " maskedid:" + mlc.symbol + ")" : ""), - "color", randomOutlineColor(), - "parentLayerIndex", "" + parentIndex, - "locked", "true" - }); - writer.writeStartElement("frames"); + int parentIndex = index; + index++; - int clipFrame2 = 0; - for (int p2 = 0; p2 < clipPlaces.size() - 1; p2++) { - if (clipPlaces.get(p2) == po2) { - clipFrame2 = clipFrameSplitters.get(p2); + 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)) { + continue; + } + if (frameToDepthToClips.containsKey(fx) + && frameToDepthToClips.get(fx).containsKey(nd)) { + List clips = frameToDepthToClips.get(fx).get(nd); + if (clips.size() > 1) { + PlaceObjectTypeTag po2 = clips.get(1); + if (handledClips.contains(po2)) { + continue; + } + handledClips.add(po2); + + MultiLevelClip mlc = placeToMaskedSymbol.get(po2); + + writer.writeStartElement("DOMLayer", new String[]{ + "name", "Layer " + (index + 1) + (DEBUG_EXPORT_LAYER_DEPTHS ? " (depth " + po2.getDepth() + " clipdepth:" + po2.getClipDepth() + " maskedid:" + mlc.symbol + ")" : ""), + "color", randomOutlineColor(), + "parentLayerIndex", "" + parentIndex, + "locked", "true" + }); + writer.writeStartElement("frames"); + + int clipFrame2 = 0; + for (int p2 = 0; p2 < clipPlaces.size() - 1; p2++) { + if (clipPlaces.get(p2) == po2) { + clipFrame2 = clipFrameSplitters.get(p2); + } + } + //int nextFrame2 = clipFinishFrames.get(po2); + + if (clipFrame2 > 0) { + writer.writeStartElement("DOMFrame", new String[]{ + "index", "0", + "duration", "" + clipFrame2, + "keyMode", "" + KEY_MODE_NORMAL + }); + writer.writeEmptyElement("elements"); + writer.writeEndElement(); } - } - //int nextFrame2 = clipFinishFrames.get(po2); - if (clipFrame2 > 0) { writer.writeStartElement("DOMFrame", new String[]{ - "index", "0", - "duration", "" + clipFrame2, + "index", "" + clipFrame2, + "duration", "" + mlc.numFrames, "keyMode", "" + KEY_MODE_NORMAL }); - writer.writeEmptyElement("elements"); + writer.writeStartElement("elements"); + writer.writeStartElement("DOMSymbolInstance", new String[]{ + "libraryItemName", generateMaskedSymbolName(mlc.symbol), + "symbolType", "graphic", + "loop", "loop" + }); + + writer.writeStartElement("matrix"); + convertMatrix(new MATRIX(), writer); + writer.writeEndElement(); //matrix + + writer.writeStartElement("transformationPoint"); + writer.writeEmptyElement("Point"); + writer.writeEndElement(); //transformationPoint + + writer.writeEndElement(); //DOMSymbolInstance + writer.writeEndElement(); //elements + writer.writeEndElement(); //DOMFrame + + writer.writeEndElement(); //frames + writer.writeEndElement(); - } + index++; - writer.writeStartElement("DOMFrame", new String[]{ - "index", "" + clipFrame2, - "duration", "" + mlc.numFrames, - "keyMode", "" + KEY_MODE_NORMAL - }); - writer.writeStartElement("elements"); - writer.writeStartElement("DOMSymbolInstance", new String[]{ - "libraryItemName", generateMaskedSymbolName(mlc.symbol), - "symbolType", "graphic", - "loop", "loop" - }); - - writer.writeStartElement("matrix"); - convertMatrix(new MATRIX(), writer); - writer.writeEndElement(); //matrix - - writer.writeStartElement("transformationPoint"); - writer.writeEmptyElement("Point"); - writer.writeEndElement(); //transformationPoint - - writer.writeEndElement(); //DOMSymbolInstance - writer.writeEndElement(); //elements - writer.writeEndElement(); //DOMFrame - - writer.writeEndElement(); //frames - - writer.writeEndElement(); - index++; - - for (int nd2 = po2.getDepth(); nd2 <= po2.getClipDepth(); nd2++) { - for (int i = clipFrame2; i < clipFrame2 + mlc.numFrames; i++) { - depthToFramesList.get(nd2).remove((Integer) i); + for (int nd2 = po2.getDepth(); nd2 <= po2.getClipDepth(); nd2++) { + for (int i = clipFrame2; i < clipFrame2 + mlc.numFrames; i++) { + depthToFramesList.get(nd2).remove((Integer) i); + } } } } } } - } - for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) { - boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack); + for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) { + boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(nd), nd, clipFrame, lastFrame, parentIndex, writer, nonLibraryShapes, tags, sceneTimelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack); + for (int i = clipFrame; i <= lastFrame; i++) { + depthToFramesList.get(nd).remove((Integer) i); + } + if (nonEmpty) { + index++; + } + } for (int i = clipFrame; i <= lastFrame; i++) { - depthToFramesList.get(nd).remove((Integer) i); - } - if (nonEmpty) { - index++; + depthToFramesList.get(po.getDepth()).remove((Integer) i); } } - for (int i = clipFrame; i <= lastFrame; i++) { - depthToFramesList.get(po.getDepth()).remove((Integer) i); - } + } + + boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, sceneTimelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack); + if (nonEmpty) { + index++; } } - boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, timelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack); - if (nonEmpty) { + if (index == 0) { + writeEmptyLayer(writer, frameCount); index++; } - } - if (index == 0) { - writeEmptyLayer(writer, frameCount); - index++; + convertSoundLayer(sceneTimelineTags, files, writer); + writer.writeEndElement(); //layers + writer.writeEndElement(); //DOMTimeline } - - convertSoundLayer(timelineTags, files, writer); - writer.writeEndElement(); //layers - writer.writeEndElement(); //DOMTimeline - writer.writeEndElement(); //timeline/s + writer.writeEndElement(); //timeline/s } private void writeEmptyLayer(XFLXmlWriter writer, int frameCount) throws XMLStreamException { @@ -4678,7 +4778,7 @@ public class XFLConverter { //domDocument.writeStartElement("timelines"); ScriptPack documentScriptPack = characterScriptPacks.containsKey(0) ? characterScriptPacks.get(0) : null; statusStack.pushStatus("main timeline"); - convertTimeline(swf, swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, "Scene 1", flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); + convertTimelines(swf, swf.getAbcIndex(), -1, null, nonLibraryShapes, backgroundColor, swf.getTags(), swf.getTags(), characters, null, flaVersion, files, domDocument, documentScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack); statusStack.popStatus(); //domDocument.writeEndElement(); diff --git a/libsrc/ffdec_lib/testdata/scenes/scenes.swf b/libsrc/ffdec_lib/testdata/scenes/scenes.swf index e8f530b379497fa96e363a5a21bf6a3cd97eb95c..28fa11ba84ffba01d0b71be4c89748f2650f3b96 100644 GIT binary patch delta 639 zcmah^!EO^V5cSw=yStmTY13|Vsn|n~G_|d58d4!hAVEDqgxZQ$qS7u|M-8GCO2UOp zzMu!R5-}m1@gCBG|&3;!2DWAw*Ps&cKC0{Dl4}w!~2|NaO#anEdvG&cB_0Jhh_(I5M$6Kw zS2E#jZ_WyWFN=~}7wz}cp}y7A~hXL@K}xRg13dr94N<}TWj31Ik+5395y+v za=gv)4#ytHyR_KWERBds%cR*J0NVrsCV;R7IRh4$K-5sSLsId|KolJJ(Zb2>RG2(L zS9G3{I!Z^RzMYP&0ktyM=rA_AN-D0{Gzz&0 i*FoNF*TV(gw(ZFJw_U-2KqPmMeP delta 321 zcmeAb-6YKB79PyMiE|^HGb6Wwv96&(h@pX%iP7W`Mk8eY=824{j7*NElXF=VCQC5u zz?f;w+b17n(U|PY@kviJ5u&AY!rto2;-PW{A;DKx09+Ffws6gY9BuVq|1tWB_s)Ik}iG z7`THZ8M(OmKC*q_ykG#to(vzjfuc-6g`D~q41nTnsSF9s1_>+%39JSQYz7J9>;?%O X1_?|+6u}@10U3-R**0@=G&2DJu}eVN diff --git a/libsrc/ffdec_lib/testdata/scenes/scenes/DOMDocument.xml b/libsrc/ffdec_lib/testdata/scenes/scenes/DOMDocument.xml index b9002d813..39f659ad3 100644 --- a/libsrc/ffdec_lib/testdata/scenes/scenes/DOMDocument.xml +++ b/libsrc/ffdec_lib/testdata/scenes/scenes/DOMDocument.xml @@ -4,9 +4,19 @@ - + - + + + + + + + + + + + @@ -20,7 +30,7 @@ - + @@ -46,9 +56,19 @@ - + - + + + + + + + + + + + @@ -62,7 +82,7 @@ - + diff --git a/libsrc/ffdec_lib/testdata/scenes/scenes/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/scenes/scenes/META-INF/metadata.xml index 79bc1d7ce..b42a75ad0 100644 --- a/libsrc/ffdec_lib/testdata/scenes/scenes/META-INF/metadata.xml +++ b/libsrc/ffdec_lib/testdata/scenes/scenes/META-INF/metadata.xml @@ -5,8 +5,8 @@ xmlns:xmp="http://ns.adobe.com/xap/1.0/"> Adobe Flash Professional CS6 - build 481 2023-03-10T10:15:53-08:00 - 2023-03-10T10:16:12-08:00 - 2023-03-10T10:16:12-08:00 + 2023-12-02T06:17:50-08:00 + 2023-12-02T06:17:50-08:00 @@ -15,7 +15,7 @@ - xmp.iid:9F42FDA3CDBDED11B7A6DD84E1B1550C + xmp.iid:4AA6FB201191EE11A881D22F4484EE24 xmp.did:9F42FDA3CDBDED11B7A6DD84E1B1550C xmp.did:9F42FDA3CDBDED11B7A6DD84E1B1550C @@ -26,6 +26,18 @@ 2023-03-10T10:15:53-08:00 Adobe Flash Professional CS6 - build 481 + + created + xmp.iid:49A6FB201191EE11A881D22F4484EE24 + 2023-03-10T10:15:53-08:00 + Adobe Flash Professional CS6 - build 481 + + + created + xmp.iid:4AA6FB201191EE11A881D22F4484EE24 + 2023-03-10T10:15:53-08:00 + Adobe Flash Professional CS6 - build 481 + diff --git a/libsrc/ffdec_lib/testdata/scenes/scenes/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/scenes/scenes/bin/SymDepend.cache index f8b0b40df5570a39ec37b10289baa678a49508a8..940fd977027198141ff9ac53007247ec66dee768 100644 GIT binary patch literal 54 ycmYdiU|@L8&cndQzy!pC3`Rgc6A-g81f?dIFtl(mIA<0m=cF^>{|;Ih5`V