1 // tag_loaders.cpp: SWF tags loaders, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h" // HAVE_ZLIB_H, USE_SWFTREE
23 #endif
24
25 #include "tag_loaders.h"
26
27 #include <cassert>
28
29 #include "utility.h"
30 #include "log.h"
31 #include "SWFStream.h"
32 #include "sprite_definition.h"
33 #include "SWFMovieDefinition.h"
34 #include "SWF.h"
35 #include "swf/TagLoadersTable.h"
36 #include "URL.h"
37 #include "GnashException.h"
38 #include "GnashAlgorithm.h"
39 #include "sound_definition.h"
40 #include "SoundInfo.h"
41 #include "MediaHandler.h"
42 #include "SimpleBuffer.h"
43 #include "sound_handler.h"
44 #include "RunResources.h"
45
46 namespace gnash {
47 namespace SWF {
48
49 // Anonymous namespace
50 namespace {
51
52 const std::uint32_t samplerates[] = { 5512, 11025, 22050, 44100 };
53
54 }
55
56 // Label the current frame of m with the name from the SWFStream.
57 void
frame_label_loader(SWFStream & in,TagType tag,movie_definition & m,const RunResources &)58 frame_label_loader(SWFStream& in, TagType tag, movie_definition& m,
59 const RunResources& /*r*/)
60 {
61 assert(tag == SWF::FRAMELABEL); // 43
62
63 std::string name;
64 in.read_string(name);
65
66 m.add_frame_name(name);
67
68 // FIXME: support SWF6 "named anchors"
69 //
70 // If SWF version is >= 6 check the byte after terminating NULL
71 // if it is 1 this label can be accessed by #name and it's
72 // entrance sets the browser URL with anchor appended
73 //
74 // To avoid relying on SWFStream::tell (see task #5838)
75 // we should add a new method to that class
76 // (ie: SWFStream::current_tag_length)
77 //
78 // See server/sample/test_clipping_layer.swf for a testcase.
79 //
80 size_t end_tag = in.get_tag_end_position();
81 size_t curr_pos = in.tell();
82 if (end_tag != curr_pos) {
83 if (end_tag == curr_pos + 1) {
84 log_unimpl(_("anchor-labeled frame not supported"));
85 }
86 else {
87 IF_VERBOSE_MALFORMED_SWF(
88 log_swferror(_("frame_label_loader end position %d, "
89 "read up to %d"),
90 end_tag, curr_pos);
91 );
92 }
93 }
94 }
95
96
97 // Create and initialize a sprite, and add it to the movie.
98 void
sprite_loader(SWFStream & in,TagType tag,movie_definition & m,const RunResources & r)99 sprite_loader(SWFStream& in, TagType tag, movie_definition& m,
100 const RunResources& r)
101 {
102 assert(tag == SWF::DEFINESPRITE); // 39 - DefineSprite
103
104 in.ensureBytes(2);
105 const std::uint16_t id = in.read_u16();
106
107 IF_VERBOSE_PARSE(
108 log_parse(_(" sprite: char id = %d"), id);
109 );
110
111 // A DEFINESPRITE tag as part of a DEFINESPRITE
112 // would be a malformed SWF, anyway to be compatible
113 // we should still allow that. See bug #22468.
114 IF_VERBOSE_MALFORMED_SWF(
115 try {
116 dynamic_cast<SWFMovieDefinition&>(m);
117 }
118 catch (const std::bad_cast&) {
119 log_swferror(_("Nested DEFINESPRITE tags. Will add to "
120 "top-level DisplayObjects dictionary."));
121 }
122 );
123
124 // will automatically read the sprite
125 sprite_definition* ch = new sprite_definition(m, in, r, id);
126
127 IF_VERBOSE_MALFORMED_SWF(
128 if (!ch->get_frame_count()) {
129 log_swferror(_("Sprite %d advertise no frames"), id);
130 }
131 );
132
133 m.addDisplayObject(id, ch);
134 }
135
136 // Common data
137
138 /// Sample rate table for DEFINESOUNDHEAD tags
139 //
140 /// The value found in the tag is encoded as 2 bits and
141 /// represent a multiple of 5512.5.
142 /// NOTE that the first element of this table lacks the .5
143 /// portion of the actual value. Dunno what consequences
144 /// it could have...
145
146 // @@ There are two sets of code to decode/expand/byteswap audio here.
147 // @@ There should be one (search for ADPCM).
148
149 // Load a DefineSound tag.
150 void
define_sound_loader(SWFStream & in,TagType tag,movie_definition & m,const RunResources & r)151 define_sound_loader(SWFStream& in, TagType tag, movie_definition& m,
152 const RunResources& r)
153 {
154 assert(tag == SWF::DEFINESOUND); // 14
155
156 sound::sound_handler* handler = r.soundHandler();
157
158 in.ensureBytes(2+4+1+4); // DisplayObject id + flags + sample count
159
160 const std::uint16_t id = in.read_u16();
161
162 #ifdef USE_SOUND
163 media::audioCodecType format = static_cast<media::audioCodecType>(
164 in.read_uint(4));
165
166 std::uint8_t sample_rate_in = in.read_uint(2);
167
168 if (sample_rate_in >= arraySize(samplerates)) {
169 IF_VERBOSE_MALFORMED_SWF(
170 log_swferror(_("DEFINESOUNDLOADER: sound sample rate %d (expected "
171 "0 to %u)"), +sample_rate_in, arraySize(samplerates));
172 );
173 sample_rate_in = 0;
174 }
175 const std::uint32_t sample_rate = samplerates[sample_rate_in];
176
177 const bool sample_16bit = in.read_bit();
178 const bool stereo = in.read_bit();
179
180 const std::uint32_t sample_count = in.read_u32();
181
182 std::int16_t delaySeek = 0;
183
184 if (format == media::AUDIO_CODEC_MP3) {
185 in.ensureBytes(2);
186 delaySeek = in.read_s16();
187 }
188
189 IF_VERBOSE_PARSE(
190 log_parse(_("define sound: ch=%d, format=%s, rate=%d, 16=%d, "
191 "stereo=%d, ct=%d, delay=%d"), id, format, sample_rate,
192 sample_16bit, stereo, sample_count, delaySeek);
193 );
194
195 // If we have a sound_handler, ask it to init this sound.
196
197 if (handler) {
198 // First it is the amount of data from file,
199 // then the amount allocated at *data (it may grow)
200 const unsigned dataLength = in.get_tag_end_position() - in.tell();
201
202 // Allocate MediaHandler::getInputPaddingSize() bytes more for the
203 // SimpleBuffer
204 size_t allocSize = dataLength;
205 media::MediaHandler* mh = r.mediaHandler();
206 if (mh) allocSize += mh->getInputPaddingSize();
207
208 std::unique_ptr<SimpleBuffer> data(new SimpleBuffer(allocSize));
209
210 // dataLength is already calculated from the end of the tag, which
211 // should be inside the end of the file. TODO: check that this is
212 // the case.
213 const unsigned int bytesRead = in.read(
214 reinterpret_cast<char*>(data->data()), dataLength);
215 data->resize(bytesRead); // in case it's shorter...
216
217 if (bytesRead < dataLength) {
218 throw ParserException(_("Tag boundary reported past end of "
219 "SWFStream!"));
220 }
221
222 // Store all the data in a SoundInfo object
223 const media::SoundInfo sinfo(format, stereo, sample_rate,
224 sample_count, sample_16bit, delaySeek);
225
226 // Stores the sounddata in the soundhandler, and the ID returned
227 // can be used to starting, stopping and deleting that sound
228 const int handler_id = handler->create_sound(std::move(data), sinfo);
229
230 if (handler_id >= 0) {
231 sound_sample* sam = new sound_sample(handler_id, r);
232 m.add_sound_sample(id, sam);
233 }
234
235 }
236 else {
237 // is this nice to do?
238 log_error(_("There is no sound handler currently active, "
239 "so DisplayObject with id %d will not be added to "
240 "the dictionary"), id);
241 }
242 #endif // USE_SOUND
243 }
244
245
246 void
file_attributes_loader(SWFStream & in,TagType tag,movie_definition & m,const RunResources &)247 file_attributes_loader(SWFStream& in, TagType tag, movie_definition& m,
248 const RunResources& /*r*/)
249 {
250 assert(tag == SWF::FILEATTRIBUTES); // 69
251
252 struct file_attrs_flags {
253 unsigned reserved1;
254 bool metadata;
255 bool as3;
256 unsigned reserved2;
257 bool network;
258 unsigned reserved3;
259 };
260
261 file_attrs_flags flags;
262
263 in.ensureBytes(1 + 3);
264 flags.reserved1 = in.read_uint(3);
265 flags.metadata = in.read_bit();
266 flags.as3 = in.read_bit();
267 flags.reserved2 = in.read_uint(2);
268 flags.network = in.read_bit();
269 flags.reserved3 = in.read_uint(24);
270
271 IF_VERBOSE_PARSE(
272 log_parse(_("File attributes: metadata=%s network=%s"),
273 flags.metadata ? _("true") : _("false"),
274 flags.network ? _("true") : _("false"))
275 );
276
277 if (!flags.network) {
278 log_unimpl(_("FileAttributes tag in the SWF requests that "
279 "network access is not granted to this movie "
280 "(or application?) when loaded from the filesystem. "
281 "Anyway Gnash won't care; "
282 "use white/black listing in your .gnashrc instead"));
283 }
284
285 if (flags.as3) {
286 log_unimpl(_("This SWF file requires AVM2: there will be no "
287 "ActionScript interpretation"));
288 }
289 else log_debug("This SWF uses AVM1");
290
291 // TODO: - don't allow later FileAttributes tags in the same movie
292 // to override the first one used.
293 // - only use if it is the *first* tag in the SWFStream.
294
295 if (flags.as3) m.setAS3();
296
297 }
298
299
300 void
metadata_loader(SWFStream & in,TagType tag,movie_definition & m,const RunResources &)301 metadata_loader(SWFStream& in, TagType tag, movie_definition& m,
302 const RunResources& /*r*/)
303 {
304 assert(tag == SWF::METADATA);
305
306 // this is supposed to be an XML string
307 std::string metadata;
308 in.read_string(metadata);
309
310 IF_VERBOSE_PARSE(
311 log_parse(_(" RDF metadata (information only): [[\n%s\n]]"),
312 metadata);
313 );
314
315 // The metadata tag exists exclusively for external
316 // description of the SWF file and should be ignored
317 // by the SWF player.
318 //
319 // Note: the presence of metadata should correspond to the
320 // file attributes flag hasMetadata; otherwise the SWF
321 // is malformed.
322 //
323 // This should be in RDF format, so should be easy to parse
324 // (knowing how well Adobe conform to XML standards...) if
325 // it's worth it.
326 // See http://www.w3.org/TR/rdf-syntax-grammar/
327 log_debug(_("Descriptive metadata from movie %s: %s"),
328 m.get_url(), metadata);
329
330 #ifdef USE_SWFTREE
331 // If the Movie Properties tree is disabled, the metadata
332 // is discarded to save parsing time and memory. There seems
333 // to be no limit on its length, although you'd have to be
334 // malicious or stupid to put really enormous amounts of
335 // descriptive metadata in a SWF. There can be one tag for each
336 // loaded SWF, however, so it could mount up.
337 m.storeDescriptiveMetadata(metadata);
338 #endif
339
340 }
341
342 void
serialnumber_loader(SWFStream & in,TagType tag,movie_definition &,const RunResources &)343 serialnumber_loader(SWFStream& in, TagType tag, movie_definition& /*m*/,
344 const RunResources& /*r*/)
345 {
346 assert(tag == SWF::SERIALNUMBER); // 41
347
348 in.ensureBytes(26);
349
350 const std::uint32_t id = in.read_u32();
351 const std::uint32_t edition = in.read_u32();
352 const std::uint8_t major = in.read_u8();
353 const std::uint8_t minor = in.read_u8();
354
355 const std::uint32_t buildL = in.read_u32();
356 const std::uint32_t buildH = in.read_u32();
357 const std::uint64_t build =
358 (static_cast<std::uint64_t>(buildH) << 32) + buildL;
359
360 const std::uint32_t timestampL = in.read_u32();
361 const std::uint32_t timestampH = in.read_u32();
362 // This timestamp is number of milliseconds since 1 Jan 1970 (epoch)
363 std::uint64_t timestamp =
364 (static_cast<std::uint64_t>(timestampH) << 32) + timestampL;
365
366 std::stringstream ss;
367 ss << "SERIALNUMBER: Version " << id << "." << edition
368 << "." << +major << "." << +minor;
369 ss << " - Build " << build;
370 ss << " - Timestamp " << timestamp;
371
372 log_debug("%s", ss.str());
373
374 // attach to movie_definition ?
375 }
376
377 void
reflex_loader(SWFStream & in,TagType tag,movie_definition &,const RunResources &)378 reflex_loader(SWFStream& in, TagType tag, movie_definition& /*m*/,
379 const RunResources& /*r*/)
380 {
381 assert(tag == SWF::REFLEX); // 777
382
383 in.ensureBytes(3);
384 const std::uint8_t first = in.read_u8();
385 const std::uint8_t second = in.read_u8();
386 const std::uint8_t third = in.read_u8();
387
388 IF_VERBOSE_PARSE(
389 log_parse(_(" reflex = \"%c%c%c\""), first, second, third);
390 );
391 }
392
393 } // namespace gnash::SWF
394
395 // Local Variables:
396 // mode: C++
397 // indent-tabs-mode: t
398 // End:
399
400 } // namespace gnash
401