/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "avmshell.h" #include "zlib.h" namespace avmshell { // it's silly to write this again, but i'm being lazy. class SwfParser { int bitPos; int bitBuf; public: avmplus::ScriptBuffer swf; uint32_t pos; SwfParser(avmplus::ScriptBuffer swf) : bitPos(0) , bitBuf(0) , swf(swf) , pos(0) { } void skipHeader() { // skip rect int n = readUBits(5); readUBits(n); // xmin readUBits(n); // xmax readUBits(n); // ymin readUBits(n); // ymax // skip frame rate and frame count pos += 4; } int readU8() { return swf[pos++]; } int readU16() { return readU8() | readU8()<<8; } uint32_t readU32() { return readU8() | readU8()<<8 | readU8()<<16 | readU8()<<24; } void fillBitBuf() { bitBuf = readU8(); bitPos = 8; } int readUBits(int n) { if (n == 0) return 0; int bitsLeft = n; int result = 0; if (!bitPos) fillBitBuf(); int shift; for (shift = bitsLeft - bitPos; shift > 0; shift = bitsLeft - bitPos) { result |= bitBuf << shift; bitsLeft -= bitPos; fillBitBuf(); } // consume part of buffer result |= bitBuf >> -shift; bitPos -= bitsLeft; bitBuf &= 0xff >> (8 - bitPos); // mask consumed bits return result; } int readSBits(int n) { AvmAssert(n <= 32); int num = readUBits(n); int shift = 32 - n; return (num << shift) >> shift; // sign extend } void skipString() { while (readU8() != 0) {} } }; /** * isSwf() - return true if the swf magic # is present, ignoring version. */ bool isSwf(avmplus::ScriptBuffer swf) { if (swf.getSize() < 4) return false; uint32_t magic = swf[0] | swf[1]<<8 | swf[2]<<16; const uint32_t SWF = 'S'<<16 | 'W'<<8 | 'F'; const uint32_t SWC = 'S'<<16 | 'W'<<8 | 'C'; return magic == SWF || magic == SWC; } static const int stagDoABC = 72; static const int stagDoABC2 = 82; static void handleDoABC(int type, SwfParser &parser, int taglen, avmplus::Toplevel* toplevel, avmplus::CodeContext* codeContext, avmplus::GCList& deferred) { avmplus::AvmCore *core = toplevel->core(); int tagstart = parser.pos; const int kDoAbcLazyInitializeFlag = 1; uint32_t flags = 0; if (type == stagDoABC2) { // Flags (UI32) A 32-bit flags value, which may // contain the following bits set: kDoAbcLazyInitializeFlag = 1: Indicates that // the ABC block should not be executed immediately, but only parsed. A later // finddef may cause its scripts to execute. flags = parser.readU32(); // skip the abc name parser.skipString(); } // parse and execute the abc. // allocate a new buffer and copy abc into it; the abc buffer will be referenced // by PoolObject and can outlive the swf it came from. Using a ReadOnlyScriptBuffer // avoids copying, but interior pointers to the swf data do not pin the swf in memory. int abclen = taglen - (parser.pos - tagstart); avmplus::ScriptBuffer code(core->newScriptBuffer(abclen)); VMPI_memcpy(&code[0], &parser.swf[parser.pos], abclen); // FIXME get api from the SWF avmplus::ApiVersion apiVersion = core->getApiVersionFromCallStack(); if (flags & kDoAbcLazyInitializeFlag) { avmplus::PoolObject* pool = core->parseActionBlock(code, 0, toplevel, codeContext->domainEnv()->domain(), NULL, apiVersion); deferred.add(pool); // defer: handleActionPool(pool/* result of parse */, domainEnv, toplevel, codeContext); } else { core->handleActionBlock(code, 0, toplevel, NULL, codeContext, apiVersion); } parser.pos += abclen; } /* * Execute a swf as follows: * skip the header * for each DoABC2 tag * if lazy, parse it but don't run it: parseActionBlock() * else * run it via handleActionBlock() just as if it were on the commandline */ bool handleSwf(const char *filename, avmplus::ScriptBuffer swf, avmplus::Toplevel* toplevel, avmplus::CodeContext* codeContext, bool test_only) { bool has_abc = false; SwfParser parser(swf); parser.pos = 4; // skip magic # uint32_t swflen = parser.readU32(); avmplus::AvmCore *core = toplevel->core(); avmplus::GCList deferred(core->gc, avmplus::kListInitialCapacity); if (swf[0] == 'C') { // decompress the swf swflen -= 8; avmplus::ScriptBuffer newswf(core->newScriptBuffer(swflen)); uLongf dlen = swflen; int e = uncompress((Bytef*)&newswf[0], &dlen, (Bytef*)&swf[8], (uLongf)swf.getSize()-8); if (e != Z_OK) { if (!test_only) core->console << filename << ": error decompressing body: " << e << "\n"; return false; } swf = newswf; parser = SwfParser(newswf); parser.pos = 0; } if (swflen != swf.getSize()) { if (!test_only) core->console << filename << ": incorrect size: " << (uint32_t)swf.getSize() << " should be " << swflen << "\n"; return false; } parser.skipHeader(); uint32_t oldpos = parser.pos; while (parser.pos < swflen) { int tag = parser.readU16(); int type = tag >> 6; uint32_t taglen = (tag & 63); if (taglen == 63) taglen = parser.readU32(); if (type == stagDoABC || type == stagDoABC2) { has_abc = true; if (!test_only) handleDoABC(type, parser, taglen, toplevel, codeContext, deferred); } else parser.pos += taglen; if (parser.pos <= oldpos) { has_abc = false; // broken file or broken parser, but either way we can't process it break; } oldpos = parser.pos; } if (!test_only) { for (int i = 0, n = deferred.length(); i < n; i++) { core->handleActionPool(deferred[i], toplevel, codeContext); } } return has_abc; } }