From 3c83eefc5c384487f251693a816ea11ec7bfe567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Fri, 17 May 2013 23:13:20 +0200 Subject: [PATCH] FLA export - sounds --- trunk/src/com/jpexs/decompiler/flash/SWF.java | 53 ++- .../jpexs/decompiler/flash/gui/TagNode.java | 2 +- .../decompiler/flash/tags/DefineSoundTag.java | 12 + .../flash/tags/SoundStreamHead2Tag.java | 28 +- .../flash/tags/SoundStreamHeadTag.java | 28 +- .../flash/tags/SoundStreamHeadTypeTag.java | 8 + .../flash/types/sound/MP3FRAME.java | 109 +++++ .../decompiler/flash/xfl/XFLConverter.java | 409 ++++++++++++++---- 8 files changed, 569 insertions(+), 80 deletions(-) create mode 100644 trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 0ba051161..2d4c3525c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -608,6 +608,56 @@ public class SWF { exportSounds(outdir, tags, mp3); } + public byte[] exportSound(Tag t) throws IOException { + boolean mp3 = true; + ByteArrayOutputStream fos = new ByteArrayOutputStream(); + int id = 0; + if (t instanceof DefineSoundTag) { + id = ((DefineSoundTag) t).soundId; + } + + + if (t instanceof DefineSoundTag) { + DefineSoundTag st = (DefineSoundTag) t; + if ((st.soundFormat == DefineSoundTag.FORMAT_MP3) && mp3) { + fos = new ByteArrayOutputStream(); + fos.write(st.soundData); + } else { + fos = new ByteArrayOutputStream(); + FLVOutputStream flv = new FLVOutputStream(fos); + flv.writeHeader(true, false); + flv.writeTag(new FLVTAG(0, new AUDIODATA(st.soundFormat, st.soundRate, st.soundSize, st.soundType, st.soundData))); + } + } + if (t instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag shead = (SoundStreamHeadTypeTag) t; + List blocks = new ArrayList(); + List objs = new ArrayList(this.tags); + populateSoundStreamBlocks(objs, t, blocks); + if ((shead.getSoundFormat() == 2) && mp3) { + fos = new ByteArrayOutputStream(); + for (int b = 0; b < blocks.size(); b++) { + byte data[] = blocks.get(b).getData(SWF.DEFAULT_VERSION); + fos.write(data, 4, data.length - 4); + } + } else { + fos = new ByteArrayOutputStream(); + FLVOutputStream flv = new FLVOutputStream(fos); + flv.writeHeader(true, false); + + int ms = (int) (1000.0f / ((float) frameRate)); + for (int b = 0; b < blocks.size(); b++) { + byte data[] = blocks.get(b).getData(SWF.DEFAULT_VERSION); + if (shead.getSoundFormat() == 2) { //MP3 + data = Arrays.copyOfRange(data, 4, data.length); + } + flv.writeTag(new FLVTAG(ms * b, new AUDIODATA(shead.getSoundFormat(), shead.getSoundRate(), shead.getSoundSize(), shead.getSoundType(), data))); + } + } + } + return fos.toByteArray(); + } + public void exportSounds(String outdir, List tags, boolean mp3) throws IOException { if (tags.isEmpty()) { return; @@ -623,8 +673,7 @@ public class SWF { if (t instanceof DefineSoundTag) { id = ((DefineSoundTag) t).soundId; } - if (mp3) { - } + if (t instanceof DefineSoundTag) { DefineSoundTag st = (DefineSoundTag) t; diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/TagNode.java b/trunk/src/com/jpexs/decompiler/flash/gui/TagNode.java index d26235371..bddbcecf1 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/TagNode.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/TagNode.java @@ -207,7 +207,7 @@ public class TagNode { ((DefineButton2Tag) ret.get(i).tag).exportAssetsTags = exportAssetsTags; } if (ret.get(i).tag instanceof DoInitActionTag) { - ((DoInitActionTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags; + //((DoInitActionTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags; } if (ret.get(i).tag instanceof ASMSource) { ASMSource ass = (ASMSource) ret.get(i).tag; diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java index 4055c1328..565aea05d 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/DefineSoundTag.java @@ -94,4 +94,16 @@ public class DefineSoundTag extends CharacterTag { soundSampleCount = sis.readUI32(); soundData = sis.readBytes(sis.available()); } + + public String getExportFormat() { + if (soundFormat == FORMAT_MP3) { + return "mp3"; + } + return "flv"; + } + + public double getkHz() { + double rateMap[] = {5.5, 11, 22, 44}; + return rateMap[soundRate]; + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java index 0a9a0f81c..a8b5bf784 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHead2Tag.java @@ -18,6 +18,8 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import static com.jpexs.decompiler.flash.tags.DefineSoundTag.FORMAT_MP3; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -28,7 +30,7 @@ import java.io.OutputStream; * * @author JPEXS */ -public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag { +public class SoundStreamHead2Tag extends CharacterTag implements SoundStreamHeadTypeTag { public int playBackSoundRate; public int playBackSoundSize; @@ -39,6 +41,30 @@ public class SoundStreamHead2Tag extends Tag implements SoundStreamHeadTypeTag { public int streamSoundType; public int streamSoundSampleCount; public int latencySeek; + private int virtualCharacterId = 0; + + @Override + public int getCharacterID() { + return virtualCharacterId; + } + + @Override + public String getExportFormat() { + if (streamSoundCompression == FORMAT_MP3) { + return "mp3"; + } + return "flv"; + } + + @Override + public long getSoundSampleCount() { + return streamSoundSampleCount; + } + + @Override + public void setVirtualCharacterId(int ch) { + virtualCharacterId = ch; + } /** * Gets data bytes diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java index 9e5fb49c4..69c4115ff 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTag.java @@ -18,6 +18,8 @@ package com.jpexs.decompiler.flash.tags; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import static com.jpexs.decompiler.flash.tags.DefineSoundTag.FORMAT_MP3; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -28,7 +30,7 @@ import java.io.OutputStream; * * @author JPEXS */ -public class SoundStreamHeadTag extends Tag implements SoundStreamHeadTypeTag { +public class SoundStreamHeadTag extends CharacterTag implements SoundStreamHeadTypeTag { public int playBackSoundRate; public int playBackSoundSize; @@ -39,6 +41,30 @@ public class SoundStreamHeadTag extends Tag implements SoundStreamHeadTypeTag { public int streamSoundType; public int streamSoundSampleCount; public int latencySeek; + private int virtualCharacterId = 0; + + @Override + public String getExportFormat() { + if (streamSoundCompression == FORMAT_MP3) { + return "mp3"; + } + return "flv"; + } + + @Override + public int getCharacterID() { + return virtualCharacterId; + } + + @Override + public void setVirtualCharacterId(int ch) { + virtualCharacterId = ch; + } + + @Override + public long getSoundSampleCount() { + return streamSoundSampleCount; + } /** * Gets data bytes diff --git a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTypeTag.java b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTypeTag.java index 12a70a22c..e84cab2df 100644 --- a/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTypeTag.java +++ b/trunk/src/com/jpexs/decompiler/flash/tags/SoundStreamHeadTypeTag.java @@ -13,4 +13,12 @@ public interface SoundStreamHeadTypeTag { public int getSoundSize(); public int getSoundType(); + + public long getSoundSampleCount(); + + public void setVirtualCharacterId(int ch); + + public int getCharacterID(); + + public String getExportFormat(); } diff --git a/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java new file mode 100644 index 000000000..9d8382714 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/types/sound/MP3FRAME.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.types.sound; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author JPEXS + */ +public class MP3FRAME { + + public int syncWord; + public int mpegVersion; + public int layer; + public boolean protectionBit; + public int bitRate; + public int samplingRate; + public boolean paddingBit; + public int reserved; + public int channelMode; + public int modelExtension; + public boolean copyright; + public boolean original; + public int emphasis; + + public MP3FRAME(InputStream is) throws IOException { + SWFInputStream sis = new SWFInputStream(is, SWF.DEFAULT_VERSION); + syncWord = (int) sis.readUB(11); + mpegVersion = (int) sis.readUB(2); + layer = (int) sis.readUB(2); + protectionBit = sis.readUB(1) == 1; + bitRate = (int) sis.readUB(4); + samplingRate = (int) sis.readUB(2); + paddingBit = sis.readUB(1) == 1; + reserved = (int) sis.readUB(1); + channelMode = (int) sis.readUB(2); + modelExtension = (int) sis.readUB(2); + //TODO:read sample data + } + + public boolean isMPEG2() { + return mpegVersion == 2; + } + + public boolean isMPEG1() { + return mpegVersion == 3; + } + + public boolean isLayerI() { + return layer == 3; + } + + public boolean isLayerII() { + return layer == 2; + } + + public boolean isLayerIII() { + return layer == 1; + } + + public int getBitrate() { + int v1_l1_map[] = {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1}; + int v1_l2_map[] = {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1}; + int v1_l3_map[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1}; + int v2_l1_map[] = {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1}; + int v2_l2l3_map[] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}; + + int map[][][] = { + {v1_l1_map, v1_l2_map, v1_l3_map}, + {v2_l1_map, v2_l2l3_map, v2_l2l3_map} + }; + int layer = 0; + if (isLayerI()) { + layer = 0; + } + if (isLayerII()) { + layer = 1; + } + if (isLayerIII()) { + layer = 2; + } + int mver = 0; + if (isMPEG1()) { + mver = 0; + } + if (isMPEG2()) { + mver = 1; + } + return map[mver][layer][bitRate]; + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index cf9b35fec..de7159848 100644 --- a/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/trunk/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.xfl; import com.jpexs.decompiler.flash.Main; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.helpers.Highlighting; import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; @@ -25,6 +26,7 @@ import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; import com.jpexs.decompiler.flash.tags.DefineButtonTag; import com.jpexs.decompiler.flash.tags.DefineEditTextTag; import com.jpexs.decompiler.flash.tags.DefineFontNameTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineText2Tag; import com.jpexs.decompiler.flash.tags.DefineTextTag; @@ -36,6 +38,9 @@ import com.jpexs.decompiler.flash.tags.FrameLabelTag; import com.jpexs.decompiler.flash.tags.PlaceObjectTypeTag; 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.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.StartSoundTag; import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.base.ASMSource; @@ -63,6 +68,7 @@ import com.jpexs.decompiler.flash.types.RGB; import com.jpexs.decompiler.flash.types.RGBA; import com.jpexs.decompiler.flash.types.SHAPE; import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; import com.jpexs.decompiler.flash.types.TEXTRECORD; import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; import com.jpexs.decompiler.flash.types.filters.BLURFILTER; @@ -76,6 +82,7 @@ import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.decompiler.flash.types.sound.MP3FRAME; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.Point; @@ -586,7 +593,20 @@ public class XFLConverter { private static HashMap getCharacters(List tags) { HashMap ret = new HashMap(); + int maxId = 0; for (Tag t : tags) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (ct.getCharacterID() > maxId) { + maxId = ct.getCharacterID(); + } + } + } + for (Tag t : tags) { + if (t instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag ssh = (SoundStreamHeadTypeTag) t; + ssh.setVirtualCharacterId(maxId++); + } if (t instanceof CharacterTag) { CharacterTag ct = (CharacterTag) t; ret.put(ct.getCharacterID(), ct); @@ -958,7 +978,7 @@ public class XFLConverter { return new Date().getTime() / 1000; } - public static String convertLibrary(Map characterVariables, Map characterClasses, List oneInstanceShapes, String backgroundColor, List tags, HashMap characters, HashMap files) { + public static String convertLibrary(SWF swf, Map characterVariables, Map characterClasses, List oneInstanceShapes, String backgroundColor, List tags, HashMap characters, HashMap files) { //TODO: Imported assets //linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf" @@ -1122,6 +1142,156 @@ public class XFLConverter { mediaLinkStr += " quality=\"50\" href=\"" + symbolFile + "\" bitmapDataHRef=\"M " + (media.size() + 1) + " " + getTimestamp() + ".dat\" frameRight=\"" + image.getWidth() + "\" frameBottom=\"" + image.getHeight() + "\"/>\n"; media.add(mediaLinkStr); + } else if ((symbol instanceof SoundStreamHeadTypeTag) || (symbol instanceof DefineSoundTag)) { + int soundFormat = 0; + int soundRate = 0; + int soundType = 0; + int soundSize = 0; + long soundSampleCount = 0; + byte soundData[] = new byte[0]; + double rateMap[] = {5.5, 11, 22, 44}; + String exportFormat = "flv"; + if (symbol instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag sstream = (SoundStreamHeadTypeTag) symbol; + soundFormat = sstream.getSoundFormat(); + soundRate = sstream.getSoundRate(); + soundType = sstream.getSoundType(); + soundSize = sstream.getSoundSize(); + soundSampleCount = sstream.getSoundSampleCount(); + boolean found = false; + for (Tag t : tags) { + if (found && (t instanceof SoundStreamBlockTag)) { + SoundStreamBlockTag bl = (SoundStreamBlockTag) t; + soundData = bl.getData(SWF.DEFAULT_VERSION); + break; + } + if (t == symbol) { + found = true; + } + } + } else if (symbol instanceof DefineSoundTag) { + DefineSoundTag sound = (DefineSoundTag) symbol; + soundFormat = sound.soundFormat; + soundRate = sound.soundRate; + soundType = sound.soundType; + soundData = sound.soundData; + soundSize = sound.soundSize; + soundSampleCount = sound.soundSampleCount; + } + int format = 0; + int bits = 0; + if ((soundFormat == DefineSoundTag.FORMAT_ADPCM) + || (soundFormat == DefineSoundTag.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) + || (soundFormat == DefineSoundTag.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN)) { + if (soundType == 1) { //stereo + format += 1; + } + switch (soundRate) { + case 0: + format += 2; + break; + case 1: + format += 6; + break; + case 2: + format += 10; + break; + case 3: + format += 14; + break; + } + } + if (soundFormat == DefineSoundTag.FORMAT_SPEEX) { + bits = 18; + } + if (soundFormat == DefineSoundTag.FORMAT_ADPCM) { + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(soundData), SWF.DEFAULT_VERSION); + + try { + int adpcmCodeSize = (int) sis.readUB(2); + bits = 2 + adpcmCodeSize; + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + } + if (soundFormat == DefineSoundTag.FORMAT_MP3) { + exportFormat = "mp3"; + if (soundType == 0) { //mono + format += 1; + } + format += 4; //quality best + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(soundData), SWF.DEFAULT_VERSION); + try { + sis.readSI16(); + MP3FRAME frame = new MP3FRAME(sis); + int bitRate = frame.getBitrate(); + + switch (bitRate) { + case 8: + bits = 6; + break; + case 16: + bits = 7; + break; + case 20: + bits = 8; + break; + case 24: + bits = 9; + break; + case 32: + bits = 10; + break; + case 48: + bits = 11; + break; + case 56: + bits = 12; + break; + case 64: + bits = 13; + break; + case 80: + bits = 14; + break; + case 112: + bits = 15; + break; + case 128: + bits = 16; + break; + case 160: + bits = 17; + break; + + } + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + } + byte data[] = new byte[0]; + try { + data = swf.exportSound(symbol); + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + String symbolFile = "sound" + symbol.getCharacterID() + "." + exportFormat; + files.put(symbolFile, data); + String mediaLinkStr = " characters, List tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, String frameName, boolean isAnchor, int duration, String actionScript, String elements) { + String ret = ""; + DefineSoundTag sound = null; + if (startSound != null) { + for (Tag t : tags) { + if (t instanceof DefineSoundTag) { + DefineSoundTag s = (DefineSoundTag) t; + if (s.soundId == startSound.soundId) { + sound = s; + break; + } + } + } + } + + ret += " 1) { + ret += " duration=\"" + duration + "\""; + } + if (frameName != null) { + ret += " name=\"" + frameName + "\""; + if (isAnchor) { + ret += " labelType=\"anchor\" bookmark=\"true\""; + } else { + ret += " labelType=\"name\""; + } + isAnchor = false; + frameName = null; + } + ret += " keyMode=\"" + KEY_MODE_NORMAL + "\""; + + String soundEnvelopeStr = ""; + if (soundStreamHead != null) { + ret += " soundName=\"sound" + soundStreamHead.getCharacterID() + "." + soundStreamHead.getExportFormat() + "\""; + ret += " soundSync=\"stream\""; + soundEnvelopeStr += ""; + soundEnvelopeStr += ""; + soundEnvelopeStr += ""; + } + if (startSound != null) { + ret += " soundName=\"sound" + sound.soundId + "." + sound.getExportFormat() + "\""; + if (startSound.soundInfo.hasInPoint) { + ret += " inPoint44=\"" + startSound.soundInfo.inPoint + "\""; + } + if (startSound.soundInfo.hasOutPoint) { + ret += " outPoint44=\"" + startSound.soundInfo.outPoint + "\""; + } + if (startSound.soundInfo.hasLoops) { + if (startSound.soundInfo.loopCount == 32767) { + ret += " soundLoopMode=\"loop\""; + } + ret += " soundLoop=\"" + startSound.soundInfo.loopCount + "\""; + } + + if (!startSound.soundInfo.syncNoMultiple && !startSound.soundInfo.syncStop) { + //event + } else if (startSound.soundInfo.syncNoMultiple && !startSound.soundInfo.syncStop) { + ret += " soundSync=\"start\""; + } + soundEnvelopeStr += ""; + if (startSound.soundInfo.hasEnvelope) { + for (SOUNDENVELOPE env : startSound.soundInfo.envelopeRecords) { + soundEnvelopeStr += ""; + } + + if (startSound.soundInfo.envelopeRecords.length == 1 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 0) { + ret += " soundEffect=\"left channel\""; + } else if (startSound.soundInfo.envelopeRecords.length == 1 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768) { + ret += " soundEffect=\"right channel\""; + } else if (startSound.soundInfo.envelopeRecords.length == 2 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 0 + && startSound.soundInfo.envelopeRecords[1].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount + && startSound.soundInfo.envelopeRecords[1].rightLevel == 32768) { + ret += " soundEffect=\"fade left to right\""; + } else if (startSound.soundInfo.envelopeRecords.length == 2 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768 + && startSound.soundInfo.envelopeRecords[1].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount + && startSound.soundInfo.envelopeRecords[1].rightLevel == 0) { + ret += " soundEffect=\"fade right to left\""; + } else { + ret += " soundEffect=\"custom\""; + } + //TODO: fade in, fade out + + } else { + soundEnvelopeStr += ""; + } + soundEnvelopeStr += ""; + } + ret += ">"; + + ret += soundEnvelopeStr; + if (!actionScript.equals("")) { + ret += ""; + } + ret += ""; + ret += elements; + ret += ""; + ret += ""; + return ret; + } + private static String convertFrames(String initActionScript, String prevStr, String afterStr, List oneInstanceShapes, List tags, HashMap characters, int depth) { String ret = ""; prevStr += ""; @@ -1173,6 +1459,8 @@ public class XFLConverter { String lastActionScript = ""; String frameName = null; boolean isAnchor = false; + StartSoundTag startSound = null; + SoundStreamHeadTypeTag soundStreamHead = null; for (Tag t : tags) { if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; @@ -1194,75 +1482,68 @@ public class XFLConverter { } } } + if (t instanceof StartSoundTag) { + if (startSound != null) { + lastActionScript = initActionScript + lastActionScript; + initActionScript = ""; + ret += convertFrame(characters, tags, soundStreamHead, startSound, frame, frameName, isAnchor, duration, lastActionScript, lastElements); + frame += duration; + duration = 1; + lastElements = elements; + lastActionScript = actionScript; + elements = ""; + actionScript = ""; + frameName = null; + isAnchor = false; + soundStreamHead = null; + } + startSound = (StartSoundTag) t; + } + if (t instanceof SoundStreamHeadTypeTag) { + if (soundStreamHead != null) { + lastActionScript = initActionScript + lastActionScript; + initActionScript = ""; + ret += convertFrame(characters, tags, soundStreamHead, startSound, frame, frameName, isAnchor, duration, lastActionScript, lastElements); + frame += duration; + duration = 1; + lastElements = elements; + lastActionScript = actionScript; + elements = ""; + actionScript = ""; + frameName = null; + isAnchor = false; + startSound = null; + soundStreamHead = null; + } + soundStreamHead = (SoundStreamHeadTypeTag) t; + } if (t instanceof DoActionTag) { actionScript += convertActionScript((DoActionTag) t); } if (t instanceof FrameLabelTag) { FrameLabelTag flt = (FrameLabelTag) t; if (frameName != null) { - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - ret += " name=\"" + frameName + "\""; - if (isAnchor) { - ret += " labelType=\"anchor\" bookmark=\"true\""; - } else { - ret += " labelType=\"name\""; - } - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; lastActionScript = initActionScript + lastActionScript; initActionScript = ""; - - if (!lastActionScript.equals("")) { - ret += ""; - } - ret += ""; - ret += lastElements; - ret += ""; - ret += ""; + ret += convertFrame(characters, tags, soundStreamHead, startSound, frame, frameName, isAnchor, duration, lastActionScript, lastElements); frame += duration; duration = 1; lastElements = elements; lastActionScript = actionScript; elements = ""; actionScript = ""; + startSound = null; + soundStreamHead = null; } frameName = flt.getLabelName(); isAnchor = flt.isNamedAnchor(); } if (t instanceof ShowFrameTag) { - if (!elements.equals("") || !actionScript.equals("")) { + if (!elements.equals("") || !actionScript.equals("") || soundStreamHead != null || startSound != null) { if (!lastElements.equals("") || !lastActionScript.equals("")) { - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - if (frameName != null) { - ret += " name=\"" + frameName + "\""; - if (isAnchor) { - ret += " labelType=\"anchor\" bookmark=\"true\""; - } else { - ret += " labelType=\"name\""; - } - isAnchor = false; - frameName = null; - } - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; lastActionScript = initActionScript + lastActionScript; initActionScript = ""; - - if (!lastActionScript.equals("")) { - ret += ""; - } - ret += ""; - ret += lastElements; - ret += ""; - ret += ""; + ret += convertFrame(characters, tags, soundStreamHead, startSound, frame, frameName, isAnchor, duration, lastActionScript, lastElements); } frame += duration; duration = 1; @@ -1270,6 +1551,8 @@ public class XFLConverter { lastActionScript = actionScript; elements = ""; actionScript = ""; + soundStreamHead = null; + startSound = null; } else { duration++; } @@ -1277,36 +1560,12 @@ public class XFLConverter { } lastActionScript = initActionScript + lastActionScript; initActionScript = ""; - if (!lastElements.equals("") || !lastActionScript.equals("")) { + if (!lastElements.equals("") || !lastActionScript.equals("") || soundStreamHead != null || startSound != null) { if (frame < 0) { frame = 0; duration = 1; } - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; - if (!lastActionScript.equals("")) { - ret += ""; - } - ret += ""; - ret += lastElements; - ret += ""; - ret += ""; - frame += duration; - duration = 1; - elements = ""; + ret += convertFrame(characters, tags, soundStreamHead, startSound, frame, frameName, isAnchor, duration, lastActionScript, lastElements); } afterStr = "" + afterStr; @@ -1699,7 +1958,7 @@ public class XFLConverter { domDocument += " frameRate=\"" + swf.frameRate + "\""; domDocument += ">"; - domDocument += convertLibrary(characterVariables, characterClasses, oneInstaceShapes, backgroundColor, swf.tags, characters, files); + domDocument += convertLibrary(swf, characterVariables, characterClasses, oneInstaceShapes, backgroundColor, swf.tags, characters, files); domDocument += ""; domDocument += convertTimeline("", oneInstaceShapes, backgroundColor, swf.tags, characters, "Scene 1"); domDocument += "";