Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / shell / swf.cpp
blob51a6d21c085df4e2a37d892c36e375899def0646
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2009
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Adobe AS3 Team
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "avmshell.h"
41 #include "zlib.h"
43 namespace avmshell
45 // it's silly to write this again, but i'm being lazy.
46 class SwfParser {
47 int bitPos;
48 int bitBuf;
49 public:
50 ScriptBuffer swf;
51 uint32_t pos;
52 SwfParser(ScriptBuffer swf)
53 : bitPos(0)
54 , bitBuf(0)
55 , swf(swf)
56 , pos(0)
60 void skipHeader() {
61 // skip rect
62 int n = readUBits(5);
63 readUBits(n); // xmin
64 readUBits(n); // xmax
65 readUBits(n); // ymin
66 readUBits(n); // ymax
68 // skip frame rate and frame count
69 pos += 4;
72 int readU8() {
73 return swf[pos++];
75 int readU16() {
76 return readU8() | readU8()<<8;
78 uint32_t readU32() {
79 return readU8() | readU8()<<8 | readU8()<<16 | readU8()<<24;
82 void fillBitBuf() {
83 bitBuf = readU8();
84 bitPos = 8;
87 int readUBits(int n) {
88 if (n == 0)
89 return 0;
90 int bitsLeft = n;
91 int result = 0;
92 if (!bitPos)
93 fillBitBuf();
94 int shift;
95 for (shift = bitsLeft - bitPos; shift > 0; shift = bitsLeft - bitPos) {
96 result |= bitBuf << shift;
97 bitsLeft -= bitPos;
98 fillBitBuf();
100 // consume part of buffer
101 result |= bitBuf >> -shift;
102 bitPos -= bitsLeft;
103 bitBuf &= 0xff >> (8 - bitPos); // mask consumed bits
104 return result;
107 int readSBits(int n) {
108 AvmAssert(n <= 32);
109 int num = readUBits(n);
110 int shift = 32 - n;
111 return (num << shift) >> shift; // sign extend
114 void skipString() {
115 while (readU8() != 0)
121 * isSwf() - return true if the swf magic # is present, ignoring version.
123 bool isSwf(ScriptBuffer swf) {
124 if (swf.getSize() < 4)
125 return false;
126 uint32_t magic = swf[0] | swf[1]<<8 | swf[2]<<16;
127 const uint32_t SWF = 'S'<<16 | 'W'<<8 | 'F';
128 const uint32_t SWC = 'S'<<16 | 'W'<<8 | 'C';
129 return magic == SWF || magic == SWC;
132 static const int stagDoABC = 72;
133 static const int stagDoABC2 = 82;
135 static void handleDoABC(int type, SwfParser &parser, int taglen,
136 Toplevel* toplevel, CodeContext* codeContext,
137 GCList<PoolObject>& deferred)
139 AvmCore *core = toplevel->core();
140 int tagstart = parser.pos;
141 const int kDoAbcLazyInitializeFlag = 1;
142 uint32_t flags = 0;
144 if (type == stagDoABC2)
146 // Flags (UI32) A 32-bit flags value, which may
147 // contain the following bits set: kDoAbcLazyInitializeFlag = 1: Indicates that
148 // the ABC block should not be executed immediately, but only parsed. A later
149 // finddef may cause its scripts to execute.
150 flags = parser.readU32();
152 // skip the abc name
153 parser.skipString();
156 // parse and execute the abc.
158 // allocate a new buffer and copy abc into it; the abc buffer will be referenced
159 // by PoolObject and can outlive the swf it came from. Using a ReadOnlyScriptBuffer
160 // avoids copying, but interior pointers to the swf data do not pin the swf in memory.
161 int abclen = taglen - (parser.pos - tagstart);
162 ScriptBuffer code(core->newScriptBuffer(abclen));
163 VMPI_memcpy(&code[0], &parser.swf[parser.pos], abclen);
165 // FIXME get api from the SWF
166 ApiVersion apiVersion = core->getApiVersionFromCallStack();
167 if (flags & kDoAbcLazyInitializeFlag) {
168 PoolObject* pool = core->parseActionBlock(code, 0, toplevel, codeContext->domainEnv()->domain(), NULL, apiVersion);
169 deferred.add(pool);
170 // defer: handleActionPool(pool/* result of parse */, domainEnv, toplevel, codeContext);
171 } else {
172 core->handleActionBlock(code, 0, toplevel, NULL, codeContext, apiVersion);
174 parser.pos += abclen;
178 * Execute a swf as follows:
179 * skip the header
180 * for each DoABC2 tag
181 * if lazy, parse it but don't run it: parseActionBlock()
182 * else
183 * run it via handleActionBlock() just as if it were on the commandline
185 bool handleSwf(const char *filename, ScriptBuffer swf,
186 Toplevel* toplevel, CodeContext* codeContext,
187 bool test_only)
189 bool has_abc = false;
190 SwfParser parser(swf);
191 parser.pos = 4; // skip magic #
192 uint32_t swflen = parser.readU32();
193 AvmCore *core = toplevel->core();
194 GCList<PoolObject> deferred(core->gc, kListInitialCapacity);
195 if (swf[0] == 'C') {
196 // decompress the swf
197 swflen -= 8;
198 ScriptBuffer newswf(core->newScriptBuffer(swflen));
199 uLongf dlen = swflen;
200 int e = uncompress((Bytef*)&newswf[0], &dlen, (Bytef*)&swf[8], (uLongf)swf.getSize()-8);
201 if (e != Z_OK) {
202 if (!test_only)
203 core->console << filename << ": error decompressing body: " << e << "\n";
204 return false;
206 swf = newswf;
207 parser = SwfParser(newswf);
208 parser.pos = 0;
210 if (swflen != swf.getSize()) {
211 if (!test_only)
212 core->console << filename <<
213 ": incorrect size: " << (uint32_t)swf.getSize() <<
214 " should be " << swflen << "\n";
215 return false;
217 parser.skipHeader();
218 uint32_t oldpos = parser.pos;
219 while (parser.pos < swflen) {
220 int tag = parser.readU16();
221 int type = tag >> 6;
222 uint32_t taglen = (tag & 63);
223 if (taglen == 63)
224 taglen = parser.readU32();
225 if (type == stagDoABC || type == stagDoABC2) {
226 has_abc = true;
227 if (!test_only)
228 handleDoABC(type, parser, taglen, toplevel, codeContext, deferred);
230 else
231 parser.pos += taglen;
232 if (parser.pos <= oldpos) {
233 has_abc = false; // broken file or broken parser, but either way we can't process it
234 break;
236 oldpos = parser.pos;
238 if (!test_only) {
239 for (int i = 0, n = deferred.length(); i < n; i++) {
240 core->handleActionPool(deferred[i], toplevel, codeContext);
243 return has_abc;