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