don't crash when loading images > 65kB (fixes #13529)
[swfdec.git] / libswfdec / swfdec_sprite_movie.c
blob1626ede1a6869f97c18caf25a0c43e707c515717
1 /* Swfdec
2 * Copyright (C) 2006 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <strings.h>
26 #include "swfdec_sprite_movie.h"
27 #include "swfdec_as_internal.h"
28 #include "swfdec_as_strings.h"
29 #include "swfdec_audio_event.h"
30 #include "swfdec_audio_stream.h"
31 #include "swfdec_debug.h"
32 #include "swfdec_filter.h"
33 #include "swfdec_graphic_movie.h"
34 #include "swfdec_player_internal.h"
35 #include "swfdec_ringbuffer.h"
36 #include "swfdec_script_internal.h"
37 #include "swfdec_sprite.h"
38 #include "swfdec_resource.h"
39 #include "swfdec_tag.h"
41 /*** SWFDEC_SPRITE_MOVIE ***/
43 static gboolean
44 swfdec_sprite_movie_remove_child (SwfdecMovie *movie, int depth)
46 SwfdecMovie *child = swfdec_movie_find (movie, depth);
48 if (child == NULL)
49 return FALSE;
51 swfdec_movie_remove (child);
52 return TRUE;
55 static int
56 swfdec_get_clipeventflags (SwfdecMovie *movie, SwfdecBits * bits)
58 if (SWFDEC_SWF_DECODER (movie->resource->decoder)->version <= 5) {
59 return swfdec_bits_get_u16 (bits);
60 } else {
61 return swfdec_bits_get_u32 (bits);
65 static gboolean
66 swfdec_sprite_movie_perform_old_place (SwfdecSpriteMovie *movie,
67 SwfdecBits *bits, guint tag)
69 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context);
70 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
71 SwfdecMovie *cur;
72 SwfdecSwfDecoder *dec;
73 int depth;
74 cairo_matrix_t transform;
75 gboolean has_ctrans;
76 SwfdecColorTransform ctrans;
77 guint id;
78 SwfdecGraphic *graphic;
80 dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
82 SWFDEC_LOG ("performing PlaceObject on movie %s", mov->name);
84 id = swfdec_bits_get_u16 (bits);
85 SWFDEC_LOG (" id = %d", id);
87 depth = swfdec_bits_get_u16 (bits);
88 if (depth >= 16384) {
89 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth);
91 SWFDEC_LOG (" depth = %d (=> %d)", depth, depth - 16384);
92 depth -= 16384;
94 swfdec_bits_get_matrix (bits, &transform, NULL);
95 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
96 transform.xx, transform.yx,
97 transform.xy, transform.yy,
98 transform.x0, transform.y0);
100 if (swfdec_bits_left (bits)) {
101 has_ctrans = TRUE;
102 swfdec_bits_get_color_transform (bits, &ctrans);
103 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
104 ctrans.ra, ctrans.rb,
105 ctrans.ga, ctrans.gb,
106 ctrans.ba, ctrans.bb,
107 ctrans.aa, ctrans.ab);
108 } else {
109 has_ctrans = FALSE;
112 /* 3) perform the actions depending on the set properties */
113 cur = swfdec_movie_find (mov, depth);
114 graphic = swfdec_swf_decoder_get_character (dec, id);
116 if (!SWFDEC_IS_GRAPHIC (graphic)) {
117 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
118 return FALSE;
121 cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, NULL);
122 swfdec_movie_set_static_properties (cur, &transform,
123 has_ctrans ? &ctrans : NULL, -1, 0, 0, NULL);
124 swfdec_movie_queue_script (cur, SWFDEC_EVENT_INITIALIZE);
125 swfdec_movie_queue_script (cur, SWFDEC_EVENT_CONSTRUCT);
126 swfdec_movie_queue_script (cur, SWFDEC_EVENT_LOAD);
127 swfdec_movie_initialize (cur);
129 return TRUE;
133 static gboolean
134 swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, guint tag)
136 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context);
137 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
138 SwfdecMovie *cur;
139 SwfdecSwfDecoder *dec;
140 gboolean has_clip_actions;
141 gboolean has_clip_depth;
142 gboolean has_name;
143 gboolean has_ratio;
144 gboolean has_ctrans;
145 gboolean has_transform;
146 gboolean has_character;
147 gboolean move;
148 int depth;
149 gboolean cache;
150 gboolean has_blend_mode = 0;
151 gboolean has_filter = 0;
152 int clip_depth;
153 cairo_matrix_t transform;
154 SwfdecColorTransform ctrans;
155 guint ratio, id, version;
156 SwfdecEventList *events;
157 const char *name;
158 guint blend_mode;
159 SwfdecGraphic *graphic;
161 dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
162 version = dec->version;
164 /* 1) check which stuff is set */
165 has_clip_actions = swfdec_bits_getbit (bits);
166 has_clip_depth = swfdec_bits_getbit (bits);
167 has_name = swfdec_bits_getbit (bits);
168 has_ratio = swfdec_bits_getbit (bits);
169 has_ctrans = swfdec_bits_getbit (bits);
170 has_transform = swfdec_bits_getbit (bits);
171 has_character = swfdec_bits_getbit (bits);
172 move = swfdec_bits_getbit (bits);
174 SWFDEC_LOG ("performing PlaceObject%d on movie %s", tag == SWFDEC_TAG_PLACEOBJECT2 ? 2 : 3, mov->name);
175 SWFDEC_LOG (" has_clip_actions = %d", has_clip_actions);
176 SWFDEC_LOG (" has_clip_depth = %d", has_clip_depth);
177 SWFDEC_LOG (" has_name = %d", has_name);
178 SWFDEC_LOG (" has_ratio = %d", has_ratio);
179 SWFDEC_LOG (" has_ctrans = %d", has_ctrans);
180 SWFDEC_LOG (" has_transform = %d", has_transform);
181 SWFDEC_LOG (" has_character = %d", has_character);
182 SWFDEC_LOG (" move = %d", move);
184 if (tag == SWFDEC_TAG_PLACEOBJECT3) {
185 swfdec_bits_getbits (bits, 5);
186 cache = swfdec_bits_getbit (bits);
187 has_blend_mode = swfdec_bits_getbit (bits);
188 has_filter = swfdec_bits_getbit (bits);
189 SWFDEC_LOG (" cache = %d", cache);
190 SWFDEC_LOG (" has filter = %d", has_filter);
191 SWFDEC_LOG (" has blend mode = %d", has_blend_mode);
194 /* 2) read all properties */
195 depth = swfdec_bits_get_u16 (bits);
196 if (depth >= 16384) {
197 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth);
199 SWFDEC_LOG (" depth = %d (=> %d)", depth, depth - 16384);
200 depth -= 16384;
201 if (has_character) {
202 id = swfdec_bits_get_u16 (bits);
203 SWFDEC_LOG (" id = %d", id);
204 } else {
205 id = 0;
208 if (has_transform) {
209 swfdec_bits_get_matrix (bits, &transform, NULL);
210 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
211 transform.xx, transform.yx,
212 transform.xy, transform.yy,
213 transform.x0, transform.y0);
215 if (has_ctrans) {
216 swfdec_bits_get_color_transform (bits, &ctrans);
217 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
218 ctrans.ra, ctrans.rb,
219 ctrans.ga, ctrans.gb,
220 ctrans.ba, ctrans.bb,
221 ctrans.aa, ctrans.ab);
224 if (has_ratio) {
225 ratio = swfdec_bits_get_u16 (bits);
226 SWFDEC_LOG (" ratio = %d", ratio);
227 } else {
228 ratio = -1;
231 if (has_name) {
232 char *s = swfdec_bits_get_string (bits, version);
233 name = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), s);
234 SWFDEC_LOG (" name = %s", name);
235 } else {
236 name = NULL;
239 if (has_clip_depth) {
240 clip_depth = swfdec_bits_get_u16 (bits) - 16384;
241 SWFDEC_LOG (" clip_depth = %d (=> %d)", clip_depth + 16384, clip_depth);
242 } else {
243 clip_depth = 0;
246 if (has_filter) {
247 GSList *filters = swfdec_filter_parse (player, bits);
248 g_slist_free (filters);
251 if (has_blend_mode) {
252 blend_mode = swfdec_bits_get_u8 (bits);
253 SWFDEC_LOG (" blend mode = %u", blend_mode);
254 } else {
255 blend_mode = 0;
258 if (has_clip_actions) {
259 int reserved, clip_event_flags, event_flags, key_code;
260 char *script_name;
262 events = swfdec_event_list_new (player);
263 reserved = swfdec_bits_get_u16 (bits);
264 clip_event_flags = swfdec_get_clipeventflags (mov, bits);
266 if (name)
267 script_name = g_strdup (name);
268 else if (id)
269 script_name = g_strdup_printf ("Sprite%u", id);
270 else
271 script_name = g_strdup ("unknown");
272 while ((event_flags = swfdec_get_clipeventflags (mov, bits)) != 0) {
273 guint length = swfdec_bits_get_u32 (bits);
274 SwfdecBits action_bits;
276 swfdec_bits_init_bits (&action_bits, bits, length);
277 if (event_flags & (1<<SWFDEC_EVENT_KEY_PRESS))
278 key_code = swfdec_bits_get_u8 (&action_bits);
279 else
280 key_code = 0;
282 SWFDEC_INFO ("clip event with flags 0x%X, key code %d", event_flags, key_code);
283 #define SWFDEC_UNIMPLEMENTED_EVENTS \
284 ((1<< SWFDEC_EVENT_DATA) | (1<<SWFDEC_EVENT_KEY_PRESS))
285 if (event_flags & SWFDEC_UNIMPLEMENTED_EVENTS) {
286 SWFDEC_ERROR ("using non-implemented clip events %u", event_flags & SWFDEC_UNIMPLEMENTED_EVENTS);
288 swfdec_event_list_parse (events, &action_bits, version,
289 event_flags, key_code, script_name);
290 if (swfdec_bits_left (&action_bits)) {
291 SWFDEC_ERROR ("not all action data was parsed: %u bytes left",
292 swfdec_bits_left (&action_bits));
295 g_free (script_name);
296 } else {
297 events = NULL;
300 /* 3) perform the actions depending on the set properties */
301 cur = swfdec_movie_find (mov, depth);
302 graphic = swfdec_swf_decoder_get_character (dec, id);
303 if (move) {
304 if (cur == NULL) {
305 SWFDEC_INFO ("no movie at depth %d, ignoring move command", depth);
306 goto out;
308 if (graphic) {
309 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (cur);
310 if (klass->replace)
311 klass->replace (cur, graphic);
313 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
314 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
315 } else {
316 if (cur != NULL && version > 5) {
317 SWFDEC_INFO ("depth %d is already occupied by movie %s, not placing", depth, cur->name);
318 goto out;
320 if (!SWFDEC_IS_GRAPHIC (graphic)) {
321 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
322 if (events)
323 swfdec_event_list_free (events);
324 return FALSE;
326 cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, name);
327 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
328 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
329 swfdec_movie_queue_script (cur, SWFDEC_EVENT_INITIALIZE);
330 swfdec_movie_queue_script (cur, SWFDEC_EVENT_CONSTRUCT);
331 swfdec_movie_queue_script (cur, SWFDEC_EVENT_LOAD);
332 swfdec_movie_initialize (cur);
335 out:
336 if (events)
337 swfdec_event_list_free (events);
338 return TRUE;
341 static void
342 swfdec_sprite_movie_start_sound (SwfdecMovie *movie, SwfdecBits *bits)
344 SwfdecSoundChunk *chunk;
345 int id;
347 id = swfdec_bits_get_u16 (bits);
348 chunk = swfdec_sound_parse_chunk (SWFDEC_SWF_DECODER (movie->resource->decoder), bits, id);
349 if (chunk) {
350 SwfdecAudio *audio = swfdec_audio_event_new_from_chunk (SWFDEC_PLAYER (
351 SWFDEC_AS_OBJECT (movie)->context), chunk);
352 if (audio)
353 g_object_unref (audio);
357 static gboolean
358 swfdec_sprite_movie_perform_one_action (SwfdecSpriteMovie *movie, guint tag, SwfdecBuffer *buffer,
359 gboolean skip_scripts, gboolean first_time)
361 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
362 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov)->context);
363 SwfdecBits bits;
365 g_assert (mov->resource);
366 swfdec_bits_init (&bits, buffer);
368 SWFDEC_LOG ("%p: executing %uth tag %s in frame %u", movie, movie->next_action - 1,
369 swfdec_swf_decoder_get_tag_name (tag), movie->frame);
370 switch (tag) {
371 case SWFDEC_TAG_DOACTION:
372 SWFDEC_LOG ("SCRIPT action");
373 if (!skip_scripts) {
374 SwfdecScript *script = swfdec_swf_decoder_get_script (
375 SWFDEC_SWF_DECODER (mov->resource->decoder), buffer->data);
376 g_assert (script);
377 swfdec_player_add_action_script (player, mov, script, 2);
379 return TRUE;
380 case SWFDEC_TAG_PLACEOBJECT:
381 return swfdec_sprite_movie_perform_old_place (movie, &bits, tag);
382 case SWFDEC_TAG_PLACEOBJECT2:
383 case SWFDEC_TAG_PLACEOBJECT3:
384 return swfdec_sprite_movie_perform_place (movie, &bits, tag);
385 case SWFDEC_TAG_REMOVEOBJECT:
386 /* yes, this code is meant to be like this - the following u16 is the
387 * character id, that we don't care about, the rest is like RemoveObject2
389 swfdec_bits_get_u16 (&bits);
390 /* fall through */
391 case SWFDEC_TAG_REMOVEOBJECT2:
393 int depth = swfdec_bits_get_u16 (&bits);
394 SWFDEC_LOG ("REMOVE action: depth %d => %d", depth, depth - 16384);
395 depth -= 16384;
396 if (!swfdec_sprite_movie_remove_child (mov, depth))
397 SWFDEC_INFO ("could not remove, no child at depth %d", depth);
399 return TRUE;
400 case SWFDEC_TAG_STARTSOUND:
401 swfdec_sprite_movie_start_sound (mov, &bits);
402 return TRUE;
403 case SWFDEC_TAG_SHOWFRAME:
404 if (movie->frame < movie->n_frames) {
405 movie->frame++;
406 } else {
407 SWFDEC_ERROR ("too many ShowFrame tags");
409 return FALSE;
410 case SWFDEC_TAG_EXPORTASSETS:
412 SwfdecResource *resource = swfdec_movie_get_own_resource (mov);
413 guint i, count;
415 g_assert (resource); /* must hold, ExportAssets can only be in root movies */
416 if (!first_time)
417 return TRUE;
418 count = swfdec_bits_get_u16 (&bits);
419 SWFDEC_LOG ("exporting %u assets", count);
420 for (i = 0; i < count && swfdec_bits_left (&bits); i++) {
421 guint id;
422 SwfdecCharacter *object;
423 char *name;
424 id = swfdec_bits_get_u16 (&bits);
425 object = swfdec_swf_decoder_get_character (SWFDEC_SWF_DECODER (resource->decoder), id);
426 name = swfdec_bits_get_string (&bits, SWFDEC_AS_CONTEXT (player)->version);
427 if (object == NULL) {
428 SWFDEC_ERROR ("cannot export id %u as %s, id wasn't found", id, name);
429 } else if (name == NULL) {
430 SWFDEC_ERROR ("cannot export id %u, no name was given", id);
431 } else {
432 SWFDEC_LOG ("exporting %s %u as %s", G_OBJECT_TYPE_NAME (object), id, name);
433 swfdec_resource_add_export (resource, object, name);
435 g_free (name);
438 return TRUE;
439 case SWFDEC_TAG_DOINITACTION:
440 if (!first_time)
441 return TRUE;
442 if (!swfdec_movie_get_own_resource (mov)) {
443 SWFDEC_FIXME ("behavior of init actions in DefineSprite untested");
446 guint id;
447 SwfdecSprite *sprite;
448 char *name;
450 id = swfdec_bits_get_u16 (&bits);
451 SWFDEC_LOG ("InitAction");
452 SWFDEC_LOG (" id = %u", id);
453 sprite = swfdec_swf_decoder_get_character (SWFDEC_SWF_DECODER (mov->resource->decoder), id);
454 if (!SWFDEC_IS_SPRITE (sprite)) {
455 SWFDEC_ERROR ("character %u is not a sprite", id);
456 return TRUE;
458 if (sprite->init_action != NULL) {
459 SWFDEC_ERROR ("sprite %u already has an init action", id);
460 return TRUE;
462 name = g_strdup_printf ("InitAction %u", id);
463 sprite->init_action = swfdec_script_new_from_bits (&bits, name, SWFDEC_AS_CONTEXT (player)->version);
464 g_free (name);
465 if (sprite->init_action) {
466 swfdec_player_add_action_script (player, mov, sprite->init_action, 0);
469 return TRUE;
470 default:
471 g_assert_not_reached ();
472 return FALSE;
476 static gboolean
477 swfdec_movie_is_compatible (SwfdecMovie *movie, SwfdecMovie *with)
479 g_assert (movie->depth == with->depth);
481 if (movie->original_ratio != with->original_ratio)
482 return FALSE;
484 if (G_OBJECT_TYPE (movie) != G_OBJECT_TYPE (with))
485 return FALSE;
487 return TRUE;
490 static GList *
491 my_g_list_split (GList *list, GList *split)
493 GList *prev;
495 if (split == NULL)
496 return list;
498 prev = split->prev;
499 if (prev == NULL)
500 return NULL;
501 prev->next = NULL;
502 split->prev = NULL;
503 return list;
506 void
507 swfdec_sprite_movie_goto (SwfdecSpriteMovie *movie, guint goto_frame)
509 SwfdecMovie *mov;
510 SwfdecPlayer *player;
511 GList *old;
512 guint n;
514 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie));
516 mov = SWFDEC_MOVIE (movie);
517 /* lots of things where we've got nothing to do */
518 if (goto_frame == 0 || goto_frame > movie->n_frames ||
519 movie->sprite == NULL || mov->state >= SWFDEC_MOVIE_STATE_REMOVED || goto_frame == movie->frame)
520 return;
522 if (goto_frame > movie->sprite->parse_frame) {
523 SWFDEC_WARNING ("jumping to not-yet-loaded frame %u (loaded: %u/%u)",
524 goto_frame, movie->sprite->parse_frame, movie->sprite->n_frames);
525 return;
528 player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (movie)->context);
529 SWFDEC_LOG ("doing goto %u for %p %d", goto_frame, movie,
530 SWFDEC_CHARACTER (movie->sprite)->id);
532 SWFDEC_DEBUG ("performing goto %u -> %u for character %u",
533 movie->frame, goto_frame, SWFDEC_CHARACTER (movie->sprite)->id);
534 if (goto_frame < movie->frame) {
535 GList *walk;
536 movie->frame = 0;
537 for (walk = mov->list; walk &&
538 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) != SWFDEC_DEPTH_CLASS_TIMELINE;
539 walk = walk->next) {
540 /* do nothing */
542 old = walk;
543 mov->list = my_g_list_split (mov->list, old);
544 for (walk = old; walk &&
545 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) == SWFDEC_DEPTH_CLASS_TIMELINE;
546 walk = walk->next) {
547 /* do nothing */
549 old = my_g_list_split (old, walk);
550 mov->list = g_list_concat (mov->list, walk);
551 n = goto_frame;
552 movie->next_action = 0;
553 } else {
554 /* NB: this path is also taken on init */
555 old = NULL;
556 n = goto_frame - movie->frame;
558 while (n) {
559 guint tag;
560 gboolean first_time;
561 SwfdecBuffer *buffer;
562 if (!swfdec_sprite_get_action (movie->sprite, movie->next_action, &tag, &buffer))
563 break;
564 movie->next_action++;
565 if (movie->next_action > movie->max_action) {
566 first_time = TRUE;
567 movie->max_action = movie->next_action;
568 } else {
569 first_time = FALSE;
571 if (!swfdec_sprite_movie_perform_one_action (movie, tag, buffer, n > 1, first_time))
572 n--;
574 /* now try to copy eventual movies */
575 if (old) {
576 SwfdecMovie *prev, *cur;
577 GList *old_walk, *walk;
578 walk = mov->list;
579 old_walk = old;
580 if (!walk)
581 goto out;
582 cur = walk->data;
583 for (; old_walk; old_walk = old_walk->next) {
584 prev = old_walk->data;
585 while (cur->depth < prev->depth) {
586 walk = walk->next;
587 if (!walk)
588 goto out;
589 cur = walk->data;
591 if (cur->depth == prev->depth &&
592 swfdec_movie_is_compatible (prev, cur)) {
593 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (prev);
594 walk->data = prev;
595 /* FIXME: This merging stuff probably needs to be improved a _lot_ */
596 if (klass->replace)
597 klass->replace (prev, cur->graphic);
598 swfdec_movie_set_static_properties (prev, &cur->original_transform,
599 &cur->original_ctrans, cur->original_ratio, cur->clip_depth,
600 cur->blend_mode, cur->events);
601 swfdec_movie_destroy (cur);
602 cur = prev;
603 continue;
605 swfdec_movie_remove (prev);
607 out:
608 for (; old_walk; old_walk = old_walk->next) {
609 swfdec_movie_remove (old_walk->data);
611 g_list_free (old);
615 /*** MOVIE ***/
617 G_DEFINE_TYPE (SwfdecSpriteMovie, swfdec_sprite_movie, SWFDEC_TYPE_MOVIE)
619 static void
620 swfdec_sprite_movie_dispose (GObject *object)
622 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (object);
624 g_assert (movie->sound_stream == NULL);
626 G_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->dispose (object);
629 static void
630 swfdec_sprite_movie_init_movie (SwfdecMovie *mov)
632 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
634 g_assert (movie->frame == (guint) -1);
635 movie->frame = 0;
636 swfdec_sprite_movie_goto (movie, 1);
639 static void
640 swfdec_sprite_movie_add (SwfdecAsObject *object)
642 SwfdecPlayer *player = SWFDEC_PLAYER (object->context);
644 if (player->MovieClip)
645 swfdec_as_object_set_constructor (object, player->MovieClip);
647 SWFDEC_AS_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->add (object);
650 static void
651 swfdec_sprite_movie_iterate (SwfdecMovie *mov)
653 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
654 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov)->context);
655 guint goto_frame;
657 if (mov->state >= SWFDEC_MOVIE_STATE_REMOVED)
658 return;
660 if (movie->sprite && movie->frame == (guint) -1)
661 movie->frame = 0;
663 swfdec_player_add_action (player, mov, SWFDEC_EVENT_ENTER, 2);
664 if (movie->playing && movie->sprite != NULL) {
665 if (movie->frame == movie->n_frames)
666 goto_frame = 1;
667 else if (movie->sprite && movie->frame == movie->sprite->parse_frame)
668 goto_frame = movie->frame;
669 else
670 goto_frame = movie->frame + 1;
671 swfdec_sprite_movie_goto (movie, goto_frame);
675 /* FIXME: This function is a mess */
676 static gboolean
677 swfdec_sprite_movie_iterate_end (SwfdecMovie *mov)
679 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
680 SwfdecSpriteFrame *last;
681 SwfdecSpriteFrame *current;
682 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov)->context);
684 if (!SWFDEC_MOVIE_CLASS (swfdec_sprite_movie_parent_class)->iterate_end (mov)) {
685 g_assert (movie->sound_stream == NULL);
686 return FALSE;
689 if (movie->sprite == NULL)
690 return TRUE;
691 g_assert (movie->frame <= movie->n_frames);
692 if (movie->frame == 0)
693 return TRUE;
694 current = &movie->sprite->frames[movie->frame - 1];
696 /* then do the streaming thing */
697 if (current->sound_head == NULL ||
698 !movie->playing) {
699 if (movie->sound_stream) {
700 swfdec_audio_remove (movie->sound_stream);
701 g_object_unref (movie->sound_stream);
702 movie->sound_stream = NULL;
704 goto exit;
706 if (movie->sound_stream == NULL && current->sound_block == NULL)
707 goto exit;
708 SWFDEC_LOG ("iterating audio (from %u to %u)", movie->sound_frame, movie->frame);
709 if (movie->sound_frame + 1 != movie->frame)
710 goto new_decoder;
711 if (movie->sound_frame == (guint) -1)
712 goto new_decoder;
713 if (current->sound_head && movie->sound_stream == NULL)
714 goto new_decoder;
715 last = &movie->sprite->frames[movie->sound_frame];
716 if (last->sound_head != current->sound_head)
717 goto new_decoder;
718 exit:
719 movie->sound_frame = movie->frame;
720 return TRUE;
722 new_decoder:
723 if (movie->sound_stream) {
724 swfdec_audio_remove (movie->sound_stream);
725 g_object_unref (movie->sound_stream);
726 movie->sound_stream = NULL;
729 if (current->sound_block) {
730 movie->sound_stream = swfdec_audio_stream_new (player,
731 movie->sprite, movie->frame - 1);
732 movie->sound_frame = movie->frame;
734 return TRUE;
737 static void
738 swfdec_sprite_movie_finish_movie (SwfdecMovie *mov)
740 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
741 SwfdecPlayer *player = SWFDEC_PLAYER (SWFDEC_AS_OBJECT (mov)->context);
743 swfdec_player_remove_all_actions (player, mov);
744 if (movie->sound_stream) {
745 swfdec_audio_remove (movie->sound_stream);
746 g_object_unref (movie->sound_stream);
747 movie->sound_stream = NULL;
751 static void
752 swfdec_sprite_movie_mark (SwfdecAsObject *object)
754 GList *walk;
756 for (walk = SWFDEC_MOVIE (object)->list; walk; walk = walk->next) {
757 SwfdecAsObject *child = walk->data;
758 g_assert (child->properties != NULL);
759 swfdec_as_object_mark (child);
762 SWFDEC_AS_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->mark (object);
765 static void
766 swfdec_sprite_movie_class_init (SwfdecSpriteMovieClass * g_class)
768 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
769 SwfdecAsObjectClass *asobject_class = SWFDEC_AS_OBJECT_CLASS (g_class);
770 SwfdecMovieClass *movie_class = SWFDEC_MOVIE_CLASS (g_class);
772 object_class->dispose = swfdec_sprite_movie_dispose;
774 asobject_class->add = swfdec_sprite_movie_add;
775 asobject_class->mark = swfdec_sprite_movie_mark;
777 movie_class->init_movie = swfdec_sprite_movie_init_movie;
778 movie_class->finish_movie = swfdec_sprite_movie_finish_movie;
779 movie_class->iterate_start = swfdec_sprite_movie_iterate;
780 movie_class->iterate_end = swfdec_sprite_movie_iterate_end;
783 static void
784 swfdec_sprite_movie_init (SwfdecSpriteMovie * movie)
786 movie->playing = TRUE;
787 movie->frame = (guint) -1;
790 /* cute little hack */
791 extern void
792 swfdec_sprite_movie_clear (SwfdecAsContext *cx, SwfdecAsObject *object,
793 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval);
795 * swfdec_sprite_movie_unload:
796 * @movie: a #SwfdecMovie
798 * Clears all contents from the given movie. This means deleting all
799 * variables and removing all children movie clips.
801 void
802 swfdec_sprite_movie_unload (SwfdecSpriteMovie *movie)
804 SwfdecMovie *mov;
805 SwfdecAsValue hack;
807 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie));
809 mov = SWFDEC_MOVIE (movie);
810 swfdec_sprite_movie_clear (SWFDEC_AS_OBJECT (movie)->context,
811 SWFDEC_AS_OBJECT (movie), 0, NULL, &hack);
812 /* FIXME: destroy or unload? */
813 while (mov->list)
814 swfdec_movie_remove (mov->list->data);
815 swfdec_as_object_delete_all_variables (SWFDEC_AS_OBJECT (movie));
816 movie->frame = (guint) -1;
817 movie->n_frames = 0;
818 movie->next_action = 0;
819 movie->max_action = 0;
820 movie->sprite = NULL;
821 swfdec_movie_queue_update (SWFDEC_MOVIE (movie), SWFDEC_MOVIE_INVALID_EXTENTS);
825 * swfdec_sprite_movie_get_frames_loaded:
826 * @movie: a #SwfdecSpriteMovie
828 * Computes the number of loaded frames as used by the _framesloaded property
829 * or the WaitForFrame actions. If the @movie is fully loaded, this is the
830 * amount of total frames of the sprite it displays, or 0 if it has no sprite.
831 * If the movie is not fully loaded, it is the amount of frames that are
832 * completely loaded minus one. Welcome to the world of Flash.
834 * Returns: The number of loaded frames as reported by ActionScript.
837 swfdec_sprite_movie_get_frames_loaded (SwfdecSpriteMovie *movie)
839 SwfdecResource *resource;
840 SwfdecDecoder *dec;
842 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
844 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
845 if (resource == NULL) {
846 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
847 if (movie->sprite)
848 return movie->n_frames;
849 else
850 return 1;
852 dec = resource->decoder;
853 if (dec == NULL)
854 return -1;
855 if (dec->frames_loaded < dec->frames_total)
856 return dec->frames_loaded - 1;
857 return dec->frames_total;
861 swfdec_sprite_movie_get_frames_total (SwfdecSpriteMovie *movie)
863 SwfdecResource *resource;
864 SwfdecDecoder *dec;
866 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
868 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
869 if (resource == NULL) {
870 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
871 if (movie->sprite)
872 return movie->n_frames;
873 else
874 return 1;
876 dec = resource->decoder;
877 if (dec == NULL)
878 return 0;
879 return dec->frames_total;