1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 
20 #include "MovieFactory.h"
21 
22 #include <string>
23 #include <map>
24 #include <memory>
25 #include <algorithm>
26 
27 #include "GnashEnums.h"
28 #include "GnashImage.h"
29 #include "IOChannel.h"
30 #include "utility.h"
31 #include "log.h"
32 #include "SWFMovieDefinition.h"
33 #include "BitmapMovieDefinition.h"
34 #include "RunResources.h"
35 #include "URL.h"
36 #include "StreamProvider.h"
37 #include "MovieLibrary.h"
38 #include "fontlib.h"
39 
40 namespace gnash {
41 
42 namespace {
43     /// Get type of file looking at first bytes
44     FileType getFileType(IOChannel& in);
45 
46     boost::intrusive_ptr<SWFMovieDefinition> createSWFMovie(
47             std::unique_ptr<IOChannel> in, const std::string& url,
48             const RunResources& runResources, bool startLoaderThread);
49 
50     boost::intrusive_ptr<BitmapMovieDefinition> createBitmapMovie(
51             std::unique_ptr<IOChannel> in, const std::string& url,
52             const RunResources& r, FileType type);
53 
54     boost::intrusive_ptr<movie_definition> createNonLibraryMovie(
55             const URL& url, const RunResources& runResources,
56             const char* reset_url, bool startLoaderThread,
57             const std::string* postdata);
58 }
59 
60 MovieLibrary MovieFactory::movieLibrary;
61 
62 boost::intrusive_ptr<movie_definition>
makeMovie(std::unique_ptr<IOChannel> in,const std::string & url,const RunResources & runResources,bool startLoaderThread)63 MovieFactory::makeMovie(std::unique_ptr<IOChannel> in, const std::string& url,
64         const RunResources& runResources, bool startLoaderThread)
65 {
66     boost::intrusive_ptr<movie_definition> ret;
67 
68     assert(in.get());
69 
70     // see if it's a jpeg or an swf
71     FileType type = getFileType(*in);
72 
73     switch (type) {
74         case GNASH_FILETYPE_JPEG:
75         case GNASH_FILETYPE_PNG:
76         case GNASH_FILETYPE_GIF:
77         {
78             if (!startLoaderThread) {
79               log_unimpl(_("Requested to keep from completely loading "
80                            "a movie, but the movie in question is an "
81                            "image, for which we don't yet have the "
82                            "concept of a 'loading thread'"));
83             }
84             ret = createBitmapMovie(std::move(in), url, runResources, type);
85             break;
86         }
87 
88 
89         case GNASH_FILETYPE_SWF:
90             ret = createSWFMovie(std::move(in), url, runResources, startLoaderThread);
91             break;
92 
93         case GNASH_FILETYPE_FLV:
94             log_unimpl(_("FLV can't be loaded directly as a movie"));
95             return ret;
96 
97         default:
98             log_error(_("Unknown file type"));
99             break;
100     }
101 
102     return ret;
103 }
104 
105 // Try to load a movie from the given url, if we haven't
106 // loaded it already.  Add it to our library on success, and
107 // return a pointer to it.
108 boost::intrusive_ptr<movie_definition>
makeMovie(const URL & url,const RunResources & runResources,const char * real_url,bool startLoaderThread,const std::string * postdata)109 MovieFactory::makeMovie(const URL& url, const RunResources& runResources,
110         const char* real_url, bool startLoaderThread,
111         const std::string* postdata)
112 {
113     boost::intrusive_ptr<movie_definition> mov;
114 
115     // Use real_url as label for cache if available
116     const std::string& cache_label = real_url ? URL(real_url).str() : url.str();
117 
118     // Is the movie already in the library? (don't check if we have post data!)
119     if (!postdata) {
120         if (movieLibrary.get(cache_label, &mov)) {
121             log_debug("Movie %s already in library", cache_label);
122             return mov;
123         }
124     }
125 
126     // Try to open a file under the filename, but DO NOT start
127     // the loader thread now to avoid IMPORT tag loaders from
128     // calling createMovie() again and NOT finding
129     // the just-created movie.
130     mov = createNonLibraryMovie(url, runResources, real_url, false, postdata);
131 
132     if (!mov) {
133         log_error(_("Couldn't load library movie '%s'"), url.str());
134         return mov;
135     }
136 
137     // Movie is good, add to the library, but not if we used POST
138     if (!postdata) {
139         movieLibrary.add(cache_label, mov.get());
140         log_debug("Movie %s (SWF%d) added to library",
141                 cache_label, mov->get_version());
142     }
143     else {
144         log_debug("Movie %s (SWF%d) NOT added to library (resulted from "
145                     "a POST)", cache_label, mov->get_version());
146     }
147 
148     /// Now complete the load if the movie is an SWF movie
149     //
150     /// This is a no-op except for SWF movies.
151     if (startLoaderThread) mov->completeLoad();
152 
153     return mov;
154 }
155 
156 void
clear()157 MovieFactory::clear()
158 {
159     movieLibrary.clear();
160 }
161 
162 namespace {
163 
164 /// Get type of file looking at first bytes
165 FileType
getFileType(IOChannel & in)166 getFileType(IOChannel& in)
167 {
168     in.seek(0);
169 
170     char buf[3];
171 
172     if (in.read(buf, 3) < 3) {
173         log_error(_("Can't read file header"));
174         in.seek(0);
175         return GNASH_FILETYPE_UNKNOWN;
176     }
177 
178     // This is the magic number {0xff, 0xd8, 0xff} for JPEG format files
179     if (std::equal(buf, buf + 3, "\xff\xd8\xff")) {
180         in.seek(0);
181         return GNASH_FILETYPE_JPEG;
182     }
183 
184     // This is the magic number for any PNG format file
185     // buf[3] == 'G' (we didn't read so far)
186     if (std::equal(buf, buf + 3, "\x89PN")) {
187         in.seek(0);
188         return GNASH_FILETYPE_PNG;
189     }
190 
191     // This is the magic number for any GIF format file
192     if (std::equal(buf, buf + 3, "GIF")) {
193         in.seek(0);
194         return GNASH_FILETYPE_GIF;
195     }
196 
197     // This is for SWF (FWS or CWS)
198     if (std::equal(buf, buf + 3, "FWS") || std::equal(buf, buf + 3, "CWS")) {
199         in.seek(0);
200         return GNASH_FILETYPE_SWF;
201     }
202 
203     // Take one guess at what this is. (It's an FLV-format file).
204     if (std::equal(buf, buf + 3, "FLV")) {
205         return GNASH_FILETYPE_FLV;
206     }
207 
208     // Check if it is an swf embedded in a player (.exe-file)
209     if (std::equal(buf, buf + 2, "MZ")) {
210 
211         if (in.read(buf, 3) < 3) {
212             log_error(_("Can't read 3 bytes after an MZ (.exe) header"));
213             in.seek(0);
214             return GNASH_FILETYPE_UNKNOWN;
215         }
216 
217         while ((buf[0]!='F' && buf[0]!='C') || buf[1]!='W' || buf[2]!='S') {
218             buf[0] = buf[1];
219             buf[1] = buf[2];
220             buf[2] = in.read_byte();
221             if (in.eof()) {
222                 log_error(_("Could not find SWF inside an .exe file"));
223                 in.seek(0);
224                 return GNASH_FILETYPE_UNKNOWN;
225             }
226         }
227         in.seek(in.tell() - static_cast<std::streamoff>(3));
228         return GNASH_FILETYPE_SWF;
229     }
230 
231     log_error(_("unknown file type, buffer is %c%c%c"), buf[0], buf[1], buf[2]);
232     return GNASH_FILETYPE_UNKNOWN;
233 }
234 
235 // Create a SWFMovieDefinition from an SWF stream
236 // NOTE: this method assumes this *is* an SWF stream
237 boost::intrusive_ptr<SWFMovieDefinition>
createSWFMovie(std::unique_ptr<IOChannel> in,const std::string & url,const RunResources & runResources,bool startLoaderThread)238 createSWFMovie(std::unique_ptr<IOChannel> in, const std::string& url,
239         const RunResources& runResources, bool startLoaderThread)
240 {
241 
242     boost::intrusive_ptr<SWFMovieDefinition> m = new SWFMovieDefinition(runResources);
243 
244     const std::string& absURL = URL(url).str();
245 
246     if (!m->readHeader(std::move(in), absURL)) return nullptr;
247     if (startLoaderThread && !m->completeLoad()) return nullptr;
248 
249     return m;
250 }
251 
252 // Create a movie_definition from an image format stream
253 // NOTE: this method assumes this *is* the format described in the
254 // FileType type
255 // TODO: The pp won't display PNGs for SWF7 or below.
256 boost::intrusive_ptr<BitmapMovieDefinition>
createBitmapMovie(std::unique_ptr<IOChannel> in,const std::string & url,const RunResources & r,FileType type)257 createBitmapMovie(std::unique_ptr<IOChannel> in, const std::string& url,
258         const RunResources& r, FileType type)
259 {
260     assert (in.get());
261 
262     boost::intrusive_ptr<BitmapMovieDefinition> ret;
263 
264     // readImageData takes a shared pointer because JPEG streams sometimes need
265     // to transfer ownership.
266     std::unique_ptr<IOChannel> imageData(in.release());
267 
268     try {
269         std::unique_ptr<image::GnashImage> im(
270                 std::move(image::Input::readImageData(std::move(imageData), type)));
271 
272         if (!im.get()) {
273             log_error(_("Can't read image file from %s"), url);
274             return ret;
275         }
276 
277         Renderer* renderer = r.renderer();
278         ret = new BitmapMovieDefinition(std::move(im), renderer, url);
279         return ret;
280 
281     }
282     catch (const ParserException& e) {
283         log_error(_("Parsing error: %s"), e.what());
284         return ret;
285     }
286 
287 }
288 
289 boost::intrusive_ptr<movie_definition>
createNonLibraryMovie(const URL & url,const RunResources & runResources,const char * reset_url,bool startLoaderThread,const std::string * postdata)290 createNonLibraryMovie(const URL& url, const RunResources& runResources,
291         const char* reset_url, bool startLoaderThread,
292         const std::string* postdata)
293 {
294 
295     boost::intrusive_ptr<movie_definition> ret;
296 
297     std::unique_ptr<IOChannel> in;
298 
299     const StreamProvider& streamProvider = runResources.streamProvider();
300 
301     const RcInitFile& rcfile = RcInitFile::getDefaultInstance();
302 
303     if (postdata) {
304         in = streamProvider.getStream(url, *postdata, rcfile.saveLoadedMedia());
305     }
306     else in = streamProvider.getStream(url, rcfile.saveLoadedMedia());
307 
308     if (!in.get()) {
309         log_error(_("failed to open '%s'; can't create movie"), url);
310         return ret;
311     }
312 
313     if (in->bad()) {
314         log_error(_("streamProvider opener can't open '%s'"), url);
315         return ret;
316     }
317 
318     const std::string& movie_url = reset_url ? reset_url : url.str();
319     ret = MovieFactory::makeMovie(std::move(in), movie_url, runResources,
320             startLoaderThread);
321 
322     return ret;
323 
324 }
325 
326 } // unnamed namespace
327 
328 } // namespace gnash
329 
330 // Local Variables:
331 // mode: C++
332 // indent-tabs-mode: t
333 // End:
334