/*
* Copyright (C) 2010-2016 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;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.special.ActionEnd;
import com.jpexs.decompiler.flash.action.special.ActionUnknown;
import com.jpexs.decompiler.flash.action.swf3.ActionGetURL;
import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel;
import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame;
import com.jpexs.decompiler.flash.action.swf3.ActionNextFrame;
import com.jpexs.decompiler.flash.action.swf3.ActionPlay;
import com.jpexs.decompiler.flash.action.swf3.ActionPrevFrame;
import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget;
import com.jpexs.decompiler.flash.action.swf3.ActionStop;
import com.jpexs.decompiler.flash.action.swf3.ActionStopSounds;
import com.jpexs.decompiler.flash.action.swf3.ActionToggleQuality;
import com.jpexs.decompiler.flash.action.swf3.ActionWaitForFrame;
import com.jpexs.decompiler.flash.action.swf4.ActionAdd;
import com.jpexs.decompiler.flash.action.swf4.ActionAnd;
import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar;
import com.jpexs.decompiler.flash.action.swf4.ActionCall;
import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii;
import com.jpexs.decompiler.flash.action.swf4.ActionCloneSprite;
import com.jpexs.decompiler.flash.action.swf4.ActionDivide;
import com.jpexs.decompiler.flash.action.swf4.ActionEndDrag;
import com.jpexs.decompiler.flash.action.swf4.ActionEquals;
import com.jpexs.decompiler.flash.action.swf4.ActionGetProperty;
import com.jpexs.decompiler.flash.action.swf4.ActionGetTime;
import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2;
import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable;
import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2;
import com.jpexs.decompiler.flash.action.swf4.ActionIf;
import com.jpexs.decompiler.flash.action.swf4.ActionJump;
import com.jpexs.decompiler.flash.action.swf4.ActionLess;
import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar;
import com.jpexs.decompiler.flash.action.swf4.ActionMBCharToAscii;
import com.jpexs.decompiler.flash.action.swf4.ActionMBStringExtract;
import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength;
import com.jpexs.decompiler.flash.action.swf4.ActionMultiply;
import com.jpexs.decompiler.flash.action.swf4.ActionNot;
import com.jpexs.decompiler.flash.action.swf4.ActionOr;
import com.jpexs.decompiler.flash.action.swf4.ActionPop;
import com.jpexs.decompiler.flash.action.swf4.ActionPush;
import com.jpexs.decompiler.flash.action.swf4.ActionRandomNumber;
import com.jpexs.decompiler.flash.action.swf4.ActionRemoveSprite;
import com.jpexs.decompiler.flash.action.swf4.ActionSetProperty;
import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2;
import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable;
import com.jpexs.decompiler.flash.action.swf4.ActionStartDrag;
import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd;
import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals;
import com.jpexs.decompiler.flash.action.swf4.ActionStringExtract;
import com.jpexs.decompiler.flash.action.swf4.ActionStringLength;
import com.jpexs.decompiler.flash.action.swf4.ActionStringLess;
import com.jpexs.decompiler.flash.action.swf4.ActionSubtract;
import com.jpexs.decompiler.flash.action.swf4.ActionToInteger;
import com.jpexs.decompiler.flash.action.swf4.ActionTrace;
import com.jpexs.decompiler.flash.action.swf4.ActionWaitForFrame2;
import com.jpexs.decompiler.flash.action.swf5.ActionAdd2;
import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd;
import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitOr;
import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift;
import com.jpexs.decompiler.flash.action.swf5.ActionBitXor;
import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod;
import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool;
import com.jpexs.decompiler.flash.action.swf5.ActionDecrement;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal;
import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2;
import com.jpexs.decompiler.flash.action.swf5.ActionDelete;
import com.jpexs.decompiler.flash.action.swf5.ActionDelete2;
import com.jpexs.decompiler.flash.action.swf5.ActionEnumerate;
import com.jpexs.decompiler.flash.action.swf5.ActionEquals2;
import com.jpexs.decompiler.flash.action.swf5.ActionGetMember;
import com.jpexs.decompiler.flash.action.swf5.ActionIncrement;
import com.jpexs.decompiler.flash.action.swf5.ActionInitArray;
import com.jpexs.decompiler.flash.action.swf5.ActionInitObject;
import com.jpexs.decompiler.flash.action.swf5.ActionLess2;
import com.jpexs.decompiler.flash.action.swf5.ActionModulo;
import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod;
import com.jpexs.decompiler.flash.action.swf5.ActionNewObject;
import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate;
import com.jpexs.decompiler.flash.action.swf5.ActionReturn;
import com.jpexs.decompiler.flash.action.swf5.ActionSetMember;
import com.jpexs.decompiler.flash.action.swf5.ActionStackSwap;
import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister;
import com.jpexs.decompiler.flash.action.swf5.ActionTargetPath;
import com.jpexs.decompiler.flash.action.swf5.ActionToNumber;
import com.jpexs.decompiler.flash.action.swf5.ActionToString;
import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf;
import com.jpexs.decompiler.flash.action.swf5.ActionWith;
import com.jpexs.decompiler.flash.action.swf6.ActionEnumerate2;
import com.jpexs.decompiler.flash.action.swf6.ActionGreater;
import com.jpexs.decompiler.flash.action.swf6.ActionInstanceOf;
import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals;
import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater;
import com.jpexs.decompiler.flash.action.swf7.ActionCastOp;
import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2;
import com.jpexs.decompiler.flash.action.swf7.ActionExtends;
import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp;
import com.jpexs.decompiler.flash.action.swf7.ActionThrow;
import com.jpexs.decompiler.flash.action.swf7.ActionTry;
import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream;
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecial;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType;
import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag;
import com.jpexs.decompiler.flash.tags.DebugIDTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag;
import com.jpexs.decompiler.flash.tags.DefineBitsTag;
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag;
import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag;
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
import com.jpexs.decompiler.flash.tags.DefineFont2Tag;
import com.jpexs.decompiler.flash.tags.DefineFont3Tag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag;
import com.jpexs.decompiler.flash.tags.DefineFontInfo2Tag;
import com.jpexs.decompiler.flash.tags.DefineFontInfoTag;
import com.jpexs.decompiler.flash.tags.DefineFontNameTag;
import com.jpexs.decompiler.flash.tags.DefineFontTag;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
import com.jpexs.decompiler.flash.tags.DefineScalingGridTag;
import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag;
import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineShape3Tag;
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
import com.jpexs.decompiler.flash.tags.DefineShapeTag;
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;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
import com.jpexs.decompiler.flash.tags.DoABCTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag;
import com.jpexs.decompiler.flash.tags.EnableDebuggerTag;
import com.jpexs.decompiler.flash.tags.EnableTelemetryTag;
import com.jpexs.decompiler.flash.tags.EndTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.FrameLabelTag;
import com.jpexs.decompiler.flash.tags.FreeAllTag;
import com.jpexs.decompiler.flash.tags.FreeCharacterTag;
import com.jpexs.decompiler.flash.tags.ImportAssets2Tag;
import com.jpexs.decompiler.flash.tags.ImportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.MetadataTag;
import com.jpexs.decompiler.flash.tags.NameCharacterTag;
import com.jpexs.decompiler.flash.tags.PathsArePostScriptTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject4Tag;
import com.jpexs.decompiler.flash.tags.PlaceObjectTag;
import com.jpexs.decompiler.flash.tags.ProductInfoTag;
import com.jpexs.decompiler.flash.tags.ProtectTag;
import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
import com.jpexs.decompiler.flash.tags.ScriptLimitsTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.SetTabIndexTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
import com.jpexs.decompiler.flash.tags.StartSound2Tag;
import com.jpexs.decompiler.flash.tags.StartSoundTag;
import com.jpexs.decompiler.flash.tags.SymbolClassTag;
import com.jpexs.decompiler.flash.tags.SyncFrameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.TagStub;
import com.jpexs.decompiler.flash.tags.UnknownTag;
import com.jpexs.decompiler.flash.tags.VideoFrameTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound;
import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound;
import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap;
import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage;
import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo;
import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA;
import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA;
import com.jpexs.decompiler.flash.types.ARGB;
import com.jpexs.decompiler.flash.types.BITMAPDATA;
import com.jpexs.decompiler.flash.types.BUTTONCONDACTION;
import com.jpexs.decompiler.flash.types.BUTTONRECORD;
import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD;
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
import com.jpexs.decompiler.flash.types.CLIPEVENTFLAGS;
import com.jpexs.decompiler.flash.types.COLORMAPDATA;
import com.jpexs.decompiler.flash.types.CXFORM;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.FOCALGRADIENT;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.KERNINGRECORD;
import com.jpexs.decompiler.flash.types.LANGCODE;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFOCALGRADIENT;
import com.jpexs.decompiler.flash.types.MORPHGRADIENT;
import com.jpexs.decompiler.flash.types.MORPHGRADRECORD;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLE;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.PIX15;
import com.jpexs.decompiler.flash.types.PIX24;
import com.jpexs.decompiler.flash.types.RECT;
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.SOUNDINFO;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.decompiler.flash.types.ZONEDATA;
import com.jpexs.decompiler.flash.types.ZONERECORD;
import com.jpexs.decompiler.flash.types.filters.BEVELFILTER;
import com.jpexs.decompiler.flash.types.filters.BLURFILTER;
import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER;
import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER;
import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.decompiler.flash.types.filters.GLOWFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER;
import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
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.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.ImmediateFuture;
import com.jpexs.helpers.MemoryInputStream;
import com.jpexs.helpers.ProgressListener;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.InflaterInputStream;
/**
* Class for reading data from SWF file
*
* @author JPEXS
*/
public class SWFInputStream implements AutoCloseable {
private MemoryInputStream is;
private long startingPos;
private static final Logger logger = Logger.getLogger(SWFInputStream.class.getName());
public static final byte[] BYTE_ARRAY_EMPTY = new byte[0];
private final List<ProgressListener> listeners = new ArrayList<>();
private long percentMax;
private SWF swf;
public DumpInfo dumpInfo;
public void addPercentListener(ProgressListener listener) {
listeners.add(listener);
}
public void removePercentListener(ProgressListener listener) {
int index = listeners.indexOf(listener);
if (index > -1) {
listeners.remove(index);
}
}
private void informListeners() {
if (listeners.size() > 0 && percentMax > 0) {
int percent = (int) (getPos() * 100 / percentMax);
if (lastPercent != percent) {
for (ProgressListener pl : listeners) {
pl.progress(percent);
}
lastPercent = percent;
}
}
}
public void setPercentMax(long percentMax) {
this.percentMax = percentMax;
}
/**
* Constructor
*
* @param swf SWF to read
* @param data SWF data
* @param startingPos
* @param limit
* @throws java.io.IOException
*/
public SWFInputStream(SWF swf, byte[] data, long startingPos, int limit) throws IOException {
this.swf = swf;
this.startingPos = startingPos;
is = new MemoryInputStream(data, 0, limit);
}
/**
* Constructor
*
* @param swf SWF to read
* @param data SWF data
* @throws java.io.IOException
*/
public SWFInputStream(SWF swf, byte[] data) throws IOException {
this(swf, data, 0L, data.length);
}
public SWF getSwf() {
return swf;
}
/**
* Gets position in bytes in the stream
*
* @return Number of bytes
*/
public long getPos() {
return startingPos + is.getPos();
}
/**
* Sets position in bytes in the stream
*
* @param pos Number of bytes
* @throws java.io.IOException
*/
public void seek(long pos) throws IOException {
is.seek(pos - startingPos);
}
private DumpInfo newDumpLevel(String name, String type) {
return newDumpLevel(name, type, DumpInfoSpecialType.NONE, null);
}
private DumpInfo newDumpLevel(String name, String type, DumpInfoSpecialType specialType, Object specialValue) {
if (dumpInfo != null) {
long startByte = is.getPos();
if (bitPos > 0) {
startByte--;
}
DumpInfo di = specialType == DumpInfoSpecialType.NONE
? new DumpInfo(name, type, null, startByte, bitPos, 0, 0)
: new DumpInfoSpecial(name, type, null, startByte, bitPos, 0, 0, specialType, specialValue);
di.parent = dumpInfo;
dumpInfo.getChildInfos().add(di);
dumpInfo = di;
}
return dumpInfo;
}
private void endDumpLevel() {
endDumpLevel(null);
}
private void endDumpLevel(Object value) {
if (dumpInfo != null) {
if (dumpInfo.startBit == 0 && bitPos == 0) {
dumpInfo.lengthBytes = is.getPos() - dumpInfo.startByte;
} else {
dumpInfo.lengthBits = (int) ((is.getPos() - dumpInfo.startByte - 1) * 8 - dumpInfo.startBit + (bitPos == 0 ? 8 : bitPos));
}
dumpInfo.previewValue = value;
dumpInfo = dumpInfo.parent;
}
}
private void endDumpLevelUntil(DumpInfo di) {
if (di != null) {
while (dumpInfo != null && dumpInfo != di) {
endDumpLevel();
}
}
}
/**
* Reads one byte from the stream
*
* @return byte
* @throws IOException
*/
private int readEx() throws IOException {
bitPos = 0;
return readNoBitReset();
}
private void alignByte() {
bitPos = 0;
}
private int lastPercent = -1;
private int readNoBitReset() throws IOException, EndOfStreamException {
int r = is.read();
if (r == -1) {
throw new EndOfStreamException();
}
informListeners();
return r;
}
/**
* Reads one UI8 (Unsigned 8bit integer) value from the stream
*
* @param name
* @return UI8 value or -1 on error
* @throws IOException
*/
public int readUI8(String name) throws IOException {
newDumpLevel(name, "UI8");
int ret = readEx();
endDumpLevel(ret);
return ret;
}
/**
* Reads one null terminated string value from the stream
*
* @param name
* @return String value
* @throws IOException
*/
public String readString(String name) throws IOException {
newDumpLevel(name, "string");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int r;
while (true) {
r = readEx();
if (r == 0) {
endDumpLevel();
return new String(baos.toByteArray(), Utf8Helper.charset);
}
baos.write(r);
}
}
/**
* Reads one netstring (length + string) value from the stream
*
* @param name
* @return String value
* @throws IOException
*/
public String readNetString(String name) throws IOException {
newDumpLevel(name, "string");
int length = readEx();
String ret = new String(readBytesInternalEx(length));
endDumpLevel();
return ret;
}
/**
* Reads one netstring (length + string) value from the stream
*
* @param name
* @param charset
* @return String value
* @throws IOException
*/
public String readNetString(String name, Charset charset) throws IOException {
newDumpLevel(name, "string");
int length = readEx();
String ret = new String(readBytesInternalEx(length), charset);
endDumpLevel();
return ret;
}
/**
* Reads one UI32 (Unsigned 32bit integer) value from the stream
*
* @param name
* @return UI32 value
* @throws IOException
*/
public long readUI32(String name) throws IOException {
newDumpLevel(name, "UI32");
long ret = readUI32Internal();
endDumpLevel(ret);
return ret;
}
/**
* Reads one UI32 (Unsigned 32bit integer) value from the stream
*
* @return UI32 value
* @throws IOException
*/
private long readUI32Internal() throws IOException {
return (readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24)) & 0xffffffff;
}
/**
* Reads one UI16 (Unsigned 16bit integer) value from the stream
*
* @param name
* @return UI16 value
* @throws IOException
*/
public int readUI16(String name) throws IOException {
newDumpLevel(name, "UI16");
int ret = readUI16Internal();
endDumpLevel(ret);
return ret;
}
/**
* Reads one UI16 (Unsigned 16bit integer) value from the stream
*
* @return UI16 value
* @throws IOException
*/
private int readUI16Internal() throws IOException {
return readEx() + (readEx() << 8);
}
public int readUI24(String name) throws IOException {
newDumpLevel(name, "UI24");
int ret = readEx() + (readEx() << 8) + (readEx() << 16);
endDumpLevel(ret);
return ret;
}
/**
* Reads one SI32 (Signed 32bit integer) value from the stream
*
* @param name
* @return SI32 value
* @throws IOException
*/
public long readSI32(String name) throws IOException {
newDumpLevel(name, "SI32");
long uval = readEx() + (readEx() << 8) + (readEx() << 16) + (readEx() << 24);
if (uval >= 0x80000000) {
uval = -(((~uval) & 0xffffffff) + 1);
}
endDumpLevel(uval);
return uval;
}
/**
* Reads one SI16 (Signed 16bit integer) value from the stream
*
* @param name
* @return SI16 value
* @throws IOException
*/
public int readSI16(String name) throws IOException {
newDumpLevel(name, "SI16");
int uval = readEx() + (readEx() << 8);
if (uval >= 0x8000) {
uval = -(((~uval) & 0xffff) + 1);
}
endDumpLevel(uval);
return uval;
}
/**
* Reads one SI8 (Signed 8bit integer) value from the stream
*
* @param name
* @return SI8 value
* @throws IOException
*/
public int readSI8(String name) throws IOException {
newDumpLevel(name, "SI8");
int ret = readSI8Internal();
endDumpLevel(ret);
return ret;
}
/**
* Reads one SI8 (Signed 8bit integer) value from the stream
*
* @return SI8 value
* @throws IOException
*/
public int readSI8Internal() throws IOException {
int uval = readEx();
if (uval >= 0x80) {
uval = -(((~uval) & 0xff) + 1);
}
return uval;
}
/**
* Reads one FIXED (Fixed point 16.16) value from the stream
*
* @param name
* @return FIXED value
* @throws IOException
*/
public double readFIXED(String name) throws IOException {
newDumpLevel(name, "FIXED");
int afterPoint = readUI16Internal();
int beforePoint = readUI16Internal();
double ret = beforePoint + ((double) (afterPoint)) / 65536;
endDumpLevel(ret);
return ret;
}
/**
* Reads one FIXED8 (Fixed point 8.8) signed value from the stream
*
* @param name
* @return FIXED8 value
* @throws IOException
*/
public float readFIXED8(String name) throws IOException {
newDumpLevel(name, "FIXED8");
int afterPoint = readEx();
int beforePoint = readSI8Internal();
float ret;
if (beforePoint < 0) {
ret = beforePoint - ((float) afterPoint) / 256;
} else {
ret = beforePoint + ((float) afterPoint) / 256;
}
endDumpLevel(ret);
return ret;
}
private long readLong() throws IOException {
byte[] readBuffer = readBytesInternalEx(8);
return (((long) readBuffer[3] << 56)
+ ((long) (readBuffer[2] & 255) << 48)
+ ((long) (readBuffer[1] & 255) << 40)
+ ((long) (readBuffer[0] & 255) << 32)
+ ((long) (readBuffer[7] & 255) << 24)
+ ((readBuffer[6] & 255) << 16)
+ ((readBuffer[5] & 255) << 8)
+ ((readBuffer[4] & 255)));
}
/**
* Reads one DOUBLE (double precision floating point value) value from the
* stream
*
* @param name
* @return DOUBLE value
* @throws IOException
*/
public double readDOUBLE(String name) throws IOException {
newDumpLevel(name, "DOUBLE");
long el = readLong();
double ret = Double.longBitsToDouble(el);
endDumpLevel(ret);
return ret;
}
/**
* Reads one FLOAT (single precision floating point value) value from the
* stream
*
* @param name
* @return FLOAT value
* @throws IOException
*/
public float readFLOAT(String name) throws IOException {
newDumpLevel(name, "FLOAT");
int val = (int) readUI32Internal();
float ret = Float.intBitsToFloat(val);
endDumpLevel(ret);
/*int sign = val >> 31;
int mantisa = val & 0x3FFFFF;
int exp = (val >> 22) & 0xFF;
float ret =(sign == 1 ? -1 : 1) * (float) Math.pow(2, exp)* (1+((mantisa)/ (float)(1<<23)));*/
return ret;
}
/**
* Reads one FLOAT16 (16bit floating point value) value from the stream
*
* @param name
* @return FLOAT16 value
* @throws IOException
*/
public float readFLOAT16(String name) throws IOException {
newDumpLevel(name, "FLOAT16");
int val = readUI16Internal();
int sign = val >> 15;
int mantisa = val & 0x3FF;
int exp = (val >> 10) & 0x1F;
float ret = (sign == 1 ? -1 : 1) * (float) Math.pow(2, exp) * (1 + ((mantisa) / (float) (1 << 10)));
endDumpLevel(ret);
return ret;
}
/**
* Reads bytes from the stream
*
* @param count Number of bytes to read
* @param name
* @return Array of read bytes
* @throws IOException
*/
public byte[] readBytesEx(long count, String name) throws IOException {
if (count <= 0) {
return BYTE_ARRAY_EMPTY;
}
newDumpLevel(name, "bytes");
byte[] ret = readBytesInternalEx(count);
endDumpLevel();
return ret;
}
/**
* Reads AMF3 encoded value from the stream
*
* @param name
* @return
* @throws IOException
*/
public Amf3Value readAmf3Object(String name) throws IOException, NoSerializerExistsException {
Amf3InputStream ai = new Amf3InputStream(is);
ai.dumpInfo = this.dumpInfo;
return new Amf3Value(ai.readValue("amfData"));
}
/**
* Reads byte range from the stream
*
* @param count Number of bytes to read
* @param name
* @return ByteArrayRange object
* @throws IOException
*/
public ByteArrayRange readByteRangeEx(long count, String name) throws IOException {
return readByteRangeEx(count, name, DumpInfoSpecialType.NONE, null);
}
/**
* Reads byte range from the stream
*
* @param count Number of bytes to read
* @param name
* @param specialType
* @param specialValue
* @return ByteArrayRange object
* @throws IOException
*/
public ByteArrayRange readByteRangeEx(long count, String name, DumpInfoSpecialType specialType, Object specialValue) throws IOException {
if (count <= 0) {
return ByteArrayRange.EMPTY;
}
newDumpLevel(name, "bytes", specialType, specialValue);
int startPos = (int) getPos();
skipBytesEx(count);
endDumpLevel();
return new ByteArrayRange(swf.uncompressedData, startPos, (int) count);
}
/**
* Reads bytes from the stream
*
* @param count Number of bytes to read
* @return Array of read bytes
* @throws IOException
*/
private byte[] readBytesInternalEx(long count) throws IOException {
if (count <= 0) {
return BYTE_ARRAY_EMPTY;
}
bitPos = 0;
byte[] ret = new byte[(int) count];
if (is.read(ret) != count) {
throw new EndOfStreamException();
}
informListeners();
return ret;
}
/**
* Skip bytes from the stream
*
* @param count Number of bytes to skip
* @throws IOException
*/
public void skipBytesEx(long count) throws IOException {
if (count <= 0) {
return;
}
bitPos = 0;
is.seek(is.getPos() + count);
if (is.available() < 0) {
throw new EndOfStreamException();
}
informListeners();
}
/**
* Skip bytes from the stream
*
* @param count Number of bytes to skip
* @param name
* @throws IOException
*/
public void skipBytesEx(long count, String name) throws IOException {
if (count <= 0) {
return;
}
newDumpLevel(name, "bytes");
skipBytesEx(count);
endDumpLevel();
}
/**
* Skip bytes from the stream
*
* @param count Number of bytes to skip
* @throws IOException
*/
public void skipBytes(long count) throws IOException {
try {
skipBytesEx(count);
} catch (EOFException | EndOfStreamException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
/**
* Reads bytes from the stream
*
* @param count Number of bytes to read
* @param name
* @return Array of read bytes
* @throws IOException
*/
public byte[] readBytes(int count, String name) throws IOException {
if (count <= 0) {
return BYTE_ARRAY_EMPTY;
}
newDumpLevel(name, "bytes");
byte[] ret = new byte[count];
int i = 0;
try {
for (i = 0; i < count; i++) {
ret[i] = (byte) readEx();
}
} catch (EOFException | EndOfStreamException ex) {
ret = Arrays.copyOf(ret, i); // truncate array
logger.log(Level.SEVERE, null, ex);
}
endDumpLevel();
return ret;
}
public byte[] readBytesZlib(long count, String name) throws IOException {
if (count == 0) {
return BYTE_ARRAY_EMPTY;
}
newDumpLevel(name, "bytesZlib");
byte[] data = readBytesInternalEx(count);
endDumpLevel();
return uncompressByteArray(data);
}
public static byte[] uncompressByteArray(byte[] data) throws IOException {
return uncompressByteArray(data, 0, data.length);
}
public static byte[] uncompressByteArray(byte[] data, int offset, int length) throws IOException {
InflaterInputStream dis = new InflaterInputStream(new ByteArrayInputStream(data, offset, length));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int c;
while ((c = dis.read(buf)) > 0) {
baos.write(buf, 0, c);
}
return baos.toByteArray();
}
/**
* Reads one EncodedU32 (Encoded unsigned 32bit value) value from the stream
*
* @param name
* @return U32 value
* @throws IOException
*/
public long readEncodedU32(String name) throws IOException {
newDumpLevel(name, "encodedU32");
int result = readEx();
if ((result & 0x00000080) == 0) {
endDumpLevel(result);
return result;
}
result = (result & 0x0000007f) | (readEx()) << 7;
if ((result & 0x00004000) == 0) {
endDumpLevel(result);
return result;
}
result = (result & 0x00003fff) | (readEx()) << 14;
if ((result & 0x00200000) == 0) {
endDumpLevel(result);
return result;
}
result = (result & 0x001fffff) | (readEx()) << 21;
if ((result & 0x10000000) == 0) {
endDumpLevel(result);
return result;
}
result = (result & 0x0fffffff) | (readEx()) << 28;
endDumpLevel(result);
return result;
}
private int bitPos = 0;
private int tempByte = 0;
/**
* Reads UB[nBits] (Unsigned-bit value) value from the stream
*
* @param nBits Number of bits which represent value
* @param name
* @return Unsigned value
* @throws IOException
*/
public long readUB(int nBits, String name) throws IOException {
if (nBits == 0) {
return 0;
}
newDumpLevel(name, "UB");
long ret = readUBInternal(nBits);
endDumpLevel(ret);
return ret;
}
/**
* Reads UB[nBits] (Unsigned-bit value) value from the stream
*
* @param nBits Number of bits which represent value
* @return Unsigned value
* @throws IOException
*/
private long readUBInternal(int nBits) throws IOException {
if (nBits == 0) {
return 0;
}
long ret = 0;
if (bitPos == 0) {
tempByte = readNoBitReset();
}
for (int bit = 0; bit < nBits; bit++) {
int nb = (tempByte >> (7 - bitPos)) & 1;
ret += (nb << (nBits - 1 - bit));
bitPos++;
if (bitPos == 8) {
bitPos = 0;
if (bit != nBits - 1) {
tempByte = readNoBitReset();
}
}
}
return ret;
}
/**
* Reads SB[nBits] (Signed-bit value) value from the stream
*
* @param nBits Number of bits which represent value
* @param name
* @return Signed value
* @throws IOException
*/
public long readSB(int nBits, String name) throws IOException {
if (nBits == 0) {
return 0;
}
newDumpLevel(name, "SB");
long ret = readSBInternal(nBits);
endDumpLevel(ret);
return ret;
}
/**
* Reads SB[nBits] (Signed-bit value) value from the stream
*
* @param nBits Number of bits which represent value
* @return Signed value
* @throws IOException
*/
private long readSBInternal(int nBits) throws IOException {
int uval = (int) readUBInternal(nBits);
int shift = 32 - nBits;
// sign extension
uval = (uval << shift) >> shift;
return uval;
}
/**
* Reads FB[nBits] (Signed fixed-point bit value) value from the stream
*
* @param nBits Number of bits which represent value
* @param name
* @return Fixed-point value
* @throws IOException
*/
public float readFB(int nBits, String name) throws IOException {
if (nBits == 0) {
return 0;
}
newDumpLevel(name, "FB");
float val = readSBInternal(nBits);
float ret = val / 0x10000;
endDumpLevel(ret);
return ret;
}
/**
* Reads one RECT value from the stream
*
* @param name
* @return RECT value
* @throws IOException
*/
public RECT readRECT(String name) throws IOException {
RECT ret = new RECT();
newDumpLevel(name, "RECT");
int NBits = (int) readUB(5, "NBits");
ret.Xmin = (int) readSB(NBits, "Xmin");
ret.Xmax = (int) readSB(NBits, "Xmax");
ret.Ymin = (int) readSB(NBits, "Ymin");
ret.Ymax = (int) readSB(NBits, "Ymax");
ret.nbits = NBits;
alignByte();
endDumpLevel();
return ret;
}
private static void dumpTag(PrintStream out, Tag tag, int index, int level) {
StringBuilder sb = new StringBuilder();
sb.append(Helper.formatHex((int) tag.getPos(), 8));
sb.append(": ");
sb.append(Helper.indent(level, "", " "));
sb.append(Helper.formatInt(index, 4));
sb.append(". ");
sb.append(Helper.format(tag.toString(), 25 - 2 * level));
sb.append(" tagId=");
sb.append(Helper.formatInt(tag.getId(), 3));
sb.append(" len=");
sb.append(Helper.formatInt(tag.getOriginalDataLength(), 8));
sb.append(" ");
sb.append(Helper.bytesToHexString(64, tag.getOriginalData(), 0));
out.println(sb.toString());
// out.println(Utils.formatHex((int)tag.getPos(), 8) + ": " + Utils.indent(level, "") + Utils.format(tag.toString(), 25 - 2*level) + " tagId="+tag.getId()+" len="+tag.getOrigDataLength()+": "+Utils.bytesToHexString(64, tag.getData(version), 0));
if (tag instanceof DefineSpriteTag) {
int i = 0;
for (Tag subTag : ((DefineSpriteTag) tag).getTags()) {
dumpTag(out, subTag, i++, level + 1);
}
}
}
@Override
public void close() {
}
private class TagResolutionTask implements Callable<Tag> {
private final TagStub tag;
private final DumpInfo dumpInfo;
private final int level;
private final boolean parallel;
private final boolean skipUnusualTags;
private final boolean lazy;
public TagResolutionTask(TagStub tag, DumpInfo dumpInfo, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) {
this.tag = tag;
this.dumpInfo = dumpInfo;
this.level = level;
this.parallel = parallel;
this.skipUnusualTags = skipUnusualTags;
this.lazy = lazy;
}
@Override
public Tag call() throws Exception {
DumpInfo di = dumpInfo;
try {
Tag t = resolveTag(tag, level, parallel, skipUnusualTags, lazy);
if (dumpInfo != null && t != null) {
dumpInfo.name = t.getName();
}
return t;
} catch (Exception ex) {
tag.getDataStream().endDumpLevelUntil(di);
logger.log(Level.SEVERE, null, ex);
return tag;
}
}
}
/**
* Reads list of tags from the stream. Reading ends with End tag(=0) or end
* of the stream. Optionally can skip AS1/2 tags when file is AS3
*
* @param timelined
* @param level
* @param parallel
* @param skipUnusualTags
* @param parseTags
* @param lazy
* @return List of tags
* @throws IOException
* @throws java.lang.InterruptedException
*/
public List<Tag> readTagList(Timelined timelined, int level, boolean parallel, boolean skipUnusualTags, boolean parseTags, boolean lazy) throws IOException, InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
boolean parallel1 = level == 0 && parallel;
ExecutorService executor = null;
List<Future<Tag>> futureResults = new ArrayList<>();
if (parallel1) {
executor = Executors.newFixedThreadPool(Configuration.getParallelThreadCount());
futureResults = new ArrayList<>();
}
List<Tag> tags = new ArrayList<>();
Tag tag;
boolean isAS3 = false;
while (available() > 0) {
long pos = getPos();
newDumpLevel(null, "TAG", DumpInfoSpecialType.TAG, getPos());
try {
tag = readTag(timelined, level, pos, false, parallel1, skipUnusualTags, lazy);
} catch (EOFException | EndOfStreamException ex) {
tag = null;
}
boolean doParse = true;
if (!skipUnusualTags) {
doParse = true;
} else if (tag != null) {
switch (tag.getId()) {
case FileAttributesTag.ID: // FileAttributes
if (tag instanceof TagStub) {
tag = resolveTag((TagStub) tag, level, parallel1, skipUnusualTags, lazy);
}
FileAttributesTag fileAttributes = (FileAttributesTag) tag;
if (fileAttributes.actionScript3) {
isAS3 = true;
}
doParse = true;
break;
case DoActionTag.ID:
case DoInitActionTag.ID:
doParse = !isAS3;
break;
case ShowFrameTag.ID:
case PlaceObjectTag.ID:
case PlaceObject2Tag.ID:
case RemoveObjectTag.ID:
case RemoveObject2Tag.ID:
case PlaceObject3Tag.ID: // ?
case StartSoundTag.ID:
case FrameLabelTag.ID:
case SoundStreamHeadTag.ID:
case SoundStreamHead2Tag.ID:
case SoundStreamBlockTag.ID:
case VideoFrameTag.ID:
case EndTag.ID:
doParse = true;
break;
default:
if (level > 0) { //No such tags in DefineSprite allowed
logger.log(Level.FINE, "Tag({0}) found in DefineSprite => Ignored", tag.getId());
doParse = false;
} else {
doParse = true;
}
}
}
if (parseTags && !parallel1 && doParse && (tag instanceof TagStub)) {
tag = resolveTag((TagStub) tag, level, parallel, skipUnusualTags, lazy);
}
DumpInfo di = dumpInfo;
if (di != null && tag != null) {
di.name = tag.getName();
}
endDumpLevel(tag == null ? null : tag.getId());
if (tag == null) {
break;
}
tag.setTimelined(timelined);
if (!parallel1) {
tags.add(tag);
}
if (Configuration.dumpTags.get() && level == 0) {
dumpTag(System.out, tag, tags.size() - 1, level);
}
if (parseTags && doParse && parallel1 && tag instanceof TagStub && executor != null) {
Future<Tag> future = executor.submit(new TagResolutionTask((TagStub) tag, di, level, parallel1, skipUnusualTags, lazy));
futureResults.add(future);
} else {
Future<Tag> future = new ImmediateFuture<>(tag);
futureResults.add(future);
if (!(tag instanceof TagStub)) {
if (di != null) {
di.name = tag.getName();
}
}
}
if (tag.getId() == EndTag.ID) {
break;
}
}
if (parallel1) {
for (Future<Tag> future : futureResults) {
try {
tags.add(future.get());
} catch (InterruptedException ex) {
future.cancel(true);
} catch (ExecutionException e) {
logger.log(Level.SEVERE, "Error during tag reading", e);
}
}
if (executor != null) {
executor.shutdown();
}
}
return tags;
}
public static Tag resolveTag(TagStub tag, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws InterruptedException {
Tag ret;
ByteArrayRange data = tag.getOriginalRange();
SWF swf = tag.getSwf();
SWFInputStream sis = tag.getDataStream();
try {
switch (tag.getId()) {
case 0:
ret = new EndTag(sis, data);
break;
case 1:
ret = new ShowFrameTag(sis, data);
break;
case 2:
ret = new DefineShapeTag(sis, data, lazy);
break;
case 3:
ret = new FreeCharacterTag(sis, data);
break;
case 4:
ret = new PlaceObjectTag(sis, data);
break;
case 5:
ret = new RemoveObjectTag(sis, data);
break;
case 6:
ret = new DefineBitsTag(sis, data);
break;
case 7:
ret = new DefineButtonTag(sis, data);
break;
case 8:
ret = new JPEGTablesTag(sis, data);
break;
case 9:
ret = new SetBackgroundColorTag(sis, data);
break;
case 10:
ret = new DefineFontTag(sis, data);
break;
case 11:
ret = new DefineTextTag(sis, data);
break;
case 12:
ret = new DoActionTag(sis, data);
break;
case 13:
ret = new DefineFontInfoTag(sis, data);
break;
case 14:
ret = new DefineSoundTag(sis, data);
break;
case 15:
ret = new StartSoundTag(sis, data);
break;
//case 16: StopSound
case 17:
ret = new DefineButtonSoundTag(sis, data);
break;
case 18:
ret = new SoundStreamHeadTag(sis, data);
break;
case 19:
ret = new SoundStreamBlockTag(sis, data);
break;
case 20:
ret = new DefineBitsLosslessTag(sis, data);
break;
case 21:
ret = new DefineBitsJPEG2Tag(sis, data);
break;
case 22:
ret = new DefineShape2Tag(sis, data, lazy);
break;
case 23:
ret = new DefineButtonCxformTag(sis, data);
break;
case 24:
ret = new ProtectTag(sis, data);
break;
case 25:
ret = new PathsArePostScriptTag(sis, data);
break;
case 26:
ret = new PlaceObject2Tag(sis, data);
break;
//case 27:
case 28:
ret = new RemoveObject2Tag(sis, data);
break;
case 29:
ret = new SyncFrameTag(sis, data);
break;
//case 30:
case 31:
ret = new FreeAllTag(sis, data);
break;
case 32:
ret = new DefineShape3Tag(sis, data, lazy);
break;
case 33:
ret = new DefineText2Tag(sis, data);
break;
case 34:
ret = new DefineButton2Tag(sis, data);
break;
case 35:
ret = new DefineBitsJPEG3Tag(sis, data);
break;
case 36:
ret = new DefineBitsLossless2Tag(sis, data);
break;
case 37:
ret = new DefineEditTextTag(sis, data);
break;
//case 38: DefineMouseTarget
case 39:
ret = new DefineSpriteTag(sis, level, data, parallel, skipUnusualTags);
break;
case 40:
ret = new NameCharacterTag(sis, data);
break;
case 41: //or NameObject
ret = new ProductInfoTag(sis, data);
break;
//case 42: DefineTextFormat
case 43:
ret = new FrameLabelTag(sis, data);
break;
//case 44: DefineBehavior
case 45:
ret = new SoundStreamHead2Tag(sis, data);
break;
case 46:
ret = new DefineMorphShapeTag(sis, data);
break;
//case 47: FrameTag
case 48:
ret = new DefineFont2Tag(sis, data);
break;
//case 49: GenCommand
//case 50: DefineCommandObj
//case 51: CharacterSet
//case 52: FontRef
//case 53: DefineFunction
//case 54: PlaceFunction
//case 55: GenTagObject
case 56:
ret = new ExportAssetsTag(sis, data);
break;
case 57:
ret = new ImportAssetsTag(sis, data);
break;
case 58:
ret = new EnableDebuggerTag(sis, data);
break;
case 59:
ret = new DoInitActionTag(sis, data);
break;
case 60:
ret = new DefineVideoStreamTag(sis, data);
break;
case 61:
ret = new VideoFrameTag(sis, data);
break;
case 62:
ret = new DefineFontInfo2Tag(sis, data);
break;
case 63:
ret = new DebugIDTag(sis, data);
break;
case 64:
ret = new EnableDebugger2Tag(sis, data);
break;
case 65:
ret = new ScriptLimitsTag(sis, data);
break;
case 66:
ret = new SetTabIndexTag(sis, data);
break;
//case 67: DefineShape4 ???
//case 68: DefineMorphShape2 ???
case 69:
ret = new FileAttributesTag(sis, data);
break;
case 70:
ret = new PlaceObject3Tag(sis, data);
break;
case 71:
ret = new ImportAssets2Tag(sis, data);
break;
case 72:
ret = new DoABCTag(sis, data);
break;
case 73:
ret = new DefineFontAlignZonesTag(sis, data);
break;
case 74:
ret = new CSMTextSettingsTag(sis, data);
break;
case 75:
ret = new DefineFont3Tag(sis, data);
break;
case 76:
ret = new SymbolClassTag(sis, data);
break;
case 77:
ret = new MetadataTag(sis, data);
break;
case 78:
ret = new DefineScalingGridTag(sis, data);
break;
//case 79: DefineDeviceVideo
//case 80-81:
case 82:
ret = new DoABC2Tag(sis, data);
break;
case 83:
ret = new DefineShape4Tag(sis, data, lazy);
break;
case 84:
ret = new DefineMorphShape2Tag(sis, data);
break;
//case 85: PlaceImagePrivate
case 86:
ret = new DefineSceneAndFrameLabelDataTag(sis, data);
break;
case 87:
ret = new DefineBinaryDataTag(sis, data);
break;
case 88:
ret = new DefineFontNameTag(sis, data);
break;
case 89:
ret = new StartSound2Tag(sis, data);
break;
case 90:
ret = new DefineBitsJPEG4Tag(sis, data);
break;
case 91:
ret = new DefineFont4Tag(sis, data);
break;
//case 92: certificate
case 93:
ret = new EnableTelemetryTag(sis, data);
break;
case 94:
ret = new PlaceObject4Tag(sis, data);
break;
default:
if (swf.gfx) { // GFX tags only in GFX files. There may be incorrect GFX tags in non GFX files
switch (tag.getId()) {
case 1000:
ret = new ExporterInfo(sis, data);
break;
case 1001:
ret = new DefineExternalImage(sis, data);
break;
case 1002:
ret = new FontTextureInfo(sis, data);
break;
case 1003:
ret = new DefineExternalGradient(sis, data);
break;
case 1004:
ret = new DefineGradientMap(sis, data);
break;
case 1005:
ret = new DefineCompactedFont(sis, data);
break;
case 1006:
ret = new DefineExternalSound(sis, data);
break;
case 1007:
ret = new DefineExternalStreamSound(sis, data);
break;
case 1008:
ret = new DefineSubImage(sis, data);
break;
case 1009:
ret = new DefineExternalImage2(sis, data);
break;
default:
ret = new UnknownTag(sis, tag.getId(), data);
}
} else {
ret = new UnknownTag(sis, tag.getId(), data);
}
}
if (sis.available() > 0) {
ret.remainingData = sis.readByteRangeEx(sis.available(), "remaining");
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error during tag reading. SWF: " + swf.getShortFileName() + " ID: " + tag.getId() + " name: " + tag.getName() + " pos: " + data.getPos(), ex);
ret = new TagStub(swf, tag.getId(), "ErrorTag", data, null);
}
ret.forceWriteAsLong = tag.forceWriteAsLong;
ret.setTimelined(tag.getTimelined());
return ret;
}
/**
* Reads one Tag from the stream with optional resolving (= reading tag
* content)
*
* @param timelined
* @param level
* @param pos
* @param resolve
* @param parallel
* @param skipUnusualTags
* @param lazy
* @return Tag or null when End tag
* @throws IOException
* @throws java.lang.InterruptedException
*/
public Tag readTag(Timelined timelined, int level, long pos, boolean resolve, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException, InterruptedException {
int tagIDTagLength = readUI16("tagIDTagLength");
int tagID = (tagIDTagLength) >> 6;
logger.log(Level.FINE, "Reading tag. ID={0}, position: {1}", new Object[]{tagID, pos});
long tagLength = (tagIDTagLength & 0x003F);
boolean readLong = false;
if (tagLength == 0x3f) {
tagLength = readSI32("tagLength");
readLong = true;
}
int headerLength = readLong ? 6 : 2;
SWFInputStream tagDataStream = getLimitedStream((int) tagLength);
int available = available();
if (tagLength > available) {
tagLength = available;
}
ByteArrayRange dataRange = new ByteArrayRange(swf.uncompressedData, (int) pos, (int) (tagLength + headerLength));
skipBytes(tagLength);
TagStub tagStub = new TagStub(swf, tagID, "Unresolved", dataRange, tagDataStream);
tagStub.forceWriteAsLong = readLong;
Tag ret = tagStub;
if (tagDataStream.dumpInfo == null && dumpInfo != null) {
dumpInfo.tagToResolve = tagStub;
}
if (resolve) {
DumpInfo di = dumpInfo;
try {
ret = resolveTag(tagStub, level, parallel, skipUnusualTags, lazy);
} catch (Exception ex) {
tagDataStream.endDumpLevelUntil(di);
logger.log(Level.SEVERE, "Problem in " + timelined.toString(), ex);
}
if (Configuration._debugMode.get()) {
byte[] data = ret.getOriginalData();
byte[] dataNew = ret.getData();
int ignoreFirst = 0;
for (int i = 0; i < data.length; i++) {
if (i >= dataNew.length) {
break;
}
if (dataNew[i] != data[i]) {
if (ignoreFirst > 0) {
ignoreFirst--;
continue;
}
String e = "TAG " + ret.toString() + " WRONG, ";
for (int j = i - 10; j <= i + 5; j++) {
while (j < 0) {
j++;
}
if (j >= data.length) {
break;
}
if (j >= dataNew.length) {
break;
}
if (j >= i) {
e += (Long.toHexString(data[j] & 0xff) + " ( is " + Long.toHexString(dataNew[j] & 0xff) + ") ");
} else {
e += (Long.toHexString(data[j] & 0xff) + " ");
}
}
logger.fine(e);
}
}
}
}
return ret;
}
/**
* Reads one Action from the stream
*
* @return Action or null when ActionEndFlag or end of the stream
* @throws IOException
*/
public Action readAction() throws IOException {
int actionCode;
try {
actionCode = readUI8("actionCode");
if (actionCode == 0) {
return new ActionEnd();
}
if (actionCode == -1) {
return null;
}
int actionLength = 0;
if (actionCode >= 0x80) {
actionLength = readUI16("actionLength");
}
switch (actionCode) {
// SWF3 Actions
case 0x81:
return new ActionGotoFrame(actionLength, this);
case 0x83:
return new ActionGetURL(actionLength, this, swf.version);
case 0x04:
return new ActionNextFrame();
case 0x05:
return new ActionPrevFrame();
case 0x06:
return new ActionPlay();
case 0x07:
return new ActionStop();
case 0x08:
return new ActionToggleQuality();
case 0x09:
return new ActionStopSounds();
case 0x8A:
return new ActionWaitForFrame(actionLength, this);
case 0x8B:
return new ActionSetTarget(actionLength, this, swf.version);
case 0x8C:
return new ActionGoToLabel(actionLength, this, swf.version);
// SWF4 Actions
case 0x96:
return new ActionPush(actionLength, this, swf.version);
case 0x17:
return new ActionPop();
case 0x0A:
return new ActionAdd();
case 0x0B:
return new ActionSubtract();
case 0x0C:
return new ActionMultiply();
case 0x0D:
return new ActionDivide();
case 0x0E:
return new ActionEquals();
case 0x0F:
return new ActionLess();
case 0x10:
return new ActionAnd();
case 0x11:
return new ActionOr();
case 0x12:
return new ActionNot();
case 0x13:
return new ActionStringEquals();
case 0x14:
return new ActionStringLength();
case 0x21:
return new ActionStringAdd();
case 0x15:
return new ActionStringExtract();
case 0x29:
return new ActionStringLess();
case 0x31:
return new ActionMBStringLength();
case 0x35:
return new ActionMBStringExtract();
case 0x18:
return new ActionToInteger();
case 0x32:
return new ActionCharToAscii();
case 0x33:
return new ActionAsciiToChar();
case 0x36:
return new ActionMBCharToAscii();
case 0x37:
return new ActionMBAsciiToChar();
case 0x99:
return new ActionJump(actionLength, this);
case 0x9D:
return new ActionIf(actionLength, this);
case 0x9E:
return new ActionCall(actionLength);
case 0x1C:
return new ActionGetVariable();
case 0x1D:
return new ActionSetVariable();
case 0x9A:
return new ActionGetURL2(actionLength, this);
case 0x9F:
return new ActionGotoFrame2(actionLength, this);
case 0x20:
return new ActionSetTarget2();
case 0x22:
return new ActionGetProperty();
case 0x23:
return new ActionSetProperty();
case 0x24:
return new ActionCloneSprite();
case 0x25:
return new ActionRemoveSprite();
case 0x27:
return new ActionStartDrag();
case 0x28:
return new ActionEndDrag();
case 0x8D:
return new ActionWaitForFrame2(actionLength, this);
case 0x26:
return new ActionTrace();
case 0x34:
return new ActionGetTime();
case 0x30:
return new ActionRandomNumber();
// SWF5 Actions
case 0x3D:
return new ActionCallFunction();
case 0x52:
return new ActionCallMethod();
case 0x88:
return new ActionConstantPool(actionLength, this, swf.version);
case 0x9B:
return new ActionDefineFunction(actionLength, this, swf.version);
case 0x3C:
return new ActionDefineLocal();
case 0x41:
return new ActionDefineLocal2();
case 0x3A:
return new ActionDelete();
case 0x3B:
return new ActionDelete2();
case 0x46:
return new ActionEnumerate();
case 0x49:
return new ActionEquals2();
case 0x4E:
return new ActionGetMember();
case 0x42:
return new ActionInitArray();
case 0x43:
return new ActionInitObject();
case 0x53:
return new ActionNewMethod();
case 0x40:
return new ActionNewObject();
case 0x4F:
return new ActionSetMember();
case 0x45:
return new ActionTargetPath();
case 0x94:
return new ActionWith(actionLength, this, swf.version);
case 0x4A:
return new ActionToNumber();
case 0x4B:
return new ActionToString();
case 0x44:
return new ActionTypeOf();
case 0x47:
return new ActionAdd2();
case 0x48:
return new ActionLess2();
case 0x3F:
return new ActionModulo();
case 0x60:
return new ActionBitAnd();
case 0x63:
return new ActionBitLShift();
case 0x61:
return new ActionBitOr();
case 0x64:
return new ActionBitRShift();
case 0x65:
return new ActionBitURShift();
case 0x62:
return new ActionBitXor();
case 0x51:
return new ActionDecrement();
case 0x50:
return new ActionIncrement();
case 0x4C:
return new ActionPushDuplicate();
case 0x3E:
return new ActionReturn();
case 0x4D:
return new ActionStackSwap();
case 0x87:
return new ActionStoreRegister(actionLength, this);
// SWF6 Actions
case 0x54:
return new ActionInstanceOf();
case 0x55:
return new ActionEnumerate2();
case 0x66:
return new ActionStrictEquals();
case 0x67:
return new ActionGreater();
case 0x68:
return new ActionStringGreater();
// SWF7 Actions
case 0x8E:
return new ActionDefineFunction2(actionLength, this, swf.version);
case 0x69:
return new ActionExtends();
case 0x2B:
return new ActionCastOp();
case 0x2C:
return new ActionImplementsOp();
case 0x8F:
return new ActionTry(actionLength, this, swf.version);
case 0x2A:
return new ActionThrow();
default:
/*if (actionLength > 0) {
//skip(actionLength);
}*/
//throw new UnknownActionException(actionCode);
Action r = new ActionUnknown(actionCode, actionLength);
if (Configuration.useDetailedLogging.get()) {
logger.log(Level.SEVERE, "Unknown action code: {0}", actionCode);
}
return r;
}
} catch (EndOfStreamException | ArrayIndexOutOfBoundsException eos) {
return null;
}
}
/**
* Reads one MATRIX value from the stream
*
* @param name
* @return MATRIX value
* @throws IOException
*/
public MATRIX readMatrix(String name) throws IOException {
MATRIX ret = new MATRIX();
newDumpLevel(name, "MATRIX");
ret.hasScale = readUB(1, "hasScale") == 1;
if (ret.hasScale) {
int NScaleBits = (int) readUB(5, "NScaleBits");
ret.scaleX = (int) readSB(NScaleBits, "scaleX");
ret.scaleY = (int) readSB(NScaleBits, "scaleY");
ret.nScaleBits = NScaleBits;
}
ret.hasRotate = readUB(1, "hasRotate") == 1;
if (ret.hasRotate) {
int NRotateBits = (int) readUB(5, "NRotateBits");
ret.rotateSkew0 = (int) readSB(NRotateBits, "rotateSkew0");
ret.rotateSkew1 = (int) readSB(NRotateBits, "rotateSkew1");
ret.nRotateBits = NRotateBits;
}
int NTranslateBits = (int) readUB(5, "NTranslateBits");
ret.translateX = (int) readSB(NTranslateBits, "translateX");
ret.translateY = (int) readSB(NTranslateBits, "translateY");
ret.nTranslateBits = NTranslateBits;
alignByte();
endDumpLevel();
return ret;
}
/**
* Reads one CXFORMWITHALPHA value from the stream
*
* @param name
* @return CXFORMWITHALPHA value
* @throws IOException
*/
public CXFORMWITHALPHA readCXFORMWITHALPHA(String name) throws IOException {
CXFORMWITHALPHA ret = new CXFORMWITHALPHA();
newDumpLevel(name, "CXFORMWITHALPHA");
ret.hasAddTerms = readUB(1, "hasAddTerms") == 1;
ret.hasMultTerms = readUB(1, "hasMultTerms") == 1;
int Nbits = (int) readUB(4, "Nbits");
ret.nbits = Nbits;
if (ret.hasMultTerms) {
ret.redMultTerm = (int) readSB(Nbits, "redMultTerm");
ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm");
ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm");
ret.alphaMultTerm = (int) readSB(Nbits, "alphaMultTerm");
}
if (ret.hasAddTerms) {
ret.redAddTerm = (int) readSB(Nbits, "redAddTerm");
ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm");
ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm");
ret.alphaAddTerm = (int) readSB(Nbits, "alphaAddTerm");
}
alignByte();
endDumpLevel();
return ret;
}
/**
* Reads one CXFORM value from the stream
*
* @param name
* @return CXFORM value
* @throws IOException
*/
public CXFORM readCXFORM(String name) throws IOException {
CXFORM ret = new CXFORM();
newDumpLevel(name, "CXFORM");
ret.hasAddTerms = readUB(1, "hasAddTerms") == 1;
ret.hasMultTerms = readUB(1, "hasMultTerms") == 1;
int Nbits = (int) readUB(4, "Nbits");
ret.nbits = Nbits;
if (ret.hasMultTerms) {
ret.redMultTerm = (int) readSB(Nbits, "redMultTerm");
ret.greenMultTerm = (int) readSB(Nbits, "greenMultTerm");
ret.blueMultTerm = (int) readSB(Nbits, "blueMultTerm");
}
if (ret.hasAddTerms) {
ret.redAddTerm = (int) readSB(Nbits, "redAddTerm");
ret.greenAddTerm = (int) readSB(Nbits, "greenAddTerm");
ret.blueAddTerm = (int) readSB(Nbits, "blueAddTerm");
}
alignByte();
endDumpLevel();
return ret;
}
/**
* Reads one CLIPEVENTFLAGS value from the stream
*
* @param name
* @return CLIPEVENTFLAGS value
* @throws IOException
*/
public CLIPEVENTFLAGS readCLIPEVENTFLAGS(String name) throws IOException {
CLIPEVENTFLAGS ret = new CLIPEVENTFLAGS();
newDumpLevel(name, "CLIPEVENTFLAGS");
ret.clipEventKeyUp = readUB(1, "clipEventKeyUp") == 1;
ret.clipEventKeyDown = readUB(1, "clipEventKeyDown") == 1;
ret.clipEventMouseUp = readUB(1, "clipEventMouseUp") == 1;
ret.clipEventMouseDown = readUB(1, "clipEventMouseDown") == 1;
ret.clipEventMouseMove = readUB(1, "clipEventMouseMove") == 1;
ret.clipEventUnload = readUB(1, "clipEventUnload") == 1;
ret.clipEventEnterFrame = readUB(1, "clipEventEnterFrame") == 1;
ret.clipEventLoad = readUB(1, "clipEventLoad") == 1;
ret.clipEventDragOver = readUB(1, "clipEventDragOver") == 1;
ret.clipEventRollOut = readUB(1, "clipEventRollOut") == 1;
ret.clipEventRollOver = readUB(1, "clipEventRollOver") == 1;
ret.clipEventReleaseOutside = readUB(1, "clipEventReleaseOutside") == 1;
ret.clipEventRelease = readUB(1, "clipEventRelease") == 1;
ret.clipEventPress = readUB(1, "clipEventPress") == 1;
ret.clipEventInitialize = readUB(1, "clipEventInitialize") == 1;
ret.clipEventData = readUB(1, "clipEventData") == 1;
if (swf.version >= 6) {
ret.reserved = (int) readUB(5, "reserved");
ret.clipEventConstruct = readUB(1, "clipEventConstruct") == 1;
ret.clipEventKeyPress = readUB(1, "clipEventKeyPress") == 1;
ret.clipEventDragOut = readUB(1, "clipEventDragOut") == 1;
ret.reserved2 = (int) readUB(8, "reserved2");
}
endDumpLevel();
return ret;
}
/**
* Reads one CLIPACTIONRECORD value from the stream
*
* @param swf
* @param tag
* @param name
* @return CLIPACTIONRECORD value
* @throws IOException
*/
public CLIPACTIONRECORD readCLIPACTIONRECORD(SWF swf, Tag tag, String name) throws IOException {
newDumpLevel(name, "CLIPACTIONRECORD");
CLIPACTIONRECORD ret = new CLIPACTIONRECORD(swf, this, tag);
endDumpLevel();
if (ret.eventFlags.isClear()) {
return null;
}
return ret;
}
/**
* Reads one CLIPACTIONS value from the stream
*
* @param swf
* @param tag
* @param name
* @return CLIPACTIONS value
* @throws IOException
*/
public CLIPACTIONS readCLIPACTIONS(SWF swf, Tag tag, String name) throws IOException {
CLIPACTIONS ret = new CLIPACTIONS();
newDumpLevel(name, "CLIPACTIONS");
ret.reserved = readUI16("reserved");
ret.allEventFlags = readCLIPEVENTFLAGS("allEventFlags");
CLIPACTIONRECORD cr;
ret.clipActionRecords = new ArrayList<>();
while ((cr = readCLIPACTIONRECORD(swf, tag, "record")) != null) {
ret.clipActionRecords.add(cr);
}
endDumpLevel();
return ret;
}
/**
* Reads one COLORMATRIXFILTER value from the stream
*
* @param name
* @return COLORMATRIXFILTER value
* @throws IOException
*/
public COLORMATRIXFILTER readCOLORMATRIXFILTER(String name) throws IOException {
COLORMATRIXFILTER ret = new COLORMATRIXFILTER();
newDumpLevel(name, "COLORMATRIXFILTER");
ret.matrix = new float[20];
for (int i = 0; i < 20; i++) {
ret.matrix[i] = readFLOAT("cell");
}
endDumpLevel();
return ret;
}
/**
* Reads one RGBA value from the stream
*
* @param name
* @return RGBA value
* @throws IOException
*/
public RGBA readRGBA(String name) throws IOException {
RGBA ret = new RGBA();
newDumpLevel(name, "RGBA");
ret.red = readUI8("red");
ret.green = readUI8("green");
ret.blue = readUI8("blue");
ret.alpha = readUI8("alpha");
endDumpLevel();
return ret;
}
/**
* Reads one RGBA value from the stream
*
* @param name
* @return RGBA value
* @throws IOException
*/
public int readRGBAInt(String name) throws IOException {
newDumpLevel(name, "RGBA");
int ret = (readUI8("red") << 16)
| (readUI8("green") << 8)
| readUI8("blue")
| (readUI8("alpha") << 24);
endDumpLevel();
return ret;
}
/**
* Reads one ARGB value from the stream
*
* @param name
* @return ARGB value
* @throws IOException
*/
public ARGB readARGB(String name) throws IOException {
ARGB ret = new ARGB();
newDumpLevel(name, "ARGB");
ret.alpha = readUI8("alpha");
ret.red = readUI8("red");
ret.green = readUI8("green");
ret.blue = readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one ARGB value from the stream
*
* @param name
* @return ARGB value
* @throws IOException
*/
public int readARGBInt(String name) throws IOException {
newDumpLevel(name, "ARGB");
int ret = (readUI8("alpha") << 24)
| (readUI8("red") << 16)
| (readUI8("green") << 8)
| readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one RGB value from the stream
*
* @param name
* @return RGB value
* @throws IOException
*/
public RGB readRGB(String name) throws IOException {
RGB ret = new RGB();
newDumpLevel(name, "RGB");
ret.red = readUI8("red");
ret.green = readUI8("green");
ret.blue = readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one RGB value from the stream
*
* @param name
* @return RGB value
* @throws IOException
*/
public int readRGBInt(String name) throws IOException {
newDumpLevel(name, "RGB");
int ret = (0xff << 24)
| (readUI8("red") << 16)
| (readUI8("green") << 8)
| readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one CONVOLUTIONFILTER value from the stream
*
* @param name
* @return CONVOLUTIONFILTER value
* @throws IOException
*/
public CONVOLUTIONFILTER readCONVOLUTIONFILTER(String name) throws IOException {
CONVOLUTIONFILTER ret = new CONVOLUTIONFILTER();
newDumpLevel(name, "CONVOLUTIONFILTER");
ret.matrixX = readUI8("matrixX");
ret.matrixY = readUI8("matrixY");
ret.divisor = readFLOAT("divisor");
ret.bias = readFLOAT("bias");
ret.matrix = new float[ret.matrixX][ret.matrixY];
for (int x = 0; x < ret.matrixX; x++) {
for (int y = 0; y < ret.matrixY; y++) {
ret.matrix[x][y] = readFLOAT("cell");
}
}
ret.defaultColor = readRGBA("defaultColor");
ret.reserved = (int) readUB(6, "reserved");
ret.clamp = readUB(1, "clamp") == 1;
ret.preserveAlpha = readUB(1, "preserveAlpha") == 1;
endDumpLevel();
return ret;
}
/**
* Reads one BLURFILTER value from the stream
*
* @param name
* @return BLURFILTER value
* @throws IOException
*/
public BLURFILTER readBLURFILTER(String name) throws IOException {
BLURFILTER ret = new BLURFILTER();
newDumpLevel(name, "BLURFILTER");
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.passes = (int) readUB(5, "passes");
ret.reserved = (int) readUB(3, "reserved");
endDumpLevel();
return ret;
}
/**
* Reads one DROPSHADOWFILTER value from the stream
*
* @param name
* @return DROPSHADOWFILTER value
* @throws IOException
*/
public DROPSHADOWFILTER readDROPSHADOWFILTER(String name) throws IOException {
DROPSHADOWFILTER ret = new DROPSHADOWFILTER();
newDumpLevel(name, "DROPSHADOWFILTER");
ret.dropShadowColor = readRGBA("dropShadowColor");
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.angle = readFIXED("angle");
ret.distance = readFIXED("distance");
ret.strength = readFIXED8("strength");
ret.innerShadow = readUB(1, "innerShadow") == 1;
ret.knockout = readUB(1, "knockout") == 1;
ret.compositeSource = readUB(1, "compositeSource") == 1;
ret.passes = (int) readUB(5, "passes");
endDumpLevel();
return ret;
}
/**
* Reads one GLOWFILTER value from the stream
*
* @param name
* @return GLOWFILTER value
* @throws IOException
*/
public GLOWFILTER readGLOWFILTER(String name) throws IOException {
GLOWFILTER ret = new GLOWFILTER();
newDumpLevel(name, "GLOWFILTER");
ret.glowColor = readRGBA("glowColor");
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.strength = readFIXED8("strength");
ret.innerGlow = readUB(1, "innerGlow") == 1;
ret.knockout = readUB(1, "knockout") == 1;
ret.compositeSource = readUB(1, "compositeSource") == 1;
ret.passes = (int) readUB(5, "passes");
endDumpLevel();
return ret;
}
/**
* Reads one BEVELFILTER value from the stream
*
* @param name
* @return BEVELFILTER value
* @throws IOException
*/
public BEVELFILTER readBEVELFILTER(String name) throws IOException {
BEVELFILTER ret = new BEVELFILTER();
newDumpLevel(name, "BEVELFILTER");
ret.highlightColor = readRGBA("highlightColor"); // Highlight color first. It it opposite of the documentation
ret.shadowColor = readRGBA("shadowColor");
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.angle = readFIXED("angle");
ret.distance = readFIXED("distance");
ret.strength = readFIXED8("strength");
ret.innerShadow = readUB(1, "innerShadow") == 1;
ret.knockout = readUB(1, "knockout") == 1;
ret.compositeSource = readUB(1, "compositeSource") == 1;
ret.onTop = readUB(1, "onTop") == 1;
ret.passes = (int) readUB(4, "passes");
endDumpLevel();
return ret;
}
/**
* Reads one GRADIENTGLOWFILTER value from the stream
*
* @param name
* @return GRADIENTGLOWFILTER value
* @throws IOException
*/
public GRADIENTGLOWFILTER readGRADIENTGLOWFILTER(String name) throws IOException {
GRADIENTGLOWFILTER ret = new GRADIENTGLOWFILTER();
newDumpLevel(name, "GRADIENTGLOWFILTER");
int numColors = readUI8("numColors");
ret.gradientColors = new RGBA[numColors];
ret.gradientRatio = new int[numColors];
for (int i = 0; i < numColors; i++) {
ret.gradientColors[i] = readRGBA("gradientColor");
}
for (int i = 0; i < numColors; i++) {
ret.gradientRatio[i] = readUI8("gradientRatio");
}
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.angle = readFIXED("angle");
ret.distance = readFIXED("distance");
ret.strength = readFIXED8("strength");
ret.innerShadow = readUB(1, "innerShadow") == 1;
ret.knockout = readUB(1, "knockout") == 1;
ret.compositeSource = readUB(1, "compositeSource") == 1;
ret.onTop = readUB(1, "onTop") == 1;
ret.passes = (int) readUB(4, "passes");
endDumpLevel();
return ret;
}
/**
* Reads one GRADIENTBEVELFILTER value from the stream
*
* @param name
* @return GRADIENTBEVELFILTER value
* @throws IOException
*/
public GRADIENTBEVELFILTER readGRADIENTBEVELFILTER(String name) throws IOException {
GRADIENTBEVELFILTER ret = new GRADIENTBEVELFILTER();
newDumpLevel(name, "GRADIENTBEVELFILTER");
int numColors = readUI8("numColors");
ret.gradientColors = new RGBA[numColors];
ret.gradientRatio = new int[numColors];
for (int i = 0; i < numColors; i++) {
ret.gradientColors[i] = readRGBA("gradientColor");
}
for (int i = 0; i < numColors; i++) {
ret.gradientRatio[i] = readUI8("gradientRatio");
}
ret.blurX = readFIXED("blurX");
ret.blurY = readFIXED("blurY");
ret.angle = readFIXED("angle");
ret.distance = readFIXED("distance");
ret.strength = readFIXED8("strength");
ret.innerShadow = readUB(1, "innerShadow") == 1;
ret.knockout = readUB(1, "knockout") == 1;
ret.compositeSource = readUB(1, "compositeSource") == 1;
ret.onTop = readUB(1, "onTop") == 1;
ret.passes = (int) readUB(4, "passes");
endDumpLevel();
return ret;
}
/**
* Reads list of FILTER values from the stream
*
* @param name
* @return List of FILTER values
* @throws IOException
*/
public List<FILTER> readFILTERLIST(String name) throws IOException {
newDumpLevel(name, "FILTERLIST");
int numberOfFilters = readUI8("numberOfFilters");
List<FILTER> ret = new ArrayList<>(numberOfFilters);
for (int i = 0; i < numberOfFilters; i++) {
ret.add(readFILTER("filter"));
}
endDumpLevel();
return ret;
}
/**
* Reads one FILTER value from the stream
*
* @param name
* @return FILTER value
* @throws IOException
*/
public FILTER readFILTER(String name) throws IOException {
newDumpLevel(name, "FILTER");
int filterId = readUI8("filterId");
FILTER ret = null;
switch (filterId) {
case 0:
ret = readDROPSHADOWFILTER("filter");
break;
case 1:
ret = readBLURFILTER("filter");
break;
case 2:
ret = readGLOWFILTER("filter");
break;
case 3:
ret = readBEVELFILTER("filter");
break;
case 4:
ret = readGRADIENTGLOWFILTER("filter");
break;
case 5:
ret = readCONVOLUTIONFILTER("filter");
break;
case 6:
ret = readCOLORMATRIXFILTER("filter");
break;
case 7:
ret = readGRADIENTBEVELFILTER("filter");
break;
}
endDumpLevel();
return ret;
}
/**
* Reads list of BUTTONRECORD values from the stream
*
* @param inDefineButton2 Whether read from inside of DefineButton2Tag or
* not
* @param name
* @return List of BUTTONRECORD values
* @throws IOException
*/
public List<BUTTONRECORD> readBUTTONRECORDList(boolean inDefineButton2, String name) throws IOException {
List<BUTTONRECORD> ret = new ArrayList<>();
newDumpLevel(name, "BUTTONRECORDList");
BUTTONRECORD br;
while ((br = readBUTTONRECORD(inDefineButton2, "record")) != null) {
ret.add(br);
}
endDumpLevel();
return ret;
}
/**
* Reads one BUTTONRECORD value from the stream
*
* @param inDefineButton2 True when in DefineButton2
* @param name
* @return BUTTONRECORD value
* @throws IOException
*/
public BUTTONRECORD readBUTTONRECORD(boolean inDefineButton2, String name) throws IOException {
BUTTONRECORD ret = new BUTTONRECORD();
newDumpLevel(name, "BUTTONRECORD");
ret.reserved = (int) readUB(2, "reserved");
ret.buttonHasBlendMode = readUB(1, "buttonHasBlendMode") == 1;
ret.buttonHasFilterList = readUB(1, "buttonHasFilterList") == 1;
ret.buttonStateHitTest = readUB(1, "buttonStateHitTest") == 1;
ret.buttonStateDown = readUB(1, "buttonStateDown") == 1;
ret.buttonStateOver = readUB(1, "buttonStateOver") == 1;
ret.buttonStateUp = readUB(1, "buttonStateUp") == 1;
if (!ret.buttonHasBlendMode && !ret.buttonHasFilterList
&& !ret.buttonStateHitTest && !ret.buttonStateDown
&& !ret.buttonStateOver && !ret.buttonStateUp && ret.reserved == 0) {
endDumpLevel();
return null;
}
ret.characterId = readUI16("characterId");
ret.placeDepth = readUI16("placeDepth");
ret.placeMatrix = readMatrix("placeMatrix");
if (inDefineButton2) {
ret.colorTransform = readCXFORMWITHALPHA("colorTransform");
if (ret.buttonHasFilterList) {
ret.filterList = readFILTERLIST("filterList");
}
if (ret.buttonHasBlendMode) {
ret.blendMode = readUI8("blendMode");
}
}
endDumpLevel();
return ret;
}
/**
* Reads list of BUTTONCONDACTION values from the stream
*
* @param swf
* @param tag
* @param name
* @return List of BUTTONCONDACTION values
* @throws IOException
*/
public List<BUTTONCONDACTION> readBUTTONCONDACTIONList(SWF swf, Tag tag, String name) throws IOException {
List<BUTTONCONDACTION> ret = new ArrayList<>();
newDumpLevel(name, "BUTTONCONDACTIONList");
BUTTONCONDACTION bc;
while (!(bc = readBUTTONCONDACTION(swf, tag, "action")).isLast) {
ret.add(bc);
}
ret.add(bc);
endDumpLevel();
return ret;
}
/**
* Reads one BUTTONCONDACTION value from the stream
*
* @param swf
* @param tag
* @param name
* @return BUTTONCONDACTION value
* @throws IOException
*/
public BUTTONCONDACTION readBUTTONCONDACTION(SWF swf, Tag tag, String name) throws IOException {
newDumpLevel(name, "BUTTONCONDACTION");
BUTTONCONDACTION ret = new BUTTONCONDACTION(swf, this, tag);
//ret.actions = readActionList();
endDumpLevel();
return ret;
}
/**
* Reads one GRADRECORD value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return GRADRECORD value
* @throws IOException
*/
public GRADRECORD readGRADRECORD(int shapeNum, String name) throws IOException {
GRADRECORD ret = new GRADRECORD();
newDumpLevel(name, "GRADRECORD");
ret.ratio = readUI8("ratio");
if (shapeNum >= 3) {
ret.color = readRGBA("color");
} else {
ret.color = readRGB("color");
}
endDumpLevel();
return ret;
}
/**
* Reads one GRADIENT value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return GRADIENT value
* @throws IOException
*/
public GRADIENT readGRADIENT(int shapeNum, String name) throws IOException {
GRADIENT ret = new GRADIENT();
newDumpLevel(name, "GRADIENT");
ret.spreadMode = (int) readUB(2, "spreadMode");
ret.interpolationMode = (int) readUB(2, "interpolationMode");
int numGradients = (int) readUB(4, "numGradients");
ret.gradientRecords = new GRADRECORD[numGradients];
for (int i = 0; i < numGradients; i++) {
ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord");
}
endDumpLevel();
return ret;
}
/**
* Reads one FOCALGRADIENT value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return FOCALGRADIENT value
* @throws IOException
*/
public FOCALGRADIENT readFOCALGRADIENT(int shapeNum, String name) throws IOException {
FOCALGRADIENT ret = new FOCALGRADIENT();
newDumpLevel(name, "FOCALGRADIENT");
ret.spreadMode = (int) readUB(2, "spreadMode");
ret.interpolationMode = (int) readUB(2, "interpolationMode");
int numGradients = (int) readUB(4, "numGradients");
ret.gradientRecords = new GRADRECORD[numGradients];
for (int i = 0; i < numGradients; i++) {
ret.gradientRecords[i] = readGRADRECORD(shapeNum, "gradientRecord");
}
ret.focalPoint = readFIXED8("focalPoint");
endDumpLevel();
return ret;
}
/**
* Reads one FILLSTYLE value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return FILLSTYLE value
* @throws IOException
*/
public FILLSTYLE readFILLSTYLE(int shapeNum, String name) throws IOException {
FILLSTYLE ret = new FILLSTYLE();
newDumpLevel(name, "FILLSTYLE");
ret.fillStyleType = readUI8("fillStyleType");
if (ret.fillStyleType == FILLSTYLE.SOLID) {
if (shapeNum >= 3) {
ret.color = readRGBA("color");
} else {
ret.color = readRGB("color");
}
}
if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT)
|| (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)
|| (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT)) {
ret.gradientMatrix = readMatrix("gradientMatrix");
}
if ((ret.fillStyleType == FILLSTYLE.LINEAR_GRADIENT)
|| (ret.fillStyleType == FILLSTYLE.RADIAL_GRADIENT)) {
ret.gradient = readGRADIENT(shapeNum, "gradient");
}
if (ret.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) {
ret.gradient = readFOCALGRADIENT(shapeNum, "gradient");
}
if ((ret.fillStyleType == FILLSTYLE.REPEATING_BITMAP)
|| (ret.fillStyleType == FILLSTYLE.CLIPPED_BITMAP)
|| (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP)
|| (ret.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) {
ret.bitmapId = readUI16("bitmapId");
ret.bitmapMatrix = readMatrix("bitmapMatrix");
}
endDumpLevel();
return ret;
}
/**
* Reads one FILLSTYLEARRAY value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return FILLSTYLEARRAY value
* @throws IOException
*/
public FILLSTYLEARRAY readFILLSTYLEARRAY(int shapeNum, String name) throws IOException {
FILLSTYLEARRAY ret = new FILLSTYLEARRAY();
newDumpLevel(name, "FILLSTYLEARRAY");
int fillStyleCount = readUI8("fillStyleCount");
if (shapeNum > 1 && fillStyleCount == 0xff) {
fillStyleCount = readUI16("fillStyleCount");
}
ret.fillStyles = new FILLSTYLE[fillStyleCount];
for (int i = 0; i < fillStyleCount; i++) {
ret.fillStyles[i] = readFILLSTYLE(shapeNum, "fillStyle");
}
endDumpLevel();
return ret;
}
/**
* Reads one LINESTYLE value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return LINESTYLE value
* @throws IOException
*/
public LINESTYLE readLINESTYLE(int shapeNum, String name) throws IOException {
LINESTYLE ret = new LINESTYLE();
newDumpLevel(name, "LINESTYLE");
ret.width = readUI16("width");
if (shapeNum == 1 || shapeNum == 2) {
ret.color = readRGB("color");
} else if (shapeNum == 3) {
ret.color = readRGBA("color");
}
endDumpLevel();
return ret;
}
/**
* Reads one LINESTYLE2 value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return LINESTYLE2 value
* @throws IOException
*/
public LINESTYLE2 readLINESTYLE2(int shapeNum, String name) throws IOException {
LINESTYLE2 ret = new LINESTYLE2();
newDumpLevel(name, "LINESTYLE2");
ret.width = readUI16("width");
ret.startCapStyle = (int) readUB(2, "startCapStyle");
ret.joinStyle = (int) readUB(2, "joinStyle");
ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1;
ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1;
ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1;
ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1;
ret.reserved = (int) readUB(5, "reserved");
ret.noClose = (int) readUB(1, "noClose") == 1;
ret.endCapStyle = (int) readUB(2, "endCapStyle");
if (ret.joinStyle == LINESTYLE2.MITER_JOIN) {
ret.miterLimitFactor = readFIXED8("miterLimitFactor");
}
if (!ret.hasFillFlag) {
ret.color = readRGBA("color");
} else {
ret.fillType = readFILLSTYLE(shapeNum, "fillType");
}
endDumpLevel();
return ret;
}
/**
* Reads one LINESTYLEARRAY value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param name
* @return LINESTYLEARRAY value
* @throws IOException
*/
public LINESTYLEARRAY readLINESTYLEARRAY(int shapeNum, String name) throws IOException {
LINESTYLEARRAY ret = new LINESTYLEARRAY();
newDumpLevel(name, "LINESTYLEARRAY");
int lineStyleCount = readUI8("lineStyleCount");
if (lineStyleCount == 0xff) {
lineStyleCount = readUI16("lineStyleCount");
}
if (shapeNum <= 3) {
ret.lineStyles = new LINESTYLE[lineStyleCount];
for (int i = 0; i < lineStyleCount; i++) {
ret.lineStyles[i] = readLINESTYLE(shapeNum, "lineStyle");
}
} else {
ret.lineStyles = new LINESTYLE2[lineStyleCount];
for (int i = 0; i < lineStyleCount; i++) {
ret.lineStyles[i] = readLINESTYLE2(shapeNum, "lineStyle");
}
}
endDumpLevel();
return ret;
}
/**
* Reads one SHAPERECORD value from the stream
*
* @param fillBits
* @param lineBits
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @return SHAPERECORD value
* @throws IOException
*/
private SHAPERECORD readSHAPERECORD(int fillBits, int lineBits, int shapeNum, boolean morphShape, String name) throws IOException {
SHAPERECORD ret;
newDumpLevel(name, "SHAPERECORD");
int typeFlag = (int) readUB(1, "typeFlag");
if (typeFlag == 0) {
boolean stateNewStyles = readUB(1, "stateNewStyles") == 1;
boolean stateLineStyle = readUB(1, "stateLineStyle") == 1;
boolean stateFillStyle1 = readUB(1, "stateFillStyle1") == 1;
boolean stateFillStyle0 = readUB(1, "stateFillStyle0") == 1;
boolean stateMoveTo = readUB(1, "stateMoveTo") == 1;
if ((!stateNewStyles) && (!stateLineStyle) && (!stateFillStyle1) && (!stateFillStyle0) && (!stateMoveTo)) {
ret = new EndShapeRecord();
} else {
StyleChangeRecord scr = new StyleChangeRecord();
scr.stateNewStyles = stateNewStyles;
scr.stateLineStyle = stateLineStyle;
scr.stateFillStyle0 = stateFillStyle0;
scr.stateFillStyle1 = stateFillStyle1;
scr.stateMoveTo = stateMoveTo;
if (stateMoveTo) {
scr.moveBits = (int) readUB(5, "moveBits");
scr.moveDeltaX = (int) readSB(scr.moveBits, "moveDeltaX");
scr.moveDeltaY = (int) readSB(scr.moveBits, "moveDeltaY");
}
if (stateFillStyle0) {
scr.fillStyle0 = (int) readUB(fillBits, "fillStyle0");
}
if (stateFillStyle1) {
scr.fillStyle1 = (int) readUB(fillBits, "fillStyle1");
}
if (stateLineStyle) {
scr.lineStyle = (int) readUB(lineBits, "lineStyle");
}
if (stateNewStyles) {
if (morphShape) {
// This should never happen in a valid SWF
throw new IOException("MorphShape should not have new styles.");
} else {
scr.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles");
scr.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles");
}
scr.numFillBits = (int) readUB(4, "numFillBits");
scr.numLineBits = (int) readUB(4, "numLineBits");
}
ret = scr;
}
} else { // typeFlag==1
int straightFlag = (int) readUB(1, "straightFlag");
if (straightFlag == 1) {
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.numBits = (int) readUB(4, "numBits");
ser.generalLineFlag = readUB(1, "generalLineFlag") == 1;
if (!ser.generalLineFlag) {
ser.vertLineFlag = readUB(1, "vertLineFlag") == 1;
}
if (ser.generalLineFlag || (!ser.vertLineFlag)) {
ser.deltaX = (int) readSB(ser.numBits + 2, "deltaX");
}
if (ser.generalLineFlag || (ser.vertLineFlag)) {
ser.deltaY = (int) readSB(ser.numBits + 2, "deltaY");
}
ret = ser;
} else {
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.numBits = (int) readUB(4, "numBits");
cer.controlDeltaX = (int) readSB(cer.numBits + 2, "controlDeltaX");
cer.controlDeltaY = (int) readSB(cer.numBits + 2, "controlDeltaY");
cer.anchorDeltaX = (int) readSB(cer.numBits + 2, "anchorDeltaX");
cer.anchorDeltaY = (int) readSB(cer.numBits + 2, "anchorDeltaY");
ret = cer;
}
}
endDumpLevel();
return ret;
}
/**
* Reads one SHAPE value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param morphShape
* @param name
* @return SHAPE value
* @throws IOException
*/
public SHAPE readSHAPE(int shapeNum, boolean morphShape, String name) throws IOException {
SHAPE ret = new SHAPE();
newDumpLevel(name, "SHAPE");
ret.numFillBits = (int) readUB(4, "numFillBits");
ret.numLineBits = (int) readUB(4, "numLineBits");
ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords");
endDumpLevel();
return ret;
}
/**
* Reads one SHAPEWITHSTYLE value from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param morphShape
* @param name
* @return SHAPEWITHSTYLE value
* @throws IOException
*/
public SHAPEWITHSTYLE readSHAPEWITHSTYLE(int shapeNum, boolean morphShape, String name) throws IOException {
SHAPEWITHSTYLE ret = new SHAPEWITHSTYLE();
newDumpLevel(name, "SHAPEWITHSTYLE");
ret.fillStyles = readFILLSTYLEARRAY(shapeNum, "fillStyles");
ret.lineStyles = readLINESTYLEARRAY(shapeNum, "lineStyles");
ret.numFillBits = (int) readUB(4, "numFillBits");
ret.numLineBits = (int) readUB(4, "numLineBits");
ret.shapeRecords = readSHAPERECORDS(shapeNum, ret.numFillBits, ret.numLineBits, morphShape, "shapeRecords");
endDumpLevel();
return ret;
}
/**
* Reads list of SHAPERECORDs from the stream
*
* @param shapeNum 1 in DefineShape, 2 in DefineShape2...
* @param fillBits
* @param lineBits
* @return SHAPERECORDs array
* @throws IOException
*/
private List<SHAPERECORD> readSHAPERECORDS(int shapeNum, int fillBits, int lineBits, boolean morphShape, String name) throws IOException {
List<SHAPERECORD> ret = new ArrayList<>();
newDumpLevel(name, "SHAPERECORDS");
SHAPERECORD rec;
do {
rec = readSHAPERECORD(fillBits, lineBits, shapeNum, morphShape, "record");
if (rec instanceof StyleChangeRecord) {
StyleChangeRecord scRec = (StyleChangeRecord) rec;
if (scRec.stateNewStyles) {
fillBits = scRec.numFillBits;
lineBits = scRec.numLineBits;
}
}
ret.add(rec);
} while (!(rec instanceof EndShapeRecord));
alignByte();
endDumpLevel();
return ret;
}
/**
* Reads one SOUNDINFO value from the stream
*
* @param name
* @return SOUNDINFO value
* @throws IOException
*/
public SOUNDINFO readSOUNDINFO(String name) throws IOException {
SOUNDINFO ret = new SOUNDINFO();
newDumpLevel(name, "SOUNDINFO");
ret.reserved = (int) readUB(2, "reserved");
ret.syncStop = readUB(1, "syncStop") == 1;
ret.syncNoMultiple = readUB(1, "syncNoMultiple") == 1;
ret.hasEnvelope = readUB(1, "hasEnvelope") == 1;
ret.hasLoops = readUB(1, "hasLoops") == 1;
ret.hasOutPoint = readUB(1, "hasOutPoint") == 1;
ret.hasInPoint = readUB(1, "hasInPoint") == 1;
if (ret.hasInPoint) {
ret.inPoint = readUI32("inPoint");
}
if (ret.hasOutPoint) {
ret.outPoint = readUI32("outPoint");
}
if (ret.hasLoops) {
ret.loopCount = readUI16("loopCount");
}
if (ret.hasEnvelope) {
int envPoints = readUI8("envPoints");
ret.envelopeRecords = new SOUNDENVELOPE[envPoints];
for (int i = 0; i < envPoints; i++) {
ret.envelopeRecords[i] = readSOUNDENVELOPE("envelopeRecord");
}
}
endDumpLevel();
return ret;
}
/**
* Reads one SOUNDENVELOPE value from the stream
*
* @param name
* @return SOUNDENVELOPE value
* @throws IOException
*/
public SOUNDENVELOPE readSOUNDENVELOPE(String name) throws IOException {
SOUNDENVELOPE ret = new SOUNDENVELOPE();
newDumpLevel(name, "SOUNDENVELOPE");
ret.pos44 = readUI32("pos44");
ret.leftLevel = readUI16("leftLevel");
ret.rightLevel = readUI16("rightLevel");
endDumpLevel();
return ret;
}
/**
* Reads one GLYPHENTRY value from the stream
*
* @param glyphBits
* @param advanceBits
* @param name
* @return GLYPHENTRY value
* @throws IOException
*/
public GLYPHENTRY readGLYPHENTRY(int glyphBits, int advanceBits, String name) throws IOException {
GLYPHENTRY ret = new GLYPHENTRY();
newDumpLevel(name, "GLYPHENTRY");
ret.glyphIndex = (int) readUB(glyphBits, "glyphIndex");
ret.glyphAdvance = (int) readSB(advanceBits, "glyphAdvance");
endDumpLevel();
return ret;
}
/**
* Reads one TEXTRECORD value from the stream
*
* @param defineTextNum
* @param glyphBits
* @param advanceBits
* @param name
* @return TEXTRECORD value
* @throws IOException
*/
public TEXTRECORD readTEXTRECORD(int defineTextNum, int glyphBits, int advanceBits, String name) throws IOException {
TEXTRECORD ret = new TEXTRECORD();
newDumpLevel(name, "TEXTRECORD");
int first = (int) readUB(1, "first"); // always 1
readUB(3, "styleFlagsHasReserved"); // always 0
ret.styleFlagsHasFont = readUB(1, "styleFlagsHasFont") == 1;
ret.styleFlagsHasColor = readUB(1, "styleFlagsHasColor") == 1;
ret.styleFlagsHasYOffset = readUB(1, "styleFlagsHasYOffset") == 1;
ret.styleFlagsHasXOffset = readUB(1, "styleFlagsHasXOffset") == 1;
if ((!ret.styleFlagsHasFont) && (!ret.styleFlagsHasColor) && (!ret.styleFlagsHasYOffset) && (!ret.styleFlagsHasXOffset) && (first == 0)) { // final text record
endDumpLevel();
return null;
}
if (ret.styleFlagsHasFont) {
ret.fontId = readUI16("fontId");
}
if (ret.styleFlagsHasColor) {
if (defineTextNum == 2) {
ret.textColorA = readRGBA("textColorA");
} else {
ret.textColor = readRGB("textColor");
}
}
if (ret.styleFlagsHasXOffset) {
ret.xOffset = readSI16("xOffset");
}
if (ret.styleFlagsHasYOffset) {
ret.yOffset = readSI16("yOffset");
}
if (ret.styleFlagsHasFont) {
ret.textHeight = readUI16("textHeight");
}
int glyphCount = readUI8("glyphCount");
ret.glyphEntries = new ArrayList<>(glyphCount);
for (int i = 0; i < glyphCount; i++) {
ret.glyphEntries.add(readGLYPHENTRY(glyphBits, advanceBits, "glyphEntry"));
}
alignByte();
endDumpLevel();
return ret;
}
/**
* Reads one MORPHGRADRECORD value from the stream
*
* @param name
* @return MORPHGRADRECORD value
* @throws IOException
*/
public MORPHGRADRECORD readMORPHGRADRECORD(String name) throws IOException {
MORPHGRADRECORD ret = new MORPHGRADRECORD();
newDumpLevel(name, "MORPHGRADRECORD");
ret.startRatio = readUI8("startRatio");
ret.startColor = readRGBA("startColor");
ret.endRatio = readUI8("endRatio");
ret.endColor = readRGBA("endColor");
endDumpLevel();
return ret;
}
/**
* Reads one MORPHGRADIENT value from the stream
*
* @param name
* @return MORPHGRADIENT value
* @throws IOException
*/
public MORPHGRADIENT readMORPHGRADIENT(String name) throws IOException {
MORPHGRADIENT ret = new MORPHGRADIENT();
newDumpLevel(name, "MORPHGRADIENT");
// Despite of documentation (UI8 1-8), there are two fields
// spreadMode and interPolationMode which are same as in GRADIENT
ret.spreadMode = (int) readUB(2, "spreadMode");
ret.interPolationMode = (int) readUB(2, "interPolationMode");
int numGradients = (int) readUB(4, "numGradients");
ret.gradientRecords = new MORPHGRADRECORD[numGradients];
for (int i = 0; i < numGradients; i++) {
ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord");
}
endDumpLevel();
return ret;
}
/**
* Reads one MORPHFOCALGRADIENT value from the stream
*
* This is undocumented feature
*
* @param name
* @return MORPHGRADIENT value
* @throws IOException
*/
public MORPHFOCALGRADIENT readMORPHFOCALGRADIENT(String name) throws IOException {
MORPHFOCALGRADIENT ret = new MORPHFOCALGRADIENT();
newDumpLevel(name, "MORPHFOCALGRADIENT");
ret.spreadMode = (int) readUB(2, "spreadMode");
ret.interPolationMode = (int) readUB(2, "interPolationMode");
int numGradients = (int) readUB(4, "numGradients");
ret.gradientRecords = new MORPHGRADRECORD[numGradients];
for (int i = 0; i < numGradients; i++) {
ret.gradientRecords[i] = readMORPHGRADRECORD("gradientRecord");
}
ret.startFocalPoint = readFIXED8("startFocalPoint");
ret.endFocalPoint = readFIXED8("endFocalPoint");
endDumpLevel();
return ret;
}
/**
* Reads one MORPHFILLSTYLE value from the stream
*
* @param name
* @return MORPHFILLSTYLE value
* @throws IOException
*/
public MORPHFILLSTYLE readMORPHFILLSTYLE(String name) throws IOException {
MORPHFILLSTYLE ret = new MORPHFILLSTYLE();
newDumpLevel(name, "MORPHFILLSTYLE");
ret.fillStyleType = readUI8("fillStyleType");
if (ret.fillStyleType == MORPHFILLSTYLE.SOLID) {
ret.startColor = readRGBA("startColor");
ret.endColor = readRGBA("endColor");
}
if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT)
|| (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)
|| (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT)) {
ret.startGradientMatrix = readMatrix("startGradientMatrix");
ret.endGradientMatrix = readMatrix("endGradientMatrix");
}
if ((ret.fillStyleType == MORPHFILLSTYLE.LINEAR_GRADIENT)
|| (ret.fillStyleType == MORPHFILLSTYLE.RADIAL_GRADIENT)) {
ret.gradient = readMORPHGRADIENT("gradient");
}
if (ret.fillStyleType == MORPHFILLSTYLE.FOCAL_RADIAL_GRADIENT) {
ret.gradient = readMORPHFOCALGRADIENT("gradient");
}
if ((ret.fillStyleType == MORPHFILLSTYLE.REPEATING_BITMAP)
|| (ret.fillStyleType == MORPHFILLSTYLE.CLIPPED_BITMAP)
|| (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP)
|| (ret.fillStyleType == MORPHFILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) {
ret.bitmapId = readUI16("bitmapId");
ret.startBitmapMatrix = readMatrix("startBitmapMatrix");
ret.endBitmapMatrix = readMatrix("endBitmapMatrix");
}
endDumpLevel();
return ret;
}
/**
* Reads one MORPHFILLSTYLEARRAY value from the stream
*
* @param name
* @return MORPHFILLSTYLEARRAY value
* @throws IOException
*/
public MORPHFILLSTYLEARRAY readMORPHFILLSTYLEARRAY(String name) throws IOException {
MORPHFILLSTYLEARRAY ret = new MORPHFILLSTYLEARRAY();
newDumpLevel(name, "MORPHFILLSTYLEARRAY");
int fillStyleCount = readUI8("fillStyleCount");
if (fillStyleCount == 0xff) {
fillStyleCount = readUI16("fillStyleCount");
}
ret.fillStyles = new MORPHFILLSTYLE[fillStyleCount];
for (int i = 0; i < fillStyleCount; i++) {
ret.fillStyles[i] = readMORPHFILLSTYLE("fillStyle");
}
endDumpLevel();
return ret;
}
/**
* Reads one MORPHLINESTYLE value from the stream
*
* @param name
* @return MORPHLINESTYLE value
* @throws IOException
*/
public MORPHLINESTYLE readMORPHLINESTYLE(String name) throws IOException {
MORPHLINESTYLE ret = new MORPHLINESTYLE();
newDumpLevel(name, "MORPHLINESTYLE");
ret.startWidth = readUI16("startWidth");
ret.endWidth = readUI16("endWidth");
ret.startColor = readRGBA("startColor");
ret.endColor = readRGBA("endColor");
endDumpLevel();
return ret;
}
/**
* Reads one MORPHLINESTYLE2 value from the stream
*
* @param name
* @return MORPHLINESTYLE2 value
* @throws IOException
*/
public MORPHLINESTYLE2 readMORPHLINESTYLE2(String name) throws IOException {
MORPHLINESTYLE2 ret = new MORPHLINESTYLE2();
newDumpLevel(name, "MORPHLINESTYLE2");
ret.startWidth = readUI16("startWidth");
ret.endWidth = readUI16("endWidth");
ret.startCapStyle = (int) readUB(2, "startCapStyle");
ret.joinStyle = (int) readUB(2, "joinStyle");
ret.hasFillFlag = (int) readUB(1, "hasFillFlag") == 1;
ret.noHScaleFlag = (int) readUB(1, "noHScaleFlag") == 1;
ret.noVScaleFlag = (int) readUB(1, "noVScaleFlag") == 1;
ret.pixelHintingFlag = (int) readUB(1, "pixelHintingFlag") == 1;
ret.reserved = (int) readUB(5, "reserved");
ret.noClose = (int) readUB(1, "noClose") == 1;
ret.endCapStyle = (int) readUB(2, "endCapStyle");
if (ret.joinStyle == LINESTYLE2.MITER_JOIN) {
ret.miterLimitFactor = readUI16("miterLimitFactor");
}
if (!ret.hasFillFlag) {
ret.startColor = readRGBA("startColor");
ret.endColor = readRGBA("endColor");
} else {
ret.fillType = readMORPHFILLSTYLE("fillType");
}
endDumpLevel();
return ret;
}
/**
* Reads one MORPHLINESTYLEARRAY value from the stream
*
* @param morphShapeNum 1 on DefineMorphShape, 2 on DefineMorphShape2
* @param name
* @return MORPHLINESTYLEARRAY value
* @throws IOException
*/
public MORPHLINESTYLEARRAY readMORPHLINESTYLEARRAY(int morphShapeNum, String name) throws IOException {
MORPHLINESTYLEARRAY ret = new MORPHLINESTYLEARRAY();
newDumpLevel(name, "MORPHLINESTYLEARRAY");
int lineStyleCount = readUI8("lineStyleCount");
if (lineStyleCount == 0xff) {
lineStyleCount = readUI16("lineStyleCount");
}
if (morphShapeNum == 1) {
ret.lineStyles = new MORPHLINESTYLE[lineStyleCount];
for (int i = 0; i < lineStyleCount; i++) {
ret.lineStyles[i] = readMORPHLINESTYLE("lineStyle");
}
} else if (morphShapeNum == 2) {
ret.lineStyles2 = new MORPHLINESTYLE2[lineStyleCount];
for (int i = 0; i < lineStyleCount; i++) {
ret.lineStyles2[i] = readMORPHLINESTYLE2("lineStyle2");
}
}
endDumpLevel();
return ret;
}
/**
* Reads one KERNINGRECORD value from the stream
*
* @param fontFlagsWideCodes
* @param name
* @return KERNINGRECORD value
* @throws IOException
*/
public KERNINGRECORD readKERNINGRECORD(boolean fontFlagsWideCodes, String name) throws IOException {
KERNINGRECORD ret = new KERNINGRECORD();
newDumpLevel(name, "KERNINGRECORD");
if (fontFlagsWideCodes) {
ret.fontKerningCode1 = readUI16("fontKerningCode1");
ret.fontKerningCode2 = readUI16("fontKerningCode2");
} else {
ret.fontKerningCode1 = readUI8("fontKerningCode1");
ret.fontKerningCode2 = readUI8("fontKerningCode2");
}
ret.fontKerningAdjustment = readSI16("fontKerningAdjustment");
endDumpLevel();
return ret;
}
/**
* Reads one LANGCODE value from the stream
*
* @param name
* @return LANGCODE value
* @throws IOException
*/
public LANGCODE readLANGCODE(String name) throws IOException {
LANGCODE ret = new LANGCODE();
newDumpLevel(name, "LANGCODE");
ret.languageCode = readUI8("languageCode");
endDumpLevel();
return ret;
}
/**
* Reads one ZONERECORD value from the stream
*
* @param name
* @return ZONERECORD value
* @throws IOException
*/
public ZONERECORD readZONERECORD(String name) throws IOException {
ZONERECORD ret = new ZONERECORD();
newDumpLevel(name, "ZONERECORD");
int numZoneData = readUI8("numZoneData");
ret.zonedata = new ZONEDATA[numZoneData];
for (int i = 0; i < numZoneData; i++) {
ret.zonedata[i] = readZONEDATA("zonedata");
}
readUB(6, "reserved");
ret.zoneMaskY = readUB(1, "zoneMaskY") == 1;
ret.zoneMaskX = readUB(1, "zoneMaskX") == 1;
endDumpLevel();
return ret;
}
/**
* Reads one ZONEDATA value from the stream
*
* @param name
* @return ZONEDATA value
* @throws IOException
*/
public ZONEDATA readZONEDATA(String name) throws IOException {
ZONEDATA ret = new ZONEDATA();
newDumpLevel(name, "ZONEDATA");
ret.alignmentCoordinate = readUI16("alignmentCoordinate");
ret.range = readUI16("range");
endDumpLevel();
return ret;
}
/**
* Reads one PIX15 value from the stream
*
* @param name
* @return PIX15 value
* @throws IOException
*/
public PIX15 readPIX15(String name) throws IOException {
PIX15 ret = new PIX15();
newDumpLevel(name, "PIX15");
ret.reserved = (int) readUB(1, "reserved");
ret.red = (int) readUB(5, "red");
ret.green = (int) readUB(5, "green");
ret.blue = (int) readUB(5, "blue");
endDumpLevel();
return ret;
}
/**
* Reads one PIX15 value from the stream
*
* @param name
* @return PIX15 value
* @throws IOException
*/
public int readPIX15Int(String name) throws IOException {
newDumpLevel(name, "PIX15");
int ret = ((int) readUB(1, "reserved") << 24)
| ((int) readUB(5, "red") << 19)
| ((int) readUB(5, "green") << 11)
| ((int) readUB(5, "blue") << 3);
endDumpLevel();
return ret;
}
/**
* Reads one PIX24 value from the stream
*
* @param name
* @return PIX24 value
* @throws IOException
*/
public PIX24 readPIX24(String name) throws IOException {
PIX24 ret = new PIX24();
newDumpLevel(name, "PIX24");
ret.reserved = readUI8("reserved");
ret.red = readUI8("red");
ret.green = readUI8("green");
ret.blue = readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one PIX24 value from the stream
*
* @param name
* @return PIX24 value
* @throws IOException
*/
public int readPIX24Int(String name) throws IOException {
newDumpLevel(name, "PIX24");
int ret = (readUI8("reserved") << 24)
| (readUI8("red") << 16)
| (readUI8("green") << 8)
| readUI8("blue");
endDumpLevel();
return ret;
}
/**
* Reads one COLORMAPDATA value from the stream
*
* @param colorTableSize
* @param bitmapWidth
* @param bitmapHeight
* @param name
* @return COLORMAPDATA value
* @throws IOException
*/
public COLORMAPDATA readCOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException {
COLORMAPDATA ret = new COLORMAPDATA();
newDumpLevel(name, "COLORMAPDATA");
ret.colorTableRGB = new int[colorTableSize + 1];
for (int i = 0; i < colorTableSize + 1; i++) {
ret.colorTableRGB[i] = readRGBInt("colorTableRGB");
}
int dataLen = 0;
for (int y = 0; y < bitmapHeight; y++) {
int x = 0;
for (; x < bitmapWidth; x++) {
dataLen++;
}
while ((x % 4) != 0) {
dataLen++;
x++;
}
}
ret.colorMapPixelData = readBytesEx(dataLen, "colorMapPixelData");
endDumpLevel();
return ret;
}
/**
* Reads one BITMAPDATA value from the stream
*
* @param bitmapFormat
* @param bitmapWidth
* @param bitmapHeight
* @param name
* @return COLORMAPDATA value
* @throws IOException
*/
public BITMAPDATA readBITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException {
BITMAPDATA ret = new BITMAPDATA();
newDumpLevel(name, "BITMAPDATA");
int pixelCount = bitmapWidth * bitmapHeight;
int[] pix15 = bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB ? new int[pixelCount] : null;
int[] pix24 = bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB ? new int[pixelCount] : null;
int dataLen = 0;
int pos = 0;
for (int y = 0; y < bitmapHeight; y++) {
for (int x = 0; x < bitmapWidth; x++) {
if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) {
dataLen += 2;
pix15[pos++] = readPIX15Int("pix15");
} else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) {
dataLen += 4;
pix24[pos++] = readPIX24Int("pix24");
}
}
while ((dataLen % 4) != 0) {
dataLen++;
readUI8("padding");
}
}
if (bitmapFormat == DefineBitsLosslessTag.FORMAT_15BIT_RGB) {
ret.bitmapPixelDataPix15 = pix15;
} else if (bitmapFormat == DefineBitsLosslessTag.FORMAT_24BIT_RGB) {
ret.bitmapPixelDataPix24 = pix24;
}
endDumpLevel();
return ret;
}
/**
* Reads one BITMAPDATA value from the stream
*
* @param bitmapFormat
* @param bitmapWidth
* @param bitmapHeight
* @param name
* @return COLORMAPDATA value
* @throws IOException
*/
public ALPHABITMAPDATA readALPHABITMAPDATA(int bitmapFormat, int bitmapWidth, int bitmapHeight, String name) throws IOException {
ALPHABITMAPDATA ret = new ALPHABITMAPDATA();
newDumpLevel(name, "ALPHABITMAPDATA");
ret.bitmapPixelData = new int[bitmapWidth * bitmapHeight];
for (int y = 0; y < bitmapHeight; y++) {
for (int x = 0; x < bitmapWidth; x++) {
ret.bitmapPixelData[y * bitmapWidth + x] = readARGBInt("bitmapPixelData");
}
}
endDumpLevel();
return ret;
}
/**
* Reads one ALPHACOLORMAPDATA value from the stream
*
* @param colorTableSize
* @param bitmapWidth
* @param bitmapHeight
* @param name
* @return ALPHACOLORMAPDATA value
* @throws IOException
*/
public ALPHACOLORMAPDATA readALPHACOLORMAPDATA(int colorTableSize, int bitmapWidth, int bitmapHeight, String name) throws IOException {
ALPHACOLORMAPDATA ret = new ALPHACOLORMAPDATA();
newDumpLevel(name, "ALPHACOLORMAPDATA");
ret.colorTableRGB = new int[colorTableSize + 1];
for (int i = 0; i < colorTableSize + 1; i++) {
ret.colorTableRGB[i] = readRGBAInt("colorTableRGB");
}
int dataLen = 0;
for (int y = 0; y < bitmapHeight; y++) {
int x = 0;
for (; x < bitmapWidth; x++) {
dataLen++;
}
while ((x % 4) != 0) {
dataLen++;
x++;
}
}
ret.colorMapPixelData = readBytesEx(dataLen, "colorMapPixelData");
endDumpLevel();
return ret;
}
public int available() throws IOException {
return is.available();
}
public long availableBits() throws IOException {
if (bitPos > 0) {
return available() * 8 + (8 - bitPos);
}
return available() * 8;
}
public MemoryInputStream getBaseStream() throws IOException {
int pos = (int) is.getPos();
MemoryInputStream mis = new MemoryInputStream(is.getAllRead(), 0, pos + is.available());
mis.seek(pos);
return mis;
}
public SWFInputStream getLimitedStream(int limit) throws IOException {
SWFInputStream sis = new SWFInputStream(swf, is.getAllRead(), startingPos, (int) (is.getPos() + limit));
// uncomment the following line to turn off lazy dump info collecting
//sis.dumpInfo = dumpInfo;
sis.seek(is.getPos() + startingPos);
return sis;
}
}