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