/* * Copyright (C) 2010-2011 JPEXS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.jpexs.asdec; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Stack; import com.jpexs.asdec.action.Action; import com.jpexs.asdec.action.swf3.ActionGetURL; import com.jpexs.asdec.action.swf3.ActionGoToLabel; import com.jpexs.asdec.action.swf3.ActionGotoFrame; import com.jpexs.asdec.action.swf3.ActionNextFrame; import com.jpexs.asdec.action.swf3.ActionPlay; import com.jpexs.asdec.action.swf3.ActionPrevFrame; import com.jpexs.asdec.action.swf3.ActionSetTarget; import com.jpexs.asdec.action.swf3.ActionStop; import com.jpexs.asdec.action.swf3.ActionStopSounds; import com.jpexs.asdec.action.swf3.ActionToggleQuality; import com.jpexs.asdec.action.swf3.ActionWaitForFrame; import com.jpexs.asdec.action.swf4.ActionAdd; import com.jpexs.asdec.action.swf4.ActionAnd; import com.jpexs.asdec.action.swf4.ActionAsciiToChar; import com.jpexs.asdec.action.swf4.ActionCall; import com.jpexs.asdec.action.swf4.ActionCharToAscii; import com.jpexs.asdec.action.swf4.ActionCloneSprite; import com.jpexs.asdec.action.swf4.ActionDivide; import com.jpexs.asdec.action.swf4.ActionEndDrag; import com.jpexs.asdec.action.swf4.ActionEquals; import com.jpexs.asdec.action.swf4.ActionGetProperty; import com.jpexs.asdec.action.swf4.ActionGetTime; import com.jpexs.asdec.action.swf4.ActionGetURL2; import com.jpexs.asdec.action.swf4.ActionGetVariable; import com.jpexs.asdec.action.swf4.ActionGotoFrame2; import com.jpexs.asdec.action.swf4.ActionIf; import com.jpexs.asdec.action.swf4.ActionJump; import com.jpexs.asdec.action.swf4.ActionLess; import com.jpexs.asdec.action.swf4.ActionMBAsciiToChar; import com.jpexs.asdec.action.swf4.ActionMBCharToAscii; import com.jpexs.asdec.action.swf4.ActionMBStringExtract; import com.jpexs.asdec.action.swf4.ActionMBStringLength; import com.jpexs.asdec.action.swf4.ActionMultiply; import com.jpexs.asdec.action.swf4.ActionNot; import com.jpexs.asdec.action.swf4.ActionOr; import com.jpexs.asdec.action.swf4.ActionPop; import com.jpexs.asdec.action.swf4.ActionPush; import com.jpexs.asdec.action.swf4.ActionRandomNumber; import com.jpexs.asdec.action.swf4.ActionRemoveSprite; import com.jpexs.asdec.action.swf4.ActionSetProperty; import com.jpexs.asdec.action.swf4.ActionSetTarget2; import com.jpexs.asdec.action.swf4.ActionSetVariable; import com.jpexs.asdec.action.swf4.ActionStartDrag; import com.jpexs.asdec.action.swf4.ActionStringAdd; import com.jpexs.asdec.action.swf4.ActionStringEquals; import com.jpexs.asdec.action.swf4.ActionStringExtract; import com.jpexs.asdec.action.swf4.ActionStringLength; import com.jpexs.asdec.action.swf4.ActionStringLess; import com.jpexs.asdec.action.swf4.ActionSubtract; import com.jpexs.asdec.action.swf4.ActionToInteger; import com.jpexs.asdec.action.swf4.ActionTrace; import com.jpexs.asdec.action.swf4.ActionWaitForFrame2; import com.jpexs.asdec.action.swf5.ActionAdd2; import com.jpexs.asdec.action.swf5.ActionBitAnd; import com.jpexs.asdec.action.swf5.ActionBitLShift; import com.jpexs.asdec.action.swf5.ActionBitOr; import com.jpexs.asdec.action.swf5.ActionBitRShift; import com.jpexs.asdec.action.swf5.ActionBitURShift; import com.jpexs.asdec.action.swf5.ActionBitXor; import com.jpexs.asdec.action.swf5.ActionCallFunction; import com.jpexs.asdec.action.swf5.ActionCallMethod; import com.jpexs.asdec.action.swf5.ActionConstantPool; import com.jpexs.asdec.action.swf5.ActionDecrement; import com.jpexs.asdec.action.swf5.ActionDefineFunction; import com.jpexs.asdec.action.swf5.ActionDefineLocal; import com.jpexs.asdec.action.swf5.ActionDefineLocal2; import com.jpexs.asdec.action.swf5.ActionDelete; import com.jpexs.asdec.action.swf5.ActionDelete2; import com.jpexs.asdec.action.swf5.ActionEnumerate; import com.jpexs.asdec.action.swf5.ActionEquals2; import com.jpexs.asdec.action.swf5.ActionGetMember; import com.jpexs.asdec.action.swf5.ActionIncrement; import com.jpexs.asdec.action.swf5.ActionInitArray; import com.jpexs.asdec.action.swf5.ActionInitObject; import com.jpexs.asdec.action.swf5.ActionLess2; import com.jpexs.asdec.action.swf5.ActionModulo; import com.jpexs.asdec.action.swf5.ActionNewMethod; import com.jpexs.asdec.action.swf5.ActionNewObject; import com.jpexs.asdec.action.swf5.ActionPushDuplicate; import com.jpexs.asdec.action.swf5.ActionReturn; import com.jpexs.asdec.action.swf5.ActionSetMember; import com.jpexs.asdec.action.swf5.ActionStackSwap; import com.jpexs.asdec.action.swf5.ActionStoreRegister; import com.jpexs.asdec.action.swf5.ActionTargetPath; import com.jpexs.asdec.action.swf5.ActionToNumber; import com.jpexs.asdec.action.swf5.ActionToString; import com.jpexs.asdec.action.swf5.ActionTypeOf; import com.jpexs.asdec.action.swf5.ActionWith; import com.jpexs.asdec.action.swf6.ActionEnumerate2; import com.jpexs.asdec.action.swf6.ActionGreater; import com.jpexs.asdec.action.swf6.ActionInstanceOf; import com.jpexs.asdec.action.swf6.ActionStrictEquals; import com.jpexs.asdec.action.swf6.ActionStringGreater; import com.jpexs.asdec.action.swf7.ActionCastOp; import com.jpexs.asdec.action.swf7.ActionDefineFunction2; import com.jpexs.asdec.action.swf7.ActionExtends; import com.jpexs.asdec.action.swf7.ActionImplementsOp; import com.jpexs.asdec.action.swf7.ActionThrow; import com.jpexs.asdec.action.swf7.ActionTry; import com.jpexs.asdec.helpers.Helper; import com.jpexs.asdec.tags.DefineBinaryData; import com.jpexs.asdec.tags.DefineBits; import com.jpexs.asdec.tags.DefineBitsJPEG2; import com.jpexs.asdec.tags.DefineBitsJPEG3; import com.jpexs.asdec.tags.DefineBitsLossless; import com.jpexs.asdec.tags.DefineBitsLossless2; import com.jpexs.asdec.tags.DefineButton2Tag; import com.jpexs.asdec.tags.DefineButtonTag; import com.jpexs.asdec.tags.DefineFont3; import com.jpexs.asdec.tags.DefineFont4; import com.jpexs.asdec.tags.DefineFontAlignZones; import com.jpexs.asdec.tags.DefineFontName; import com.jpexs.asdec.tags.DefineScalingGrid; import com.jpexs.asdec.tags.DefineShape; import com.jpexs.asdec.tags.DefineShape2; import com.jpexs.asdec.tags.DefineShape3; import com.jpexs.asdec.tags.DefineShape4; import com.jpexs.asdec.tags.DefineSpriteTag; import com.jpexs.asdec.tags.DoABCTag; import com.jpexs.asdec.tags.DoActionTag; import com.jpexs.asdec.tags.DoInitActionTag; import com.jpexs.asdec.tags.ExportAssetsTag; import com.jpexs.asdec.tags.FileAttributes; import com.jpexs.asdec.tags.FrameLabel; import com.jpexs.asdec.tags.JPEGTables; import com.jpexs.asdec.tags.Metadata; import com.jpexs.asdec.tags.PlaceObject2Tag; import com.jpexs.asdec.tags.PlaceObject3Tag; import com.jpexs.asdec.tags.ProductInfo; import com.jpexs.asdec.tags.RemoveObject2; import com.jpexs.asdec.tags.ScriptLimits; import com.jpexs.asdec.tags.SetBackgroundColor; import com.jpexs.asdec.tags.ShowFrameTag; import com.jpexs.asdec.tags.SymbolClass; import com.jpexs.asdec.tags.Tag; import com.jpexs.asdec.types.BUTTONCONDACTION; import com.jpexs.asdec.types.BUTTONRECORD; import com.jpexs.asdec.types.CLIPACTIONRECORD; import com.jpexs.asdec.types.CLIPACTIONS; import com.jpexs.asdec.types.CLIPEVENTFLAGS; import com.jpexs.asdec.types.CXFORMWITHALPHA; import com.jpexs.asdec.types.MATRIX; import com.jpexs.asdec.types.RECT; import com.jpexs.asdec.types.RGBA; import com.jpexs.asdec.types.filters.BEVELFILTER; import com.jpexs.asdec.types.filters.BLURFILTER; import com.jpexs.asdec.types.filters.COLORMATRIXFILTER; import com.jpexs.asdec.types.filters.CONVOLUTIONFILTER; import com.jpexs.asdec.types.filters.DROPSHADOWFILTER; import com.jpexs.asdec.types.filters.FILTER; import com.jpexs.asdec.types.filters.GLOWFILTER; import com.jpexs.asdec.types.filters.GRADIENTBEVELFILTER; import com.jpexs.asdec.types.filters.GRADIENTGLOWFILTER; /** * Class for reading data from SWF file * * @author JPEXS */ public class SWFInputStream extends InputStream { private InputStream is; private Stack margedPos = new Stack(); private long pos; private int version; /** * Constructor * * @param is Existing inputstream * @param version Version of SWF to read */ public SWFInputStream(InputStream is, int version, long startingPos) { this.version = version; this.is = is; pos = startingPos; } /** * Constructor * * @param is Existing inputstream * @param version Version of SWF to read */ public SWFInputStream(InputStream is, int version) { this(is, version, 0L); } /** * Gets position in bytes in the stream * * @return Number of bytes */ public long getPos() { return pos; } /** * Reads one byte from the stream * * @return byte or -1 on error * @throws IOException */ @Override public int read() throws IOException { pos++; bitPos = 0; return is.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { int bytesRead = super.read(b, off, len); bitPos = 0; pos += bytesRead; return bytesRead; } private void alignByte() { bitPos = 0; } private int readNoBitReset() throws IOException { pos++; return is.read(); } /** * Reads one UI8 (Unsigned 8bit integer) value from the stream * * @return UI8 value or -1 on error * @throws IOException */ public int readUI8() throws IOException { return read(); } /** * Reads one string value from the stream * * @return String value * @throws IOException */ public String readString() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int r = 0; while (true) { r = read(); if (r <= 0) return new String(baos.toByteArray(), "utf8"); baos.write(r); } } /** * Reads one UI32 (Unsigned 32bit integer) value from the stream * * @return UI32 value * @throws IOException */ public long readUI32() throws IOException { return (read() + (read() << 8) + (read() << 16) + (read() << 24)) & 0xffffffff; } /** * Reads one UI16 (Unsigned 16bit integer) value from the stream * * @return UI16 value * @throws IOException */ public int readUI16() throws IOException { return read() + (read() << 8); } /** * Reads one SI32 (Signed 32bit integer) value from the stream * * @return SI32 value * @throws IOException */ public long readSI32() throws IOException { long uval = read() + (read() << 8) + (read() << 16) + (read() << 24); if (uval >= 0x80000000) { return -(((~uval) & 0xffffffff) + 1); } else { return uval; } } /** * Reads one SI16 (Signed 16bit integer) value from the stream * * @return SI16 value * @throws IOException */ public int readSI16() throws IOException { int uval = read() + (read() << 8); if (uval >= 0x8000) { return -(((~uval) & 0xffff) + 1); } else { return uval; } } /** * Reads one SI8 (Signed 8bit integer) value from the stream * * @return SI8 value * @throws IOException */ public int readSI8() throws IOException { int uval = read(); if (uval >= 0x80) { return -(((~uval) & 0xff) + 1); } else { return uval; } } /** * Reads one FIXED (Fixed point 16.16) value from the stream * * @return FIXED value * @throws IOException */ public double readFIXED() throws IOException { int afterPoint = readUI16(); int beforePoint = readUI16(); return ((double) ((beforePoint << 16) + afterPoint)) / 65536; } /** * Reads one FIXED8 (Fixed point 8.8) value from the stream * * @return FIXED8 value * @throws IOException */ public float readFIXED8() throws IOException { int afterPoint = read(); int beforePoint = read(); return beforePoint + (((float) afterPoint) / 256); } private long readLong() throws IOException { byte readBuffer[] = readBytes(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) << 0)); } /** * Reads one DOUBLE (double precision floating point value) value from the stream * * @return DOUBLE value * @throws IOException */ public double readDOUBLE() throws IOException { long el = readLong(); double ret = Double.longBitsToDouble(el); return ret; } /** * Reads one FLOAT (single precision floating point value) value from the stream * * @return FLOAT value * @throws IOException */ public float readFLOAT() throws IOException { int val = (int) readUI32(); float ret = Float.intBitsToFloat(val); /*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 * * @return FLOAT16 value * @throws IOException */ public float readFLOAT16() throws IOException { int val = readUI16(); 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))); return ret; } /** * Reads bytes from the stream * * @param count Number of bytes to read * @return Array of read bytes * @throws IOException */ public byte[] readBytes(long count) throws IOException { if(count<=0) return new byte[0]; byte ret[] = new byte[(int) count]; for (int i = 0; i < count; i++) { ret[i] = (byte) read(); } return ret; } /** * Reads one EncodedU32 (Encoded unsigned 32bit value) value from the stream * * @return U32 value * @throws IOException */ public long readEncodedU32() throws IOException { int result = read(); if ((result & 0x00000080) == 0) { return result; } result = (result & 0x0000007f) | (read()) << 7; if ((result & 0x00004000) == 0) { return result; } result = (result & 0x00003fff) | (read()) << 14; if ((result & 0x00200000) == 0) { return result; } result = (result & 0x001fffff) | (read()) << 21; if ((result & 0x10000000) == 0) { return result; } result = (result & 0x0fffffff) | (read()) << 28; 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 * @return Unsigned value * @throws IOException */ public long readUB(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 = 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 * @return Signed value * @throws IOException */ public long readSB(int nBits) throws IOException { int uval = (int)readUB(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 * @return Fixed-point value * @throws IOException */ public double readFB(int nBits) throws IOException { double val = readSB(nBits); double ret = val / (1 << 16); return ret; } /** * Reads one RECT value from the stream * * @return RECT value * @throws IOException */ public RECT readRECT() throws IOException { RECT ret = new RECT(); int NBits = (int) readUB(5); ret.Xmin = (int) readSB(NBits); ret.Xmax = (int) readSB(NBits); ret.Ymin = (int) readSB(NBits); ret.Ymax = (int) readSB(NBits); return ret; } /** * Reads list of actions from the stream. Reading ends with ActionEndFlag(=0) or end of the stream. * * @return List of actions * @throws IOException */ public List readActionList() throws IOException { List ret = new ArrayList(); Action a; while ((a = readAction()) != null) { ret.add(a); } return ret; } private static void dumpTag(PrintStream out, int version, Tag tag, int level) { StringBuilder sb = new StringBuilder(); sb.append(Helper.formatHex((int)tag.getPos(), 8)); sb.append(": "); sb.append(Helper.indent(level, "")); 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((int)tag.getOrigDataLength(), 8)); sb.append(" "); sb.append(Helper.bytesToHexString(64, tag.getData(version), 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.hasSubTags()) { for (Tag subTag: tag.getSubTags()) { dumpTag(out, version, subTag, level + 1); } } } /** * Reads list of tags from the stream. Reading ends with End tag(=0) or end of the stream. * * @return List of tags * @throws IOException */ public List readTagList(int level) throws IOException { List tags = new ArrayList(); Tag tag; while (true) { long pos = getPos(); tag = readTag(level, pos); if (tag == null) { break; } tags.add(tag); if (Main.DUMP_TAGS && level == 0) { dumpTag(System.out, version, tag, level); } } return tags; } /** * Reads one Tag from the stream * * @return Tag or null when End tag * @throws IOException */ public Tag readTag(int level, long pos) throws IOException { int tagIDTagLength = readUI16(); int tagID = (tagIDTagLength) >> 6; if (tagID == 0) { return null; } long tagLength = (tagIDTagLength & 0x003F); boolean readLong = false; if (tagLength == 0x3f) { tagLength = readSI32(); readLong = true; } byte data[] = readBytes((int) tagLength); Tag ret; switch (tagID) { case 82: ret = new DoABCTag(data, version, pos); break; case 12: ret = new DoActionTag(data, version, pos); break; case 59: ret = new DoInitActionTag(data, version, pos); break; case 39: ret = new DefineSpriteTag(data, version, level, pos); break; case 1: ret = new ShowFrameTag(pos); break; case 26: ret = new PlaceObject2Tag(data, version, pos); break; case 56: ret = new ExportAssetsTag(data, version, pos); break; case 70: ret = new PlaceObject3Tag(data, version, pos); break; case 7: ret = new DefineButtonTag(data, version, pos); break; case 34: ret = new DefineButton2Tag(data, version, pos); break; case 69: ret = new FileAttributes(data, version, pos); break; case 77: ret = new Metadata(data, pos); break; case 65: ret = new ScriptLimits(data, version, pos); break; case 9: ret = new SetBackgroundColor(data, pos); break; case 41: ret = new ProductInfo(data, version, pos); break; case 43: ret = new FrameLabel(data, version, pos); break; case 36: ret = new DefineBitsLossless2(data, version, pos); break; case 76: ret = new SymbolClass(data, version, pos); break; case 32: ret = new DefineShape3(data, version, pos); break; case 28: ret = new RemoveObject2(data, version, pos); break; case 78: ret = new DefineScalingGrid(data, version, pos); break; case 2: ret = new DefineShape(data, version, pos); break; case 22: ret = new DefineShape2(data, version, pos); break; case 83: ret = new DefineShape4(data, version, pos); break; case 20: ret = new DefineBitsLossless(data, version, pos); break; case 35: ret = new DefineBitsJPEG3(data, version, pos); break; case 87: ret = new DefineBinaryData(data, version, pos); break; case 8: ret = new JPEGTables(data, pos); break; case 6: ret = new DefineBits(data, version, pos); break; case 21: ret = new DefineBitsJPEG2(data, version, pos); break; case 75: ret = new DefineFont3(data, version, pos); break; case 73: ret = new DefineFontAlignZones(data, version, pos); break; case 88: ret = new DefineFontName(data, version, pos); break; case 91: ret = new DefineFont4(data, version, pos); break; default: ret = new Tag(tagID, data, pos); } ret.forceWriteAsLong = readLong; 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 = readUI8(); if (actionCode == 0) return null; if (actionCode == -1) return null; int actionLength = 0; if (actionCode >= 0x80) { actionLength = readUI16(); } switch (actionCode) { //SWF3 Actions case 0x81: return new ActionGotoFrame(this); case 0x83: return new ActionGetURL(actionLength, this,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(this); case 0x8B: return new ActionSetTarget(actionLength, this,version); case 0x8C: return new ActionGoToLabel(actionLength, this,version); //SWF4 Actions case 0x96: return new ActionPush(actionLength, this, 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(this); case 0x9D: return new ActionIf(this); case 0x9E: return new ActionCall(); case 0x1C: return new ActionGetVariable(); case 0x1D: return new ActionSetVariable(); case 0x9A: return new ActionGetURL2(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(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,version); case 0x9B: return new ActionDefineFunction(actionLength, this, 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(this, 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(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, version); case 0x69: return new ActionExtends(); case 0x2B: return new ActionCastOp(); case 0x2C: return new ActionImplementsOp(); case 0x8F: return new ActionTry(actionLength, this, version); case 0x2A: return new ActionThrow(); default: if (actionLength > 0) skip(actionLength); return new Action(actionCode, actionLength); } } } /** * Reads one MATRIX value from the stream * * @return MATRIX value * @throws IOException */ public MATRIX readMatrix() throws IOException { MATRIX ret = new MATRIX(); ret.hasScale = readUB(1) == 1; if (ret.hasScale) { int NScaleBits = (int) readUB(5); ret.scaleNBits = NScaleBits; ret.scaleX = readFB(NScaleBits); ret.scaleY = readFB(NScaleBits); } ret.hasRotate = readUB(1) == 1; if (ret.hasRotate) { int NRotateBits = (int) readUB(5); ret.rotateNBits = NRotateBits; ret.rotateSkew0 = readFB(NRotateBits); ret.rotateSkew1 = readFB(NRotateBits); } int NTranslateBits = (int) readUB(5); ret.translateNBits = NTranslateBits; ret.translateX = (int)readSB(NTranslateBits); ret.translateY = (int)readSB(NTranslateBits); alignByte(); return ret; } /** * Reads one CXFORMWITHALPHA value from the stream * * @return CXFORMWITHALPHA value * @throws IOException */ public CXFORMWITHALPHA readCXFORMWITHALPHA() throws IOException { CXFORMWITHALPHA ret = new CXFORMWITHALPHA(); ret.hasAddTerms = readUB(1) == 1; ret.hasMultTerms = readUB(1) == 1; int Nbits = (int) readUB(4); ret.nbits = Nbits; if (ret.hasMultTerms) { ret.redMultTerm = (int) readSB(Nbits); ret.greenMultTerm = (int) readSB(Nbits); ret.blueMultTerm = (int) readSB(Nbits); ret.alphaMultTerm = (int) readSB(Nbits); } if (ret.hasAddTerms) { ret.redAddTerm = (int) readSB(Nbits); ret.greenAddTerm = (int) readSB(Nbits); ret.blueAddTerm = (int) readSB(Nbits); ret.alphaAddTerm = (int) readSB(Nbits); } alignByte(); return ret; } /** * Reads one CLIPEVENTFLAGS value from the stream * * @return CLIPEVENTFLAGS value * @throws IOException */ public CLIPEVENTFLAGS readCLIPEVENTFLAGS() throws IOException { CLIPEVENTFLAGS ret = new CLIPEVENTFLAGS(); ret.clipEventKeyUp = readUB(1) == 1; ret.clipEventKeyDown = readUB(1) == 1; ret.clipEventMouseUp = readUB(1) == 1; ret.clipEventMouseDown = readUB(1) == 1; ret.clipEventMouseMove = readUB(1) == 1; ret.clipEventUnload = readUB(1) == 1; ret.clipEventEnterFrame = readUB(1) == 1; ret.clipEventLoad = readUB(1) == 1; ret.clipEventDragOver = readUB(1) == 1; ret.clipEventRollOut = readUB(1) == 1; ret.clipEventRollOver = readUB(1) == 1; ret.clipEventReleaseOutside = readUB(1) == 1; ret.clipEventRelease = readUB(1) == 1; ret.clipEventPress = readUB(1) == 1; ret.clipEventInitialize = readUB(1) == 1; ret.clipEventData = readUB(1) == 1; if (version >= 6) { readUB(5); ret.clipEventConstruct = readUB(1) == 1; ret.clipEventKeyPress = readUB(1) == 1; ret.clipEventDragOut = readUB(1) == 1; readUB(8); } return ret; } /** * Reads one CLIPACTIONRECORD value from the stream * * @return CLIPACTIONRECORD value * @throws IOException */ public CLIPACTIONRECORD readCLIPACTIONRECORD() throws IOException { CLIPACTIONRECORD ret = new CLIPACTIONRECORD(); ret.eventFlags = readCLIPEVENTFLAGS(); if (ret.eventFlags.isClear()) return null; long actionRecordSize = readUI32(); if (ret.eventFlags.clipEventKeyPress) { ret.keyCode = readUI8(); actionRecordSize--; } ret.actionBytes=readBytes(actionRecordSize); //ret.actions = (new SWFInputStream(new ByteArrayInputStream(readBytes(actionRecordSize)), version)).readActionList(); return ret; } /** * Reads one CLIPACTIONS value from the stream * * @return CLIPACTIONS value * @throws IOException */ public CLIPACTIONS readCLIPACTIONS() throws IOException { CLIPACTIONS ret = new CLIPACTIONS(); readUI16();//reserved ret.allEventFlags = readCLIPEVENTFLAGS(); CLIPACTIONRECORD cr; ret.clipActionRecords = new ArrayList(); while ((cr = readCLIPACTIONRECORD()) != null) { ret.clipActionRecords.add(cr); } return ret; } /** * Reads one COLORMATRIXFILTER value from the stream * * @return COLORMATRIXFILTER value * @throws IOException */ public COLORMATRIXFILTER readCOLORMATRIXFILTER() throws IOException { COLORMATRIXFILTER ret = new COLORMATRIXFILTER(); ret.matrix = new float[20]; for (int i = 0; i < 20; i++) { ret.matrix[i] = readFLOAT(); } return ret; } /** * Reads one RGBA value from the stream * * @return RGBA value * @throws IOException */ public RGBA readRGBA() throws IOException { RGBA ret = new RGBA(); ret.red = readUI8(); ret.green = readUI8(); ret.blue = readUI8(); ret.alpha = readUI8(); return ret; } /** * Reads one CONVOLUTIONFILTER value from the stream * * @return CONVOLUTIONFILTER value * @throws IOException */ public CONVOLUTIONFILTER readCONVOLUTIONFILTER() throws IOException { CONVOLUTIONFILTER ret = new CONVOLUTIONFILTER(); ret.matrixX = readUI8(); ret.matrixY = readUI8(); ret.divisor = readFLOAT(); ret.bias = readFLOAT(); 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(); } } ret.defaultColor = readRGBA(); readUB(6);//reserved ret.clamp = readUB(1) == 1; ret.preserveAlpha = readUB(1) == 1; return ret; } /** * Reads one BLURFILTER value from the stream * * @return BLURFILTER value * @throws IOException */ public BLURFILTER readBLURFILTER() throws IOException { BLURFILTER ret = new BLURFILTER(); ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.passes = (int) readUB(5); readUB(3); //reserved return ret; } /** * Reads one DROPSHADOWFILTER value from the stream * * @return DROPSHADOWFILTER value * @throws IOException */ public DROPSHADOWFILTER readDROPSHADOWFILTER() throws IOException { DROPSHADOWFILTER ret = new DROPSHADOWFILTER(); ret.dropShadowColor = readRGBA(); ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.angle = readFIXED(); ret.distance = readFIXED(); ret.strength = readFIXED8(); ret.innerShadow = readUB(1) == 1; ret.knockout = readUB(1) == 1; ret.compositeSource = readUB(1) == 1; ret.passes = (int) readUB(5); return ret; } /** * Reads one GLOWFILTER value from the stream * * @return GLOWFILTER value * @throws IOException */ public GLOWFILTER readGLOWFILTER() throws IOException { GLOWFILTER ret = new GLOWFILTER(); ret.glowColor = readRGBA(); ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.strength = readFIXED8(); ret.innerGlow = readUB(1) == 1; ret.knockout = readUB(1) == 1; ret.compositeSource = readUB(1) == 1; ret.passes = (int) readUB(5); return ret; } /** * Reads one BEVELFILTER value from the stream * * @return BEVELFILTER value * @throws IOException */ public BEVELFILTER readBEVELFILTER() throws IOException { BEVELFILTER ret = new BEVELFILTER(); ret.shadowColor = readRGBA(); ret.highlightColor = readRGBA(); ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.angle = readFIXED(); ret.distance = readFIXED(); ret.strength = readFIXED8(); ret.innerShadow = readUB(1) == 1; ret.knockout = readUB(1) == 1; ret.compositeSource = readUB(1) == 1; ret.onTop = readUB(1) == 1; ret.passes = (int) readUB(4); return ret; } /** * Reads one GRADIENTGLOWFILTER value from the stream * * @return GRADIENTGLOWFILTER value * @throws IOException */ public GRADIENTGLOWFILTER readGRADIENTGLOWFILTER() throws IOException { GRADIENTGLOWFILTER ret = new GRADIENTGLOWFILTER(); int numColors = readUI8(); ret.gradientColors = new RGBA[numColors]; ret.gradientRatio = new int[numColors]; for (int i = 0; i < numColors; i++) { ret.gradientColors[i] = readRGBA(); } for (int i = 0; i < numColors; i++) { ret.gradientRatio[i] = readUI8(); } ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.angle = readFIXED(); ret.distance = readFIXED(); ret.strength = readFIXED8(); ret.innerShadow = readUB(1) == 1; ret.knockout = readUB(1) == 1; ret.compositeSource = readUB(1) == 1; ret.onTop = readUB(1) == 1; ret.passes = (int) readUB(4); return ret; } /** * Reads one GRADIENTBEVELFILTER value from the stream * * @return GRADIENTBEVELFILTER value * @throws IOException */ public GRADIENTBEVELFILTER readGRADIENTBEVELFILTER() throws IOException { GRADIENTBEVELFILTER ret = new GRADIENTBEVELFILTER(); int numColors = readUI8(); ret.gradientColors = new RGBA[numColors]; ret.gradientRatio = new int[numColors]; for (int i = 0; i < numColors; i++) { ret.gradientColors[i] = readRGBA(); } for (int i = 0; i < numColors; i++) { ret.gradientRatio[i] = readUI8(); } ret.blurX = readFIXED(); ret.blurY = readFIXED(); ret.angle = readFIXED(); ret.distance = readFIXED(); ret.strength = readFIXED8(); ret.innerShadow = readUB(1) == 1; ret.knockout = readUB(1) == 1; ret.compositeSource = readUB(1) == 1; ret.onTop = readUB(1) == 1; ret.passes = (int) readUB(4); return ret; } /** * Reads list of FILTER values from the stream * * @return List of FILTER values * @throws IOException */ public List readFILTERLIST() throws IOException { List ret = new ArrayList(); int numberOfFilters = readUI8(); for (int i = 0; i < numberOfFilters; i++) { ret.add(readFILTER()); } return ret; } /** * Reads one FILTER value from the stream * * @return FILTER value * @throws IOException */ public FILTER readFILTER() throws IOException { int filterId = readUI8(); switch (filterId) { case 0: return readDROPSHADOWFILTER(); case 1: return readBLURFILTER(); case 2: return readGLOWFILTER(); case 3: return readBEVELFILTER(); case 4: return readGRADIENTGLOWFILTER(); case 5: return readCONVOLUTIONFILTER(); case 6: return readCOLORMATRIXFILTER(); case 7: return readGRADIENTBEVELFILTER(); default: return null; } } /** * Reads list of BUTTONRECORD values from the stream * * @param inDefineButton2 Whether read from inside of DefineButton2Tag or not * @return List of BUTTONRECORD values * @throws IOException */ public List readBUTTONRECORDList(boolean inDefineButton2) throws IOException { List ret = new ArrayList(); BUTTONRECORD br; while ((br = readBUTTONRECORD(inDefineButton2)) != null) { ret.add(br); } return ret; } /** * Reads one BUTTONRECORD value from the stream * * @return BUTTONRECORD value * @throws IOException */ public BUTTONRECORD readBUTTONRECORD(boolean inDefineButton2) throws IOException { BUTTONRECORD ret = new BUTTONRECORD(); int res = (int) readUB(2); //reserved ret.buttonHasBlendMode = readUB(1) == 1; ret.buttonHasFilterList = readUB(1) == 1; ret.buttonStateHitTest = readUB(1) == 1; ret.buttonStateDown = readUB(1) == 1; ret.buttonStateOver = readUB(1) == 1; ret.buttonStateUp = readUB(1) == 1; if (!ret.buttonHasBlendMode) if (!ret.buttonHasFilterList) if (!ret.buttonStateHitTest) if (!ret.buttonStateDown) if (!ret.buttonStateOver) if (!ret.buttonStateUp) if (res == 0) return null; ret.characterId = readUI16(); ret.placeDepth = readUI16(); ret.placeMatrix = readMatrix(); if (inDefineButton2) { ret.colorTransform = readCXFORMWITHALPHA(); if (ret.buttonHasFilterList) { ret.filterList = readFILTERLIST(); } if (ret.buttonHasBlendMode) { ret.blendMode = readUI8(); } } return ret; } /** * Reads list of BUTTONCONDACTION values from the stream * * @return List of BUTTONCONDACTION values * @throws IOException */ public List readBUTTONCONDACTIONList() throws IOException { List ret = new ArrayList(); BUTTONCONDACTION bc; while (!(bc = readBUTTONCONDACTION()).isLast) { ret.add(bc); } ret.add(bc); return ret; } /** * Reads one BUTTONCONDACTION value from the stream * * @return BUTTONCONDACTION value * @throws IOException */ public BUTTONCONDACTION readBUTTONCONDACTION() throws IOException { BUTTONCONDACTION ret = new BUTTONCONDACTION(); int condActionSize = readUI16(); ret.isLast = condActionSize <= 0; ret.condIdleToOverDown = readUB(1) == 1; ret.condOutDownToIdle = readUB(1) == 1; ret.condOutDownToOverDown = readUB(1) == 1; ret.condOverDownToOutDown = readUB(1) == 1; ret.condOverDownToOverUp = readUB(1) == 1; ret.condOverUpToOverDown = readUB(1) == 1; ret.condOverUpToIddle = readUB(1) == 1; ret.condIdleToOverUp = readUB(1) == 1; ret.condKeyPress = (int) readUB(7); ret.condOverDownToIddle = readUB(1) == 1; if(condActionSize<=0){ ret.actionBytes=readBytes(available()); }else{ ret.actionBytes=readBytes(condActionSize-4); } //ret.actions = readActionList(); return ret; } @Override public int available() throws IOException { return is.available(); } }