From 2fa2a2e022c8e64f7a7dae3a939a7f2b00f8f2a1 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Tue, 19 May 2015 14:21:28 +0200 Subject: [PATCH] faster populating sound stream blocks (do it only once instead of for every head tag) --- .../flash/tags/SoundStreamHead2Tag.java | 509 ++++++++--------- .../flash/tags/SoundStreamHeadTag.java | 531 +++++++++--------- .../decompiler/flash/timeline/Timeline.java | 41 ++ 3 files changed, 550 insertions(+), 531 deletions(-) diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java index 02ca2861f..0a608375b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java @@ -1,254 +1,255 @@ -/* - * Copyright (C) 2010-2015 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.helpers.ByteArrayRange; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * - * - * @author JPEXS - */ -public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag { - - public static final int ID = 45; - - public static final String NAME = "SoundStreamHead2"; - - @Reserved - @SWFType(value = BasicType.UB, count = 4) - public int reserved; - - @SWFType(value = BasicType.UB, count = 2) - public int playBackSoundRate; - - public boolean playBackSoundSize; - - public boolean playBackSoundType; - - @SWFType(value = BasicType.UB, count = 4) - public int streamSoundCompression; - - @SWFType(value = BasicType.UB, count = 2) - public int streamSoundRate; - - public boolean streamSoundSize; - - public boolean streamSoundType; - - @SWFType(BasicType.UI16) - public int streamSoundSampleCount; - - @SWFType(BasicType.SI16) - @Conditional(value = "streamSoundCompression", options = {2}) - public int latencySeek; - - @Internal - private int virtualCharacterId = 0; - - /** - * Constructor - * - * @param swf - */ - public SoundStreamHead2Tag(SWF swf) { - super(swf, ID, NAME, null); - } - - /** - * Constructor - * - * @param sis - * @param data - * @throws IOException - */ - public SoundStreamHead2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - reserved = (int) sis.readUB(4, "reserved"); - playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate"); - playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1; - playBackSoundType = sis.readUB(1, "playBackSoundType") == 1; - streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression"); - streamSoundRate = (int) sis.readUB(2, "streamSoundRate"); - streamSoundSize = sis.readUB(1, "streamSoundSize") == 1; - streamSoundType = sis.readUB(1, "streamSoundType") == 1; - streamSoundSampleCount = sis.readUI16("streamSoundSampleCount"); - if (streamSoundCompression == 2) { - latencySeek = sis.readSI16("latencySeek"); - } - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUB(4, reserved); - sos.writeUB(2, playBackSoundRate); - sos.writeUB(1, playBackSoundSize ? 1 : 0); - sos.writeUB(1, playBackSoundType ? 1 : 0); - sos.writeUB(4, streamSoundCompression); - sos.writeUB(2, streamSoundRate); - sos.writeUB(1, streamSoundSize ? 1 : 0); - sos.writeUB(1, streamSoundType ? 1 : 0); - sos.writeUI16(streamSoundSampleCount); - if (streamSoundCompression == 2) { - sos.writeSI16(latencySeek); - } - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - - @Override - public int getCharacterId() { - return virtualCharacterId; - } - - @Override - public void setCharacterId(int characterId) { - virtualCharacterId = characterId; - } - - @Override - public String getExportFormat() { - if (streamSoundCompression == SoundFormat.FORMAT_MP3) { - return "mp3"; - } - if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) { - return "wav"; - } - return "flv"; - } - - @Override - public long getSoundSampleCount() { - return streamSoundSampleCount; - } - - @Override - public void setVirtualCharacterId(int ch) { - virtualCharacterId = ch; - } - - @Override - public int getSoundFormatId() { - return streamSoundCompression; - } - - @Override - public int getSoundRate() { - return streamSoundRate; - } - - @Override - public boolean getSoundSize() { - return streamSoundSize; - } - - @Override - public boolean getSoundType() { - return streamSoundType; - } - - @Override - public List getBlocks() { - List ret = new ArrayList<>(); - SoundStreamHeadTag.populateSoundStreamBlocks(0, swf.tags, this, ret); - return ret; - - } - - @Override - public boolean importSupported() { - return false; - } - - @Override - public boolean setSound(InputStream is, int newSoundFormat) { - return false; - } - - @Override - public List getRawSoundData() { - List ret = new ArrayList<>(); - List blocks = getBlocks(); - for (SoundStreamBlockTag block : blocks) { - ByteArrayRange data = block.streamSoundData; - if (streamSoundCompression == SoundFormat.FORMAT_MP3) { - ret.add(data.getSubRange(4, data.getLength() - 4)); - } else { - ret.add(data); - } - } - return ret; - } - - @Override - public long getTotalSoundSampleCount() { - return getBlocks().size() * streamSoundSampleCount; - } - - @Override - public SoundFormat getSoundFormat() { - final int[] rateMap = {5512, 11025, 22050, 44100}; - return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType()); - } - - @Override - public String getCharacterExportFileName() { - String exportName = swf.getExportName(getCharacterId()); - return getCharacterId() + (exportName != null ? "_" + exportName : ""); - } -} +/* + * Copyright (C) 2010-2015 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.helpers.ByteArrayRange; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * + * + * @author JPEXS + */ +public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag { + + public static final int ID = 45; + + public static final String NAME = "SoundStreamHead2"; + + @Reserved + @SWFType(value = BasicType.UB, count = 4) + public int reserved; + + @SWFType(value = BasicType.UB, count = 2) + public int playBackSoundRate; + + public boolean playBackSoundSize; + + public boolean playBackSoundType; + + @SWFType(value = BasicType.UB, count = 4) + public int streamSoundCompression; + + @SWFType(value = BasicType.UB, count = 2) + public int streamSoundRate; + + public boolean streamSoundSize; + + public boolean streamSoundType; + + @SWFType(BasicType.UI16) + public int streamSoundSampleCount; + + @SWFType(BasicType.SI16) + @Conditional(value = "streamSoundCompression", options = {2}) + public int latencySeek; + + @Internal + private int virtualCharacterId = 0; + + /** + * Constructor + * + * @param swf + */ + public SoundStreamHead2Tag(SWF swf) { + super(swf, ID, NAME, null); + } + + /** + * Constructor + * + * @param sis + * @param data + * @throws IOException + */ + public SoundStreamHead2Tag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + reserved = (int) sis.readUB(4, "reserved"); + playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate"); + playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1; + playBackSoundType = sis.readUB(1, "playBackSoundType") == 1; + streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression"); + streamSoundRate = (int) sis.readUB(2, "streamSoundRate"); + streamSoundSize = sis.readUB(1, "streamSoundSize") == 1; + streamSoundType = sis.readUB(1, "streamSoundType") == 1; + streamSoundSampleCount = sis.readUI16("streamSoundSampleCount"); + if (streamSoundCompression == 2) { + latencySeek = sis.readSI16("latencySeek"); + } + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUB(4, reserved); + sos.writeUB(2, playBackSoundRate); + sos.writeUB(1, playBackSoundSize ? 1 : 0); + sos.writeUB(1, playBackSoundType ? 1 : 0); + sos.writeUB(4, streamSoundCompression); + sos.writeUB(2, streamSoundRate); + sos.writeUB(1, streamSoundSize ? 1 : 0); + sos.writeUB(1, streamSoundType ? 1 : 0); + sos.writeUI16(streamSoundSampleCount); + if (streamSoundCompression == 2) { + sos.writeSI16(latencySeek); + } + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + + @Override + public int getCharacterId() { + return virtualCharacterId; + } + + @Override + public void setCharacterId(int characterId) { + virtualCharacterId = characterId; + } + + @Override + public String getExportFormat() { + if (streamSoundCompression == SoundFormat.FORMAT_MP3) { + return "mp3"; + } + if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) { + return "wav"; + } + return "flv"; + } + + @Override + public long getSoundSampleCount() { + return streamSoundSampleCount; + } + + @Override + public void setVirtualCharacterId(int ch) { + virtualCharacterId = ch; + } + + @Override + public int getSoundFormatId() { + return streamSoundCompression; + } + + @Override + public int getSoundRate() { + return streamSoundRate; + } + + @Override + public boolean getSoundSize() { + return streamSoundSize; + } + + @Override + public boolean getSoundType() { + return streamSoundType; + } + + @Override + public List getBlocks() { + Timeline timeline = swf.getTimeline(); + List ret = timeline.getSoundStreamBlocks(this); + return ret; + + } + + @Override + public boolean importSupported() { + return false; + } + + @Override + public boolean setSound(InputStream is, int newSoundFormat) { + return false; + } + + @Override + public List getRawSoundData() { + List ret = new ArrayList<>(); + List blocks = getBlocks(); + for (SoundStreamBlockTag block : blocks) { + ByteArrayRange data = block.streamSoundData; + if (streamSoundCompression == SoundFormat.FORMAT_MP3) { + ret.add(data.getSubRange(4, data.getLength() - 4)); + } else { + ret.add(data); + } + } + return ret; + } + + @Override + public long getTotalSoundSampleCount() { + return getBlocks().size() * streamSoundSampleCount; + } + + @Override + public SoundFormat getSoundFormat() { + final int[] rateMap = {5512, 11025, 22050, 44100}; + return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType()); + } + + @Override + public String getCharacterExportFileName() { + String exportName = swf.getExportName(getCharacterId()); + return getCharacterId() + (exportName != null ? "_" + exportName : ""); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java index bfd9b9488..8575794bb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java @@ -1,277 +1,254 @@ -/* - * Copyright (C) 2010-2015 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.helpers.ByteArrayRange; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * - * - * @author JPEXS - */ -public class SoundStreamHeadTag extends Tag implements SoundStreamHeadTypeTag { - - public static final int ID = 18; - - public static final String NAME = "SoundStreamHead"; - - @Reserved - @SWFType(value = BasicType.UB, count = 4) - public int reserved; - - @SWFType(value = BasicType.UB, count = 2) - public int playBackSoundRate; - - public boolean playBackSoundSize; - - public boolean playBackSoundType; - - @SWFType(value = BasicType.UB, count = 4) - public int streamSoundCompression; - - @SWFType(value = BasicType.UB, count = 2) - public int streamSoundRate; - - public boolean streamSoundSize; - - public boolean streamSoundType; - - @SWFType(value = BasicType.UI16) - public int streamSoundSampleCount; - - @Conditional(value = "streamSoundCompression", options = {2}) - public int latencySeek; - - @Internal - private int virtualCharacterId = 0; - - /** - * Constructor - * - * @param swf - */ - public SoundStreamHeadTag(SWF swf) { - super(swf, ID, NAME, null); - } - - /** - * Constructor - * - * @param sis - * @param data - * @throws IOException - */ - public SoundStreamHeadTag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - reserved = (int) sis.readUB(4, "reserved"); - playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate"); - playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1; - playBackSoundType = sis.readUB(1, "playBackSoundType") == 1; - streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression"); - streamSoundRate = (int) sis.readUB(2, "streamSoundRate"); - streamSoundSize = sis.readUB(1, "streamSoundSize") == 1; - streamSoundType = sis.readUB(1, "streamSoundType") == 1; - streamSoundSampleCount = sis.readUI16("streamSoundSampleCount"); - if (streamSoundCompression == 2) { - latencySeek = sis.readSI16("latencySeek"); - } - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUB(4, reserved); - sos.writeUB(2, playBackSoundRate); - sos.writeUB(1, playBackSoundSize ? 1 : 0); - sos.writeUB(1, playBackSoundType ? 1 : 0); - sos.writeUB(4, streamSoundCompression); - sos.writeUB(2, streamSoundRate); - sos.writeUB(1, streamSoundSize ? 1 : 0); - sos.writeUB(1, streamSoundType ? 1 : 0); - sos.writeUI16(streamSoundSampleCount); - if (streamSoundCompression == 2) { - sos.writeSI16(latencySeek); - } - } catch (IOException e) { - throw new Error("This should never happen.", e); - } - return baos.toByteArray(); - } - - @Override - public String getExportFormat() { - if (streamSoundCompression == SoundFormat.FORMAT_MP3) { - return "mp3"; - } - if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) { - return "wav"; - } - if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) { - return "wav"; - } - return "flv"; - } - - @Override - public int getCharacterId() { - return virtualCharacterId; - } - - @Override - public void setCharacterId(int characterId) { - virtualCharacterId = characterId; - } - - @Override - public void setVirtualCharacterId(int ch) { - virtualCharacterId = ch; - } - - @Override - public long getSoundSampleCount() { - return streamSoundSampleCount; - } - - @Override - public int getSoundFormatId() { - return streamSoundCompression; - } - - @Override - public int getSoundRate() { - return streamSoundRate; - } - - @Override - public boolean getSoundSize() { - return streamSoundSize; - } - - @Override - public boolean getSoundType() { - return streamSoundType; - } - - public static void populateSoundStreamBlocks(int containerId, List tags, SoundStreamHeadTypeTag head, List output) { - boolean found = false; - for (Tag t : tags) { - if (t == head) { - found = true; - head.setVirtualCharacterId(containerId); - continue; - } - if (t instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) t; - populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getSubTags(), head, output); - } - if (!found) { - continue; - } - if (t instanceof SoundStreamBlockTag) { - output.add((SoundStreamBlockTag) t); - } - if (t instanceof SoundStreamHeadTypeTag) { - break; - } - } - } - - @Override - public List getBlocks() { - List ret = new ArrayList<>(); - populateSoundStreamBlocks(0, swf.tags, this, ret); - return ret; - - } - - @Override - public boolean importSupported() { - return false; - } - - @Override - public boolean setSound(InputStream is, int newSoundFormat) { - return false; - } - - @Override - public List getRawSoundData() { - List ret = new ArrayList<>(); - List blocks = getBlocks(); - for (SoundStreamBlockTag block : blocks) { - ByteArrayRange data = block.streamSoundData; - if (streamSoundCompression == SoundFormat.FORMAT_MP3) { - ret.add(data.getSubRange(4, data.getLength() - 4)); - } else { - ret.add(data); - } - } - return ret; - } - - @Override - public long getTotalSoundSampleCount() { - return getBlocks().size() * streamSoundSampleCount; - } - - @Override - public SoundFormat getSoundFormat() { - final int[] rateMap = {5512, 11025, 22050, 44100}; - return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType()); - } - - @Override - public String getCharacterExportFileName() { - String exportName = swf.getExportName(getCharacterId()); - return getCharacterId() + (exportName != null ? "_" + exportName : ""); - } -} +/* + * Copyright (C) 2010-2015 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.helpers.ByteArrayRange; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * + * + * @author JPEXS + */ +public class SoundStreamHeadTag extends Tag implements SoundStreamHeadTypeTag { + + public static final int ID = 18; + + public static final String NAME = "SoundStreamHead"; + + @Reserved + @SWFType(value = BasicType.UB, count = 4) + public int reserved; + + @SWFType(value = BasicType.UB, count = 2) + public int playBackSoundRate; + + public boolean playBackSoundSize; + + public boolean playBackSoundType; + + @SWFType(value = BasicType.UB, count = 4) + public int streamSoundCompression; + + @SWFType(value = BasicType.UB, count = 2) + public int streamSoundRate; + + public boolean streamSoundSize; + + public boolean streamSoundType; + + @SWFType(value = BasicType.UI16) + public int streamSoundSampleCount; + + @Conditional(value = "streamSoundCompression", options = {2}) + public int latencySeek; + + @Internal + private int virtualCharacterId = 0; + + /** + * Constructor + * + * @param swf + */ + public SoundStreamHeadTag(SWF swf) { + super(swf, ID, NAME, null); + } + + /** + * Constructor + * + * @param sis + * @param data + * @throws IOException + */ + public SoundStreamHeadTag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + reserved = (int) sis.readUB(4, "reserved"); + playBackSoundRate = (int) sis.readUB(2, "playBackSoundRate"); + playBackSoundSize = sis.readUB(1, "playBackSoundSize") == 1; + playBackSoundType = sis.readUB(1, "playBackSoundType") == 1; + streamSoundCompression = (int) sis.readUB(4, "streamSoundCompression"); + streamSoundRate = (int) sis.readUB(2, "streamSoundRate"); + streamSoundSize = sis.readUB(1, "streamSoundSize") == 1; + streamSoundType = sis.readUB(1, "streamSoundType") == 1; + streamSoundSampleCount = sis.readUI16("streamSoundSampleCount"); + if (streamSoundCompression == 2) { + latencySeek = sis.readSI16("latencySeek"); + } + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUB(4, reserved); + sos.writeUB(2, playBackSoundRate); + sos.writeUB(1, playBackSoundSize ? 1 : 0); + sos.writeUB(1, playBackSoundType ? 1 : 0); + sos.writeUB(4, streamSoundCompression); + sos.writeUB(2, streamSoundRate); + sos.writeUB(1, streamSoundSize ? 1 : 0); + sos.writeUB(1, streamSoundType ? 1 : 0); + sos.writeUI16(streamSoundSampleCount); + if (streamSoundCompression == 2) { + sos.writeSI16(latencySeek); + } + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + return baos.toByteArray(); + } + + @Override + public String getExportFormat() { + if (streamSoundCompression == SoundFormat.FORMAT_MP3) { + return "mp3"; + } + if (streamSoundCompression == SoundFormat.FORMAT_ADPCM) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN) { + return "wav"; + } + if (streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER16KHZ || streamSoundCompression == SoundFormat.FORMAT_NELLYMOSER8KHZ) { + return "wav"; + } + return "flv"; + } + + @Override + public int getCharacterId() { + return virtualCharacterId; + } + + @Override + public void setCharacterId(int characterId) { + virtualCharacterId = characterId; + } + + @Override + public void setVirtualCharacterId(int ch) { + virtualCharacterId = ch; + } + + @Override + public long getSoundSampleCount() { + return streamSoundSampleCount; + } + + @Override + public int getSoundFormatId() { + return streamSoundCompression; + } + + @Override + public int getSoundRate() { + return streamSoundRate; + } + + @Override + public boolean getSoundSize() { + return streamSoundSize; + } + + @Override + public boolean getSoundType() { + return streamSoundType; + } + + @Override + public List getBlocks() { + Timeline timeline = swf.getTimeline(); + List ret = timeline.getSoundStreamBlocks(this); + return ret; + + } + + @Override + public boolean importSupported() { + return false; + } + + @Override + public boolean setSound(InputStream is, int newSoundFormat) { + return false; + } + + @Override + public List getRawSoundData() { + List ret = new ArrayList<>(); + List blocks = getBlocks(); + for (SoundStreamBlockTag block : blocks) { + ByteArrayRange data = block.streamSoundData; + if (streamSoundCompression == SoundFormat.FORMAT_MP3) { + ret.add(data.getSubRange(4, data.getLength() - 4)); + } else { + ret.add(data); + } + } + return ret; + } + + @Override + public long getTotalSoundSampleCount() { + return getBlocks().size() * streamSoundSampleCount; + } + + @Override + public SoundFormat getSoundFormat() { + final int[] rateMap = {5512, 11025, 22050, 44100}; + return new SoundFormat(getSoundFormatId(), rateMap[getSoundRate()], getSoundType()); + } + + @Override + public String getCharacterExportFileName() { + String exportName = swf.getExportName(getCharacterId()); + return getCharacterId() + (exportName != null ? "_" + exportName : ""); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 8c9ef9076..b7ecb5192 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -19,10 +19,12 @@ package com.jpexs.decompiler.flash.timeline; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.exporters.FrameExporter; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.DoInitActionTag; import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; import com.jpexs.decompiler.flash.tags.StartSound2Tag; import com.jpexs.decompiler.flash.tags.StartSoundTag; import com.jpexs.decompiler.flash.tags.Tag; @@ -35,6 +37,7 @@ import com.jpexs.decompiler.flash.tags.base.DrawableTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.RemoveTag; import com.jpexs.decompiler.flash.tags.base.RenderContext; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.types.CLIPACTIONS; import com.jpexs.decompiler.flash.types.ColorTransform; import com.jpexs.decompiler.flash.types.MATRIX; @@ -85,6 +88,8 @@ public class Timeline { private final Map actionFrames = new HashMap<>(); + private final Map> soundStramBlocks = new HashMap<>(); + private AS2Package as2RootPackage; public final List otherTags = new ArrayList<>(); @@ -124,6 +129,11 @@ public class Timeline { return depthMaxFrame; } + public List getSoundStreamBlocks(SoundStreamHeadTypeTag head) { + ensureInitialized(); + return soundStramBlocks.get(head); + } + public void reset(SWF swf) { reset(swf, null, swf.tags, 0, swf.displayRect); } @@ -135,6 +145,7 @@ public class Timeline { asmSources.clear(); asmSourceContainers.clear(); actionFrames.clear(); + soundStramBlocks.clear(); otherTags.clear(); this.id = id; this.swf = swf; @@ -342,6 +353,10 @@ public class Timeline { } createASPackages(); + if (parentTag == null) { + // popuplate only for main timeline + populateSoundStreamBlocks(0, tags); + } initialized = true; } @@ -410,6 +425,32 @@ public class Timeline { } } + private void populateSoundStreamBlocks(int containerId, List tags) { + List blocks = null; + for (Tag t : tags) { + if (t instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t; + head.setVirtualCharacterId(containerId); + blocks = new ArrayList<>(); + soundStramBlocks.put(head, blocks); + continue; + } + + if (t instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) t; + populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getSubTags()); + } + + if (blocks == null) { + continue; + } + + if (t instanceof SoundStreamBlockTag) { + blocks.add((SoundStreamBlockTag) t); + } + } + } + public void getNeededCharacters(Set usedCharacters) { for (int i = 0; i < getFrameCount(); i++) { getNeededCharacters(i, usedCharacters);