Added #2132 Show and export streamed sound (SoundStreamHead/SoundStreamBlock) in frame ranges

Fixed #1194 FLA Export - stream sound export
This commit is contained in:
Jindra Petřík
2023-12-30 18:06:08 +01:00
parent 35dda78ffe
commit bd82522f16
20 changed files with 652 additions and 307 deletions
+4 -1
View File
@@ -25,6 +25,7 @@ All notable changes to this project will be documented in this file.
- [#2131] Breakpoint list dialog - [#2131] Breakpoint list dialog
- ExportAssets tag - show first item as description in the tree when there is only single item - 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 - [#2134] FLA Export - split main timeline into scenes when DefineSceneAndFrameLabelData tag is present
- [#2132] Show and export streamed sound (SoundStreamHead/SoundStreamBlock) in frame ranges
### Fixed ### Fixed
- [#2021], [#2000] Caret position in editors when using tabs and / or unicode - [#2021], [#2000] Caret position in editors when using tabs and / or unicode
@@ -64,6 +65,7 @@ All notable changes to this project will be documented in this file.
- Wordrapping long words in DefineEditText - Wordrapping long words in DefineEditText
- [#2133] Linux/Mac - ffdec.sh not correctly parsing java build number on javas without it - [#2133] Linux/Mac - ffdec.sh not correctly parsing java build number on javas without it
- [#2135] FLA Export - framescripts handling when addFrameScript uses Multinames instead of QNames - [#2135] FLA Export - framescripts handling when addFrameScript uses Multinames instead of QNames
- [#1194] FLA Export - stream sound export
### Changed ### Changed
- [#2120] Exported assets no longer take names from assigned classes if there is more than 1 assigned class - [#2120] Exported assets no longer take names from assigned classes if there is more than 1 assigned class
@@ -3324,6 +3326,7 @@ Major version of SWF to XML export changed to 2.
[#2131]: https://www.free-decompiler.com/flash/issues/2131 [#2131]: https://www.free-decompiler.com/flash/issues/2131
[#2124]: https://www.free-decompiler.com/flash/issues/2124 [#2124]: https://www.free-decompiler.com/flash/issues/2124
[#2134]: https://www.free-decompiler.com/flash/issues/2134 [#2134]: https://www.free-decompiler.com/flash/issues/2134
[#2132]: https://www.free-decompiler.com/flash/issues/2132
[#2021]: https://www.free-decompiler.com/flash/issues/2021 [#2021]: https://www.free-decompiler.com/flash/issues/2021
[#2000]: https://www.free-decompiler.com/flash/issues/2000 [#2000]: https://www.free-decompiler.com/flash/issues/2000
[#2116]: https://www.free-decompiler.com/flash/issues/2116 [#2116]: https://www.free-decompiler.com/flash/issues/2116
@@ -3335,6 +3338,7 @@ Major version of SWF to XML export changed to 2.
[#2053]: https://www.free-decompiler.com/flash/issues/2053 [#2053]: https://www.free-decompiler.com/flash/issues/2053
[#2133]: https://www.free-decompiler.com/flash/issues/2133 [#2133]: https://www.free-decompiler.com/flash/issues/2133
[#2135]: https://www.free-decompiler.com/flash/issues/2135 [#2135]: https://www.free-decompiler.com/flash/issues/2135
[#1194]: https://www.free-decompiler.com/flash/issues/1194
[#2120]: https://www.free-decompiler.com/flash/issues/2120 [#2120]: https://www.free-decompiler.com/flash/issues/2120
[#1130]: https://www.free-decompiler.com/flash/issues/1130 [#1130]: https://www.free-decompiler.com/flash/issues/1130
[#1220]: https://www.free-decompiler.com/flash/issues/1220 [#1220]: https://www.free-decompiler.com/flash/issues/1220
@@ -3765,7 +3769,6 @@ Major version of SWF to XML export changed to 2.
[#1200]: https://www.free-decompiler.com/flash/issues/1200 [#1200]: https://www.free-decompiler.com/flash/issues/1200
[#1198]: https://www.free-decompiler.com/flash/issues/1198 [#1198]: https://www.free-decompiler.com/flash/issues/1198
[#1205]: https://www.free-decompiler.com/flash/issues/1205 [#1205]: https://www.free-decompiler.com/flash/issues/1205
[#1194]: https://www.free-decompiler.com/flash/issues/1194
[#1210]: https://www.free-decompiler.com/flash/issues/1210 [#1210]: https://www.free-decompiler.com/flash/issues/1210
[#1217]: https://www.free-decompiler.com/flash/issues/1217 [#1217]: https://www.free-decompiler.com/flash/issues/1217
[#1244]: https://www.free-decompiler.com/flash/issues/1244 [#1244]: https://www.free-decompiler.com/flash/issues/1244
@@ -57,6 +57,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.TreeItem; import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; import com.jpexs.decompiler.flash.types.BUTTONCONDACTION;
@@ -261,7 +262,13 @@ public class PreviewExporter {
List<SoundStreamBlockTag> soundFrames = new ArrayList<>(); List<SoundStreamBlockTag> soundFrames = new ArrayList<>();
if (treeItem instanceof SoundStreamHeadTypeTag) { if (treeItem instanceof SoundStreamHeadTypeTag) {
soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks(); SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) treeItem;
for (SoundStreamFrameRange range : head.getRanges()) {
soundFrames.addAll(range.blocks);
}
}
if (treeItem instanceof SoundStreamFrameRange) {
soundFrames = ((SoundStreamFrameRange) treeItem).blocks;
frameCount = soundFrames.size(); frameCount = soundFrames.size();
} }
@@ -34,6 +34,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat; import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat; import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.ByteArrayRange;
@@ -58,8 +59,17 @@ import java.util.Set;
* @author JPEXS * @author JPEXS
*/ */
public class SoundExporter { public class SoundExporter {
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException { public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<SoundTag> sounds = new ArrayList<>();
for (Tag t : tags) {
if (t instanceof SoundTag) {
sounds.add((SoundTag) t);
}
}
return exportSounds(handler, outdir, sounds, settings, evl);
}
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<SoundTag> tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List<File> ret = new ArrayList<>(); List<File> ret = new ArrayList<>();
if (Thread.currentThread().isInterrupted()) { if (Thread.currentThread().isInterrupted()) {
return ret; return ret;
@@ -72,26 +82,16 @@ public class SoundExporter {
File foutdir = new File(outdir); File foutdir = new File(outdir);
Path.createDirectorySafe(foutdir); Path.createDirectorySafe(foutdir);
int count = 0; if (tags.isEmpty()) {
for (Tag t : tags) {
if (t instanceof SoundTag) {
count++;
}
}
if (count == 0) {
return ret; return ret;
} }
int currentIndex = 1; int currentIndex = 1;
for (Tag t : tags) { for (SoundTag st : tags) {
if (t instanceof SoundTag) {
if (evl != null) { if (evl != null) {
evl.handleExportingEvent("sound", currentIndex, count, t.getName()); evl.handleExportingEvent("sound", currentIndex, tags.size(), st.getName());
} }
final SoundTag st = (SoundTag) t;
String ext = ".wav"; String ext = ".wav";
SoundFormat fmt = st.getSoundFormat(); SoundFormat fmt = st.getSoundFormat();
switch (fmt.getNativeExportFormat()) { switch (fmt.getNativeExportFormat()) {
@@ -136,12 +136,11 @@ public class SoundExporter {
} }
if (evl != null) { if (evl != null) {
evl.handleExportedEvent("sound", currentIndex, count, t.getName()); evl.handleExportedEvent("sound", currentIndex, tags.size(), st.getName());
} }
currentIndex++; currentIndex++;
} }
}
return ret; return ret;
} }
@@ -168,11 +167,21 @@ public class SoundExporter {
for (ByteArrayRange data : datas) { for (ByteArrayRange data : datas) {
flv.writeTag(new FLVTAG(0, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data.getRangeData()))); flv.writeTag(new FLVTAG(0, new AUDIODATA(st.getSoundFormatId(), st.getSoundRate(), st.getSoundSize(), st.getSoundType(), data.getRangeData())));
} }
} else if (st instanceof SoundStreamHeadTypeTag) { } else if ((st instanceof SoundStreamFrameRange) || (st instanceof SoundStreamHeadTypeTag)) {
SoundStreamHeadTypeTag sh = (SoundStreamHeadTypeTag) st; List<SoundStreamBlockTag> blocks;
if (st instanceof SoundStreamHeadTypeTag) {
blocks = new ArrayList<>();
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) st;
for (SoundStreamFrameRange range : head.getRanges()) {
blocks.addAll(range.blocks);
}
} else {
blocks = ((SoundStreamFrameRange) st).blocks;
}
SoundStreamFrameRange sh = (SoundStreamFrameRange) st;
FLVOutputStream flv = new FLVOutputStream(fos); FLVOutputStream flv = new FLVOutputStream(fos);
flv.writeHeader(true, false); flv.writeHeader(true, false);
List<SoundStreamBlockTag> blocks = sh.getBlocks();
int ms = (int) (1000.0 / ((Tag) st).getSwf().frameRate); int ms = (int) (1000.0 / ((Tag) st).getSwf().frameRate);
for (int b = 0; b < blocks.size(); b++) { for (int b = 0; b < blocks.size(); b++) {
@@ -30,6 +30,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundImportException;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.UnsupportedSamplingRateException; import com.jpexs.decompiler.flash.tags.base.UnsupportedSamplingRateException;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.sound.MP3FRAME; import com.jpexs.decompiler.flash.types.sound.MP3FRAME;
import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA;
@@ -329,7 +330,14 @@ public class SoundImporter {
ByteArrayInputStream bais = uncompressedSoundData == null ? null : new ByteArrayInputStream(uncompressedSoundData); ByteArrayInputStream bais = uncompressedSoundData == null ? null : new ByteArrayInputStream(uncompressedSoundData);
List<SoundStreamBlockTag> existingBlocks = streamHead.getBlocks(); List<SoundStreamFrameRange> ranges = streamHead.getRanges();
List<SoundStreamBlockTag> existingBlocks = new ArrayList<>();
for (SoundStreamFrameRange range : ranges) {
existingBlocks.addAll(range.blocks);
}
int startFrame = 0; int startFrame = 0;
Timelined timelined = streamHead.getTimelined(); Timelined timelined = streamHead.getTimelined();
if (!existingBlocks.isEmpty()) { if (!existingBlocks.isEmpty()) {
@@ -237,4 +237,9 @@ public class DefineSoundTag extends CharacterTag implements SoundTag {
public void setSoundRate(int soundRate) { public void setSoundRate(int soundRate) {
this.soundRate = soundRate; this.soundRate = soundRate;
} }
@Override
public String getFlaExportName() {
return "sound" + getCharacterId();
}
} }
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.Conditional; import com.jpexs.decompiler.flash.types.annotations.Conditional;
@@ -191,9 +192,9 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
} }
@Override @Override
public List<SoundStreamBlockTag> getBlocks() { public List<SoundStreamFrameRange> getRanges() {
Timeline timeline = swf.getTimeline(); Timeline timeline = swf.getTimeline();
List<SoundStreamBlockTag> ret = timeline.getSoundStreamBlocks(this); List<SoundStreamFrameRange> ret = timeline.getSoundStreamBlocks(this);
return ret; return ret;
} }
@@ -206,9 +207,10 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
@Override @Override
public List<ByteArrayRange> getRawSoundData() { public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>(); List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks(); List<SoundStreamFrameRange> frameRanges = getRanges();
if (blocks != null) { if (frameRanges != null) {
for (SoundStreamBlockTag block : blocks) { for (SoundStreamFrameRange range : frameRanges) {
for (SoundStreamBlockTag block : range.blocks) {
ByteArrayRange data = block.streamSoundData; ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) { if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4)); ret.add(data.getSubRange(4, data.getLength() - 4));
@@ -217,12 +219,17 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
} }
} }
} }
}
return ret; return ret;
} }
@Override @Override
public long getTotalSoundSampleCount() { public long getTotalSoundSampleCount() {
return getBlocks().size() * streamSoundSampleCount; int blockCount = 0;
for (SoundStreamFrameRange range : getRanges()) {
blockCount += range.blocks.size();
}
return blockCount * streamSoundSampleCount;
} }
@Override @Override
@@ -278,4 +285,9 @@ public class SoundStreamHead2Tag extends SoundStreamHeadTypeTag {
public void setSoundRate(int soundRate) { public void setSoundRate(int soundRate) {
this.streamSoundRate = soundRate; this.streamSoundRate = soundRate;
} }
@Override
public String getFlaExportName() {
return "sound" + getCharacterId();
}
} }
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.Conditional; import com.jpexs.decompiler.flash.types.annotations.Conditional;
@@ -201,11 +202,10 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
} }
@Override @Override
public List<SoundStreamBlockTag> getBlocks() { public List<SoundStreamFrameRange> getRanges() {
Timeline timeline = swf.getTimeline(); Timeline timeline = swf.getTimeline();
List<SoundStreamBlockTag> ret = timeline.getSoundStreamBlocks(this); List<SoundStreamFrameRange> ret = timeline.getSoundStreamBlocks(this);
return ret; return ret;
} }
@Override @Override
@@ -216,9 +216,10 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
@Override @Override
public List<ByteArrayRange> getRawSoundData() { public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>(); List<ByteArrayRange> ret = new ArrayList<>();
List<SoundStreamBlockTag> blocks = getBlocks(); List<SoundStreamFrameRange> frameRanges = getRanges();
if (blocks != null) { if (frameRanges != null) {
for (SoundStreamBlockTag block : blocks) { for (SoundStreamFrameRange range : frameRanges) {
for (SoundStreamBlockTag block : range.blocks) {
ByteArrayRange data = block.streamSoundData; ByteArrayRange data = block.streamSoundData;
if (streamSoundCompression == SoundFormat.FORMAT_MP3) { if (streamSoundCompression == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4)); ret.add(data.getSubRange(4, data.getLength() - 4));
@@ -227,12 +228,17 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
} }
} }
} }
}
return ret; return ret;
} }
@Override @Override
public long getTotalSoundSampleCount() { public long getTotalSoundSampleCount() {
return getBlocks().size() * streamSoundSampleCount; int blockCount = 0;
for (SoundStreamFrameRange range : getRanges()) {
blockCount += range.blocks.size();
}
return blockCount * streamSoundSampleCount;
} }
@Override @Override
@@ -288,4 +294,9 @@ public class SoundStreamHeadTag extends SoundStreamHeadTypeTag {
public void setSoundRate(int soundRate) { public void setSoundRate(int soundRate) {
this.streamSoundRate = soundRate; this.streamSoundRate = soundRate;
} }
@Override
public String getFlaExportName() {
return "sound" + getCharacterId();
}
} }
@@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.ByteArrayRange;
import java.util.List; import java.util.List;
@@ -37,6 +38,6 @@ public abstract class SoundStreamHeadTypeTag extends Tag implements CharacterIdT
public abstract long getSoundSampleCount(); public abstract long getSoundSampleCount();
public abstract List<SoundStreamBlockTag> getBlocks(); public abstract List<SoundStreamFrameRange> getRanges();
} }
@@ -59,4 +59,10 @@ public interface SoundTag extends TreeItem {
public void setSoundRate(int soundRate); public void setSoundRate(int soundRate);
public int getCharacterId(); public int getCharacterId();
public boolean isReadOnly();
public String getName();
public String getFlaExportName();
} }
@@ -243,4 +243,9 @@ public class DefineExternalSound extends CharacterTag implements SoundTag {
tagInfo.addInfo("general", "stereo", soundFormat.stereo); tagInfo.addInfo("general", "stereo", soundFormat.stereo);
tagInfo.addInfo("general", "sampleCount", sampleCount); tagInfo.addInfo("general", "sampleCount", sampleCount);
} }
@Override
public String getFlaExportName() {
return "sound" + getCharacterId();
}
} }
@@ -255,4 +255,9 @@ public class DefineExternalStreamSound extends Tag implements CharacterIdTag, So
tagInfo.addInfo("general", "stereo", soundFormat.stereo); tagInfo.addInfo("general", "stereo", soundFormat.stereo);
tagInfo.addInfo("general", "sampleCount", sampleCount); tagInfo.addInfo("general", "sampleCount", sampleCount);
} }
@Override
public String getFlaExportName() {
return "sound" + getCharacterId();
}
} }
@@ -0,0 +1,167 @@
/*
* Copyright (C) 2010-2023 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.timeline;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class SoundStreamFrameRange implements TreeItem, SoundTag {
public int startFrame;
public int endFrame;
public List<SoundStreamBlockTag> blocks = new ArrayList<>();
private final SoundStreamHeadTypeTag head;
public SoundStreamFrameRange(SoundStreamHeadTypeTag head) {
this.head = head;
}
@Override
public Openable getOpenable() {
return head.getOpenable();
}
@Override
public boolean isModified() {
return false;
}
@Override
public SoundExportFormat getExportFormat() {
return head.getExportFormat();
}
@Override
public boolean importSupported() {
return false; //??
}
@Override
public int getSoundRate() {
return head.getSoundRate();
}
@Override
public boolean getSoundType() {
return head.getSoundType();
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (getSoundFormatId() == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(data);
}
}
return ret;
}
@Override
public int getSoundFormatId() {
return head.getSoundFormatId();
}
@Override
public long getTotalSoundSampleCount() {
return blocks.size() * head.getSoundSampleCount();
}
@Override
public boolean getSoundSize() {
return head.getSoundSize();
}
@Override
public String getCharacterExportFileName() {
return head.getCharacterExportFileName() + "_" + (startFrame + 1) + "-" + (endFrame + 1);
}
@Override
public String getName() {
return "SoundStreamBlocks";
}
@Override
public SoundFormat getSoundFormat() {
return head.getSoundFormat();
}
@Override
public void setSoundSize(boolean soundSize) {
//?
}
@Override
public void setSoundType(boolean soundType) {
//?
}
@Override
public void setSoundSampleCount(long soundSampleCount) {
//?
}
@Override
public void setSoundCompression(int soundCompression) {
//?
}
@Override
public void setSoundRate(int soundRate) {
//?
}
@Override
public int getCharacterId() {
return head.getCharacterId();
}
public SoundStreamHeadTypeTag getHead() {
return head;
}
@Override
public String toString() {
return "SoundStreamBlocks (frame " + (startFrame + 1) + " - " + (endFrame + 1) + ")";
}
@Override
public boolean isReadOnly() {
return head.isReadOnly();
}
@Override
public String getFlaExportName() {
return head.getFlaExportName() + "_" + (startFrame + 1) + "-" + (endFrame + 1);
}
}
@@ -118,7 +118,7 @@ public class Timeline {
private final Map<ASMSource, Integer> actionFrames = new HashMap<>(); private final Map<ASMSource, Integer> actionFrames = new HashMap<>();
private final Map<Integer, List<SoundStreamBlockTag>> soundStramBlocks = new LinkedHashMap<>(); private final Map<Integer, List<SoundStreamFrameRange>> soundStreamRanges = new LinkedHashMap<>();
private AS2Package as2RootPackage; private AS2Package as2RootPackage;
@@ -174,9 +174,9 @@ public class Timeline {
return depthMaxFrame; return depthMaxFrame;
} }
public List<SoundStreamBlockTag> getSoundStreamBlocks(SoundStreamHeadTypeTag head) { public List<SoundStreamFrameRange> getSoundStreamBlocks(SoundStreamHeadTypeTag head) {
ensureInitialized(); ensureInitialized();
return soundStramBlocks.get(head.getCharacterId()); return soundStreamRanges.get(head.getCharacterId());
} }
public Tag getParentTag() { public Tag getParentTag() {
@@ -194,7 +194,7 @@ public class Timeline {
asmSources.clear(); asmSources.clear();
asmSourceContainers.clear(); asmSourceContainers.clear();
actionFrames.clear(); actionFrames.clear();
soundStramBlocks.clear(); soundStreamRanges.clear();
otherTags.clear(); otherTags.clear();
this.id = id; this.id = id;
this.swf = swf; this.swf = swf;
@@ -576,13 +576,23 @@ public class Timeline {
} }
private void populateSoundStreamBlocks(int containerId, Iterable<Tag> tags) { private void populateSoundStreamBlocks(int containerId, Iterable<Tag> tags) {
List<SoundStreamBlockTag> blocks = null; List<SoundStreamFrameRange> ranges = null;
SoundStreamFrameRange range = null;
final int MIN_NUM_FRAMES_NO_SOUND = 2;
int numFramesNoSound = MIN_NUM_FRAMES_NO_SOUND;
boolean frameHasSound = false;
int frame = 0;
SoundStreamHeadTypeTag head = null;
for (Tag t : tags) { for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) { if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t; head = (SoundStreamHeadTypeTag) t;
head.setCharacterId(containerId); head.setCharacterId(containerId);
blocks = new ArrayList<>(); ranges = new ArrayList<>();
soundStramBlocks.put(containerId, blocks); range = new SoundStreamFrameRange(head);
range.startFrame = -1;
range.endFrame = -1;
numFramesNoSound = MIN_NUM_FRAMES_NO_SOUND;
soundStreamRanges.put(containerId, ranges);
continue; continue;
} }
if (t instanceof DefineExternalStreamSound) { if (t instanceof DefineExternalStreamSound) {
@@ -596,13 +606,40 @@ public class Timeline {
populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getTags()); populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getTags());
} }
if (blocks == null) { if (t instanceof ShowFrameTag) {
frame++;
if (frameHasSound) {
numFramesNoSound = 0;
} else {
numFramesNoSound++;
}
frameHasSound = false;
}
if (ranges == null) {
continue;
}
if (range == null) {
continue; continue;
} }
if (t instanceof SoundStreamBlockTag) { if (t instanceof SoundStreamBlockTag) {
blocks.add((SoundStreamBlockTag) t); if (numFramesNoSound >= MIN_NUM_FRAMES_NO_SOUND && range.endFrame > -1) {
ranges.add(range);
range = new SoundStreamFrameRange(head);
range.startFrame = -1;
range.endFrame = -1;
} }
range.blocks.add((SoundStreamBlockTag) t);
if (range.startFrame == -1) {
range.startFrame = frame;
}
range.endFrame = frame;
frameHasSound = true;
}
}
if (range != null && ranges != null && range.endFrame > -1) {
ranges.add(range);
} }
} }
@@ -102,6 +102,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.tags.font.CharacterRanges; import com.jpexs.decompiler.flash.tags.font.CharacterRanges;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; import com.jpexs.decompiler.flash.types.BUTTONCONDACTION;
import com.jpexs.decompiler.flash.types.BUTTONRECORD; import com.jpexs.decompiler.flash.types.BUTTONRECORD;
@@ -1949,119 +1950,7 @@ public class XFLConverter {
writer.writeEndElement(); writer.writeEndElement();
} }
private void convertMedia(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException { private void convertSoundMedia(SWF swf, ReadOnlyTagList tags, SoundTag symbol, XFLXmlWriter writer, HashMap<String, byte[]> files) throws XMLStreamException {
boolean hasMedia = false;
for (int ch : characters.keySet()) {
CharacterTag symbol = characters.get(ch);
if (symbol instanceof ImageTag
|| symbol instanceof DefineSoundTag
|| symbol instanceof DefineVideoStreamTag) {
//symbol instanceof SoundStreamHeadTypeTag FIXME
hasMedia = true;
}
}
if (!hasMedia) {
return;
}
int mediaCount = 0;
writer.writeStartElement("media");
for (int ch : characters.keySet()) {
CharacterTag symbol = characters.get(ch);
if (symbol instanceof ImageTag) {
statusStack.pushStatus(symbol.toString());
ImageTag imageTag = (ImageTag) symbol;
boolean allowSmoothing = false;
//find if smoothed - a bitmap is smoothed when there is a shape with fillstyle smoothed bitmap
looptags:
for (Tag tag : swf.getTags()) {
if (tag instanceof ShapeTag) {
Set<Integer> needed = new HashSet<>();
tag.getNeededCharacters(needed, swf);
ShapeTag sht = (ShapeTag) tag;
if (needed.contains(imageTag.getCharacterId())) {
List<FILLSTYLE> fs = new ArrayList<>();
SHAPEWITHSTYLE s = sht.getShapes();
for (FILLSTYLE f : s.fillStyles.fillStyles) {
fs.add(f);
}
for (SHAPERECORD r : s.shapeRecords) {
if (r instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) r;
if (scr.stateNewStyles) {
for (FILLSTYLE f : scr.fillStyles.fillStyles) {
fs.add(f);
}
}
}
}
for (FILLSTYLE f : fs) {
if (Arrays.asList(FILLSTYLE.REPEATING_BITMAP, FILLSTYLE.CLIPPED_BITMAP, FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP, FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP).contains(f.fillStyleType) && f.bitmapId == imageTag.getCharacterId()) {
allowSmoothing = f.fillStyleType == FILLSTYLE.CLIPPED_BITMAP || f.fillStyleType == FILLSTYLE.REPEATING_BITMAP;
break looptags;
}
}
}
}
}
byte[] imageBytes = Helper.readStream(imageTag.getConvertedImageData());
SerializableImage image = imageTag.getImageCached();
ImageFormat format = imageTag.getImageFormat();
String symbolFile = "bitmap" + symbol.getCharacterId() + imageTag.getImageFormat().getExtension();
files.put(symbolFile, imageBytes);
writer.writeStartElement("DOMBitmapItem", new String[]{
"name", symbolFile,
"sourceLastImported", Long.toString(getTimestamp(swf)),
"externalFileSize", Integer.toString(imageBytes.length)});
if (allowSmoothing) {
writer.writeAttribute("allowSmoothing", true);
}
switch (format) {
case PNG:
case GIF:
if (imageTag.getOriginalImageFormat() != ImageFormat.JPEG) {
writer.writeAttribute("useImportedJPEGData", false);
writer.writeAttribute("compressionType", "lossless");
}
writer.writeAttribute("originalCompressionType", "lossless");
break;
case JPEG:
writer.writeAttribute("isJPEG", true);
break;
}
if (characterClasses.containsKey(symbol.getCharacterId())) {
String className = characterClasses.get(symbol.getCharacterId());
boolean isBitmapData = false;
for (ABCContainerTag c : swf.getAbcList()) {
int classIndex = c.getABC().findClassByName(className);
if (classIndex != -1) {
if (swf.getAbcIndex().isInstanceOf(c.getABC(), classIndex, DottedChain.parseNoSuffix("flash.display.BitmapData"))) {
isBitmapData = true;
}
break;
}
}
if (isBitmapData) {
writer.writeAttribute("linkageExportForAS", true);
writer.writeAttribute("linkageClassName", characterClasses.get(symbol.getCharacterId()));
}
//if it's not BitmapData, then it should use Embed
}
writer.writeAttribute("quality", 50);
writer.writeAttribute("href", symbolFile);
writer.writeAttribute("bitmapDataHRef", "M " + (mediaCount + 1) + " " + getTimestamp(swf) + ".dat");
writer.writeAttribute("frameRight", image.getWidth());
writer.writeAttribute("frameBottom", image.getHeight());
writer.writeEndElement();
mediaCount++;
statusStack.popStatus();
} else if (/*(symbol instanceof SoundStreamHeadTypeTag) || FIXME */(symbol instanceof DefineSoundTag)) {
statusStack.pushStatus(symbol.toString());
int soundFormat = 0; int soundFormat = 0;
int soundRate = 0; int soundRate = 0;
boolean soundType = false; boolean soundType = false;
@@ -2070,13 +1959,13 @@ public class XFLConverter {
byte[] soundData = SWFInputStream.BYTE_ARRAY_EMPTY; byte[] soundData = SWFInputStream.BYTE_ARRAY_EMPTY;
int[] rateMap = {5, 11, 22, 44}; int[] rateMap = {5, 11, 22, 44};
String exportFormat = "flv"; String exportFormat = "flv";
if (false) { //FIXME symbol instanceof SoundStreamHeadTypeTag) { if (symbol instanceof SoundStreamFrameRange) {
SoundStreamHeadTypeTag sstream = null; //(SoundStreamHeadTypeTag) symbol; SoundStreamHeadTypeTag head = ((SoundStreamFrameRange) symbol).getHead();
soundFormat = sstream.getSoundFormatId(); soundFormat = head.getSoundFormatId();
soundRate = sstream.getSoundRate(); soundRate = head.getSoundRate();
soundType = sstream.getSoundType(); soundType = head.getSoundType();
soundSize = sstream.getSoundSize(); soundSize = head.getSoundSize();
soundSampleCount = sstream.getSoundSampleCount(); soundSampleCount = head.getSoundSampleCount();
boolean found = false; boolean found = false;
for (Tag t : tags) { for (Tag t : tags) {
if (found && (t instanceof SoundStreamBlockTag)) { if (found && (t instanceof SoundStreamBlockTag)) {
@@ -2084,7 +1973,7 @@ public class XFLConverter {
soundData = bl.streamSoundData.getRangeData(); soundData = bl.streamSoundData.getRangeData();
break; break;
} }
if (t == symbol) { if (t == head) {
found = true; found = true;
} }
} }
@@ -2200,7 +2089,7 @@ public class XFLConverter {
logger.log(Level.SEVERE, null, ex); logger.log(Level.SEVERE, null, ex);
} }
String symbolFile = "sound" + symbol.getCharacterId() + "." + exportFormat; String symbolFile = symbol.getFlaExportName() + "." + exportFormat;
files.put(symbolFile, data); files.put(symbolFile, data);
writer.writeStartElement("DOMSoundItem", new String[]{ writer.writeStartElement("DOMSoundItem", new String[]{
"name", symbolFile, "name", symbolFile,
@@ -2211,6 +2100,123 @@ public class XFLConverter {
writer.writeAttribute("exportFormat", format); writer.writeAttribute("exportFormat", format);
writer.writeAttribute("exportBits", bits); writer.writeAttribute("exportBits", bits);
writer.writeAttribute("sampleCount", soundSampleCount); writer.writeAttribute("sampleCount", soundSampleCount);
}
private void convertMedia(SWF swf, Map<Integer, String> characterVariables, Map<Integer, String> characterClasses, List<Integer> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<Integer, CharacterTag> characters, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
boolean hasMedia = false;
for (int ch : characters.keySet()) {
CharacterTag symbol = characters.get(ch);
if (symbol instanceof ImageTag
|| symbol instanceof DefineSoundTag
|| symbol instanceof DefineVideoStreamTag) {
//symbol instanceof SoundStreamHeadTypeTag FIXME
hasMedia = true;
}
}
if (!hasMedia) {
return;
}
int mediaCount = 0;
writer.writeStartElement("media");
for (int ch : characters.keySet()) {
CharacterTag symbol = characters.get(ch);
if (symbol instanceof ImageTag) {
statusStack.pushStatus(symbol.toString());
ImageTag imageTag = (ImageTag) symbol;
boolean allowSmoothing = false;
//find if smoothed - a bitmap is smoothed when there is a shape with fillstyle smoothed bitmap
looptags:
for (Tag tag : swf.getTags()) {
if (tag instanceof ShapeTag) {
Set<Integer> needed = new HashSet<>();
tag.getNeededCharacters(needed, swf);
ShapeTag sht = (ShapeTag) tag;
if (needed.contains(imageTag.getCharacterId())) {
List<FILLSTYLE> fs = new ArrayList<>();
SHAPEWITHSTYLE s = sht.getShapes();
for (FILLSTYLE f : s.fillStyles.fillStyles) {
fs.add(f);
}
for (SHAPERECORD r : s.shapeRecords) {
if (r instanceof StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) r;
if (scr.stateNewStyles) {
for (FILLSTYLE f : scr.fillStyles.fillStyles) {
fs.add(f);
}
}
}
}
for (FILLSTYLE f : fs) {
if (Arrays.asList(FILLSTYLE.REPEATING_BITMAP, FILLSTYLE.CLIPPED_BITMAP, FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP, FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP).contains(f.fillStyleType) && f.bitmapId == imageTag.getCharacterId()) {
allowSmoothing = f.fillStyleType == FILLSTYLE.CLIPPED_BITMAP || f.fillStyleType == FILLSTYLE.REPEATING_BITMAP;
break looptags;
}
}
}
}
}
byte[] imageBytes = Helper.readStream(imageTag.getConvertedImageData());
SerializableImage image = imageTag.getImageCached();
ImageFormat format = imageTag.getImageFormat();
String symbolFile = "bitmap" + symbol.getCharacterId() + imageTag.getImageFormat().getExtension();
files.put(symbolFile, imageBytes);
writer.writeStartElement("DOMBitmapItem", new String[]{
"name", symbolFile,
"sourceLastImported", Long.toString(getTimestamp(swf)),
"externalFileSize", Integer.toString(imageBytes.length)});
if (allowSmoothing) {
writer.writeAttribute("allowSmoothing", true);
}
switch (format) {
case PNG:
case GIF:
if (imageTag.getOriginalImageFormat() != ImageFormat.JPEG) {
writer.writeAttribute("useImportedJPEGData", false);
writer.writeAttribute("compressionType", "lossless");
}
writer.writeAttribute("originalCompressionType", "lossless");
break;
case JPEG:
writer.writeAttribute("isJPEG", true);
break;
}
if (characterClasses.containsKey(symbol.getCharacterId())) {
String className = characterClasses.get(symbol.getCharacterId());
boolean isBitmapData = false;
for (ABCContainerTag c : swf.getAbcList()) {
int classIndex = c.getABC().findClassByName(className);
if (classIndex != -1) {
if (swf.getAbcIndex().isInstanceOf(c.getABC(), classIndex, DottedChain.parseNoSuffix("flash.display.BitmapData"))) {
isBitmapData = true;
}
break;
}
}
if (isBitmapData) {
writer.writeAttribute("linkageExportForAS", true);
writer.writeAttribute("linkageClassName", characterClasses.get(symbol.getCharacterId()));
}
//if it's not BitmapData, then it should use Embed
}
writer.writeAttribute("quality", 50);
writer.writeAttribute("href", symbolFile);
writer.writeAttribute("bitmapDataHRef", "M " + (mediaCount + 1) + " " + getTimestamp(swf) + ".dat");
writer.writeAttribute("frameRight", image.getWidth());
writer.writeAttribute("frameBottom", image.getHeight());
writer.writeEndElement();
mediaCount++;
statusStack.popStatus();
} else if (symbol instanceof DefineSoundTag) {
statusStack.pushStatus(symbol.toString());
convertSoundMedia(swf, tags, (DefineSoundTag) symbol, writer, files);
boolean linkageExportForAS = false; boolean linkageExportForAS = false;
if (characterClasses.containsKey(symbol.getCharacterId())) { if (characterClasses.containsKey(symbol.getCharacterId())) {
@@ -2314,6 +2320,35 @@ public class XFLConverter {
} }
} }
for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t;
for (SoundStreamFrameRange range : head.getRanges()) {
statusStack.pushStatus(range.toString());
convertSoundMedia(swf, tags, range, writer, files);
writer.writeEndElement();
mediaCount++;
statusStack.popStatus();
}
}
if (t instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) t;
for (Tag st : sprite.getTags()) {
if (st instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) st;
for (SoundStreamFrameRange range : head.getRanges()) {
statusStack.pushStatus(range.toString());
convertSoundMedia(swf, sprite.getTags(), range, writer, files);
writer.writeEndElement();
mediaCount++;
statusStack.popStatus();
}
break;
}
}
}
}
writer.writeEndElement(); writer.writeEndElement();
} }
@@ -2420,7 +2455,7 @@ public class XFLConverter {
} }
} }
private static void convertFrame(boolean shapeTween, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap<String, byte[]> files, XFLXmlWriter writer) throws XMLStreamException { private static void convertFrame(Scene scene, boolean shapeTween, SoundStreamFrameRange soundStreamRange, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap<String, byte[]> files, XFLXmlWriter writer) throws XMLStreamException {
DefineSoundTag sound = null; DefineSoundTag sound = null;
if (startSound != null) { if (startSound != null) {
SWF swf = startSound.getSwf(); SWF swf = startSound.getSwf();
@@ -2438,13 +2473,14 @@ public class XFLConverter {
} else { } else {
writer.writeAttribute("keyMode", KEY_MODE_NORMAL); writer.writeAttribute("keyMode", KEY_MODE_NORMAL);
} }
if (soundStreamHead != null && startSound == null) { if (soundStreamRange != null && startSound == null) {
String soundName = "sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat().toString().toLowerCase(); String soundName = soundStreamRange.getFlaExportName() + "." + soundStreamRange.getExportFormat().toString().toLowerCase();
writer.writeAttribute("soundName", soundName); writer.writeAttribute("soundName", soundName);
writer.writeAttribute("soundSync", "stream"); writer.writeAttribute("soundSync", "stream");
writer.writeStartElement("SoundEnvelope"); writer.writeStartElement("SoundEnvelope");
writer.writeEmptyElement("SoundEnvelopePoint", new String[]{"level0", "32768", "level1", "32768"}); writer.writeEmptyElement("SoundEnvelopePoint", new String[]{"level0", "32768", "level1", "32768"});
writer.writeEndElement(); writer.writeEndElement();
} }
if (startSound != null && sound != null) { if (startSound != null && sound != null) {
convertSoundUsage(writer, sound, startSound.soundInfo); convertSoundUsage(writer, sound, startSound.soundInfo);
@@ -2497,7 +2533,7 @@ public class XFLConverter {
writer.writeEndElement(); writer.writeEndElement();
} }
private static void convertFrames(SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException { private static void convertFrames(Scene scene, SWF swf, List<Integer> onlyFrames, int startFrame, int endFrame, String prevStr, String afterStr, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, int depth, FLAVersion flaVersion, HashMap<String, byte[]> files, XFLXmlWriter writer, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
boolean lastIn = true; boolean lastIn = true;
XFLXmlWriter writer2 = new XFLXmlWriter(); XFLXmlWriter writer2 = new XFLXmlWriter();
prevStr += "<frames>"; prevStr += "<frames>";
@@ -2669,7 +2705,7 @@ public class XFLConverter {
SHAPEWITHSTYLE endShape = m.getShapeAtRatio(65535); //lastTweenRatio); SHAPEWITHSTYLE endShape = m.getShapeAtRatio(65535); //lastTweenRatio);
convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter); convertShape(swf, characters, matrix, m.getShapeNum() == 1 ? 3 : 4, endShape.shapeRecords, m.getFillStyles().getFillStylesAt(lastTweenRatio), m.getLineStyles().getLineStylesAt(m.getShapeNum(), lastTweenRatio), true, false, addLastWriter);
//duration--; //duration--;
convertFrame(true, null, null, frame - duration, duration, "", lastElements, files, writer2); convertFrame(scene, true, null, null, frame - duration, duration, "", lastElements, files, writer2);
duration = 1; duration = 1;
lastElements = addLastWriter.toString(); lastElements = addLastWriter.toString();
lastCharacter = m; lastCharacter = m;
@@ -2730,7 +2766,7 @@ public class XFLConverter {
frame++; frame++;
String elements = elementsWriter.toString(); String elements = elementsWriter.toString();
if (!elements.equals(lastElements) && frame > 0) { if (!elements.equals(lastElements) && frame > 0) {
convertFrame(false, null, null, frame - duration, duration, "", lastElements, files, writer2); convertFrame(scene, false, null, null, frame - duration, duration, "", lastElements, files, writer2);
duration = 1; duration = 1;
} else if (frame == 0) { } else if (frame == 0) {
duration = 1; duration = 1;
@@ -2768,7 +2804,7 @@ public class XFLConverter {
} }
if (!lastElements.isEmpty() || writer2.length() > 0) { if (!lastElements.isEmpty() || writer2.length() > 0) {
frame++; frame++;
convertFrame(false, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2); convertFrame(scene, false, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files, writer2);
} }
afterStr = "</frames>" + afterStr; afterStr = "</frames>" + afterStr;
@@ -3140,13 +3176,12 @@ public class XFLConverter {
return hasLabel; return hasLabel;
} }
private void convertSoundLayer(ReadOnlyTagList timeLineTags, HashMap<String, byte[]> files, XFLXmlWriter writer) throws XMLStreamException { private void convertSoundLayer(Scene scene, ReadOnlyTagList timeLineTags, HashMap<String, byte[]> files, XFLXmlWriter writer) throws XMLStreamException {
int soundLayerIndex = 0; int soundLayerIndex = 0;
XFLXmlWriter writer2 = new XFLXmlWriter(); XFLXmlWriter writer2 = new XFLXmlWriter();
List<StartSoundTag> startSounds = new ArrayList<>(); List<StartSoundTag> startSounds = new ArrayList<>();
List<Integer> startSoundFrameNumbers = new ArrayList<>(); List<Integer> startSoundFrameNumbers = new ArrayList<>();
List<SoundStreamHeadTypeTag> soundStreamHeads = new ArrayList<>(); List<SoundStreamFrameRange> soundStreamRanges = new ArrayList<>();
List<Integer> soundStreamHeadFrameNumbers = new ArrayList<>();
int frame = 0; int frame = 0;
for (Tag t : timeLineTags) { for (Tag t : timeLineTags) {
if (t instanceof StartSoundTag) { if (t instanceof StartSoundTag) {
@@ -3168,32 +3203,30 @@ public class XFLConverter {
} }
} else if (t instanceof SoundStreamHeadTypeTag) { } else if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag soundStreamHead = (SoundStreamHeadTypeTag) t; SoundStreamHeadTypeTag soundStreamHead = (SoundStreamHeadTypeTag) t;
if (!files.containsKey("sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat().toString().toLowerCase())) { //Sound was not exported
soundStreamHead = null; // ignore
}
if (soundStreamHead != null) { for (SoundStreamFrameRange range : soundStreamHead.getRanges()) {
soundStreamHeads.add(soundStreamHead); if (files.containsKey(range.getFlaExportName() + "." + soundStreamHead.getExportFormat().toString().toLowerCase())) { //Sound was really exported
soundStreamHeadFrameNumbers.add(frame); soundStreamRanges.add(range);
}
} }
} else if (t instanceof ShowFrameTag) { } else if (t instanceof ShowFrameTag) {
frame++; frame++;
} }
} }
for (int i = 0; i < soundStreamHeads.size(); i++) { for (int i = 0; i < soundStreamRanges.size(); i++) {
writer.writeStartElement("DOMLayer", new String[]{"name", "Sound Layer " + (soundLayerIndex++), "color", randomOutlineColor()}); writer.writeStartElement("DOMLayer", new String[]{"name", "Sound Layer " + (soundLayerIndex++), "color", randomOutlineColor()});
writer.writeStartElement("frames"); writer.writeStartElement("frames");
int startFrame = soundStreamHeadFrameNumbers.get(i); int startFrame = soundStreamRanges.get(i).startFrame - scene.startFrame;
int duration = frame - startFrame; int duration = frame - startFrame;
if (startFrame != 0) { if (startFrame != 0) {
// empty frames should be added // empty frames should be added
convertFrame(false, null, null, 0, startFrame, "", "", files, writer); convertFrame(scene, false, null, null, 0, startFrame, "", "", files, writer);
} }
convertFrame(false, soundStreamHeads.get(i), null, startFrame, duration, "", "", files, writer); convertFrame(scene, false, soundStreamRanges.get(i), null, startFrame, duration, "", "", files, writer);
writer.writeEndElement(); writer.writeEndElement();
writer.writeEndElement(); writer.writeEndElement();
@@ -3208,10 +3241,10 @@ public class XFLConverter {
if (startFrame != 0) { if (startFrame != 0) {
// empty frames should be added // empty frames should be added
convertFrame(false, null, null, 0, startFrame, "", "", files, writer); convertFrame(scene, false, null, null, 0, startFrame, "", "", files, writer);
} }
convertFrame(false, null, startSounds.get(i), startFrame, duration, "", "", files, writer); convertFrame(scene, false, null, startSounds.get(i), startFrame, duration, "", "", files, writer);
writer.writeEndElement(); writer.writeEndElement();
writer.writeEndElement(); writer.writeEndElement();
@@ -4076,7 +4109,7 @@ public class XFLConverter {
"color", randomOutlineColor(), "color", randomOutlineColor(),
"layerType", "mask", "layerType", "mask",
"locked", "true"}); "locked", "true"});
convertFrames(swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, sceneTimelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes, statusStack); convertFrames(scene, swf, depthToFramesList.get(po.getDepth()), clipFrame, lastFrame, "", "", nonLibraryShapes, tags, sceneTimelineTags, characters, po.getDepth(), flaVersion, files, writer, multiUsageMorphShapes, statusStack);
writer.writeEndElement(); writer.writeEndElement();
int parentIndex = index; int parentIndex = index;
@@ -4165,7 +4198,7 @@ public class XFLConverter {
} }
for (int nd = po.getClipDepth() - 1; nd > po.getDepth(); nd--) { 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); boolean nonEmpty = writeLayer(scene, 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++) { for (int i = clipFrame; i <= lastFrame; i++) {
depthToFramesList.get(nd).remove((Integer) i); depthToFramesList.get(nd).remove((Integer) i);
} }
@@ -4179,7 +4212,7 @@ public class XFLConverter {
} }
} }
boolean nonEmpty = writeLayer(swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, sceneTimelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack); boolean nonEmpty = writeLayer(scene, swf, index, depthToFramesList.get(d), d, 0, Integer.MAX_VALUE, -1, writer, nonLibraryShapes, tags, sceneTimelineTags, characters, flaVersion, files, multiUsageMorphShapes, statusStack);
if (nonEmpty) { if (nonEmpty) {
index++; index++;
} }
@@ -4190,7 +4223,7 @@ public class XFLConverter {
index++; index++;
} }
convertSoundLayer(sceneTimelineTags, files, writer); convertSoundLayer(scene, sceneTimelineTags, files, writer);
writer.writeEndElement(); //layers writer.writeEndElement(); //layers
writer.writeEndElement(); //DOMTimeline writer.writeEndElement(); //DOMTimeline
} }
@@ -4218,7 +4251,7 @@ public class XFLConverter {
writer.writeEndElement(); //DOMLayer writer.writeEndElement(); //DOMLayer
} }
private boolean writeLayer(SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException { private boolean writeLayer(Scene scene, SWF swf, int index, List<Integer> onlyFrames, int d, int startFrame, int endFrame, int parentLayer, XFLXmlWriter writer, List<Integer> nonLibraryShapes, ReadOnlyTagList tags, ReadOnlyTagList timelineTags, HashMap<Integer, CharacterTag> characters, FLAVersion flaVersion, HashMap<String, byte[]> files, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
XFLXmlWriter layerPrev = new XFLXmlWriter(); XFLXmlWriter layerPrev = new XFLXmlWriter();
statusStack.pushStatus("layer " + (index + 1)); statusStack.pushStatus("layer " + (index + 1));
//System.err.println("- writing layer " + (index + 1) + (startFrame == 0 && endFrame == Integer.MAX_VALUE ? ", all frames": ", frame " + startFrame + " to " + endFrame)); //System.err.println("- writing layer " + (index + 1) + (startFrame == 0 && endFrame == Integer.MAX_VALUE ? ", all frames": ", frame " + startFrame + " to " + endFrame));
@@ -4237,7 +4270,7 @@ public class XFLConverter {
layerPrev.writeCharacters(""); // todo honfika: hack to close start tag layerPrev.writeCharacters(""); // todo honfika: hack to close start tag
String layerAfter = "</DOMLayer>"; String layerAfter = "</DOMLayer>";
int prevLength = writer.length(); int prevLength = writer.length();
convertFrames(swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes, statusStack); convertFrames(scene, swf, onlyFrames, startFrame, endFrame, layerPrev.toString(), layerAfter, nonLibraryShapes, tags, timelineTags, characters, d, flaVersion, files, writer, multiUsageMorphShapes, statusStack);
statusStack.popStatus(); statusStack.popStatus();
return writer.length() != prevLength; return writer.length() != prevLength;
} }
@@ -192,6 +192,7 @@ import com.jpexs.decompiler.flash.tags.text.TextParseException;
import com.jpexs.decompiler.flash.timeline.AS3Package; import com.jpexs.decompiler.flash.timeline.AS3Package;
import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.TagScript; import com.jpexs.decompiler.flash.timeline.TagScript;
import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.timeline.Timelined;
@@ -2038,7 +2039,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
List<Tag> sprites = new ArrayList<>(); List<Tag> sprites = new ArrayList<>();
List<Tag> buttons = new ArrayList<>(); List<Tag> buttons = new ArrayList<>();
List<Tag> movies = new ArrayList<>(); List<Tag> movies = new ArrayList<>();
List<Tag> sounds = new ArrayList<>(); List<SoundTag> sounds = new ArrayList<>();
List<Tag> texts = new ArrayList<>(); List<Tag> texts = new ArrayList<>();
List<TreeItem> as12scripts = new ArrayList<>(); List<TreeItem> as12scripts = new ArrayList<>();
List<BinaryDataInterface> binaryData = new ArrayList<>(); List<BinaryDataInterface> binaryData = new ArrayList<>();
@@ -2061,7 +2062,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
} }
if (d instanceof Tag || d instanceof ASMSource || d instanceof BinaryDataInterface) { if (d instanceof SoundStreamHeadTypeTag) {
continue;
}
if (d instanceof Tag
|| d instanceof ASMSource
|| d instanceof BinaryDataInterface
|| d instanceof SoundStreamFrameRange) {
TreeNodeType nodeType = TagTree.getTreeNodeType(d); TreeNodeType nodeType = TagTree.getTreeNodeType(d);
if (nodeType == TreeNodeType.IMAGE) { if (nodeType == TreeNodeType.IMAGE) {
images.add((Tag) d); images.add((Tag) d);
@@ -2090,7 +2098,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
movies.add((Tag) d); movies.add((Tag) d);
} }
if (nodeType == TreeNodeType.SOUND) { if (nodeType == TreeNodeType.SOUND) {
sounds.add((Tag) d); sounds.add((SoundTag) d);
} }
if (nodeType == TreeNodeType.BINARY_DATA) { if (nodeType == TreeNodeType.BINARY_DATA) {
binaryData.add((BinaryDataInterface) d); binaryData.add((BinaryDataInterface) d);
@@ -2183,7 +2191,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
if (export.isOptionEnabled(SoundExportMode.class)) { if (export.isOptionEnabled(SoundExportMode.class)) {
ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(sounds), ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, sounds,
new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); new SoundExportSettings(export.getValue(SoundExportMode.class)), evl));
} }
@@ -5554,7 +5562,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
previewPanel.showImagePanel(timelinedContainer, swf, frame, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), true, false); previewPanel.showImagePanel(timelinedContainer, swf, frame, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), true, false);
} else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) {
previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32")));
previewPanel.setImageReplaceButtonVisible(false, false, false, !((Tag) treeItem).isReadOnly() && ((SoundTag) treeItem).importSupported(), false, false, false); previewPanel.setImageReplaceButtonVisible(false, false, false, !((SoundTag) treeItem).isReadOnly() && ((SoundTag) treeItem).importSupported(), false, false, false);
if (!(treeItem instanceof SoundStreamHeadTypeTag)) {
try { try {
SoundTagPlayer soundThread = new SoundTagPlayer(null, (SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); SoundTagPlayer soundThread = new SoundTagPlayer(null, (SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true);
if (!Configuration.autoPlaySounds.get()) { if (!Configuration.autoPlaySounds.get()) {
@@ -5564,6 +5573,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) {
logger.log(Level.SEVERE, null, ex); logger.log(Level.SEVERE, null, ex);
} }
}
} else if ((treeItem instanceof FontTag) && internalViewer) { } else if ((treeItem instanceof FontTag) && internalViewer) {
previewPanel.showFontPanel((FontTag) treeItem); previewPanel.showFontPanel((FontTag) treeItem);
} else if ((treeItem instanceof TextTag) && internalViewer) { } else if ((treeItem instanceof TextTag) && internalViewer) {
@@ -1529,6 +1529,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
public void showImagePanel(SerializableImage image) { public void showImagePanel(SerializableImage image) {
showCardLeft(DRAW_PREVIEW_CARD); showCardLeft(DRAW_PREVIEW_CARD);
imageTransformButton.setVisible(false);
parametersPanel.setVisible(false); parametersPanel.setVisible(false);
imagePlayControls.setMedia(imagePanel); imagePlayControls.setMedia(imagePanel);
imagePanel.setImage(image); imagePanel.setImage(image);
@@ -187,7 +187,7 @@ public class SoundTagPlayer implements MediaDisplay {
} }
private void openSound(SOUNDINFO soundInfo, SoundTag tag) throws IOException, LineUnavailableException, UnsupportedAudioFileException { private void openSound(SOUNDINFO soundInfo, SoundTag tag) throws IOException, LineUnavailableException, UnsupportedAudioFileException {
SWF swf = ((Tag) tag).getSwf(); SWF swf = (SWF) tag.getOpenable();
wavData = swf.getFromCache(soundInfo, tag); wavData = swf.getFromCache(soundInfo, tag);
if (wavData == null) { if (wavData == null) {
List<ByteArrayRange> soundData = tag.getRawSoundData(); List<ByteArrayRange> soundData = tag.getRawSoundData();
@@ -80,6 +80,7 @@ import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag; import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
@@ -89,6 +90,7 @@ import com.jpexs.decompiler.flash.timeline.AS2Package;
import com.jpexs.decompiler.flash.timeline.AS3Package; import com.jpexs.decompiler.flash.timeline.AS3Package;
import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.FrameScript; import com.jpexs.decompiler.flash.timeline.FrameScript;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.TagScript; import com.jpexs.decompiler.flash.timeline.TagScript;
import com.jpexs.decompiler.flash.treeitems.FolderItem; import com.jpexs.decompiler.flash.treeitems.FolderItem;
import com.jpexs.decompiler.flash.treeitems.HeaderItem; import com.jpexs.decompiler.flash.treeitems.HeaderItem;
@@ -174,6 +176,11 @@ public abstract class AbstractTagTree extends JTree {
} }
public static Icon getIconFor(TreeItem val, boolean folderExpanded) { public static Icon getIconFor(TreeItem val, boolean folderExpanded) {
if (val instanceof SoundStreamHeadTypeTag) {
return View.getIcon("foldersounds16");
}
TreeNodeType type = getTreeNodeType(val); TreeNodeType type = getTreeNodeType(val);
if (type == TreeNodeType.FOLDER && folderExpanded) { if (type == TreeNodeType.FOLDER && folderExpanded) {
@@ -289,7 +296,8 @@ public abstract class AbstractTagTree extends JTree {
|| (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHeadTag)
|| (t instanceof SoundStreamHead2Tag) || (t instanceof SoundStreamHead2Tag)
|| (t instanceof DefineExternalSound) || (t instanceof DefineExternalSound)
|| (t instanceof DefineExternalStreamSound)) { || (t instanceof DefineExternalStreamSound)
|| (t instanceof SoundStreamFrameRange)) {
return TreeNodeType.SOUND; return TreeNodeType.SOUND;
} }
@@ -595,7 +603,11 @@ public abstract class AbstractTagTree extends JTree {
} }
} }
if (d instanceof Tag || d instanceof ASMSource || d instanceof BinaryDataInterface) { if (d instanceof Tag
|| d instanceof ASMSource
|| d instanceof BinaryDataInterface
|| d instanceof SoundStreamFrameRange
) {
TreeNodeType nodeType = TagTree.getTreeNodeType(d); TreeNodeType nodeType = TagTree.getTreeNodeType(d);
if (nodeType == TreeNodeType.IMAGE) { if (nodeType == TreeNodeType.IMAGE) {
ret.add(d); ret.add(d);
@@ -1180,7 +1180,7 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceMenuItem.setVisible(true); replaceMenuItem.setVisible(true);
} }
if (canReplace.test(it -> it instanceof SoundTag)) { if (canReplace.test(it -> it instanceof SoundTag && ((SoundTag) it).importSupported())) {
replaceMenuItem.setVisible(true); replaceMenuItem.setVisible(true);
} }
@@ -40,6 +40,7 @@ import com.jpexs.decompiler.flash.timeline.AS2Package;
import com.jpexs.decompiler.flash.timeline.AS3Package; import com.jpexs.decompiler.flash.timeline.AS3Package;
import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.FrameScript; import com.jpexs.decompiler.flash.timeline.FrameScript;
import com.jpexs.decompiler.flash.timeline.SoundStreamFrameRange;
import com.jpexs.decompiler.flash.timeline.TagScript; import com.jpexs.decompiler.flash.timeline.TagScript;
import com.jpexs.decompiler.flash.timeline.Timeline; import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.timeline.Timelined;
@@ -264,8 +265,8 @@ public class TagTreeModel extends AbstractTagTreeModel {
for (int i = sounds.size() - 1; i >= 0; i--) { for (int i = sounds.size() - 1; i >= 0; i--) {
TreeItem sound = sounds.get(i); TreeItem sound = sounds.get(i);
if (sound instanceof SoundStreamHeadTypeTag) { if (sound instanceof SoundStreamHeadTypeTag) {
List<SoundStreamBlockTag> blocks = ((SoundStreamHeadTypeTag) sound).getBlocks(); List<SoundStreamFrameRange> ranges = ((SoundStreamHeadTypeTag) sound).getRanges();
if (blocks == null || blocks.isEmpty()) { if (ranges == null || ranges.isEmpty()) {
sounds.remove(i); sounds.remove(i);
} }
} }
@@ -580,6 +581,9 @@ public class TagTreeModel extends AbstractTagTreeModel {
} else if (parentNode instanceof ABC) { } else if (parentNode instanceof ABC) {
ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode); ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode);
return classesTreeModel.getAllChildren(classesTreeModel.getRoot()); return classesTreeModel.getAllChildren(classesTreeModel.getRoot());
} else if (parentNode instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) parentNode;
return head.getRanges();
} }
return result; return result;
@@ -662,6 +666,9 @@ public class TagTreeModel extends AbstractTagTreeModel {
} else if (parentNode instanceof ABC) { } else if (parentNode instanceof ABC) {
ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode); ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode);
return classesTreeModel.getChild(classesTreeModel.getRoot(), index); return classesTreeModel.getChild(classesTreeModel.getRoot(), index);
} else if (parentNode instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) parentNode;
return head.getRanges().get(index);
} }
throw new Error("Unsupported parent type: " + parentNode.getClass().getName()); throw new Error("Unsupported parent type: " + parentNode.getClass().getName());
@@ -713,6 +720,9 @@ public class TagTreeModel extends AbstractTagTreeModel {
} else if (parentNode instanceof ABC) { } else if (parentNode instanceof ABC) {
ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode); ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode);
return classesTreeModel.getChildCount(classesTreeModel.getRoot()); return classesTreeModel.getChildCount(classesTreeModel.getRoot());
} else if (parentNode instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) parentNode;
return head.getRanges().size();
} }
return 0; return 0;
@@ -787,6 +797,9 @@ public class TagTreeModel extends AbstractTagTreeModel {
} else if (parentNode instanceof ABC) { } else if (parentNode instanceof ABC) {
ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode); ClassesListTreeModel classesTreeModel = getClassesListTreeModel((ABC) parentNode);
return indexOfAdd(baseIndex, classesTreeModel.getIndexOfChild(classesTreeModel.getRoot(), childNode)); return indexOfAdd(baseIndex, classesTreeModel.getIndexOfChild(classesTreeModel.getRoot(), childNode));
} else if (parentNode instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) parentNode;
return indexOfAdd(baseIndex, head.getRanges().indexOf(childNode));
} }
return -1; return -1;