package com.intellij.lang.javascript.flex.importer; import org.jetbrains.annotations.NonNls; /** * @author Maxim.Mossienko * Date: Oct 20, 2008 * Time: 7:01:59 PM */ class Swf { private static class Rect { int nBits; int xMin, xMax; int yMin, yMax; public @NonNls String toString() { return "[Rect " + xMin + " " + yMin + " " + xMax + " " + yMax + "]"; } } private final FlexByteCodeInformationProcessor processor; private int bitPos; private int bitBuf; private final ByteBuffer data; private static final int stagDoABC = 72; // embedded .abc (AVM+) bytecode private static final int stagDoABC2 = 82; // revised ABC version with a name private static final @NonNls String[] tagNames = { "End", // 00 "ShowFrame", // 01 "DefineShape", // 02 "FreeCharacter", // 03 "PlaceObject", // 04 "RemoveObject", // 05 "DefineBits", // 06 "DefineButton", // 07 "JPEGTables", // 08 "SetBackgroundColor", // 09 "DefineFont", // 10 "DefineText", // 11 "DoAction", // 12 "DefineFontInfo", // 13 "DefineSound", // 14 "StartSound", // 15 "StopSound", // 16 "DefineButtonSound", // 17 "SoundStreamHead", // 18 "SoundStreamBlock", // 19 "DefineBitsLossless", // 20 "DefineBitsJPEG2", // 21 "DefineShape2", // 22 "DefineButtonCxform", // 23 "Protect", // 24 "PathsArePostScript", // 25 "PlaceObject2", // 26 "27 (invalid)", // 27 "RemoveObject2", // 28 "SyncFrame", // 29 "30 (invalid)", // 30 "FreeAll", // 31 "DefineShape3", // 32 "DefineText2", // 33 "DefineButton2", // 34 "DefineBitsJPEG3", // 35 "DefineBitsLossless2", // 36 "DefineEditText", // 37 "DefineVideo", // 38 "DefineSprite", // 39 "NameCharacter", // 40 "ProductInfo", // 41 "DefineTextFormat", // 42 "FrameLabel", // 43 "DefineBehavior", // 44 "SoundStreamHead2", // 45 "DefineMorphShape", // 46 "FrameTag", // 47 "DefineFont2", // 48 "GenCommand", // 49 "DefineCommandObj", // 50 "CharacterSet", // 51 "FontRef", // 52 "DefineFunction", // 53 "PlaceFunction", // 54 "GenTagObject", // 55 "ExportAssets", // 56 "ImportAssets", // 57 "EnableDebugger", // 58 "DoInitAction", // 59 "DefineVideoStream", // 60 "VideoFrame", // 61 "DefineFontInfo2", // 62 "DebugID", // 63 "EnableDebugger2", // 64 "ScriptLimits", // 65 "SetTabIndex", // 66 "DefineShape4", // 67 "68 (invalid)", // 68 "FileAttributes", // 69 "PlaceObject3", // 70 "ImportAssets2", // 71 "DoABC", // 72 "DefineFontAlignZones", // 73 "CSMTextSettings", // 74 "DefineFont3", // 75 "SymbolClass", // 76 "Metadata", // 77 "ScalingGrid", // 78 "79 (invalid)", // 79 "80 (invalid)", // 80 "81 (invalid)", // 81 "DoABC2", // 82 "DefineShape4", // 83 "DefineMorphShape2", // 84 "85 (invalid)", // 85 "DefineSceneAndFrameLabelData", // 86 "DefineBinaryData", // 87 "DefineFontName", // 88 "89 (unknown) ", // 89 "90 (unknown) ", // 90 "DefineFont4", // 91 "(invalid)" // end }; public Swf(final ByteBuffer _data, final FlexByteCodeInformationProcessor _processor) { data = _data; processor = _processor; final Rect rect = decodeRect(); final int rate = data.readUnsignedByte() << 8 | data.readUnsignedByte(); final int count = data.readUnsignedShort(); processor.dumpStat("size " + rect + "\n"); processor.dumpStat("frame rate " + rate + "\n"); processor.dumpStat("frame count " + count + "\n"); decodeTags(); } private void decodeTags() { int type, h, length; while (data.getPosition() < data.bytesSize()) { type = (h = data.readUnsignedShort()) >> 6; if (((length = h & 0x3F) == 0x3F)) length = data.readInt(); processor.dumpStat( (type < tagNames.length ? tagNames[type] : "undefined") + " " + length + "b " + ((int)100f * length / data.bytesSize()) + "%\n"); switch (type) { case 0: return; case stagDoABC2: int pos1 = data.getPosition(); data.readInt(); final String abcName = readString(); processor.dumpStat("\nabc name " + abcName + "\n"); length -= (data.getPosition() - pos1); // fall through case stagDoABC: ByteBuffer data2 = new ByteBuffer(); data2.setLittleEndian(); data.readBytes(data2, length); new Abc(data2, processor).dump(processor.getAbcInSwfIndent()); processor.append("\n"); break; default: data.incPosition(length); } } } private String readString() { String s = ""; int c; while ((c = data.readUnsignedByte()) != 0) s += (char)c; return s; } private void syncBits() { bitPos = 0; } private Rect decodeRect() { syncBits(); Rect rect = new Rect(); int nBits = readUBits(5); rect.xMin = readSBits(nBits); rect.xMax = readSBits(nBits); rect.yMin = readSBits(nBits); rect.yMax = readSBits(nBits); return rect; } int readSBits(int numBits) { if (numBits > 32) throw new Error("Number of bits > 32"); int num = readUBits(numBits); int shift = 32 - numBits; // sign extension num = (num << shift) >> shift; return num; } int readUBits(int numBits) { if (numBits == 0) return 0; int bitsLeft = numBits; int result = 0; if (bitPos == 0) //no value in the buffer - read a byte { bitBuf = data.readUnsignedByte(); bitPos = 8; } while (true) { int shift = bitsLeft - bitPos; if (shift > 0) { // Consume the entire buffer result |= bitBuf << shift; bitsLeft -= bitPos; // Get the next byte from the input stream bitBuf = data.readUnsignedByte(); bitPos = 8; } else { // Consume a portion of the buffer result |= bitBuf >> -shift; bitPos -= bitsLeft; bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits // if (sb.append) System.out.sb.appendln(" read"+numBits+" " + result); return result; } } } }